@fedify/vocab 2.4.0-pr.936.41 → 2.4.1
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.
- package/deno.json +1 -1
- package/dist/mod.cjs +32014 -9323
- package/dist/mod.d.cts +4770 -91
- package/dist/mod.d.ts +4770 -91
- package/dist/mod.js +31907 -9223
- package/dist-tests/actor-D_QaCMro.mjs +276 -0
- package/dist-tests/actor.test.mjs +55 -49
- package/dist-tests/{actor-MBpzF3-D.mjs → esm-YWo4aQQe.mjs} +15 -211
- package/dist-tests/gateway.test.mjs +1906 -0
- package/dist-tests/lookup-BevjwZhB.mjs +480 -0
- package/dist-tests/lookup.test.mjs +10 -268
- package/dist-tests/object.yaml +15 -0
- package/dist-tests/portable-lookup.test.mjs +653 -0
- package/dist-tests/signed-representation.test.mjs +189 -0
- package/dist-tests/translation.test.mjs +404 -0
- package/dist-tests/translation.yaml +91 -0
- package/dist-tests/type.test.mjs +2 -2
- package/dist-tests/{vocab-Bjwctrxn.mjs → vocab-DaniQwU7.mjs} +31669 -9267
- package/dist-tests/vocab.test.mjs +901 -11
- package/package.json +5 -5
- package/src/__snapshots__/vocab.test.ts.snap +291 -15
- package/src/actor.test.ts +41 -40
- package/src/actor.ts +120 -1
- package/src/gateway.test.ts +2637 -0
- package/src/lookup.test.ts +6 -5
- package/src/lookup.ts +460 -33
- package/src/mod.ts +9 -0
- package/src/object.yaml +15 -0
- package/src/portable-lookup.test.ts +889 -0
- package/src/preprocessors.ts +1 -1
- package/src/signed-representation.test.ts +277 -0
- package/src/translation.test.ts +518 -0
- package/src/translation.yaml +91 -0
- package/src/vocab.test.ts +1219 -5
- /package/dist-tests/{type-_BlsfT3S.mjs → type-BMdjm4Eg.mjs} +0 -0
- /package/dist-tests/{utils-CQjrpUkt.mjs → utils-Df2odC27.mjs} +0 -0
package/src/actor.test.ts
CHANGED
|
@@ -99,45 +99,46 @@ test({
|
|
|
99
99
|
async fn(t) {
|
|
100
100
|
fetchMock.spyGlobal();
|
|
101
101
|
|
|
102
|
+
// Node validates addresses before fetch; public IP literals avoid DNS.
|
|
102
103
|
fetchMock.get(
|
|
103
|
-
"begin:https://
|
|
104
|
+
"begin:https://1.1.1.1/.well-known/webfinger?",
|
|
104
105
|
{
|
|
105
|
-
body: { subject: "acct:johndoe@
|
|
106
|
+
body: { subject: "acct:johndoe@1.1.1.1" },
|
|
106
107
|
headers: { "Content-Type": "application/jrd+json" },
|
|
107
108
|
},
|
|
108
109
|
);
|
|
109
110
|
|
|
110
|
-
const actorId = new URL("https://
|
|
111
|
+
const actorId = new URL("https://1.1.1.1/@john");
|
|
111
112
|
const actor = new Person({
|
|
112
113
|
id: actorId,
|
|
113
114
|
preferredUsername: "john",
|
|
114
115
|
});
|
|
115
116
|
|
|
116
117
|
await t.step("WebFinger subject", async () => {
|
|
117
|
-
deepStrictEqual(await getActorHandle(actor), "@johndoe@
|
|
118
|
+
deepStrictEqual(await getActorHandle(actor), "@johndoe@1.1.1.1");
|
|
118
119
|
deepStrictEqual(
|
|
119
120
|
await getActorHandle(actor, { trimLeadingAt: true }),
|
|
120
|
-
"johndoe@
|
|
121
|
+
"johndoe@1.1.1.1",
|
|
121
122
|
);
|
|
122
123
|
deepStrictEqual(
|
|
123
124
|
await getActorHandle(actorId),
|
|
124
|
-
"@johndoe@
|
|
125
|
+
"@johndoe@1.1.1.1",
|
|
125
126
|
);
|
|
126
127
|
deepStrictEqual(
|
|
127
128
|
await getActorHandle(actorId, { trimLeadingAt: true }),
|
|
128
|
-
"johndoe@
|
|
129
|
+
"johndoe@1.1.1.1",
|
|
129
130
|
);
|
|
130
131
|
});
|
|
131
132
|
|
|
132
133
|
fetchMock.removeRoutes();
|
|
133
134
|
fetchMock.get(
|
|
134
|
-
"begin:https://
|
|
135
|
+
"begin:https://1.1.1.1/.well-known/webfinger?",
|
|
135
136
|
{
|
|
136
137
|
body: {
|
|
137
|
-
subject: "https://
|
|
138
|
+
subject: "https://1.1.1.1/@john",
|
|
138
139
|
aliases: [
|
|
139
|
-
"acct:john@
|
|
140
|
-
"acct:johndoe@
|
|
140
|
+
"acct:john@8.8.8.8",
|
|
141
|
+
"acct:johndoe@1.1.1.1",
|
|
141
142
|
],
|
|
142
143
|
},
|
|
143
144
|
headers: { "Content-Type": "application/jrd+json" },
|
|
@@ -145,28 +146,28 @@ test({
|
|
|
145
146
|
);
|
|
146
147
|
|
|
147
148
|
await t.step("WebFinger aliases", async () => {
|
|
148
|
-
deepStrictEqual(await getActorHandle(actor), "@johndoe@
|
|
149
|
+
deepStrictEqual(await getActorHandle(actor), "@johndoe@1.1.1.1");
|
|
149
150
|
deepStrictEqual(
|
|
150
151
|
await getActorHandle(actor, { trimLeadingAt: true }),
|
|
151
|
-
"johndoe@
|
|
152
|
+
"johndoe@1.1.1.1",
|
|
152
153
|
);
|
|
153
154
|
deepStrictEqual(
|
|
154
155
|
await getActorHandle(actorId),
|
|
155
|
-
"@johndoe@
|
|
156
|
+
"@johndoe@1.1.1.1",
|
|
156
157
|
);
|
|
157
158
|
deepStrictEqual(
|
|
158
159
|
await getActorHandle(actorId, { trimLeadingAt: true }),
|
|
159
|
-
"johndoe@
|
|
160
|
+
"johndoe@1.1.1.1",
|
|
160
161
|
);
|
|
161
162
|
});
|
|
162
163
|
|
|
163
164
|
fetchMock.get(
|
|
164
|
-
"begin:https://
|
|
165
|
+
"begin:https://8.8.8.8/.well-known/webfinger?",
|
|
165
166
|
{
|
|
166
167
|
body: {
|
|
167
|
-
subject: "acct:john@
|
|
168
|
+
subject: "acct:john@8.8.8.8",
|
|
168
169
|
aliases: [
|
|
169
|
-
"https://
|
|
170
|
+
"https://1.1.1.1/@john",
|
|
170
171
|
],
|
|
171
172
|
},
|
|
172
173
|
headers: { "Content-Type": "application/jrd+json" },
|
|
@@ -174,18 +175,18 @@ test({
|
|
|
174
175
|
);
|
|
175
176
|
|
|
176
177
|
await t.step("cross-origin WebFinger resources", async () => {
|
|
177
|
-
deepStrictEqual(await getActorHandle(actor), "@john@
|
|
178
|
+
deepStrictEqual(await getActorHandle(actor), "@john@8.8.8.8");
|
|
178
179
|
});
|
|
179
180
|
|
|
180
181
|
fetchMock.removeRoutes();
|
|
181
182
|
fetchMock.get(
|
|
182
|
-
"begin:https://
|
|
183
|
+
"begin:https://1.1.1.1/.well-known/webfinger?",
|
|
183
184
|
{ status: 404 },
|
|
184
185
|
);
|
|
185
186
|
|
|
186
187
|
await t.step("no WebFinger", async () => {
|
|
187
|
-
deepStrictEqual(await getActorHandle(actor), "@john@
|
|
188
|
-
rejects(() => getActorHandle(actorId), TypeError);
|
|
188
|
+
deepStrictEqual(await getActorHandle(actor), "@john@1.1.1.1");
|
|
189
|
+
await rejects(() => getActorHandle(actorId), TypeError);
|
|
189
190
|
});
|
|
190
191
|
|
|
191
192
|
fetchMock.hardReset();
|
|
@@ -197,7 +198,7 @@ test("getActorHandle() records activitypub.actor.discovery counter", {
|
|
|
197
198
|
}, async (t) => {
|
|
198
199
|
fetchMock.spyGlobal();
|
|
199
200
|
try {
|
|
200
|
-
const actorId = new URL("https://
|
|
201
|
+
const actorId = new URL("https://1.1.1.1/@john");
|
|
201
202
|
const actor = new Person({
|
|
202
203
|
id: actorId,
|
|
203
204
|
preferredUsername: "john",
|
|
@@ -206,15 +207,15 @@ test("getActorHandle() records activitypub.actor.discovery counter", {
|
|
|
206
207
|
await t.step("records result=resolved on a successful lookup", async () => {
|
|
207
208
|
fetchMock.removeRoutes();
|
|
208
209
|
fetchMock.get(
|
|
209
|
-
"begin:https://
|
|
210
|
+
"begin:https://1.1.1.1/.well-known/webfinger?",
|
|
210
211
|
{
|
|
211
|
-
body: { subject: "acct:johndoe@
|
|
212
|
+
body: { subject: "acct:johndoe@1.1.1.1" },
|
|
212
213
|
headers: { "Content-Type": "application/jrd+json" },
|
|
213
214
|
},
|
|
214
215
|
);
|
|
215
216
|
const [meterProvider, recorder] = createTestMeterProvider();
|
|
216
217
|
const handle = await getActorHandle(actor, { meterProvider });
|
|
217
|
-
deepStrictEqual(handle, "@johndoe@
|
|
218
|
+
deepStrictEqual(handle, "@johndoe@1.1.1.1");
|
|
218
219
|
|
|
219
220
|
const counters = recorder.getMeasurements(
|
|
220
221
|
"activitypub.actor.discovery",
|
|
@@ -228,7 +229,7 @@ test("getActorHandle() records activitypub.actor.discovery counter", {
|
|
|
228
229
|
);
|
|
229
230
|
deepStrictEqual(
|
|
230
231
|
counters[0].attributes["activitypub.remote.host"],
|
|
231
|
-
"
|
|
232
|
+
"1.1.1.1",
|
|
232
233
|
);
|
|
233
234
|
|
|
234
235
|
const durations = recorder.getMeasurements(
|
|
@@ -248,12 +249,12 @@ test("getActorHandle() records activitypub.actor.discovery counter", {
|
|
|
248
249
|
async () => {
|
|
249
250
|
fetchMock.removeRoutes();
|
|
250
251
|
fetchMock.get(
|
|
251
|
-
"begin:https://
|
|
252
|
+
"begin:https://1.1.1.1/.well-known/webfinger?",
|
|
252
253
|
{ status: 404 },
|
|
253
254
|
);
|
|
254
255
|
const [meterProvider, recorder] = createTestMeterProvider();
|
|
255
256
|
const handle = await getActorHandle(actor, { meterProvider });
|
|
256
|
-
deepStrictEqual(handle, "@john@
|
|
257
|
+
deepStrictEqual(handle, "@john@1.1.1.1");
|
|
257
258
|
const counter = recorder.getMeasurement("activitypub.actor.discovery");
|
|
258
259
|
ok(counter != null);
|
|
259
260
|
deepStrictEqual(
|
|
@@ -268,7 +269,7 @@ test("getActorHandle() records activitypub.actor.discovery counter", {
|
|
|
268
269
|
async () => {
|
|
269
270
|
fetchMock.removeRoutes();
|
|
270
271
|
fetchMock.get(
|
|
271
|
-
"begin:https://
|
|
272
|
+
"begin:https://1.1.1.1/.well-known/webfinger?",
|
|
272
273
|
{ status: 404 },
|
|
273
274
|
);
|
|
274
275
|
const [meterProvider, recorder] = createTestMeterProvider();
|
|
@@ -284,7 +285,7 @@ test("getActorHandle() records activitypub.actor.discovery counter", {
|
|
|
284
285
|
);
|
|
285
286
|
deepStrictEqual(
|
|
286
287
|
counter.attributes["activitypub.remote.host"],
|
|
287
|
-
"
|
|
288
|
+
"1.1.1.1",
|
|
288
289
|
);
|
|
289
290
|
const duration = recorder.getMeasurement(
|
|
290
291
|
"activitypub.actor.discovery.duration",
|
|
@@ -302,13 +303,13 @@ test("getActorHandle() records activitypub.actor.discovery counter", {
|
|
|
302
303
|
async () => {
|
|
303
304
|
fetchMock.removeRoutes();
|
|
304
305
|
fetchMock.get(
|
|
305
|
-
"begin:https://
|
|
306
|
+
"begin:https://1.1.1.1:8443/.well-known/webfinger?",
|
|
306
307
|
{ status: 404 },
|
|
307
308
|
);
|
|
308
309
|
const [meterProvider, recorder] = createTestMeterProvider();
|
|
309
310
|
await rejects(
|
|
310
311
|
() =>
|
|
311
|
-
getActorHandle(new URL("https://
|
|
312
|
+
getActorHandle(new URL("https://1.1.1.1:8443/@john"), {
|
|
312
313
|
meterProvider,
|
|
313
314
|
}),
|
|
314
315
|
TypeError,
|
|
@@ -317,7 +318,7 @@ test("getActorHandle() records activitypub.actor.discovery counter", {
|
|
|
317
318
|
ok(counter != null);
|
|
318
319
|
deepStrictEqual(
|
|
319
320
|
counter.attributes["activitypub.remote.host"],
|
|
320
|
-
"
|
|
321
|
+
"1.1.1.1:8443",
|
|
321
322
|
);
|
|
322
323
|
},
|
|
323
324
|
);
|
|
@@ -332,10 +333,10 @@ test("getActorHandle() records activitypub.actor.discovery counter", {
|
|
|
332
333
|
// `error` rather than `not_found`.
|
|
333
334
|
fetchMock.removeRoutes();
|
|
334
335
|
fetchMock.get(
|
|
335
|
-
"begin:https://
|
|
336
|
+
"begin:https://1.1.1.1/.well-known/webfinger?",
|
|
336
337
|
{
|
|
337
338
|
body: {
|
|
338
|
-
subject: "https://
|
|
339
|
+
subject: "https://1.1.1.1/@john",
|
|
339
340
|
aliases: ["acct:john@["],
|
|
340
341
|
},
|
|
341
342
|
headers: { "Content-Type": "application/jrd+json" },
|
|
@@ -362,9 +363,9 @@ test("getActorHandle() records activitypub.actor.discovery counter", {
|
|
|
362
363
|
async () => {
|
|
363
364
|
fetchMock.removeRoutes();
|
|
364
365
|
fetchMock.get(
|
|
365
|
-
"begin:https://
|
|
366
|
+
"begin:https://1.1.1.1/.well-known/webfinger?",
|
|
366
367
|
{
|
|
367
|
-
body: { subject: "acct:johndoe@
|
|
368
|
+
body: { subject: "acct:johndoe@1.1.1.1" },
|
|
368
369
|
headers: { "Content-Type": "application/jrd+json" },
|
|
369
370
|
},
|
|
370
371
|
);
|
|
@@ -384,9 +385,9 @@ test("getActorHandle() records activitypub.actor.discovery counter", {
|
|
|
384
385
|
async () => {
|
|
385
386
|
fetchMock.removeRoutes();
|
|
386
387
|
fetchMock.get(
|
|
387
|
-
"begin:https://
|
|
388
|
+
"begin:https://1.1.1.1/.well-known/webfinger?",
|
|
388
389
|
{
|
|
389
|
-
body: { subject: "acct:johndoe@
|
|
390
|
+
body: { subject: "acct:johndoe@1.1.1.1" },
|
|
390
391
|
headers: { "Content-Type": "application/jrd+json" },
|
|
391
392
|
},
|
|
392
393
|
);
|
package/src/actor.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { GetUserAgentOptions } from "@fedify/vocab-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
canonicalizePortableUri,
|
|
4
|
+
formatIri,
|
|
5
|
+
fromCompatibleEf61Id,
|
|
6
|
+
isGatewayUrl,
|
|
7
|
+
} from "@fedify/vocab-runtime";
|
|
8
|
+
import { lookupWebFinger, type ResourceDescriptor } from "@fedify/webfinger";
|
|
3
9
|
import {
|
|
4
10
|
type Attributes,
|
|
5
11
|
type Counter,
|
|
@@ -93,6 +99,11 @@ function getActorDiscoveryRemoteHost(
|
|
|
93
99
|
): string | undefined {
|
|
94
100
|
const id = actor instanceof URL ? actor : actor.id;
|
|
95
101
|
if (id == null) return undefined;
|
|
102
|
+
// The authority of a portable ID is a DID, not a host; the handle of
|
|
103
|
+
// a portable actor comes from its first gateway:
|
|
104
|
+
if (isPortableActorId(id)) {
|
|
105
|
+
return actor instanceof URL ? undefined : actor.gateway?.host;
|
|
106
|
+
}
|
|
96
107
|
return id.host === "" ? undefined : id.host;
|
|
97
108
|
}
|
|
98
109
|
|
|
@@ -234,6 +245,17 @@ export interface GetActorHandleOptions extends NormalizeActorHandleOptions {
|
|
|
234
245
|
* await getActorHandle(new URL("https://fosstodon.org/users/hongminhee"));
|
|
235
246
|
* ```
|
|
236
247
|
*
|
|
248
|
+
* For an [FEP-ef61] portable actor, whose ID is an `ap:` or `ap+ef61:` URI,
|
|
249
|
+
* the domain is taken from the first gateway in the actor's `gateways` rather
|
|
250
|
+
* than from its ID. The handle is returned only if its WebFinger response
|
|
251
|
+
* links back to the actor, since the actor's `gateways` are claimed by the
|
|
252
|
+
* actor itself; a failed WebFinger lookup is not enough information to get
|
|
253
|
+
* the handle. A portable actor without `gateways` or
|
|
254
|
+
* `preferredUsername`, or a portable actor URI (which does not tell its
|
|
255
|
+
* gateways), is not supported.
|
|
256
|
+
*
|
|
257
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
258
|
+
*
|
|
237
259
|
* @param actor The actor or actor URI to get the handle from.
|
|
238
260
|
* @param options The extra options for getting the actor handle.
|
|
239
261
|
* @returns The actor handle. It starts with `@` and is followed by the
|
|
@@ -302,6 +324,10 @@ async function getActorHandleInternal(
|
|
|
302
324
|
options: GetActorHandleOptions = {},
|
|
303
325
|
): Promise<`@${string}@${string}` | `${string}@${string}`> {
|
|
304
326
|
const actorId = actor instanceof URL ? actor : actor.id;
|
|
327
|
+
if (actorId != null && isPortableActorId(actorId)) {
|
|
328
|
+
if (actor instanceof URL) throw new ActorHandleNotFoundError();
|
|
329
|
+
return await getPortableActorHandle(actor, actorId, options);
|
|
330
|
+
}
|
|
305
331
|
if (actorId != null) {
|
|
306
332
|
const result = await lookupWebFinger(actorId, {
|
|
307
333
|
userAgent: options.userAgent,
|
|
@@ -344,6 +370,89 @@ async function getActorHandleInternal(
|
|
|
344
370
|
throw new ActorHandleNotFoundError();
|
|
345
371
|
}
|
|
346
372
|
|
|
373
|
+
function isPortableActorId(id: URL): boolean {
|
|
374
|
+
return id.protocol === "ap:" || id.protocol === "ap+ef61:";
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* The reverse discovery of an FEP-ef61 portable actor's WebFinger address,
|
|
379
|
+
* which takes the domain from the first gateway in its `gateways`.
|
|
380
|
+
*/
|
|
381
|
+
async function getPortableActorHandle(
|
|
382
|
+
actor: Actor,
|
|
383
|
+
actorId: URL,
|
|
384
|
+
options: GetActorHandleOptions,
|
|
385
|
+
): Promise<`@${string}@${string}` | `${string}@${string}`> {
|
|
386
|
+
const gateway = actor.gateway;
|
|
387
|
+
const username = actor.preferredUsername?.toString();
|
|
388
|
+
if (gateway == null || !isGatewayUrl(gateway) || username == null) {
|
|
389
|
+
throw new ActorHandleNotFoundError();
|
|
390
|
+
}
|
|
391
|
+
const canonicalId = canonicalizePortableUri(formatIri(actorId));
|
|
392
|
+
const handle = `acct:${username}@${gateway.host}`;
|
|
393
|
+
const result = await lookupWebFinger(handle, {
|
|
394
|
+
userAgent: options.userAgent,
|
|
395
|
+
tracerProvider: options.tracerProvider,
|
|
396
|
+
meterProvider: options.meterProvider,
|
|
397
|
+
});
|
|
398
|
+
// Unlike an ordinary actor, whose ID vouches for its host, a portable
|
|
399
|
+
// actor's gateways are claimed by the actor itself, so there is no
|
|
400
|
+
// fallback when the gateway does not link back to the actor:
|
|
401
|
+
if (result == null || !linksToPortableActor(result, canonicalId)) {
|
|
402
|
+
throw new ActorHandleNotFoundError();
|
|
403
|
+
}
|
|
404
|
+
// The subject is the canonical address when it differs from the queried
|
|
405
|
+
// one; follow it once if it also links back to the actor:
|
|
406
|
+
const subject = result.subject?.match(/^acct:([^@]+)@([^@]+)$/);
|
|
407
|
+
if (subject != null && result.subject !== handle) {
|
|
408
|
+
const subjectResult = await lookupWebFinger(result.subject!, {
|
|
409
|
+
userAgent: options.userAgent,
|
|
410
|
+
tracerProvider: options.tracerProvider,
|
|
411
|
+
meterProvider: options.meterProvider,
|
|
412
|
+
});
|
|
413
|
+
if (
|
|
414
|
+
subjectResult != null && linksToPortableActor(subjectResult, canonicalId)
|
|
415
|
+
) {
|
|
416
|
+
return normalizeActorHandle(`@${subject[1]}@${subject[2]}`, options);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return normalizeActorHandle(`@${username}@${gateway.host}`, options);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Checks if the first ActivityStreams `self` link of a WebFinger response
|
|
424
|
+
* identifies the portable actor, either by its portable ID or by
|
|
425
|
+
* a compatible identifier.
|
|
426
|
+
*/
|
|
427
|
+
function linksToPortableActor(
|
|
428
|
+
jrd: ResourceDescriptor,
|
|
429
|
+
canonicalId: string,
|
|
430
|
+
): boolean {
|
|
431
|
+
const link = jrd.links?.find((l) =>
|
|
432
|
+
l.rel === "self" && l.href != null &&
|
|
433
|
+
(l.type === "application/activity+json" ||
|
|
434
|
+
l.type?.match(
|
|
435
|
+
/application\/ld\+json;\s*profile="https:\/\/www.w3.org\/ns\/activitystreams"/,
|
|
436
|
+
) != null)
|
|
437
|
+
);
|
|
438
|
+
if (link?.href == null) return false;
|
|
439
|
+
try {
|
|
440
|
+
let id: string;
|
|
441
|
+
if (/^ap(?:\+ef61)?:/i.test(link.href)) id = link.href;
|
|
442
|
+
else {
|
|
443
|
+
const portable = URL.canParse(link.href)
|
|
444
|
+
? fromCompatibleEf61Id(link.href)
|
|
445
|
+
: null;
|
|
446
|
+
if (portable == null) return false;
|
|
447
|
+
id = formatIri(portable);
|
|
448
|
+
}
|
|
449
|
+
return canonicalizePortableUri(id) === canonicalId;
|
|
450
|
+
} catch (error) {
|
|
451
|
+
if (error instanceof TypeError) return false;
|
|
452
|
+
throw error;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
347
456
|
async function verifyCrossOriginActorHandle(
|
|
348
457
|
actorId: string,
|
|
349
458
|
alias: string,
|
|
@@ -432,4 +541,14 @@ export interface Recipient {
|
|
|
432
541
|
*/
|
|
433
542
|
readonly sharedInbox: URL | null;
|
|
434
543
|
} | null;
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* The [FEP-ef61] gateways of the actor, in order, if it is a portable actor.
|
|
547
|
+
* Activities to a portable inbox, i.e., an `ap:` or `ap+ef61:` URI, are
|
|
548
|
+
* delivered to the inbox's compatible identifier on one of these gateways.
|
|
549
|
+
*
|
|
550
|
+
* [FEP-ef61]: https://w3id.org/fep/ef61
|
|
551
|
+
* @since 2.4.0
|
|
552
|
+
*/
|
|
553
|
+
readonly gateways?: readonly URL[];
|
|
435
554
|
}
|