@fedify/vocab 2.4.0-pr.936.41 → 2.4.0
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 +31328 -8723
- package/dist/mod.d.cts +4599 -6
- package/dist/mod.d.ts +4599 -6
- package/dist/mod.js +31221 -8623
- package/dist-tests/actor-D7_iX4I4.mjs +276 -0
- package/dist-tests/actor.test.mjs +55 -49
- package/dist-tests/{actor-MBpzF3-D.mjs → esm-DP2Spt0u.mjs} +15 -211
- package/dist-tests/gateway.test.mjs +1906 -0
- package/dist-tests/lookup-IiNFWuXL.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-COZcxf00.mjs} +30831 -8515
- package/dist-tests/vocab.test.mjs +677 -10
- 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 +914 -5
- /package/dist-tests/{type-_BlsfT3S.mjs → type-BMdjm4Eg.mjs} +0 -0
- /package/dist-tests/{utils-CQjrpUkt.mjs → utils-Df2odC27.mjs} +0 -0
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import { A as
|
|
4
|
-
import { t as
|
|
3
|
+
import { A as Object$1, B as QuoteAuthorization, C as Intent, D as Measure, E as Link, F as Person, G as Tombstone, H as Reject, I as Place, L as PropertyValue, N as OrderedCollectionPage, O as Multikey, P as Organization, R as Proposal, S as Image, T as InteractionRule, U as Service, V as QuoteRequest, W as Source, X as vocab_exports, _ as FeaturedCollection, a as Application, b as Group, d as CryptographicKey, f as Delete, g as FeatureRequest, h as FeatureAuthorization, i as Announce, j as Offer, k as Note, l as Commitment, m as Endpoints, n as Activity, p as Document, r as Agreement, s as Collection, t as Accept, u as Create, v as FeaturedItem, w as InteractionPolicy, x as Hashtag, y as Follow, z as Question } from "./vocab-COZcxf00.mjs";
|
|
4
|
+
import { t as esm_default } from "./esm-DP2Spt0u.mjs";
|
|
5
|
+
import { t as assertInstanceOf } from "./utils-Df2odC27.mjs";
|
|
5
6
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
6
7
|
import { deepStrictEqual, notDeepStrictEqual, ok, rejects, throws } from "node:assert/strict";
|
|
8
|
+
import { FetchError, LanguageString, decodeMultibase, getDocumentLoader, parseDecimal, parseIri } from "@fedify/vocab-runtime";
|
|
7
9
|
import { configure, reset } from "@logtape/logtape";
|
|
8
|
-
import { LanguageString, decodeMultibase, parseDecimal } from "@fedify/vocab-runtime";
|
|
9
10
|
import { pascalCase } from "es-toolkit";
|
|
10
11
|
import { areAllScalarTypes, loadSchemaFiles } from "@fedify/vocab-tools";
|
|
12
|
+
import { inspect } from "node:util";
|
|
11
13
|
//#region src/vocab.test.ts
|
|
12
14
|
const NOTE_QUOTE_CONTEXT = [
|
|
13
15
|
"https://www.w3.org/ns/activitystreams",
|
|
@@ -249,6 +251,36 @@ test("Object.toJsonLd()", async () => {
|
|
|
249
251
|
}
|
|
250
252
|
});
|
|
251
253
|
});
|
|
254
|
+
test("Note.fromJsonLd() ignores malformed language tags", async () => {
|
|
255
|
+
const note = await Note.fromJsonLd({
|
|
256
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
257
|
+
type: "Note",
|
|
258
|
+
contentMap: {
|
|
259
|
+
invalid_tag: "Hello",
|
|
260
|
+
en: "Hi"
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
deepStrictEqual(note.contents, [new LanguageString("Hi", "en")]);
|
|
264
|
+
deepStrictEqual((await note.toJsonLd()).contentMap, { en: "Hi" });
|
|
265
|
+
});
|
|
266
|
+
test("Note.fromJsonLd() ignores malformed language tags in nested objects", async () => {
|
|
267
|
+
const note = await Note.fromJsonLd({
|
|
268
|
+
"@type": ["https://www.w3.org/ns/activitystreams#Note"],
|
|
269
|
+
"https://www.w3.org/ns/activitystreams#attachment": [{
|
|
270
|
+
"@type": ["http://schema.org#PropertyValue"],
|
|
271
|
+
"https://www.w3.org/ns/activitystreams#name": [{
|
|
272
|
+
"@value": "Malformed",
|
|
273
|
+
"@language": "invalid_tag"
|
|
274
|
+
}, {
|
|
275
|
+
"@value": "Valid",
|
|
276
|
+
"@language": "en"
|
|
277
|
+
}]
|
|
278
|
+
}]
|
|
279
|
+
});
|
|
280
|
+
deepStrictEqual(await Array.fromAsync(note.getAttachments()), [new PropertyValue({ name: new LanguageString("Valid", "en") })]);
|
|
281
|
+
const attachment = (await note.toJsonLd()).attachment;
|
|
282
|
+
deepStrictEqual(attachment.nameMap, { en: "Valid" });
|
|
283
|
+
});
|
|
252
284
|
test("Note.toJsonLd()", async () => {
|
|
253
285
|
const note = new Note({ tags: [new Hashtag({
|
|
254
286
|
name: "#Fedify",
|
|
@@ -380,6 +412,22 @@ test("fromJsonLd() handles portable ActivityPub IRIs", async () => {
|
|
|
380
412
|
deepStrictEqual(pageJson.next, "ap+ef61://did:key:z6Mkabc/actor/outbox?page=2");
|
|
381
413
|
deepStrictEqual(pageJson.prev, "ap+ef61://did:key:z6Mkabc/actor/outbox?page=1");
|
|
382
414
|
});
|
|
415
|
+
test("fromJsonLd() refuses portable IDs changed by URL parsing", async () => {
|
|
416
|
+
const base = "ap://did:key:z6Mkabc";
|
|
417
|
+
for (const id of [
|
|
418
|
+
`${base}/a/../b`,
|
|
419
|
+
`${base}/a/%2e%2E/b`,
|
|
420
|
+
"https://gw.example/.well-known/apgateway/did:key:z6Mkabc/a/../b",
|
|
421
|
+
"https:/gw.example/.well-known/apgateway/did:key:z6Mkabc/a/../b"
|
|
422
|
+
]) await rejects(() => Note.fromJsonLd({
|
|
423
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
424
|
+
type: "Note",
|
|
425
|
+
id
|
|
426
|
+
}, {
|
|
427
|
+
documentLoader: mockDocumentLoader,
|
|
428
|
+
contextLoader: mockDocumentLoader
|
|
429
|
+
}), TypeError);
|
|
430
|
+
});
|
|
383
431
|
test("FEP-ef61: actor gateways round-trip as an ordered URI list", async () => {
|
|
384
432
|
const actorClasses = [
|
|
385
433
|
Application,
|
|
@@ -397,17 +445,112 @@ test("FEP-ef61: actor gateways round-trip as an ordered URI list", async () => {
|
|
|
397
445
|
gateways: gateways.map((gateway) => gateway.href)
|
|
398
446
|
});
|
|
399
447
|
deepStrictEqual(actor.gateways, gateways);
|
|
400
|
-
const jsonLd = await actor.toJsonLd();
|
|
448
|
+
const jsonLd = await actor.toJsonLd({ format: "compact" });
|
|
401
449
|
deepStrictEqual(jsonLd.type, ActorClass.name);
|
|
402
|
-
deepStrictEqual(jsonLd.gateways, gateways.map((gateway) => gateway.
|
|
450
|
+
deepStrictEqual(jsonLd.gateways, gateways.map((gateway) => gateway.origin));
|
|
403
451
|
deepStrictEqual((await ActorClass.fromJsonLd(jsonLd)).gateways, gateways);
|
|
404
452
|
}
|
|
405
453
|
});
|
|
454
|
+
test("FEP-ef61: actor gateways serialize as origins", async () => {
|
|
455
|
+
const actorClasses = [
|
|
456
|
+
Application,
|
|
457
|
+
Group,
|
|
458
|
+
Organization,
|
|
459
|
+
Person,
|
|
460
|
+
Service
|
|
461
|
+
];
|
|
462
|
+
const gateways = [
|
|
463
|
+
new URL("https://first.example:443/"),
|
|
464
|
+
new URL("http://second.example:80/"),
|
|
465
|
+
new URL("https://third.example:8443/"),
|
|
466
|
+
new URL("http://fourth.example:8080/")
|
|
467
|
+
];
|
|
468
|
+
const origins = [
|
|
469
|
+
"https://first.example",
|
|
470
|
+
"http://second.example",
|
|
471
|
+
"https://third.example:8443",
|
|
472
|
+
"http://fourth.example:8080"
|
|
473
|
+
];
|
|
474
|
+
for (const ActorClass of actorClasses) {
|
|
475
|
+
const actor = new ActorClass({
|
|
476
|
+
id: parseIri("ap://did:key:z6Mkabc/actor"),
|
|
477
|
+
gateways
|
|
478
|
+
});
|
|
479
|
+
const compact = await actor.toJsonLd();
|
|
480
|
+
deepStrictEqual(compact.gateways, origins);
|
|
481
|
+
const expanded = await actor.toJsonLd({ format: "expand" });
|
|
482
|
+
deepStrictEqual(expanded[0]["https://w3id.org/fep/ef61/gateways"], [{ "@list": origins.map((origin) => ({ "@id": origin })) }]);
|
|
483
|
+
deepStrictEqual(actor.gateways, gateways);
|
|
484
|
+
deepStrictEqual(actor.gateways.map((gateway) => gateway.href), origins.map((origin) => `${origin}/`));
|
|
485
|
+
deepStrictEqual((await ActorClass.fromJsonLd(compact)).gateways, gateways);
|
|
486
|
+
deepStrictEqual((await ActorClass.fromJsonLd(expanded)).gateways, gateways);
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
test("FEP-ef61: portable actors parse unmapped gateway lists", async () => {
|
|
490
|
+
const actorClasses = [
|
|
491
|
+
Application,
|
|
492
|
+
Group,
|
|
493
|
+
Organization,
|
|
494
|
+
Person,
|
|
495
|
+
Service
|
|
496
|
+
];
|
|
497
|
+
const gateways = ["https://first.example", "https://second.example"];
|
|
498
|
+
const ids = ["ap://did:key:z6Mkabc/actor", "https://first.example/.well-known/apgateway/did:key:z6Mkabc/actor"];
|
|
499
|
+
for (const ActorClass of actorClasses) for (const id of ids) deepStrictEqual((await ActorClass.fromJsonLd({
|
|
500
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
501
|
+
type: ActorClass.name,
|
|
502
|
+
id,
|
|
503
|
+
inbox: `${id}/inbox`,
|
|
504
|
+
outbox: `${id}/outbox`,
|
|
505
|
+
gateways
|
|
506
|
+
})).gateways, gateways.map((g) => new URL(g)));
|
|
507
|
+
});
|
|
508
|
+
test("FEP-ef61: unmapped gateways retain their context meaning", async () => {
|
|
509
|
+
const actorClasses = [
|
|
510
|
+
Application,
|
|
511
|
+
Group,
|
|
512
|
+
Organization,
|
|
513
|
+
Person,
|
|
514
|
+
Service
|
|
515
|
+
];
|
|
516
|
+
const id = "ap://did:key:z6Mkabc/actor";
|
|
517
|
+
for (const ActorClass of actorClasses) {
|
|
518
|
+
const document = {
|
|
519
|
+
type: ActorClass.name,
|
|
520
|
+
id,
|
|
521
|
+
inbox: `${id}/inbox`,
|
|
522
|
+
outbox: `${id}/outbox`,
|
|
523
|
+
gateways: ["https://unmapped.example"]
|
|
524
|
+
};
|
|
525
|
+
for (const context of [
|
|
526
|
+
{ gateways: "https://example.com/unrelated" },
|
|
527
|
+
{ gateways: null },
|
|
528
|
+
{ "@vocab": "https://example.com/" },
|
|
529
|
+
{ [ActorClass.name]: {
|
|
530
|
+
"@id": `https://www.w3.org/ns/activitystreams#${ActorClass.name}`,
|
|
531
|
+
"@context": { gateways: "https://example.com/scoped" }
|
|
532
|
+
} }
|
|
533
|
+
]) deepStrictEqual((await ActorClass.fromJsonLd({
|
|
534
|
+
"@context": ["https://www.w3.org/ns/activitystreams", context],
|
|
535
|
+
...document
|
|
536
|
+
})).gateways, []);
|
|
537
|
+
deepStrictEqual((await ActorClass.fromJsonLd({
|
|
538
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
539
|
+
...document,
|
|
540
|
+
id: "https://ordinary.example/actor"
|
|
541
|
+
})).gateways, []);
|
|
542
|
+
deepStrictEqual((await ActorClass.fromJsonLd({
|
|
543
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
544
|
+
...document,
|
|
545
|
+
"https://w3id.org/fep/ef61/gateways": { "@list": [{ "@id": "https://canonical.example" }] }
|
|
546
|
+
})).gateways, [new URL("https://canonical.example")]);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
406
549
|
test("FEP-ef61: actor gateways preserve single, empty, and invalid cases", async () => {
|
|
407
550
|
deepStrictEqual((await new Person({
|
|
408
551
|
id: new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"),
|
|
409
552
|
gateways: [new URL("https://server.example/")]
|
|
410
|
-
}).toJsonLd()).gateways, ["https://server.example
|
|
553
|
+
}).toJsonLd()).gateways, ["https://server.example"]);
|
|
411
554
|
ok(!("gateways" in await new Person({
|
|
412
555
|
id: new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"),
|
|
413
556
|
gateways: []
|
|
@@ -442,7 +585,9 @@ test("FEP-ef61: actor gateways must be HTTP(S) base URIs", async () => {
|
|
|
442
585
|
"https://user@server.example/",
|
|
443
586
|
"https://server.example/path",
|
|
444
587
|
"https://server.example/?x=1",
|
|
445
|
-
"https://server.example
|
|
588
|
+
"https://server.example/?",
|
|
589
|
+
"https://server.example/#fragment",
|
|
590
|
+
"https://server.example/#"
|
|
446
591
|
]) {
|
|
447
592
|
throws(() => new Person({
|
|
448
593
|
id: new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"),
|
|
@@ -1173,6 +1318,59 @@ test({
|
|
|
1173
1318
|
});
|
|
1174
1319
|
}
|
|
1175
1320
|
});
|
|
1321
|
+
test({
|
|
1322
|
+
name: "Announce.getObject() logs suppressed failures as warnings",
|
|
1323
|
+
permissions: {
|
|
1324
|
+
env: true,
|
|
1325
|
+
read: true
|
|
1326
|
+
},
|
|
1327
|
+
async fn() {
|
|
1328
|
+
const records = [];
|
|
1329
|
+
const notFoundUrl = "https://example.com/suppressed-not-found";
|
|
1330
|
+
const invalidObjectUrl = "https://example.com/invalid-object";
|
|
1331
|
+
await reset();
|
|
1332
|
+
esm_default.spyGlobal();
|
|
1333
|
+
try {
|
|
1334
|
+
await configure({
|
|
1335
|
+
sinks: { buffer(record) {
|
|
1336
|
+
records.push(record);
|
|
1337
|
+
} },
|
|
1338
|
+
filters: {},
|
|
1339
|
+
loggers: [{
|
|
1340
|
+
category: [],
|
|
1341
|
+
sinks: ["buffer"]
|
|
1342
|
+
}]
|
|
1343
|
+
});
|
|
1344
|
+
esm_default.get(notFoundUrl, { status: 404 });
|
|
1345
|
+
deepStrictEqual(await new Announce({ object: new URL(notFoundUrl) }).getObject({ suppressError: true }), null);
|
|
1346
|
+
ok(records.some((record) => record.level === "warning" && record.rawMessage === "Failed to fetch document: {status} {url} {headers}"));
|
|
1347
|
+
ok(records.some((record) => record.level === "warning" && record.rawMessage === "Failed to fetch {url}: {error}"));
|
|
1348
|
+
deepStrictEqual(records.some((record) => record.level === "error" && (record.rawMessage === "Failed to fetch document: {status} {url} {headers}" || record.rawMessage === "Failed to fetch {url}: {error}")), false);
|
|
1349
|
+
records.length = 0;
|
|
1350
|
+
const unsuppressedFetch = new Announce({ object: new URL(notFoundUrl) });
|
|
1351
|
+
await rejects(() => unsuppressedFetch.getObject(), FetchError);
|
|
1352
|
+
ok(records.some((record) => record.level === "error" && record.rawMessage === "Failed to fetch document: {status} {url} {headers}"));
|
|
1353
|
+
records.length = 0;
|
|
1354
|
+
const invalidDocumentLoader = async (url) => ({
|
|
1355
|
+
contextUrl: null,
|
|
1356
|
+
documentUrl: url,
|
|
1357
|
+
document: null
|
|
1358
|
+
});
|
|
1359
|
+
deepStrictEqual(await new Announce({ object: new URL(invalidObjectUrl) }).getObject({
|
|
1360
|
+
documentLoader: invalidDocumentLoader,
|
|
1361
|
+
suppressError: true
|
|
1362
|
+
}), null);
|
|
1363
|
+
ok(records.some((record) => record.level === "warning" && record.rawMessage === "Failed to parse {url}: {error}"));
|
|
1364
|
+
deepStrictEqual(records.some((record) => record.level === "error" && record.rawMessage === "Failed to parse {url}: {error}"), false);
|
|
1365
|
+
records.length = 0;
|
|
1366
|
+
const unsuppressedParsing = new Announce({ object: new URL(invalidObjectUrl) });
|
|
1367
|
+
await rejects(() => unsuppressedParsing.getObject({ documentLoader: invalidDocumentLoader }), TypeError);
|
|
1368
|
+
} finally {
|
|
1369
|
+
esm_default.hardReset();
|
|
1370
|
+
await reset();
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1176
1374
|
test("Activity.getObject() fetches canonical portable IRIs", async () => {
|
|
1177
1375
|
const fetchedUrls = [];
|
|
1178
1376
|
const documentLoader = async (url) => {
|
|
@@ -1197,7 +1395,8 @@ test("Activity.getObject() fetches canonical portable IRIs", async () => {
|
|
|
1197
1395
|
contextLoader: mockDocumentLoader
|
|
1198
1396
|
})).getObject({
|
|
1199
1397
|
documentLoader,
|
|
1200
|
-
contextLoader: mockDocumentLoader
|
|
1398
|
+
contextLoader: mockDocumentLoader,
|
|
1399
|
+
verifyPortableObject: async () => ({ verified: true })
|
|
1201
1400
|
}), Note);
|
|
1202
1401
|
deepStrictEqual(fetchedUrls, ["ap+ef61://did:key:z6Mkabc/objects/1"]);
|
|
1203
1402
|
});
|
|
@@ -1226,6 +1425,72 @@ test({
|
|
|
1226
1425
|
deepStrictEqual(objects2[0].name, "Second object");
|
|
1227
1426
|
}
|
|
1228
1427
|
});
|
|
1428
|
+
function embeddedPropertyDocument(property) {
|
|
1429
|
+
const context = "https://www.w3.org/ns/activitystreams";
|
|
1430
|
+
return {
|
|
1431
|
+
"@context": [context, { CustomImage: `${context}#Image` }],
|
|
1432
|
+
type: property === "attachment" ? "Note" : "Create",
|
|
1433
|
+
[property]: [
|
|
1434
|
+
{
|
|
1435
|
+
"@context": [context, { "@vocab": "https://example.com/" }],
|
|
1436
|
+
type: "CustomImage",
|
|
1437
|
+
name: "Malformed when reparsed"
|
|
1438
|
+
},
|
|
1439
|
+
{
|
|
1440
|
+
"@context": context,
|
|
1441
|
+
type: "Image",
|
|
1442
|
+
name: "Cached image"
|
|
1443
|
+
},
|
|
1444
|
+
{
|
|
1445
|
+
type: "Image",
|
|
1446
|
+
name: "Decoded image"
|
|
1447
|
+
}
|
|
1448
|
+
]
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
for (const suppressError of [
|
|
1452
|
+
void 0,
|
|
1453
|
+
false,
|
|
1454
|
+
true
|
|
1455
|
+
]) {
|
|
1456
|
+
test(`Note.getAttachments() cached parse failure (suppressError: ${suppressError})`, async () => {
|
|
1457
|
+
const jsonLd = embeddedPropertyDocument("attachment");
|
|
1458
|
+
const note = await Note.fromJsonLd(jsonLd);
|
|
1459
|
+
if (suppressError) {
|
|
1460
|
+
for (let i = 0; i < 2; i++) deepStrictEqual((await Array.fromAsync(note.getAttachments({ suppressError }))).map((attachment) => attachment.name), ["Cached image", "Decoded image"]);
|
|
1461
|
+
await rejects(() => Array.fromAsync(note.getAttachments()), TypeError);
|
|
1462
|
+
} else await rejects(() => Array.fromAsync(note.getAttachments({ suppressError })), TypeError);
|
|
1463
|
+
deepStrictEqual(await note.toJsonLd(), jsonLd);
|
|
1464
|
+
});
|
|
1465
|
+
test(`Activity.getObject() cached parse failure (suppressError: ${suppressError})`, async () => {
|
|
1466
|
+
const jsonLd = embeddedPropertyDocument("object");
|
|
1467
|
+
const activity = await Activity.fromJsonLd(jsonLd);
|
|
1468
|
+
if (suppressError) {
|
|
1469
|
+
deepStrictEqual(await activity.getObject({ suppressError }), null);
|
|
1470
|
+
deepStrictEqual(await activity.getObject({ suppressError }), null);
|
|
1471
|
+
await rejects(() => activity.getObject(), TypeError);
|
|
1472
|
+
} else await rejects(() => activity.getObject({ suppressError }), TypeError);
|
|
1473
|
+
deepStrictEqual(await activity.toJsonLd(), jsonLd);
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
test("Note.getAttachments() suppresses cached context loader failures", async () => {
|
|
1477
|
+
const note = await Note.fromJsonLd(embeddedPropertyDocument("attachment"));
|
|
1478
|
+
const contextLoader = () => Promise.reject(/* @__PURE__ */ new Error("Context loader failed"));
|
|
1479
|
+
await rejects(() => Array.fromAsync(note.getAttachments({ contextLoader })), (error) => error instanceof Error && !(error instanceof TypeError));
|
|
1480
|
+
deepStrictEqual((await Array.fromAsync(note.getAttachments({
|
|
1481
|
+
contextLoader,
|
|
1482
|
+
suppressError: true
|
|
1483
|
+
}))).map((attachment) => attachment.name), ["Decoded image"]);
|
|
1484
|
+
});
|
|
1485
|
+
test("Activity.getObject() suppresses cached context loader failures", async () => {
|
|
1486
|
+
const activity = await Activity.fromJsonLd(embeddedPropertyDocument("object"));
|
|
1487
|
+
const contextLoader = () => Promise.reject(/* @__PURE__ */ new Error("Context loader failed"));
|
|
1488
|
+
await rejects(() => activity.getObject({ contextLoader }), (error) => error instanceof Error && !(error instanceof TypeError));
|
|
1489
|
+
deepStrictEqual(await activity.getObject({
|
|
1490
|
+
contextLoader,
|
|
1491
|
+
suppressError: true
|
|
1492
|
+
}), null);
|
|
1493
|
+
});
|
|
1229
1494
|
test("Activity.clone()", async () => {
|
|
1230
1495
|
const activity = new Activity({
|
|
1231
1496
|
actor: new Person({ name: "John Doe" }),
|
|
@@ -1273,6 +1538,21 @@ test({
|
|
|
1273
1538
|
}), "Deno" in globalThis ? "Object {\n attribution: URL \"https://example.com/foo\",\n contents: [\n <en> \"Hello\",\n <zh> \"你好\"\n ],\n id: URL \"https://example.com/\",\n name: \"Test\"\n}" : "Object {\n attribution: URL 'https://example.com/foo',\n contents: [\n <en> 'Hello',\n <zh> '你好'\n ],\n id: URL 'https://example.com/',\n name: 'Test'\n}");
|
|
1274
1539
|
}
|
|
1275
1540
|
});
|
|
1541
|
+
test("inspecting an object prints portable IRIs in their canonical form", () => {
|
|
1542
|
+
const did = "did:key:z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2";
|
|
1543
|
+
const obj = new Object$1({
|
|
1544
|
+
id: parseIri(`ap://${did}/note`),
|
|
1545
|
+
attribution: parseIri(`ap://${did}/actor`)
|
|
1546
|
+
});
|
|
1547
|
+
const inspected = [inspect(obj, { colors: false })];
|
|
1548
|
+
if ("Deno" in globalThis) inspected.push(Deno.inspect(obj, { colors: false }));
|
|
1549
|
+
for (const text of inspected) {
|
|
1550
|
+
ok(text.includes(`ap+ef61://${did}/note`), text);
|
|
1551
|
+
ok(text.includes(`ap+ef61://${did}/actor`), text);
|
|
1552
|
+
ok(!text.includes("%3A"), text);
|
|
1553
|
+
}
|
|
1554
|
+
ok(inspect(new Object$1({ id: new URL("ap://not-a-did/note") })).includes("ap://not-a-did/note"));
|
|
1555
|
+
});
|
|
1276
1556
|
test("Person.fromJsonLd()", async () => {
|
|
1277
1557
|
const person = await Person.fromJsonLd({
|
|
1278
1558
|
"@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
|
|
@@ -2103,6 +2383,83 @@ test("Delete.toJsonLd() compacts embedded QuoteRequest", async () => {
|
|
|
2103
2383
|
type: "Delete"
|
|
2104
2384
|
});
|
|
2105
2385
|
});
|
|
2386
|
+
test("Note.quoteUrl (IRI-typed alias terms)", async () => {
|
|
2387
|
+
const jsonLd = {
|
|
2388
|
+
"@context": ["https://www.w3.org/ns/activitystreams", {
|
|
2389
|
+
fedibird: "http://fedibird.com/ns#",
|
|
2390
|
+
misskey: "https://misskey-hub.net/ns#",
|
|
2391
|
+
_misskey_quote: {
|
|
2392
|
+
"@id": "misskey:_misskey_quote",
|
|
2393
|
+
"@type": "@id"
|
|
2394
|
+
},
|
|
2395
|
+
quoteUri: {
|
|
2396
|
+
"@id": "fedibird:quoteUri",
|
|
2397
|
+
"@type": "@id"
|
|
2398
|
+
}
|
|
2399
|
+
}],
|
|
2400
|
+
id: "https://example.com/notes/1",
|
|
2401
|
+
type: "Note",
|
|
2402
|
+
_misskey_quote: "https://example.com/notes/quoted",
|
|
2403
|
+
quoteUri: "https://example.com/notes/quoted2"
|
|
2404
|
+
};
|
|
2405
|
+
deepStrictEqual((await Note.fromJsonLd(jsonLd)).quoteUrl, new URL("https://example.com/notes/quoted"));
|
|
2406
|
+
delete jsonLd._misskey_quote;
|
|
2407
|
+
deepStrictEqual((await Note.fromJsonLd(jsonLd)).quoteUrl, new URL("https://example.com/notes/quoted2"));
|
|
2408
|
+
});
|
|
2409
|
+
test("Note.quoteUrl (IRI-typed primary term)", async () => {
|
|
2410
|
+
const context = ["https://www.w3.org/ns/activitystreams", {
|
|
2411
|
+
misskey: "https://misskey-hub.net/ns#",
|
|
2412
|
+
quoteUrl: {
|
|
2413
|
+
"@id": "as:quoteUrl",
|
|
2414
|
+
"@type": "@id"
|
|
2415
|
+
},
|
|
2416
|
+
_misskey_quote: "misskey:_misskey_quote"
|
|
2417
|
+
}];
|
|
2418
|
+
deepStrictEqual((await Note.fromJsonLd({
|
|
2419
|
+
"@context": context,
|
|
2420
|
+
type: "Note",
|
|
2421
|
+
quoteUrl: "https://example.com/object"
|
|
2422
|
+
})).quoteUrl, new URL("https://example.com/object"));
|
|
2423
|
+
deepStrictEqual((await Note.fromJsonLd({
|
|
2424
|
+
"@context": context,
|
|
2425
|
+
type: "Note",
|
|
2426
|
+
quoteUrl: "https://example.com/object",
|
|
2427
|
+
_misskey_quote: "https://example.com/object2"
|
|
2428
|
+
})).quoteUrl, new URL("https://example.com/object"));
|
|
2429
|
+
});
|
|
2430
|
+
test("Note.quoteUrl (unparsable IRI-valued terms)", async () => {
|
|
2431
|
+
const context = ["https://www.w3.org/ns/activitystreams", {
|
|
2432
|
+
misskey: "https://misskey-hub.net/ns#",
|
|
2433
|
+
_misskey_quote: {
|
|
2434
|
+
"@id": "misskey:_misskey_quote",
|
|
2435
|
+
"@type": "@id"
|
|
2436
|
+
}
|
|
2437
|
+
}];
|
|
2438
|
+
for (const quote of [
|
|
2439
|
+
{
|
|
2440
|
+
type: "Note",
|
|
2441
|
+
content: "An inlined quote without an id"
|
|
2442
|
+
},
|
|
2443
|
+
"_:b0",
|
|
2444
|
+
"",
|
|
2445
|
+
"notes/relative"
|
|
2446
|
+
]) {
|
|
2447
|
+
const loaded = await Note.fromJsonLd({
|
|
2448
|
+
"@context": context,
|
|
2449
|
+
id: "https://example.com/notes/1",
|
|
2450
|
+
type: "Note",
|
|
2451
|
+
content: "Hello",
|
|
2452
|
+
_misskey_quote: quote
|
|
2453
|
+
});
|
|
2454
|
+
deepStrictEqual(loaded.quoteUrl, null, JSON.stringify(quote));
|
|
2455
|
+
deepStrictEqual(loaded.content, "Hello");
|
|
2456
|
+
}
|
|
2457
|
+
deepStrictEqual((await Note.fromJsonLd({
|
|
2458
|
+
"@context": context,
|
|
2459
|
+
type: "Note",
|
|
2460
|
+
_misskey_quote: "at://did:plc:abc/app.bsky.feed.post/xyz"
|
|
2461
|
+
})).quoteUrl, new URL("at://" + encodeURIComponent("did:plc:abc/app.bsky.feed.post/xyz")));
|
|
2462
|
+
});
|
|
2106
2463
|
test("Key.publicKey", async () => {
|
|
2107
2464
|
const jwk = {
|
|
2108
2465
|
kty: "RSA",
|
|
@@ -2840,7 +3197,11 @@ test("FEP-fe34: Portable DID authorities are cross-origin boundaries", async ()
|
|
|
2840
3197
|
};
|
|
2841
3198
|
throw new Error("Document not found");
|
|
2842
3199
|
};
|
|
2843
|
-
const result = await create.getObject({
|
|
3200
|
+
const result = await create.getObject({
|
|
3201
|
+
documentLoader,
|
|
3202
|
+
contextLoader: mockDocumentLoader,
|
|
3203
|
+
verifyPortableObject: async () => ({ verified: true })
|
|
3204
|
+
});
|
|
2844
3205
|
assertInstanceOf(result, Note);
|
|
2845
3206
|
deepStrictEqual(result.content, "Fetched portable note");
|
|
2846
3207
|
});
|
|
@@ -3536,7 +3897,9 @@ for (const typeUri in types) {
|
|
|
3536
3897
|
...initValues
|
|
3537
3898
|
});
|
|
3538
3899
|
const jsonLd = await instance.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
3539
|
-
|
|
3900
|
+
const defaultContexts = Array.isArray(type.defaultContext) ? type.defaultContext : [type.defaultContext];
|
|
3901
|
+
const extraContexts = [...new Set(allProperties.flatMap((property) => property.extraContext == null || defaultContexts.some((context) => context === property.extraContext) ? [] : [property.extraContext]))];
|
|
3902
|
+
deepStrictEqual(jsonLd["@context"], extraContexts.length === 0 ? type.defaultContext : [...defaultContexts, ...extraContexts]);
|
|
3540
3903
|
if (type.entity) deepStrictEqual(jsonLd.id, "https://example.com/");
|
|
3541
3904
|
const restored = await cls.fromJsonLd(jsonLd, {
|
|
3542
3905
|
documentLoader: mockDocumentLoader,
|
|
@@ -3599,5 +3962,309 @@ for (const typeUri in types) {
|
|
|
3599
3962
|
deepStrictEqual(cls.typeId, new URL(type.uri));
|
|
3600
3963
|
});
|
|
3601
3964
|
}
|
|
3965
|
+
test("Announce.getObject() suppresses redirect, context, and private URL logs", async () => {
|
|
3966
|
+
const records = [];
|
|
3967
|
+
const objectUrl = "https://example.com/suppression-object";
|
|
3968
|
+
const contextUrl = "https://example.com/suppression-context";
|
|
3969
|
+
const scenarios = [
|
|
3970
|
+
"redirect limit",
|
|
3971
|
+
"redirect loop",
|
|
3972
|
+
"context",
|
|
3973
|
+
"private",
|
|
3974
|
+
"private redirect"
|
|
3975
|
+
];
|
|
3976
|
+
await reset();
|
|
3977
|
+
await configure({
|
|
3978
|
+
sinks: { capture: (record) => {
|
|
3979
|
+
records.push(record);
|
|
3980
|
+
} },
|
|
3981
|
+
loggers: [{
|
|
3982
|
+
category: ["fedify"],
|
|
3983
|
+
lowestLevel: "debug",
|
|
3984
|
+
sinks: ["capture"]
|
|
3985
|
+
}]
|
|
3986
|
+
});
|
|
3987
|
+
try {
|
|
3988
|
+
for (const scenario of scenarios) {
|
|
3989
|
+
esm_default.spyGlobal();
|
|
3990
|
+
let fetches = 0;
|
|
3991
|
+
esm_default.get("begin:https://example.com/", (call) => {
|
|
3992
|
+
fetches++;
|
|
3993
|
+
if (scenario === "redirect limit") return Response.redirect(`https://example.com/redirect-${fetches}`, 302);
|
|
3994
|
+
if (scenario === "redirect loop") return Response.redirect(objectUrl, 302);
|
|
3995
|
+
if (scenario === "private redirect") return Response.redirect("http://127.0.0.1/private", 302);
|
|
3996
|
+
if (call.url === contextUrl) return new Response(null, { status: 404 });
|
|
3997
|
+
return new Response(JSON.stringify({
|
|
3998
|
+
"@context": ["https://www.w3.org/ns/activitystreams", contextUrl],
|
|
3999
|
+
id: objectUrl,
|
|
4000
|
+
type: "Note"
|
|
4001
|
+
}), { headers: { "Content-Type": "application/activity+json" } });
|
|
4002
|
+
});
|
|
4003
|
+
try {
|
|
4004
|
+
for (const suppressError of [
|
|
4005
|
+
true,
|
|
4006
|
+
false,
|
|
4007
|
+
void 0
|
|
4008
|
+
]) {
|
|
4009
|
+
records.length = 0;
|
|
4010
|
+
fetches = 0;
|
|
4011
|
+
const announce = new Announce({ object: new URL(scenario === "private" ? "http://127.0.0.1/private" : objectUrl) });
|
|
4012
|
+
if (suppressError) {
|
|
4013
|
+
deepStrictEqual(await announce.getObject({ suppressError }), null);
|
|
4014
|
+
deepStrictEqual(records.filter((r) => r.level === "error"), [], scenario);
|
|
4015
|
+
ok(records.some((r) => r.level === "warning"), scenario);
|
|
4016
|
+
} else {
|
|
4017
|
+
await rejects(() => announce.getObject({ suppressError }));
|
|
4018
|
+
ok(records.some((r) => r.level === "error"), scenario);
|
|
4019
|
+
}
|
|
4020
|
+
if (scenario === "private") deepStrictEqual(fetches, 0);
|
|
4021
|
+
if (scenario === "private redirect") deepStrictEqual(fetches, 1);
|
|
4022
|
+
}
|
|
4023
|
+
} finally {
|
|
4024
|
+
esm_default.hardReset();
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
} finally {
|
|
4028
|
+
await reset();
|
|
4029
|
+
}
|
|
4030
|
+
});
|
|
4031
|
+
test("suppressed context loading does not persist on fetched objects", async () => {
|
|
4032
|
+
const states = [];
|
|
4033
|
+
const contextLoader = (url, options) => {
|
|
4034
|
+
if (url === "https://www.w3.org/ns/activitystreams") return mockDocumentLoader(url);
|
|
4035
|
+
states.push(options?.suppressError);
|
|
4036
|
+
return Promise.resolve({
|
|
4037
|
+
contextUrl: null,
|
|
4038
|
+
documentUrl: url,
|
|
4039
|
+
document: { "@context": {
|
|
4040
|
+
as: "https://www.w3.org/ns/activitystreams#",
|
|
4041
|
+
name: "as:name"
|
|
4042
|
+
} }
|
|
4043
|
+
});
|
|
4044
|
+
};
|
|
4045
|
+
const documentLoader = (url) => Promise.resolve({
|
|
4046
|
+
contextUrl: null,
|
|
4047
|
+
documentUrl: url,
|
|
4048
|
+
document: {
|
|
4049
|
+
"@context": ["https://www.w3.org/ns/activitystreams", "https://example.com/scoped-context"],
|
|
4050
|
+
id: url,
|
|
4051
|
+
type: url.endsWith("actor") ? "Person" : "Note",
|
|
4052
|
+
attributedTo: "https://example.com/scoped-actor"
|
|
4053
|
+
}
|
|
4054
|
+
});
|
|
4055
|
+
const note = await new Announce({ object: new URL("https://example.com/scoped-note") }).getObject({
|
|
4056
|
+
documentLoader,
|
|
4057
|
+
contextLoader,
|
|
4058
|
+
suppressError: true
|
|
4059
|
+
});
|
|
4060
|
+
assertInstanceOf(note, Note);
|
|
4061
|
+
ok(states.includes(true));
|
|
4062
|
+
states.length = 0;
|
|
4063
|
+
assertInstanceOf(await note.getAttribution(), Person);
|
|
4064
|
+
ok(states.length > 0);
|
|
4065
|
+
deepStrictEqual(states.includes(true), false);
|
|
4066
|
+
});
|
|
4067
|
+
test("attribution chains alternate ordinary and portable suppression", async () => {
|
|
4068
|
+
const states = [];
|
|
4069
|
+
const depths = [];
|
|
4070
|
+
let hop = 0;
|
|
4071
|
+
const contextUrl = "https://example.com/attribution-context";
|
|
4072
|
+
const contextLoader = (url, options) => {
|
|
4073
|
+
if (url !== contextUrl) return mockDocumentLoader(url, options);
|
|
4074
|
+
states.push(options?.suppressError);
|
|
4075
|
+
const stack = (/* @__PURE__ */ new Error()).stack;
|
|
4076
|
+
ok(stack);
|
|
4077
|
+
depths[hop]?.push(stack.split("\n").length);
|
|
4078
|
+
return Promise.resolve({
|
|
4079
|
+
contextUrl: null,
|
|
4080
|
+
documentUrl: url,
|
|
4081
|
+
document: { "@context": { name: "https://www.w3.org/ns/activitystreams#name" } }
|
|
4082
|
+
});
|
|
4083
|
+
};
|
|
4084
|
+
const documentLoader = (url) => {
|
|
4085
|
+
const hop = Number(new URL(url).pathname.split("/").at(-1));
|
|
4086
|
+
return Promise.resolve({
|
|
4087
|
+
contextUrl: null,
|
|
4088
|
+
documentUrl: url,
|
|
4089
|
+
document: {
|
|
4090
|
+
"@context": ["https://www.w3.org/ns/activitystreams", contextUrl],
|
|
4091
|
+
id: url,
|
|
4092
|
+
type: "Person",
|
|
4093
|
+
name: `Actor ${hop}`,
|
|
4094
|
+
attributedTo: `https://example.com/actors/${hop + 1}`
|
|
4095
|
+
}
|
|
4096
|
+
});
|
|
4097
|
+
};
|
|
4098
|
+
let person = new Person({
|
|
4099
|
+
id: new URL("https://example.com/actors/root"),
|
|
4100
|
+
attribution: new URL("https://example.com/actors/0")
|
|
4101
|
+
}, {
|
|
4102
|
+
documentLoader,
|
|
4103
|
+
contextLoader
|
|
4104
|
+
});
|
|
4105
|
+
const stackTraceLimit = globalThis.Object.getOwnPropertyDescriptor(Error, "stackTraceLimit");
|
|
4106
|
+
try {
|
|
4107
|
+
ok(Reflect.set(Error, "stackTraceLimit", Infinity));
|
|
4108
|
+
for (; hop < 100; hop++) {
|
|
4109
|
+
depths[hop] = [];
|
|
4110
|
+
const next = await person.getAttribution({
|
|
4111
|
+
suppressError: true,
|
|
4112
|
+
...hop % 2 === 0 ? {} : { verifyPortableObject: () => Promise.resolve({ verified: true }) }
|
|
4113
|
+
});
|
|
4114
|
+
assertInstanceOf(next, Person);
|
|
4115
|
+
deepStrictEqual(next.id, new URL(`https://example.com/actors/${hop}`));
|
|
4116
|
+
person = next;
|
|
4117
|
+
}
|
|
4118
|
+
ok(depths.every((values) => values.length > 0));
|
|
4119
|
+
deepStrictEqual(depths[98], depths[2]);
|
|
4120
|
+
deepStrictEqual(depths[99], depths[3]);
|
|
4121
|
+
ok(states.length >= 100);
|
|
4122
|
+
ok(states.every((state) => state === true));
|
|
4123
|
+
states.length = 0;
|
|
4124
|
+
assertInstanceOf(await person.getAttribution(), Person);
|
|
4125
|
+
ok(states.length > 0);
|
|
4126
|
+
ok(states.every((state) => state !== true));
|
|
4127
|
+
} finally {
|
|
4128
|
+
if (stackTraceLimit == null) Reflect.deleteProperty(Error, "stackTraceLimit");
|
|
4129
|
+
else globalThis.Object.defineProperty(Error, "stackTraceLimit", stackTraceLimit);
|
|
4130
|
+
}
|
|
4131
|
+
});
|
|
4132
|
+
test("portable accessors suppress document and context failure logs", async () => {
|
|
4133
|
+
const records = [];
|
|
4134
|
+
const id = new URL("ap+ef61://did%3Akey%3Az6Mkabc/objects/1");
|
|
4135
|
+
const contextUrl = "https://example.com/portable-suppression-context";
|
|
4136
|
+
await reset();
|
|
4137
|
+
await configure({
|
|
4138
|
+
sinks: { capture: (record) => {
|
|
4139
|
+
records.push(record);
|
|
4140
|
+
} },
|
|
4141
|
+
loggers: [{
|
|
4142
|
+
category: ["fedify"],
|
|
4143
|
+
lowestLevel: "debug",
|
|
4144
|
+
sinks: ["capture"]
|
|
4145
|
+
}]
|
|
4146
|
+
});
|
|
4147
|
+
try {
|
|
4148
|
+
for (const failContext of [false, true]) {
|
|
4149
|
+
esm_default.spyGlobal();
|
|
4150
|
+
esm_default.get("begin:https://example.com/", (call) => {
|
|
4151
|
+
if (!failContext || call.url === contextUrl) return new Response(null, { status: 404 });
|
|
4152
|
+
return new Response(JSON.stringify({
|
|
4153
|
+
"@context": ["https://www.w3.org/ns/activitystreams", contextUrl],
|
|
4154
|
+
id: "ap://did:key:z6Mkabc/objects/1",
|
|
4155
|
+
type: "Note"
|
|
4156
|
+
}), { headers: { "Content-Type": "application/activity+json" } });
|
|
4157
|
+
});
|
|
4158
|
+
try {
|
|
4159
|
+
for (const suppressError of [
|
|
4160
|
+
true,
|
|
4161
|
+
false,
|
|
4162
|
+
void 0
|
|
4163
|
+
]) {
|
|
4164
|
+
records.length = 0;
|
|
4165
|
+
const announce = new Announce({ object: id });
|
|
4166
|
+
const options = {
|
|
4167
|
+
suppressError,
|
|
4168
|
+
gateways: ["https://example.com/"],
|
|
4169
|
+
verifyPortableObject: () => Promise.resolve({ verified: true })
|
|
4170
|
+
};
|
|
4171
|
+
if (suppressError) {
|
|
4172
|
+
deepStrictEqual(await announce.getObject(options), null);
|
|
4173
|
+
deepStrictEqual(records.filter((r) => r.level === "error"), []);
|
|
4174
|
+
ok(records.some((r) => r.level === "warning"));
|
|
4175
|
+
} else {
|
|
4176
|
+
await rejects(() => announce.getObject(options));
|
|
4177
|
+
ok(records.some((r) => r.level === "error"));
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
} finally {
|
|
4181
|
+
esm_default.hardReset();
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
} finally {
|
|
4185
|
+
await reset();
|
|
4186
|
+
}
|
|
4187
|
+
});
|
|
4188
|
+
test("portable verifiers suppress document logs only during dereferencing", async () => {
|
|
4189
|
+
const records = [];
|
|
4190
|
+
const id = new URL("ap+ef61://did%3Akey%3Az6Mkabc/objects/1");
|
|
4191
|
+
const verificationUrl = "https://example.com/verification-document";
|
|
4192
|
+
const missingUrl = "https://example.com/missing-verification-document";
|
|
4193
|
+
const signal = new AbortController().signal;
|
|
4194
|
+
await reset();
|
|
4195
|
+
await configure({
|
|
4196
|
+
sinks: { capture: (record) => records.push(record) },
|
|
4197
|
+
loggers: [{
|
|
4198
|
+
category: ["fedify"],
|
|
4199
|
+
lowestLevel: "debug",
|
|
4200
|
+
sinks: ["capture"]
|
|
4201
|
+
}]
|
|
4202
|
+
});
|
|
4203
|
+
try {
|
|
4204
|
+
for (const failVerification of [false, true]) {
|
|
4205
|
+
esm_default.spyGlobal();
|
|
4206
|
+
esm_default.get("begin:https://example.com/.well-known/", {
|
|
4207
|
+
headers: { "Content-Type": "application/activity+json" },
|
|
4208
|
+
body: {
|
|
4209
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
4210
|
+
id: "ap://did:key:z6Mkabc/objects/1",
|
|
4211
|
+
type: "Note"
|
|
4212
|
+
}
|
|
4213
|
+
});
|
|
4214
|
+
esm_default.get(verificationUrl, {
|
|
4215
|
+
status: failVerification ? 404 : 200,
|
|
4216
|
+
headers: { "Content-Type": "application/ld+json" },
|
|
4217
|
+
body: {}
|
|
4218
|
+
});
|
|
4219
|
+
esm_default.get(missingUrl, { status: 404 });
|
|
4220
|
+
try {
|
|
4221
|
+
for (const suppressError of [
|
|
4222
|
+
true,
|
|
4223
|
+
false,
|
|
4224
|
+
void 0
|
|
4225
|
+
]) {
|
|
4226
|
+
records.length = 0;
|
|
4227
|
+
const baseLoader = getDocumentLoader();
|
|
4228
|
+
const calls = [];
|
|
4229
|
+
const documentLoader = (url, options) => {
|
|
4230
|
+
calls.push(options);
|
|
4231
|
+
return baseLoader(url, options);
|
|
4232
|
+
};
|
|
4233
|
+
let retainedLoader;
|
|
4234
|
+
const loaderOptions = { signal };
|
|
4235
|
+
const object = await new Announce({ object: id }).getObject({
|
|
4236
|
+
suppressError,
|
|
4237
|
+
documentLoader,
|
|
4238
|
+
gateways: ["https://example.com/"],
|
|
4239
|
+
verifyPortableObject: async (_document, options) => {
|
|
4240
|
+
retainedLoader = options.documentLoader;
|
|
4241
|
+
ok(options.documentLoader);
|
|
4242
|
+
await options.documentLoader(verificationUrl, loaderOptions);
|
|
4243
|
+
return { verified: true };
|
|
4244
|
+
}
|
|
4245
|
+
});
|
|
4246
|
+
if (failVerification) {
|
|
4247
|
+
deepStrictEqual(object, null);
|
|
4248
|
+
deepStrictEqual(records.some((record) => record.level === "error"), !suppressError);
|
|
4249
|
+
ok(records.some((record) => record.level === "warning"));
|
|
4250
|
+
} else assertInstanceOf(object, Note);
|
|
4251
|
+
deepStrictEqual(calls.at(-1)?.signal, signal);
|
|
4252
|
+
deepStrictEqual(calls.at(-1)?.suppressError, suppressError ? true : void 0);
|
|
4253
|
+
deepStrictEqual(loaderOptions, { signal });
|
|
4254
|
+
records.length = 0;
|
|
4255
|
+
const releasedLoader = retainedLoader;
|
|
4256
|
+
ok(releasedLoader);
|
|
4257
|
+
await rejects(() => releasedLoader(missingUrl, loaderOptions), FetchError);
|
|
4258
|
+
deepStrictEqual(calls.at(-1), loaderOptions);
|
|
4259
|
+
ok(records.some((record) => record.level === "error"));
|
|
4260
|
+
}
|
|
4261
|
+
} finally {
|
|
4262
|
+
esm_default.hardReset();
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
} finally {
|
|
4266
|
+
await reset();
|
|
4267
|
+
}
|
|
4268
|
+
});
|
|
3602
4269
|
//#endregion
|
|
3603
4270
|
export {};
|