@fedify/vocab 2.4.0-dev.1599 → 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.
- package/deno.json +1 -1
- package/dist/mod.cjs +48 -2
- package/dist/mod.d.cts +10 -0
- package/dist/mod.d.ts +10 -0
- package/dist/mod.js +49 -3
- package/dist-tests/{actor-NhcklZK0.mjs → actor-COnJgcOw.mjs} +2 -2
- package/dist-tests/actor.test.mjs +2 -2
- package/dist-tests/endpoints.yaml +14 -0
- package/dist-tests/lookup.test.mjs +71 -4
- package/dist-tests/type.test.mjs +1 -1
- package/dist-tests/{vocab-BQ8y6QS7.mjs → vocab-OU5GYbNg.mjs} +47 -1
- package/dist-tests/vocab.test.mjs +81 -2
- package/package.json +4 -4
- package/src/__snapshots__/vocab.test.ts.snap +6 -3
- package/src/endpoints.yaml +14 -0
- package/src/lookup.test.ts +121 -0
- package/src/lookup.ts +3 -1
- package/src/vocab.test.ts +128 -0
package/deno.json
CHANGED
package/dist/mod.cjs
CHANGED
|
@@ -34,7 +34,7 @@ let _fedify_vocab_runtime_temporal = require("@fedify/vocab-runtime/temporal");
|
|
|
34
34
|
let es_toolkit = require("es-toolkit");
|
|
35
35
|
//#region deno.json
|
|
36
36
|
var name = "@fedify/vocab";
|
|
37
|
-
var version = "2.4.0-dev.
|
|
37
|
+
var version = "2.4.0-dev.1618+58858243";
|
|
38
38
|
//#endregion
|
|
39
39
|
//#region src/type.ts
|
|
40
40
|
function getTypeId(object) {
|
|
@@ -3755,6 +3755,7 @@ const PORTABLE_IRI_KEYS = new Set([
|
|
|
3755
3755
|
"https://www.w3.org/ns/activitystreams#target",
|
|
3756
3756
|
"https://www.w3.org/ns/activitystreams#to",
|
|
3757
3757
|
"https://www.w3.org/ns/activitystreams#units",
|
|
3758
|
+
"https://www.w3.org/ns/activitystreams#uploadMedia",
|
|
3758
3759
|
"https://www.w3.org/ns/activitystreams#url",
|
|
3759
3760
|
"https://www.w3.org/ns/did#service",
|
|
3760
3761
|
"https://www.w3.org/ns/did#serviceEndpoint",
|
|
@@ -3816,6 +3817,7 @@ const PORTABLE_IRI_KEYS = new Set([
|
|
|
3816
3817
|
"target",
|
|
3817
3818
|
"to",
|
|
3818
3819
|
"units",
|
|
3820
|
+
"uploadMedia",
|
|
3819
3821
|
"url"
|
|
3820
3822
|
]);
|
|
3821
3823
|
/** Describes an object of any kind. The Object type serves as the base type for
|
|
@@ -32208,6 +32210,7 @@ var Endpoints = class {
|
|
|
32208
32210
|
#_8Bx9qN8oU7Bpt2xi6khaxWp1gMr_provideClientKey = [];
|
|
32209
32211
|
#_3dU7PMVQZJpsCpo2F4RQXxBXdPmS_signClientKey = [];
|
|
32210
32212
|
#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = [];
|
|
32213
|
+
#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [];
|
|
32211
32214
|
/**
|
|
32212
32215
|
* Constructs a new instance of Endpoints with the given values.
|
|
32213
32216
|
* @param values The values to initialize the instance with.
|
|
@@ -32234,6 +32237,8 @@ var Endpoints = class {
|
|
|
32234
32237
|
else throw new TypeError("The signClientKey must be of type URL.");
|
|
32235
32238
|
if ("sharedInbox" in values && values.sharedInbox != null) if (values.sharedInbox instanceof URL) this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = [values.sharedInbox];
|
|
32236
32239
|
else throw new TypeError("The sharedInbox must be of type URL.");
|
|
32240
|
+
if ("uploadMedia" in values && values.uploadMedia != null) if (values.uploadMedia instanceof URL) this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [values.uploadMedia];
|
|
32241
|
+
else throw new TypeError("The uploadMedia must be of type URL.");
|
|
32237
32242
|
}
|
|
32238
32243
|
/**
|
|
32239
32244
|
* Clones this instance, optionally updating it with the given values.
|
|
@@ -32268,6 +32273,9 @@ var Endpoints = class {
|
|
|
32268
32273
|
clone.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox;
|
|
32269
32274
|
if ("sharedInbox" in values && values.sharedInbox != null) if (values.sharedInbox instanceof URL) clone.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = [values.sharedInbox];
|
|
32270
32275
|
else throw new TypeError("The sharedInbox must be of type URL.");
|
|
32276
|
+
clone.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia;
|
|
32277
|
+
if ("uploadMedia" in values && values.uploadMedia != null) if (values.uploadMedia instanceof URL) clone.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [values.uploadMedia];
|
|
32278
|
+
else throw new TypeError("The uploadMedia must be of type URL.");
|
|
32271
32279
|
return clone;
|
|
32272
32280
|
}
|
|
32273
32281
|
/** Endpoint URI so this actor's clients may access remote ActivityStreams
|
|
@@ -32335,6 +32343,18 @@ var Endpoints = class {
|
|
|
32335
32343
|
if (this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox.length < 1) return null;
|
|
32336
32344
|
return this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox[0];
|
|
32337
32345
|
}
|
|
32346
|
+
/** Upload endpoint URI for this user for binary data, such as an image or
|
|
32347
|
+
* video, as described in the [ActivityPub Media
|
|
32348
|
+
* Upload](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload) extension.
|
|
32349
|
+
* Clients `POST` a `multipart/form-data` request containing a `file` part
|
|
32350
|
+
* (the binary payload) and an `object` part (an ActivityStreams object shell)
|
|
32351
|
+
* to this endpoint.
|
|
32352
|
+
*/
|
|
32353
|
+
get uploadMedia() {
|
|
32354
|
+
if (this._warning != null) (0, _logtape_logtape.getLogger)(this._warning.category).warn(this._warning.message, this._warning.values);
|
|
32355
|
+
if (this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.length < 1) return null;
|
|
32356
|
+
return this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia[0];
|
|
32357
|
+
}
|
|
32338
32358
|
/**
|
|
32339
32359
|
* Converts this object to a JSON-LD structure.
|
|
32340
32360
|
* @param options The options to use.
|
|
@@ -32390,6 +32410,12 @@ var Endpoints = class {
|
|
|
32390
32410
|
compactItems.push(item);
|
|
32391
32411
|
}
|
|
32392
32412
|
if (compactItems.length > 0) result["sharedInbox"] = compactItems.length > 1 ? compactItems : compactItems[0];
|
|
32413
|
+
compactItems = [];
|
|
32414
|
+
for (const v of this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia) {
|
|
32415
|
+
const item = (0, _fedify_vocab_runtime.formatIri)(v);
|
|
32416
|
+
compactItems.push(item);
|
|
32417
|
+
}
|
|
32418
|
+
if (compactItems.length > 0) result["uploadMedia"] = compactItems.length > 1 ? compactItems : compactItems[0];
|
|
32393
32419
|
if (this.id != null) result["id"] = (0, _fedify_vocab_runtime.formatIri)(this.id);
|
|
32394
32420
|
result["@context"] = "https://www.w3.org/ns/activitystreams";
|
|
32395
32421
|
return result;
|
|
@@ -32432,6 +32458,12 @@ var Endpoints = class {
|
|
|
32432
32458
|
array.push(element);
|
|
32433
32459
|
}
|
|
32434
32460
|
if (array.length > 0) values["https://www.w3.org/ns/activitystreams#sharedInbox"] = array;
|
|
32461
|
+
array = [];
|
|
32462
|
+
for (const v of this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia) {
|
|
32463
|
+
const element = { "@id": (0, _fedify_vocab_runtime.formatIri)(v) };
|
|
32464
|
+
array.push(element);
|
|
32465
|
+
}
|
|
32466
|
+
if (array.length > 0) values["https://www.w3.org/ns/activitystreams#uploadMedia"] = array;
|
|
32435
32467
|
if (this.id != null) values["@id"] = (0, _fedify_vocab_runtime.formatIri)(this.id);
|
|
32436
32468
|
if (options.format === "expand") return await _fedify_vocab_runtime_jsonld.default.expand(values, { documentLoader: options.contextLoader });
|
|
32437
32469
|
const docContext = options.context ?? "https://www.w3.org/ns/activitystreams";
|
|
@@ -32557,6 +32589,15 @@ var Endpoints = class {
|
|
|
32557
32589
|
_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox.push(decoded);
|
|
32558
32590
|
}
|
|
32559
32591
|
instance.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = _3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox;
|
|
32592
|
+
const _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [];
|
|
32593
|
+
let _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array = values["https://www.w3.org/ns/activitystreams#uploadMedia"];
|
|
32594
|
+
for (const v of _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array == null ? [] : _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array.length === 1 && "@list" in _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array[0] ? _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array[0]["@list"] : _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array) {
|
|
32595
|
+
if (v == null) continue;
|
|
32596
|
+
const decoded = (0, _fedify_vocab_runtime.parseIri)(v["@id"], options.baseUrl);
|
|
32597
|
+
if (typeof decoded === "undefined") continue;
|
|
32598
|
+
_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.push(decoded);
|
|
32599
|
+
}
|
|
32600
|
+
instance.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia;
|
|
32560
32601
|
if (!("_fromSubclass" in options) || !options._fromSubclass) try {
|
|
32561
32602
|
if (cacheJsonLd != null && cacheJsonLd !== expanded) {
|
|
32562
32603
|
const compactArray = Array.isArray(json) && json.length === 1;
|
|
@@ -32605,6 +32646,11 @@ var Endpoints = class {
|
|
|
32605
32646
|
[Symbol.for("nodejs.util.inspect.custom")]: (_depth, options, inspect) => "URL " + inspect(v.href, options)
|
|
32606
32647
|
} : v);
|
|
32607
32648
|
if (_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox.length == 1) proxy.sharedInbox = _3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox[0];
|
|
32649
|
+
const _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.map((v) => v instanceof URL ? {
|
|
32650
|
+
[Symbol.for("Deno.customInspect")]: (inspect, options) => "URL " + inspect(v.href, options),
|
|
32651
|
+
[Symbol.for("nodejs.util.inspect.custom")]: (_depth, options, inspect) => "URL " + inspect(v.href, options)
|
|
32652
|
+
} : v);
|
|
32653
|
+
if (_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.length == 1) proxy.uploadMedia = _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia[0];
|
|
32608
32654
|
return proxy;
|
|
32609
32655
|
}
|
|
32610
32656
|
};
|
|
@@ -56787,7 +56833,7 @@ async function lookupObjectInternal(identifier, options = {}) {
|
|
|
56787
56833
|
}
|
|
56788
56834
|
throw error;
|
|
56789
56835
|
}
|
|
56790
|
-
if (options.crossOrigin !== "trust" && object.id != null && !(0, _fedify_vocab_runtime.haveSameIriOrigin)(object.id, documentUrl)) {
|
|
56836
|
+
if (options.crossOrigin !== "trust" && object.id != null && !(0, _fedify_vocab_runtime.haveSameIriOrigin)(object.id, documentUrl) && !(0, _fedify_vocab_runtime.haveSameFe34Origin)(object.id, documentUrl)) {
|
|
56791
56837
|
if (options.crossOrigin === "throw") throw new Error(`The object's @id (${object.id.href}) has a different origin than the document URL (${remoteDoc.documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to "trust".`);
|
|
56792
56838
|
logger.warn("The object's @id ({objectId}) has a different origin than the document URL ({documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to \"trust\".", {
|
|
56793
56839
|
...remoteDoc,
|
package/dist/mod.d.cts
CHANGED
|
@@ -10269,6 +10269,7 @@ declare class Endpoints {
|
|
|
10269
10269
|
provideClientKey?: URL | null;
|
|
10270
10270
|
signClientKey?: URL | null;
|
|
10271
10271
|
sharedInbox?: URL | null;
|
|
10272
|
+
uploadMedia?: URL | null;
|
|
10272
10273
|
}, options?: {
|
|
10273
10274
|
documentLoader?: DocumentLoader$1;
|
|
10274
10275
|
contextLoader?: DocumentLoader$1;
|
|
@@ -10288,6 +10289,7 @@ declare class Endpoints {
|
|
|
10288
10289
|
provideClientKey?: URL | null;
|
|
10289
10290
|
signClientKey?: URL | null;
|
|
10290
10291
|
sharedInbox?: URL | null;
|
|
10292
|
+
uploadMedia?: URL | null;
|
|
10291
10293
|
}, options?: {
|
|
10292
10294
|
documentLoader?: DocumentLoader$1;
|
|
10293
10295
|
contextLoader?: DocumentLoader$1;
|
|
@@ -10333,6 +10335,14 @@ declare class Endpoints {
|
|
|
10333
10335
|
* objects which are not addressed to the `Public`` endpoint.
|
|
10334
10336
|
*/
|
|
10335
10337
|
get sharedInbox(): URL | null;
|
|
10338
|
+
/** Upload endpoint URI for this user for binary data, such as an image or
|
|
10339
|
+
* video, as described in the [ActivityPub Media
|
|
10340
|
+
* Upload](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload) extension.
|
|
10341
|
+
* Clients `POST` a `multipart/form-data` request containing a `file` part
|
|
10342
|
+
* (the binary payload) and an `object` part (an ActivityStreams object shell)
|
|
10343
|
+
* to this endpoint.
|
|
10344
|
+
*/
|
|
10345
|
+
get uploadMedia(): URL | null;
|
|
10336
10346
|
/**
|
|
10337
10347
|
* Converts this object to a JSON-LD structure.
|
|
10338
10348
|
* @param options The options to use.
|
package/dist/mod.d.ts
CHANGED
|
@@ -10269,6 +10269,7 @@ declare class Endpoints {
|
|
|
10269
10269
|
provideClientKey?: URL | null;
|
|
10270
10270
|
signClientKey?: URL | null;
|
|
10271
10271
|
sharedInbox?: URL | null;
|
|
10272
|
+
uploadMedia?: URL | null;
|
|
10272
10273
|
}, options?: {
|
|
10273
10274
|
documentLoader?: DocumentLoader$1;
|
|
10274
10275
|
contextLoader?: DocumentLoader$1;
|
|
@@ -10288,6 +10289,7 @@ declare class Endpoints {
|
|
|
10288
10289
|
provideClientKey?: URL | null;
|
|
10289
10290
|
signClientKey?: URL | null;
|
|
10290
10291
|
sharedInbox?: URL | null;
|
|
10292
|
+
uploadMedia?: URL | null;
|
|
10291
10293
|
}, options?: {
|
|
10292
10294
|
documentLoader?: DocumentLoader$1;
|
|
10293
10295
|
contextLoader?: DocumentLoader$1;
|
|
@@ -10333,6 +10335,14 @@ declare class Endpoints {
|
|
|
10333
10335
|
* objects which are not addressed to the `Public`` endpoint.
|
|
10334
10336
|
*/
|
|
10335
10337
|
get sharedInbox(): URL | null;
|
|
10338
|
+
/** Upload endpoint URI for this user for binary data, such as an image or
|
|
10339
|
+
* video, as described in the [ActivityPub Media
|
|
10340
|
+
* Upload](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload) extension.
|
|
10341
|
+
* Clients `POST` a `multipart/form-data` request containing a `file` part
|
|
10342
|
+
* (the binary payload) and an `object` part (an ActivityStreams object shell)
|
|
10343
|
+
* to this endpoint.
|
|
10344
|
+
*/
|
|
10345
|
+
get uploadMedia(): URL | null;
|
|
10336
10346
|
/**
|
|
10337
10347
|
* Converts this object to a JSON-LD structure.
|
|
10338
10348
|
* @param options The options to use.
|
package/dist/mod.js
CHANGED
|
@@ -4,13 +4,13 @@ import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
|
4
4
|
import { domainToASCII, domainToUnicode } from "node:url";
|
|
5
5
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
6
6
|
import { getLogger } from "@logtape/logtape";
|
|
7
|
-
import { LanguageString, LanguageString as LanguageString$1, decodeMultibase, encodeMultibase, exportMultibaseKey, exportSpki, formatIri, getDocumentLoader, haveSameIriOrigin, importMultibaseKey, importPem, isDecimal, parseDecimal, parseIri, parseJsonLdId } from "@fedify/vocab-runtime";
|
|
7
|
+
import { LanguageString, LanguageString as LanguageString$1, decodeMultibase, encodeMultibase, exportMultibaseKey, exportSpki, formatIri, getDocumentLoader, haveSameFe34Origin, haveSameIriOrigin, importMultibaseKey, importPem, isDecimal, parseDecimal, parseIri, parseJsonLdId } from "@fedify/vocab-runtime";
|
|
8
8
|
import { compactJsonLdCache, isTrustedIriOrigin, normalizeJsonLdIris } from "@fedify/vocab-runtime/internal/jsonld-cache";
|
|
9
9
|
import { isTemporalDuration, isTemporalInstant } from "@fedify/vocab-runtime/temporal";
|
|
10
10
|
import { delay } from "es-toolkit";
|
|
11
11
|
//#region deno.json
|
|
12
12
|
var name = "@fedify/vocab";
|
|
13
|
-
var version = "2.4.0-dev.
|
|
13
|
+
var version = "2.4.0-dev.1618+58858243";
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/type.ts
|
|
16
16
|
function getTypeId(object) {
|
|
@@ -172,6 +172,7 @@ const PORTABLE_IRI_KEYS = new Set([
|
|
|
172
172
|
"https://www.w3.org/ns/activitystreams#target",
|
|
173
173
|
"https://www.w3.org/ns/activitystreams#to",
|
|
174
174
|
"https://www.w3.org/ns/activitystreams#units",
|
|
175
|
+
"https://www.w3.org/ns/activitystreams#uploadMedia",
|
|
175
176
|
"https://www.w3.org/ns/activitystreams#url",
|
|
176
177
|
"https://www.w3.org/ns/did#service",
|
|
177
178
|
"https://www.w3.org/ns/did#serviceEndpoint",
|
|
@@ -233,6 +234,7 @@ const PORTABLE_IRI_KEYS = new Set([
|
|
|
233
234
|
"target",
|
|
234
235
|
"to",
|
|
235
236
|
"units",
|
|
237
|
+
"uploadMedia",
|
|
236
238
|
"url"
|
|
237
239
|
]);
|
|
238
240
|
/** Describes an object of any kind. The Object type serves as the base type for
|
|
@@ -28625,6 +28627,7 @@ var Endpoints = class {
|
|
|
28625
28627
|
#_8Bx9qN8oU7Bpt2xi6khaxWp1gMr_provideClientKey = [];
|
|
28626
28628
|
#_3dU7PMVQZJpsCpo2F4RQXxBXdPmS_signClientKey = [];
|
|
28627
28629
|
#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = [];
|
|
28630
|
+
#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [];
|
|
28628
28631
|
/**
|
|
28629
28632
|
* Constructs a new instance of Endpoints with the given values.
|
|
28630
28633
|
* @param values The values to initialize the instance with.
|
|
@@ -28651,6 +28654,8 @@ var Endpoints = class {
|
|
|
28651
28654
|
else throw new TypeError("The signClientKey must be of type URL.");
|
|
28652
28655
|
if ("sharedInbox" in values && values.sharedInbox != null) if (values.sharedInbox instanceof URL) this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = [values.sharedInbox];
|
|
28653
28656
|
else throw new TypeError("The sharedInbox must be of type URL.");
|
|
28657
|
+
if ("uploadMedia" in values && values.uploadMedia != null) if (values.uploadMedia instanceof URL) this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [values.uploadMedia];
|
|
28658
|
+
else throw new TypeError("The uploadMedia must be of type URL.");
|
|
28654
28659
|
}
|
|
28655
28660
|
/**
|
|
28656
28661
|
* Clones this instance, optionally updating it with the given values.
|
|
@@ -28685,6 +28690,9 @@ var Endpoints = class {
|
|
|
28685
28690
|
clone.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox;
|
|
28686
28691
|
if ("sharedInbox" in values && values.sharedInbox != null) if (values.sharedInbox instanceof URL) clone.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = [values.sharedInbox];
|
|
28687
28692
|
else throw new TypeError("The sharedInbox must be of type URL.");
|
|
28693
|
+
clone.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia;
|
|
28694
|
+
if ("uploadMedia" in values && values.uploadMedia != null) if (values.uploadMedia instanceof URL) clone.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [values.uploadMedia];
|
|
28695
|
+
else throw new TypeError("The uploadMedia must be of type URL.");
|
|
28688
28696
|
return clone;
|
|
28689
28697
|
}
|
|
28690
28698
|
/** Endpoint URI so this actor's clients may access remote ActivityStreams
|
|
@@ -28752,6 +28760,18 @@ var Endpoints = class {
|
|
|
28752
28760
|
if (this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox.length < 1) return null;
|
|
28753
28761
|
return this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox[0];
|
|
28754
28762
|
}
|
|
28763
|
+
/** Upload endpoint URI for this user for binary data, such as an image or
|
|
28764
|
+
* video, as described in the [ActivityPub Media
|
|
28765
|
+
* Upload](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload) extension.
|
|
28766
|
+
* Clients `POST` a `multipart/form-data` request containing a `file` part
|
|
28767
|
+
* (the binary payload) and an `object` part (an ActivityStreams object shell)
|
|
28768
|
+
* to this endpoint.
|
|
28769
|
+
*/
|
|
28770
|
+
get uploadMedia() {
|
|
28771
|
+
if (this._warning != null) getLogger(this._warning.category).warn(this._warning.message, this._warning.values);
|
|
28772
|
+
if (this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.length < 1) return null;
|
|
28773
|
+
return this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia[0];
|
|
28774
|
+
}
|
|
28755
28775
|
/**
|
|
28756
28776
|
* Converts this object to a JSON-LD structure.
|
|
28757
28777
|
* @param options The options to use.
|
|
@@ -28807,6 +28827,12 @@ var Endpoints = class {
|
|
|
28807
28827
|
compactItems.push(item);
|
|
28808
28828
|
}
|
|
28809
28829
|
if (compactItems.length > 0) result["sharedInbox"] = compactItems.length > 1 ? compactItems : compactItems[0];
|
|
28830
|
+
compactItems = [];
|
|
28831
|
+
for (const v of this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia) {
|
|
28832
|
+
const item = formatIri(v);
|
|
28833
|
+
compactItems.push(item);
|
|
28834
|
+
}
|
|
28835
|
+
if (compactItems.length > 0) result["uploadMedia"] = compactItems.length > 1 ? compactItems : compactItems[0];
|
|
28810
28836
|
if (this.id != null) result["id"] = formatIri(this.id);
|
|
28811
28837
|
result["@context"] = "https://www.w3.org/ns/activitystreams";
|
|
28812
28838
|
return result;
|
|
@@ -28849,6 +28875,12 @@ var Endpoints = class {
|
|
|
28849
28875
|
array.push(element);
|
|
28850
28876
|
}
|
|
28851
28877
|
if (array.length > 0) values["https://www.w3.org/ns/activitystreams#sharedInbox"] = array;
|
|
28878
|
+
array = [];
|
|
28879
|
+
for (const v of this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia) {
|
|
28880
|
+
const element = { "@id": formatIri(v) };
|
|
28881
|
+
array.push(element);
|
|
28882
|
+
}
|
|
28883
|
+
if (array.length > 0) values["https://www.w3.org/ns/activitystreams#uploadMedia"] = array;
|
|
28852
28884
|
if (this.id != null) values["@id"] = formatIri(this.id);
|
|
28853
28885
|
if (options.format === "expand") return await jsonld.expand(values, { documentLoader: options.contextLoader });
|
|
28854
28886
|
const docContext = options.context ?? "https://www.w3.org/ns/activitystreams";
|
|
@@ -28974,6 +29006,15 @@ var Endpoints = class {
|
|
|
28974
29006
|
_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox.push(decoded);
|
|
28975
29007
|
}
|
|
28976
29008
|
instance.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = _3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox;
|
|
29009
|
+
const _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [];
|
|
29010
|
+
let _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array = values["https://www.w3.org/ns/activitystreams#uploadMedia"];
|
|
29011
|
+
for (const v of _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array == null ? [] : _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array.length === 1 && "@list" in _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array[0] ? _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array[0]["@list"] : _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array) {
|
|
29012
|
+
if (v == null) continue;
|
|
29013
|
+
const decoded = parseIri(v["@id"], options.baseUrl);
|
|
29014
|
+
if (typeof decoded === "undefined") continue;
|
|
29015
|
+
_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.push(decoded);
|
|
29016
|
+
}
|
|
29017
|
+
instance.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia;
|
|
28977
29018
|
if (!("_fromSubclass" in options) || !options._fromSubclass) try {
|
|
28978
29019
|
if (cacheJsonLd != null && cacheJsonLd !== expanded) {
|
|
28979
29020
|
const compactArray = Array.isArray(json) && json.length === 1;
|
|
@@ -29022,6 +29063,11 @@ var Endpoints = class {
|
|
|
29022
29063
|
[Symbol.for("nodejs.util.inspect.custom")]: (_depth, options, inspect) => "URL " + inspect(v.href, options)
|
|
29023
29064
|
} : v);
|
|
29024
29065
|
if (_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox.length == 1) proxy.sharedInbox = _3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox[0];
|
|
29066
|
+
const _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.map((v) => v instanceof URL ? {
|
|
29067
|
+
[Symbol.for("Deno.customInspect")]: (inspect, options) => "URL " + inspect(v.href, options),
|
|
29068
|
+
[Symbol.for("nodejs.util.inspect.custom")]: (_depth, options, inspect) => "URL " + inspect(v.href, options)
|
|
29069
|
+
} : v);
|
|
29070
|
+
if (_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.length == 1) proxy.uploadMedia = _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia[0];
|
|
29025
29071
|
return proxy;
|
|
29026
29072
|
}
|
|
29027
29073
|
};
|
|
@@ -53203,7 +53249,7 @@ async function lookupObjectInternal(identifier, options = {}) {
|
|
|
53203
53249
|
}
|
|
53204
53250
|
throw error;
|
|
53205
53251
|
}
|
|
53206
|
-
if (options.crossOrigin !== "trust" && object.id != null && !haveSameIriOrigin(object.id, documentUrl)) {
|
|
53252
|
+
if (options.crossOrigin !== "trust" && object.id != null && !haveSameIriOrigin(object.id, documentUrl) && !haveSameFe34Origin(object.id, documentUrl)) {
|
|
53207
53253
|
if (options.crossOrigin === "throw") throw new Error(`The object's @id (${object.id.href}) has a different origin than the document URL (${remoteDoc.documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to "trust".`);
|
|
53208
53254
|
logger.warn("The object's @id ({objectId}) has a different origin than the document URL ({documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to \"trust\".", {
|
|
53209
53255
|
...remoteDoc,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import { A as
|
|
3
|
+
import { A as Organization, K as __toESM, R as Service, W as __commonJSMin, a as Application, j as Person, v as Group } from "./vocab-OU5GYbNg.mjs";
|
|
4
4
|
import { t as getTypeId } from "./type-_BlsfT3S.mjs";
|
|
5
5
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
6
6
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -1206,7 +1206,7 @@ var esm_default = new class FetchMock {
|
|
|
1206
1206
|
//#endregion
|
|
1207
1207
|
//#region deno.json
|
|
1208
1208
|
var name = "@fedify/vocab";
|
|
1209
|
-
var version = "2.4.0-dev.
|
|
1209
|
+
var version = "2.4.0-dev.1618+58858243";
|
|
1210
1210
|
//#endregion
|
|
1211
1211
|
//#region src/actor.ts
|
|
1212
1212
|
const ACTOR_DISCOVERY_HISTOGRAM_BUCKETS = [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import { A as
|
|
4
|
-
import { a as normalizeActorHandle, c as esm_default, i as isActor, n as getActorHandle, r as getActorTypeName, t as getActorClassByTypeName } from "./actor-
|
|
3
|
+
import { A as Organization, G as __exportAll, R as Service, a as Application, j as Person, v as Group } from "./vocab-OU5GYbNg.mjs";
|
|
4
|
+
import { a as normalizeActorHandle, c as esm_default, i as isActor, n as getActorHandle, r as getActorTypeName, t as getActorClassByTypeName } from "./actor-COnJgcOw.mjs";
|
|
5
5
|
import { createTestMeterProvider, test } from "@fedify/fixture";
|
|
6
6
|
import { deepStrictEqual, ok, rejects, strictEqual, throws } from "node:assert/strict";
|
|
7
7
|
//#region ../../node_modules/.pnpm/fast-check@3.23.2/node_modules/fast-check/lib/esm/check/precondition/PreconditionFailure.js
|
|
@@ -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"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import {
|
|
4
|
-
import { c as esm_default, i as isActor, o as name, s as version } from "./actor-
|
|
3
|
+
import { D as Object$1, E as Note, j as Person, o as Collection } from "./vocab-OU5GYbNg.mjs";
|
|
4
|
+
import { c as esm_default, i as isActor, o as name, s as version } from "./actor-COnJgcOw.mjs";
|
|
5
5
|
import { t as getTypeId } from "./type-_BlsfT3S.mjs";
|
|
6
6
|
import { t as assertInstanceOf } from "./utils-CQjrpUkt.mjs";
|
|
7
7
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
@@ -9,7 +9,7 @@ import { deepStrictEqual, equal, ok, rejects } from "node:assert/strict";
|
|
|
9
9
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
10
10
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
11
11
|
import { getLogger } from "@logtape/logtape";
|
|
12
|
-
import { getDocumentLoader, haveSameIriOrigin, parseIri } from "@fedify/vocab-runtime";
|
|
12
|
+
import { getDocumentLoader, haveSameFe34Origin, haveSameIriOrigin, parseIri } from "@fedify/vocab-runtime";
|
|
13
13
|
import { delay } from "es-toolkit";
|
|
14
14
|
//#region src/handle.ts
|
|
15
15
|
/**
|
|
@@ -223,7 +223,7 @@ async function lookupObjectInternal(identifier, options = {}) {
|
|
|
223
223
|
}
|
|
224
224
|
throw error;
|
|
225
225
|
}
|
|
226
|
-
if (options.crossOrigin !== "trust" && object.id != null && !haveSameIriOrigin(object.id, documentUrl)) {
|
|
226
|
+
if (options.crossOrigin !== "trust" && object.id != null && !haveSameIriOrigin(object.id, documentUrl) && !haveSameFe34Origin(object.id, documentUrl)) {
|
|
227
227
|
if (options.crossOrigin === "throw") throw new Error(`The object's @id (${object.id.href}) has a different origin than the document URL (${remoteDoc.documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to "trust".`);
|
|
228
228
|
logger.warn("The object's @id ({objectId}) has a different origin than the document URL ({documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to \"trust\".", {
|
|
229
229
|
...remoteDoc,
|
|
@@ -538,6 +538,73 @@ test("FEP-fe34: lookupObject() cross-origin security", {
|
|
|
538
538
|
deepStrictEqual(result.id, new URL("https://example.com/note"));
|
|
539
539
|
deepStrictEqual(result.content, "This is a legitimate note from the same origin");
|
|
540
540
|
});
|
|
541
|
+
await t.step("same-authority non-FE34 IRIs are still trusted", async () => {
|
|
542
|
+
const atProtoDocumentLoader = async (url) => {
|
|
543
|
+
if (url === "https://gateway.example/at-item") return {
|
|
544
|
+
documentUrl: "at://did%3Aplc%3Aexample/collection/item",
|
|
545
|
+
contextUrl: null,
|
|
546
|
+
document: {
|
|
547
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
548
|
+
type: "Note",
|
|
549
|
+
id: "at://did:plc:example/collection/reply",
|
|
550
|
+
content: "AT Protocol note"
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
throw new Error(`Unexpected URL: ${url}`);
|
|
554
|
+
};
|
|
555
|
+
const result = await lookupObject("https://gateway.example/at-item", {
|
|
556
|
+
documentLoader: atProtoDocumentLoader,
|
|
557
|
+
contextLoader: mockDocumentLoader
|
|
558
|
+
});
|
|
559
|
+
assertInstanceOf(result, Note);
|
|
560
|
+
deepStrictEqual(result.id, new URL("at://did%3Aplc%3Aexample/collection/reply"));
|
|
561
|
+
deepStrictEqual(result.content, "AT Protocol note");
|
|
562
|
+
});
|
|
563
|
+
await t.step("portable object IDs are trusted by DID cryptographic origin", async () => {
|
|
564
|
+
const portableDocumentLoader = async (url) => {
|
|
565
|
+
if (url === "https://gateway.example/.well-known/apgateway/did:key:z6Mkabc/note") return {
|
|
566
|
+
documentUrl: "ap+ef61://did:key:z6Mkabc/note",
|
|
567
|
+
contextUrl: null,
|
|
568
|
+
document: {
|
|
569
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
570
|
+
type: "Note",
|
|
571
|
+
id: "ap://did:key:z6Mkabc/note",
|
|
572
|
+
content: "Portable note"
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
throw new Error(`Unexpected URL: ${url}`);
|
|
576
|
+
};
|
|
577
|
+
const result = await lookupObject("https://gateway.example/.well-known/apgateway/did:key:z6Mkabc/note", {
|
|
578
|
+
documentLoader: portableDocumentLoader,
|
|
579
|
+
contextLoader: mockDocumentLoader
|
|
580
|
+
});
|
|
581
|
+
assertInstanceOf(result, Note);
|
|
582
|
+
deepStrictEqual(result.content, "Portable note");
|
|
583
|
+
});
|
|
584
|
+
await t.step("portable object IDs reject mismatched cryptographic origins", async () => {
|
|
585
|
+
const portableDocumentLoader = async (url) => {
|
|
586
|
+
if (url === "https://gateway.example/.well-known/apgateway/did:key:z6Mkabc/note") return {
|
|
587
|
+
documentUrl: "ap+ef61://did:key:z6Mkabc/note",
|
|
588
|
+
contextUrl: null,
|
|
589
|
+
document: {
|
|
590
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
591
|
+
type: "Note",
|
|
592
|
+
id: "ap://did:key:z6Mkdef/note",
|
|
593
|
+
content: "Spoofed portable note"
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
throw new Error(`Unexpected URL: ${url}`);
|
|
597
|
+
};
|
|
598
|
+
deepStrictEqual(await lookupObject("https://gateway.example/.well-known/apgateway/did:key:z6Mkabc/note", {
|
|
599
|
+
documentLoader: portableDocumentLoader,
|
|
600
|
+
contextLoader: mockDocumentLoader
|
|
601
|
+
}), null);
|
|
602
|
+
await rejects(() => lookupObject("https://gateway.example/.well-known/apgateway/did:key:z6Mkabc/note", {
|
|
603
|
+
documentLoader: portableDocumentLoader,
|
|
604
|
+
contextLoader: mockDocumentLoader,
|
|
605
|
+
crossOrigin: "throw"
|
|
606
|
+
}), Error, "The object's @id (ap+ef61://did%3Akey%3Az6Mkdef/note) has a different origin than the document URL (ap+ef61://did:key:z6Mkabc/note)");
|
|
607
|
+
});
|
|
541
608
|
await t.step("objects without @id are trusted", async () => {
|
|
542
609
|
const noIdDocumentLoader = async (url) => {
|
|
543
610
|
if (url === "https://example.com/note") return {
|
package/dist-tests/type.test.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import {
|
|
3
|
+
import { C as Link, H as isEntityType, V as getEntityTypeById, j as Person } from "./vocab-OU5GYbNg.mjs";
|
|
4
4
|
import { t as getTypeId } from "./type-_BlsfT3S.mjs";
|
|
5
5
|
import { test } from "@fedify/fixture";
|
|
6
6
|
import { deepStrictEqual } from "node:assert/strict";
|
|
@@ -282,6 +282,7 @@ const PORTABLE_IRI_KEYS = new Set([
|
|
|
282
282
|
"https://www.w3.org/ns/activitystreams#target",
|
|
283
283
|
"https://www.w3.org/ns/activitystreams#to",
|
|
284
284
|
"https://www.w3.org/ns/activitystreams#units",
|
|
285
|
+
"https://www.w3.org/ns/activitystreams#uploadMedia",
|
|
285
286
|
"https://www.w3.org/ns/activitystreams#url",
|
|
286
287
|
"https://www.w3.org/ns/did#service",
|
|
287
288
|
"https://www.w3.org/ns/did#serviceEndpoint",
|
|
@@ -343,6 +344,7 @@ const PORTABLE_IRI_KEYS = new Set([
|
|
|
343
344
|
"target",
|
|
344
345
|
"to",
|
|
345
346
|
"units",
|
|
347
|
+
"uploadMedia",
|
|
346
348
|
"url"
|
|
347
349
|
]);
|
|
348
350
|
/** Describes an object of any kind. The Object type serves as the base type for
|
|
@@ -28735,6 +28737,7 @@ var Endpoints = class {
|
|
|
28735
28737
|
#_8Bx9qN8oU7Bpt2xi6khaxWp1gMr_provideClientKey = [];
|
|
28736
28738
|
#_3dU7PMVQZJpsCpo2F4RQXxBXdPmS_signClientKey = [];
|
|
28737
28739
|
#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = [];
|
|
28740
|
+
#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [];
|
|
28738
28741
|
/**
|
|
28739
28742
|
* Constructs a new instance of Endpoints with the given values.
|
|
28740
28743
|
* @param values The values to initialize the instance with.
|
|
@@ -28761,6 +28764,8 @@ var Endpoints = class {
|
|
|
28761
28764
|
else throw new TypeError("The signClientKey must be of type URL.");
|
|
28762
28765
|
if ("sharedInbox" in values && values.sharedInbox != null) if (values.sharedInbox instanceof URL) this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = [values.sharedInbox];
|
|
28763
28766
|
else throw new TypeError("The sharedInbox must be of type URL.");
|
|
28767
|
+
if ("uploadMedia" in values && values.uploadMedia != null) if (values.uploadMedia instanceof URL) this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [values.uploadMedia];
|
|
28768
|
+
else throw new TypeError("The uploadMedia must be of type URL.");
|
|
28764
28769
|
}
|
|
28765
28770
|
/**
|
|
28766
28771
|
* Clones this instance, optionally updating it with the given values.
|
|
@@ -28795,6 +28800,9 @@ var Endpoints = class {
|
|
|
28795
28800
|
clone.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox;
|
|
28796
28801
|
if ("sharedInbox" in values && values.sharedInbox != null) if (values.sharedInbox instanceof URL) clone.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = [values.sharedInbox];
|
|
28797
28802
|
else throw new TypeError("The sharedInbox must be of type URL.");
|
|
28803
|
+
clone.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia;
|
|
28804
|
+
if ("uploadMedia" in values && values.uploadMedia != null) if (values.uploadMedia instanceof URL) clone.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [values.uploadMedia];
|
|
28805
|
+
else throw new TypeError("The uploadMedia must be of type URL.");
|
|
28798
28806
|
return clone;
|
|
28799
28807
|
}
|
|
28800
28808
|
/** Endpoint URI so this actor's clients may access remote ActivityStreams
|
|
@@ -28862,6 +28870,18 @@ var Endpoints = class {
|
|
|
28862
28870
|
if (this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox.length < 1) return null;
|
|
28863
28871
|
return this.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox[0];
|
|
28864
28872
|
}
|
|
28873
|
+
/** Upload endpoint URI for this user for binary data, such as an image or
|
|
28874
|
+
* video, as described in the [ActivityPub Media
|
|
28875
|
+
* Upload](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload) extension.
|
|
28876
|
+
* Clients `POST` a `multipart/form-data` request containing a `file` part
|
|
28877
|
+
* (the binary payload) and an `object` part (an ActivityStreams object shell)
|
|
28878
|
+
* to this endpoint.
|
|
28879
|
+
*/
|
|
28880
|
+
get uploadMedia() {
|
|
28881
|
+
if (this._warning != null) getLogger(this._warning.category).warn(this._warning.message, this._warning.values);
|
|
28882
|
+
if (this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.length < 1) return null;
|
|
28883
|
+
return this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia[0];
|
|
28884
|
+
}
|
|
28865
28885
|
/**
|
|
28866
28886
|
* Converts this object to a JSON-LD structure.
|
|
28867
28887
|
* @param options The options to use.
|
|
@@ -28917,6 +28937,12 @@ var Endpoints = class {
|
|
|
28917
28937
|
compactItems.push(item);
|
|
28918
28938
|
}
|
|
28919
28939
|
if (compactItems.length > 0) result["sharedInbox"] = compactItems.length > 1 ? compactItems : compactItems[0];
|
|
28940
|
+
compactItems = [];
|
|
28941
|
+
for (const v of this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia) {
|
|
28942
|
+
const item = formatIri(v);
|
|
28943
|
+
compactItems.push(item);
|
|
28944
|
+
}
|
|
28945
|
+
if (compactItems.length > 0) result["uploadMedia"] = compactItems.length > 1 ? compactItems : compactItems[0];
|
|
28920
28946
|
if (this.id != null) result["id"] = formatIri(this.id);
|
|
28921
28947
|
result["@context"] = "https://www.w3.org/ns/activitystreams";
|
|
28922
28948
|
return result;
|
|
@@ -28959,6 +28985,12 @@ var Endpoints = class {
|
|
|
28959
28985
|
array.push(element);
|
|
28960
28986
|
}
|
|
28961
28987
|
if (array.length > 0) values["https://www.w3.org/ns/activitystreams#sharedInbox"] = array;
|
|
28988
|
+
array = [];
|
|
28989
|
+
for (const v of this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia) {
|
|
28990
|
+
const element = { "@id": formatIri(v) };
|
|
28991
|
+
array.push(element);
|
|
28992
|
+
}
|
|
28993
|
+
if (array.length > 0) values["https://www.w3.org/ns/activitystreams#uploadMedia"] = array;
|
|
28962
28994
|
if (this.id != null) values["@id"] = formatIri(this.id);
|
|
28963
28995
|
if (options.format === "expand") return await jsonld.expand(values, { documentLoader: options.contextLoader });
|
|
28964
28996
|
const docContext = options.context ?? "https://www.w3.org/ns/activitystreams";
|
|
@@ -29084,6 +29116,15 @@ var Endpoints = class {
|
|
|
29084
29116
|
_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox.push(decoded);
|
|
29085
29117
|
}
|
|
29086
29118
|
instance.#_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox = _3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox;
|
|
29119
|
+
const _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = [];
|
|
29120
|
+
let _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array = values["https://www.w3.org/ns/activitystreams#uploadMedia"];
|
|
29121
|
+
for (const v of _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array == null ? [] : _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array.length === 1 && "@list" in _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array[0] ? _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array[0]["@list"] : _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia__array) {
|
|
29122
|
+
if (v == null) continue;
|
|
29123
|
+
const decoded = parseIri(v["@id"], options.baseUrl);
|
|
29124
|
+
if (typeof decoded === "undefined") continue;
|
|
29125
|
+
_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.push(decoded);
|
|
29126
|
+
}
|
|
29127
|
+
instance.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia;
|
|
29087
29128
|
if (!("_fromSubclass" in options) || !options._fromSubclass) try {
|
|
29088
29129
|
if (cacheJsonLd != null && cacheJsonLd !== expanded) {
|
|
29089
29130
|
const compactArray = Array.isArray(json) && json.length === 1;
|
|
@@ -29132,6 +29173,11 @@ var Endpoints = class {
|
|
|
29132
29173
|
[Symbol.for("nodejs.util.inspect.custom")]: (_depth, options, inspect) => "URL " + inspect(v.href, options)
|
|
29133
29174
|
} : v);
|
|
29134
29175
|
if (_3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox.length == 1) proxy.sharedInbox = _3JprUSDLVqqX4dwHRi37qGZZCRCc_sharedInbox[0];
|
|
29176
|
+
const _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia = this.#_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.map((v) => v instanceof URL ? {
|
|
29177
|
+
[Symbol.for("Deno.customInspect")]: (inspect, options) => "URL " + inspect(v.href, options),
|
|
29178
|
+
[Symbol.for("nodejs.util.inspect.custom")]: (_depth, options, inspect) => "URL " + inspect(v.href, options)
|
|
29179
|
+
} : v);
|
|
29180
|
+
if (_KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia.length == 1) proxy.uploadMedia = _KukVKGqFRETxAxXA15YCYAA7XAT_uploadMedia[0];
|
|
29135
29181
|
return proxy;
|
|
29136
29182
|
}
|
|
29137
29183
|
};
|
|
@@ -52874,4 +52920,4 @@ function getEntityTypeById(id) {
|
|
|
52874
52920
|
return entityTypeIds.get(typeof id === "string" ? id : id?.href);
|
|
52875
52921
|
}
|
|
52876
52922
|
//#endregion
|
|
52877
|
-
export {
|
|
52923
|
+
export { Organization as A, Tombstone as B, Link as C, Object$1 as D, Note as E, QuoteAuthorization as F, __exportAll as G, isEntityType as H, QuoteRequest as I, __toESM as K, Reject as L, Place as M, Proposal as N, Offer as O, Question as P, Service as R, InteractionRule as S, Multikey as T, vocab_exports as U, getEntityTypeById as V, __commonJSMin as W, Follow as _, Application as a, Intent as b, Create as c, Document as d, Endpoints as f, FeaturedItem as g, FeaturedCollection as h, Announce as i, Person as j, OrderedCollectionPage as k, CryptographicKey as l, FeatureRequest as m, Activity as n, Collection as o, FeatureAuthorization as p, Agreement as r, Commitment as s, Accept as t, Delete as u, Group as v, Measure as w, InteractionPolicy as x, Hashtag as y, Source as z };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import {
|
|
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
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";
|
|
@@ -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.
|
|
3
|
+
"version": "2.4.0-dev.1618+58858243",
|
|
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/vocab-tools": "2.4.0-dev.
|
|
47
|
-
"@fedify/
|
|
48
|
-
"@fedify/
|
|
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
|
|
package/src/endpoints.yaml
CHANGED
|
@@ -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"
|
package/src/lookup.test.ts
CHANGED
|
@@ -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) => {
|