@fedify/vocab 2.4.0-dev.1564 → 2.4.0-dev.1570
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 +9909 -13825
- package/dist/mod.d.cts +676 -1629
- package/dist/mod.d.ts +676 -1629
- package/dist/mod.js +9911 -13823
- package/dist-tests/{actor-B6FsWrG4.mjs → actor-BTMC-aMT.mjs} +2 -2
- package/dist-tests/actor.test.mjs +2 -2
- package/dist-tests/application.yaml +0 -13
- package/dist-tests/group.yaml +0 -13
- package/dist-tests/interactionpolicy.yaml +1 -16
- package/dist-tests/lookup.test.mjs +5 -7
- package/dist-tests/organization.yaml +0 -13
- package/dist-tests/person.yaml +0 -13
- package/dist-tests/service.yaml +0 -13
- package/dist-tests/type.test.mjs +1 -1
- package/dist-tests/{vocab-DOHLQ4UQ.mjs → vocab-XnSGFOqq.mjs} +9908 -13822
- package/dist-tests/vocab.test.mjs +1 -986
- package/package.json +4 -4
- package/src/__snapshots__/vocab.test.ts.snap +0 -609
- package/src/application.yaml +0 -13
- package/src/group.yaml +0 -13
- package/src/interactionpolicy.yaml +1 -16
- package/src/lookup.ts +2 -6
- package/src/organization.yaml +0 -13
- package/src/person.yaml +0 -13
- package/src/service.yaml +0 -13
- package/src/vocab.test.ts +121 -1539
- package/dist-tests/featureauthorization.yaml +0 -33
- package/dist-tests/featuredcollection.yaml +0 -41
- package/dist-tests/featureditem.yaml +0 -34
- package/dist-tests/featurerequest.yaml +0 -21
- package/src/featureauthorization.yaml +0 -33
- package/src/featuredcollection.yaml +0 -41
- package/src/featureditem.yaml +0 -34
- package/src/featurerequest.yaml +0 -21
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
globalThis.addEventListener = () => {};
|
|
3
|
-
import { A as
|
|
3
|
+
import { A as QuoteAuthorization, C as Offer, D as Place, E as Person, F as Tombstone, M as Reject, O as Proposal, P as Source, R as vocab_exports, S as Object$1, _ as InteractionPolicy, b as Measure, c as Create, d as Document, f as Endpoints, g as Intent, h as Hashtag, i as Announce, j as QuoteRequest, k as Question, l as CryptographicKey, n as Activity, o as Collection, p as Follow, r as Agreement, s as Commitment, t as Accept, u as Delete, v as InteractionRule, w as OrderedCollectionPage, x as Note, y as Link } from "./vocab-XnSGFOqq.mjs";
|
|
4
4
|
import { t as assertInstanceOf } from "./utils-CE8Dk5hm.mjs";
|
|
5
5
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
6
6
|
import { deepStrictEqual, notDeepStrictEqual, ok, rejects, throws } from "node:assert/strict";
|
|
@@ -53,18 +53,6 @@ const QUOTE_REQUEST_CONTEXT = [
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
];
|
|
56
|
-
const FEATURE_CONTEXT = [
|
|
57
|
-
"https://www.w3.org/ns/activitystreams",
|
|
58
|
-
"https://w3id.org/security/data-integrity/v1",
|
|
59
|
-
"https://gotosocial.org/ns",
|
|
60
|
-
"https://w3id.org/fep/7aa9"
|
|
61
|
-
];
|
|
62
|
-
const FEATURED_COLLECTION_CONTEXT = [...FEATURE_CONTEXT, {
|
|
63
|
-
Hashtag: "as:Hashtag",
|
|
64
|
-
discoverable: "toot:discoverable",
|
|
65
|
-
sensitive: "as:sensitive",
|
|
66
|
-
toot: "http://joinmastodon.org/ns#"
|
|
67
|
-
}];
|
|
68
56
|
const DELETE_QUOTE_REQUEST_CONTEXT = [
|
|
69
57
|
"https://w3id.org/identity/v1",
|
|
70
58
|
"https://www.w3.org/ns/activitystreams",
|
|
@@ -314,662 +302,6 @@ test("Activity.fromJsonLd()", async () => {
|
|
|
314
302
|
deepStrictEqual(proofs[0].proofValue, decodeMultibase("z3sXaxjKs4M3BRicwWA9peyNPJvJqxtGsDmpt1jjoHCjgeUf71TRFz56osPSfDErszyLp5Ks1EhYSgpDaNM977Rg2"));
|
|
315
303
|
deepStrictEqual(proofs[0].created, Temporal.Instant.from("2023-02-24T23:36:38Z"));
|
|
316
304
|
});
|
|
317
|
-
test("fromJsonLd() handles portable ActivityPub IRIs", async () => {
|
|
318
|
-
const did = "did:key:z6Mkabc";
|
|
319
|
-
const portableActor = `ap://${did}/actor?gateways=https%3A%2F%2Fserver.example`;
|
|
320
|
-
const portableObject = `ap://${did}/objects/1?gateways=https%3A%2F%2Fserver.example`;
|
|
321
|
-
const uppercasePortableObject = `AP://${did}/objects/1?gateways=https%3A%2F%2Fserver.example`;
|
|
322
|
-
const portablePage = `ap://${did}/actor/outbox?page=2`;
|
|
323
|
-
const note = await Note.fromJsonLd({
|
|
324
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
325
|
-
type: "Note",
|
|
326
|
-
id: uppercasePortableObject,
|
|
327
|
-
attributedTo: `ap+ef61://${encodeURIComponent(did)}/actor?gateways=https%3A%2F%2Fserver.example`
|
|
328
|
-
}, {
|
|
329
|
-
documentLoader: mockDocumentLoader,
|
|
330
|
-
contextLoader: mockDocumentLoader
|
|
331
|
-
});
|
|
332
|
-
deepStrictEqual(note.id, new URL("ap+ef61://did%3Akey%3Az6Mkabc/objects/1?gateways=https%3A%2F%2Fserver.example"));
|
|
333
|
-
deepStrictEqual(note.attributionId, new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor?gateways=https%3A%2F%2Fserver.example"));
|
|
334
|
-
const noteJson = await note.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
335
|
-
deepStrictEqual(noteJson.type, "Note");
|
|
336
|
-
deepStrictEqual(noteJson.id, "ap+ef61://did:key:z6Mkabc/objects/1?gateways=https%3A%2F%2Fserver.example");
|
|
337
|
-
deepStrictEqual(noteJson.attributedTo, "ap+ef61://did:key:z6Mkabc/actor?gateways=https%3A%2F%2Fserver.example");
|
|
338
|
-
const activity = await Activity.fromJsonLd({
|
|
339
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
340
|
-
type: "Create",
|
|
341
|
-
actor: portableActor,
|
|
342
|
-
object: portableObject
|
|
343
|
-
}, {
|
|
344
|
-
documentLoader: mockDocumentLoader,
|
|
345
|
-
contextLoader: mockDocumentLoader
|
|
346
|
-
});
|
|
347
|
-
deepStrictEqual(activity.actorId, new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor?gateways=https%3A%2F%2Fserver.example"));
|
|
348
|
-
const activityJson = await activity.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
349
|
-
deepStrictEqual(activityJson.type, "Create");
|
|
350
|
-
deepStrictEqual(activityJson.actor, "ap+ef61://did:key:z6Mkabc/actor?gateways=https%3A%2F%2Fserver.example");
|
|
351
|
-
deepStrictEqual(activityJson.object, "ap+ef61://did:key:z6Mkabc/objects/1?gateways=https%3A%2F%2Fserver.example");
|
|
352
|
-
const person = await Person.fromJsonLd({
|
|
353
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
354
|
-
type: "Person",
|
|
355
|
-
inbox: `ap+ef61://${did}/actor/inbox`,
|
|
356
|
-
outbox: `ap+ef61://${did}/actor/outbox`
|
|
357
|
-
}, {
|
|
358
|
-
documentLoader: mockDocumentLoader,
|
|
359
|
-
contextLoader: mockDocumentLoader
|
|
360
|
-
});
|
|
361
|
-
deepStrictEqual(person.inboxId, new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor/inbox"));
|
|
362
|
-
deepStrictEqual(person.outboxId, new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor/outbox"));
|
|
363
|
-
const personJson = await person.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
364
|
-
deepStrictEqual(personJson.type, "Person");
|
|
365
|
-
deepStrictEqual(personJson.inbox, "ap+ef61://did:key:z6Mkabc/actor/inbox");
|
|
366
|
-
deepStrictEqual(personJson.outbox, "ap+ef61://did:key:z6Mkabc/actor/outbox");
|
|
367
|
-
const page = await OrderedCollectionPage.fromJsonLd({
|
|
368
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
369
|
-
type: "OrderedCollectionPage",
|
|
370
|
-
next: portablePage,
|
|
371
|
-
prev: `ap+ef61://${encodeURIComponent(did)}/actor/outbox?page=1`
|
|
372
|
-
}, {
|
|
373
|
-
documentLoader: mockDocumentLoader,
|
|
374
|
-
contextLoader: mockDocumentLoader
|
|
375
|
-
});
|
|
376
|
-
deepStrictEqual(page.nextId, new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor/outbox?page=2"));
|
|
377
|
-
deepStrictEqual(page.prevId, new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor/outbox?page=1"));
|
|
378
|
-
const pageJson = await page.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
379
|
-
deepStrictEqual(pageJson.type, "OrderedCollectionPage");
|
|
380
|
-
deepStrictEqual(pageJson.next, "ap+ef61://did:key:z6Mkabc/actor/outbox?page=2");
|
|
381
|
-
deepStrictEqual(pageJson.prev, "ap+ef61://did:key:z6Mkabc/actor/outbox?page=1");
|
|
382
|
-
});
|
|
383
|
-
test("fromJsonLd() caches text that mentions portable ActivityPub IRIs", async () => {
|
|
384
|
-
const noteJson = {
|
|
385
|
-
"@context": ["https://www.w3.org/ns/activitystreams", { extra: "https://example.com/ns#extra" }],
|
|
386
|
-
type: "Note",
|
|
387
|
-
id: "https://example.com/notes/1",
|
|
388
|
-
content: "This is text about ap://did:key:z6Mkabc/actor.",
|
|
389
|
-
extra: "This extension property should stay cached."
|
|
390
|
-
};
|
|
391
|
-
deepStrictEqual(await (await Note.fromJsonLd(noteJson, {
|
|
392
|
-
documentLoader: mockDocumentLoader,
|
|
393
|
-
contextLoader: mockDocumentLoader
|
|
394
|
-
})).toJsonLd(), noteJson);
|
|
395
|
-
});
|
|
396
|
-
test("fromJsonLd() preserves extensions with portable ActivityPub IRIs", async () => {
|
|
397
|
-
const jsonLd = await (await Note.fromJsonLd({
|
|
398
|
-
"@context": ["https://www.w3.org/ns/activitystreams", { extra: "https://example.com/ns#extra" }],
|
|
399
|
-
type: "Note",
|
|
400
|
-
id: "ap://did:key:z6Mkabc/objects/1",
|
|
401
|
-
extra: "This extension property should stay cached."
|
|
402
|
-
}, {
|
|
403
|
-
documentLoader: mockDocumentLoader,
|
|
404
|
-
contextLoader: mockDocumentLoader
|
|
405
|
-
})).toJsonLd();
|
|
406
|
-
deepStrictEqual(jsonLd.extra, "This extension property should stay cached.");
|
|
407
|
-
deepStrictEqual(jsonLd.id, "ap+ef61://did:key:z6Mkabc/objects/1");
|
|
408
|
-
});
|
|
409
|
-
test("fromJsonLd() preserves unmapped terms with portable IRIs", async () => {
|
|
410
|
-
const jsonLd = await (await Note.fromJsonLd({
|
|
411
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
412
|
-
type: "Note",
|
|
413
|
-
id: "ap://did:key:z6Mkabc/objects/1",
|
|
414
|
-
extra: "This unmapped property should stay cached."
|
|
415
|
-
}, {
|
|
416
|
-
documentLoader: mockDocumentLoader,
|
|
417
|
-
contextLoader: mockDocumentLoader
|
|
418
|
-
})).toJsonLd();
|
|
419
|
-
deepStrictEqual(jsonLd.extra, "This unmapped property should stay cached.");
|
|
420
|
-
deepStrictEqual(jsonLd.id, "ap+ef61://did:key:z6Mkabc/objects/1");
|
|
421
|
-
});
|
|
422
|
-
test("fromJsonLd() preserves expanded arrays with portable IRIs", async () => {
|
|
423
|
-
const expanded = [{
|
|
424
|
-
"@id": "https://example.com/activities/1",
|
|
425
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Create"],
|
|
426
|
-
"https://www.w3.org/ns/activitystreams#actor": [{ "@id": "ap://did:key:z6Mkabc/actor" }],
|
|
427
|
-
"https://www.w3.org/ns/activitystreams#object": [{ "@id": "https://example.com/objects/1" }]
|
|
428
|
-
}, {
|
|
429
|
-
"@id": "https://example.com/objects/1",
|
|
430
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Note"],
|
|
431
|
-
"https://www.w3.org/ns/activitystreams#content": [{ "@value": "Sibling node should stay cached." }]
|
|
432
|
-
}];
|
|
433
|
-
const activity = await Activity.fromJsonLd(expanded, {
|
|
434
|
-
documentLoader: mockDocumentLoader,
|
|
435
|
-
contextLoader: mockDocumentLoader
|
|
436
|
-
});
|
|
437
|
-
deepStrictEqual(activity.actorId, new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"));
|
|
438
|
-
deepStrictEqual(await activity.toJsonLd(), [{
|
|
439
|
-
"@id": "https://example.com/activities/1",
|
|
440
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Create"],
|
|
441
|
-
"https://www.w3.org/ns/activitystreams#actor": [{ "@id": "ap+ef61://did:key:z6Mkabc/actor" }],
|
|
442
|
-
"https://www.w3.org/ns/activitystreams#object": [{ "@id": "https://example.com/objects/1" }]
|
|
443
|
-
}, {
|
|
444
|
-
"@id": "https://example.com/objects/1",
|
|
445
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Note"],
|
|
446
|
-
"https://www.w3.org/ns/activitystreams#content": [{ "@value": "Sibling node should stay cached." }]
|
|
447
|
-
}]);
|
|
448
|
-
deepStrictEqual(expanded[0]["https://www.w3.org/ns/activitystreams#actor"], [{ "@id": "ap://did:key:z6Mkabc/actor" }]);
|
|
449
|
-
});
|
|
450
|
-
test("fromJsonLd() preserves single-node expanded arrays with portable IRIs", async () => {
|
|
451
|
-
const expanded = [{
|
|
452
|
-
"@id": "ap://did:key:z6Mkabc/objects/1",
|
|
453
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Note"],
|
|
454
|
-
"https://www.w3.org/ns/activitystreams#attributedTo": [{ "@id": "ap://did:key:z6Mkabc/actor" }],
|
|
455
|
-
"https://www.w3.org/ns/activitystreams#content": [{ "@value": "Single expanded node should stay cached as an array." }]
|
|
456
|
-
}];
|
|
457
|
-
deepStrictEqual(await (await Note.fromJsonLd(expanded, {
|
|
458
|
-
documentLoader: mockDocumentLoader,
|
|
459
|
-
contextLoader: mockDocumentLoader
|
|
460
|
-
})).toJsonLd(), [{
|
|
461
|
-
"@id": "ap+ef61://did:key:z6Mkabc/objects/1",
|
|
462
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Note"],
|
|
463
|
-
"https://www.w3.org/ns/activitystreams#attributedTo": [{ "@id": "ap+ef61://did:key:z6Mkabc/actor" }],
|
|
464
|
-
"https://www.w3.org/ns/activitystreams#content": [{ "@value": "Single expanded node should stay cached as an array." }]
|
|
465
|
-
}]);
|
|
466
|
-
deepStrictEqual(expanded[0]["@id"], "ap://did:key:z6Mkabc/objects/1");
|
|
467
|
-
});
|
|
468
|
-
test("fromJsonLd() preserves no-context object shape with portable IRIs", async () => {
|
|
469
|
-
const expanded = {
|
|
470
|
-
"@id": "ap://did:key:z6Mkabc/objects/1",
|
|
471
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Note"],
|
|
472
|
-
"https://www.w3.org/ns/activitystreams#attributedTo": [{ "@id": "ap://did:key:z6Mkabc/actor" }],
|
|
473
|
-
"https://www.w3.org/ns/activitystreams#content": [{ "@value": "No-context object shape should stay cached." }]
|
|
474
|
-
};
|
|
475
|
-
deepStrictEqual(await (await Note.fromJsonLd(expanded, {
|
|
476
|
-
documentLoader: mockDocumentLoader,
|
|
477
|
-
contextLoader: mockDocumentLoader
|
|
478
|
-
})).toJsonLd(), {
|
|
479
|
-
"@id": "ap+ef61://did:key:z6Mkabc/objects/1",
|
|
480
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Note"],
|
|
481
|
-
"https://www.w3.org/ns/activitystreams#attributedTo": [{ "@id": "ap+ef61://did:key:z6Mkabc/actor" }],
|
|
482
|
-
"https://www.w3.org/ns/activitystreams#content": [{ "@value": "No-context object shape should stay cached." }]
|
|
483
|
-
});
|
|
484
|
-
deepStrictEqual(expanded["@id"], "ap://did:key:z6Mkabc/objects/1");
|
|
485
|
-
});
|
|
486
|
-
test("fromJsonLd() preserves expanded subtype cache types", async () => {
|
|
487
|
-
const expanded = [{
|
|
488
|
-
"@id": "https://example.com/activities/1",
|
|
489
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Create"],
|
|
490
|
-
"https://www.w3.org/ns/activitystreams#actor": [{ "@id": "https://example.com/actors/alice" }],
|
|
491
|
-
"https://www.w3.org/ns/activitystreams#object": [{ "@id": "https://example.com/objects/1" }]
|
|
492
|
-
}, {
|
|
493
|
-
"@id": "https://example.com/objects/1",
|
|
494
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Note"],
|
|
495
|
-
"https://www.w3.org/ns/activitystreams#attributedTo": [{ "@id": "ap://did:key:z6Mkabc/actor" }]
|
|
496
|
-
}];
|
|
497
|
-
deepStrictEqual(await (await Activity.fromJsonLd(expanded, {
|
|
498
|
-
documentLoader: mockDocumentLoader,
|
|
499
|
-
contextLoader: mockDocumentLoader
|
|
500
|
-
})).toJsonLd(), [{
|
|
501
|
-
"@id": "https://example.com/activities/1",
|
|
502
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Create"],
|
|
503
|
-
"https://www.w3.org/ns/activitystreams#actor": [{ "@id": "https://example.com/actors/alice" }],
|
|
504
|
-
"https://www.w3.org/ns/activitystreams#object": [{ "@id": "https://example.com/objects/1" }]
|
|
505
|
-
}, {
|
|
506
|
-
"@id": "https://example.com/objects/1",
|
|
507
|
-
"@type": ["https://www.w3.org/ns/activitystreams#Note"],
|
|
508
|
-
"https://www.w3.org/ns/activitystreams#attributedTo": [{ "@id": "ap+ef61://did:key:z6Mkabc/actor" }]
|
|
509
|
-
}]);
|
|
510
|
-
deepStrictEqual(expanded[0]["@type"], ["https://www.w3.org/ns/activitystreams#Create"]);
|
|
511
|
-
});
|
|
512
|
-
test("fromJsonLd() preserves compact array contexts with portable IRIs", async () => {
|
|
513
|
-
deepStrictEqual(await (await Note.fromJsonLd([{
|
|
514
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
515
|
-
type: "Note",
|
|
516
|
-
id: "ap://did:key:z6Mkabc/objects/1"
|
|
517
|
-
}], {
|
|
518
|
-
documentLoader: mockDocumentLoader,
|
|
519
|
-
contextLoader: mockDocumentLoader
|
|
520
|
-
})).toJsonLd(), [{
|
|
521
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
522
|
-
type: "Note",
|
|
523
|
-
id: "ap+ef61://did:key:z6Mkabc/objects/1"
|
|
524
|
-
}]);
|
|
525
|
-
});
|
|
526
|
-
test("fromJsonLd() preserves compact single-item arrays with portable IRIs", async () => {
|
|
527
|
-
const createJson = {
|
|
528
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
529
|
-
type: "Create",
|
|
530
|
-
actor: ["ap://did:key:z6Mkabc/actor"],
|
|
531
|
-
object: "https://example.com/objects/1"
|
|
532
|
-
};
|
|
533
|
-
deepStrictEqual(await (await Create.fromJsonLd(createJson, {
|
|
534
|
-
documentLoader: mockDocumentLoader,
|
|
535
|
-
contextLoader: mockDocumentLoader
|
|
536
|
-
})).toJsonLd(), {
|
|
537
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
538
|
-
type: "Create",
|
|
539
|
-
actor: ["ap+ef61://did:key:z6Mkabc/actor"],
|
|
540
|
-
object: "https://example.com/objects/1"
|
|
541
|
-
});
|
|
542
|
-
deepStrictEqual(createJson.actor, ["ap://did:key:z6Mkabc/actor"]);
|
|
543
|
-
});
|
|
544
|
-
test("fromJsonLd() preserves compact multi-node arrays with portable IRIs", async () => {
|
|
545
|
-
deepStrictEqual(await (await Activity.fromJsonLd([{
|
|
546
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
547
|
-
type: "Create",
|
|
548
|
-
id: "https://example.com/activities/1",
|
|
549
|
-
actor: "https://example.com/actors/alice",
|
|
550
|
-
object: "https://example.com/objects/1"
|
|
551
|
-
}, {
|
|
552
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
553
|
-
type: "Note",
|
|
554
|
-
id: "https://example.com/objects/1",
|
|
555
|
-
attributedTo: "ap://did:key:z6Mkabc/actor"
|
|
556
|
-
}], {
|
|
557
|
-
documentLoader: mockDocumentLoader,
|
|
558
|
-
contextLoader: mockDocumentLoader
|
|
559
|
-
})).toJsonLd(), [{
|
|
560
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
561
|
-
type: "Create",
|
|
562
|
-
id: "https://example.com/activities/1",
|
|
563
|
-
actor: "https://example.com/actors/alice",
|
|
564
|
-
object: "https://example.com/objects/1"
|
|
565
|
-
}, {
|
|
566
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
567
|
-
type: "Note",
|
|
568
|
-
id: "https://example.com/objects/1",
|
|
569
|
-
attributedTo: "ap+ef61://did:key:z6Mkabc/actor"
|
|
570
|
-
}]);
|
|
571
|
-
});
|
|
572
|
-
test("fromJsonLd() preserves nested unmapped terms with portable IRIs", async () => {
|
|
573
|
-
deepStrictEqual(await (await Note.fromJsonLd({
|
|
574
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
575
|
-
type: "Note",
|
|
576
|
-
id: "ap://did:key:z6Mkabc/objects/1",
|
|
577
|
-
attachment: {
|
|
578
|
-
type: "Object",
|
|
579
|
-
name: "Attachment with an unmapped extension.",
|
|
580
|
-
extra: "This nested unmapped property should stay cached."
|
|
581
|
-
}
|
|
582
|
-
}, {
|
|
583
|
-
documentLoader: mockDocumentLoader,
|
|
584
|
-
contextLoader: mockDocumentLoader
|
|
585
|
-
})).toJsonLd(), {
|
|
586
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
587
|
-
type: "Note",
|
|
588
|
-
id: "ap+ef61://did:key:z6Mkabc/objects/1",
|
|
589
|
-
attachment: {
|
|
590
|
-
type: "Object",
|
|
591
|
-
name: "Attachment with an unmapped extension.",
|
|
592
|
-
extra: "This nested unmapped property should stay cached."
|
|
593
|
-
}
|
|
594
|
-
});
|
|
595
|
-
});
|
|
596
|
-
test("fromJsonLd() preserves compact array item extension contexts with portable IRIs", async () => {
|
|
597
|
-
deepStrictEqual(await (await Activity.fromJsonLd([{
|
|
598
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
599
|
-
type: "Create",
|
|
600
|
-
id: "https://example.com/activities/1",
|
|
601
|
-
actor: "https://example.com/actors/alice",
|
|
602
|
-
object: "https://example.com/objects/1"
|
|
603
|
-
}, {
|
|
604
|
-
"@context": ["https://www.w3.org/ns/activitystreams", { extraRef: {
|
|
605
|
-
"@id": "https://example.com/ns#extraRef",
|
|
606
|
-
"@type": "@id"
|
|
607
|
-
} }],
|
|
608
|
-
type: "Note",
|
|
609
|
-
id: "https://example.com/objects/1",
|
|
610
|
-
extraRef: "ap://did:key:z6Mkabc/extra"
|
|
611
|
-
}], {
|
|
612
|
-
documentLoader: mockDocumentLoader,
|
|
613
|
-
contextLoader: mockDocumentLoader
|
|
614
|
-
})).toJsonLd(), [{
|
|
615
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
616
|
-
type: "Create",
|
|
617
|
-
id: "https://example.com/activities/1",
|
|
618
|
-
actor: "https://example.com/actors/alice",
|
|
619
|
-
object: "https://example.com/objects/1"
|
|
620
|
-
}, {
|
|
621
|
-
"@context": ["https://www.w3.org/ns/activitystreams", { extraRef: {
|
|
622
|
-
"@id": "https://example.com/ns#extraRef",
|
|
623
|
-
"@type": "@id"
|
|
624
|
-
} }],
|
|
625
|
-
type: "Note",
|
|
626
|
-
id: "https://example.com/objects/1",
|
|
627
|
-
extraRef: "ap+ef61://did:key:z6Mkabc/extra"
|
|
628
|
-
}]);
|
|
629
|
-
});
|
|
630
|
-
test("fromJsonLd() formats portable IRIs in JSON-LD containers", async () => {
|
|
631
|
-
const jsonLd = await (await Note.fromJsonLd({
|
|
632
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
633
|
-
type: "Note",
|
|
634
|
-
id: "https://example.com/notes/1",
|
|
635
|
-
attributedTo: { "@list": ["ap://did:key:z6Mkabc/actor"] },
|
|
636
|
-
to: { "@set": ["ap://did:key:z6Mkabc/followers"] }
|
|
637
|
-
}, {
|
|
638
|
-
documentLoader: mockDocumentLoader,
|
|
639
|
-
contextLoader: mockDocumentLoader
|
|
640
|
-
})).toJsonLd();
|
|
641
|
-
deepStrictEqual(jsonLd.attributedTo, { "@list": ["ap+ef61://did:key:z6Mkabc/actor"] });
|
|
642
|
-
deepStrictEqual(jsonLd.to, "ap+ef61://did:key:z6Mkabc/followers");
|
|
643
|
-
});
|
|
644
|
-
test("fromJsonLd() formats portable IRIs hidden behind JSON-LD aliases", async () => {
|
|
645
|
-
const activity = await Activity.fromJsonLd({
|
|
646
|
-
"@context": ["https://www.w3.org/ns/activitystreams", {
|
|
647
|
-
as: {
|
|
648
|
-
"@id": "https://www.w3.org/ns/activitystreams#",
|
|
649
|
-
"@prefix": true
|
|
650
|
-
},
|
|
651
|
-
extra: "https://example.com/ns#extra",
|
|
652
|
-
extraRef: {
|
|
653
|
-
"@id": "https://example.com/ns#extraRef",
|
|
654
|
-
"@type": "@id"
|
|
655
|
-
},
|
|
656
|
-
actorRef: {
|
|
657
|
-
"@id": "as:actor",
|
|
658
|
-
"@type": "@id"
|
|
659
|
-
},
|
|
660
|
-
targetRef: {
|
|
661
|
-
"@id": "as:target",
|
|
662
|
-
"@type": "@id"
|
|
663
|
-
}
|
|
664
|
-
}],
|
|
665
|
-
type: "Create",
|
|
666
|
-
actorRef: "ap://did:key:z6Mkabc/actor",
|
|
667
|
-
object: "https://example.com/objects/1",
|
|
668
|
-
targetRef: "ap://did:key:z6Mkabc/target",
|
|
669
|
-
extra: "This extension property should stay cached.",
|
|
670
|
-
extraRef: "ap://did:key:z6Mkabc/extra"
|
|
671
|
-
}, {
|
|
672
|
-
documentLoader: mockDocumentLoader,
|
|
673
|
-
contextLoader: mockDocumentLoader
|
|
674
|
-
});
|
|
675
|
-
deepStrictEqual(activity.actorId, new URL("ap+ef61://did%3Akey%3Az6Mkabc/actor"));
|
|
676
|
-
deepStrictEqual(activity.targetId, new URL("ap+ef61://did%3Akey%3Az6Mkabc/target"));
|
|
677
|
-
const jsonLd = await activity.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
678
|
-
deepStrictEqual(jsonLd.actor, "ap+ef61://did:key:z6Mkabc/actor");
|
|
679
|
-
deepStrictEqual("actorRef" in jsonLd, false);
|
|
680
|
-
deepStrictEqual(jsonLd.target, "ap+ef61://did:key:z6Mkabc/target");
|
|
681
|
-
deepStrictEqual("targetRef" in jsonLd, false);
|
|
682
|
-
deepStrictEqual(jsonLd.extra, "This extension property should stay cached.");
|
|
683
|
-
deepStrictEqual(jsonLd.extraRef, "ap+ef61://did:key:z6Mkabc/extra");
|
|
684
|
-
});
|
|
685
|
-
test("fromJsonLd() preserves portable IRIs in @id extension terms", async () => {
|
|
686
|
-
deepStrictEqual((await (await Note.fromJsonLd({
|
|
687
|
-
"@context": ["https://www.w3.org/ns/activitystreams", { extraRef: {
|
|
688
|
-
"@id": "https://example.com/ns#extraRef",
|
|
689
|
-
"@type": "@id"
|
|
690
|
-
} }],
|
|
691
|
-
type: "Note",
|
|
692
|
-
id: "https://example.com/notes/1",
|
|
693
|
-
extraRef: "ap://did:key:z6Mkabc/extra"
|
|
694
|
-
}, {
|
|
695
|
-
documentLoader: mockDocumentLoader,
|
|
696
|
-
contextLoader: mockDocumentLoader
|
|
697
|
-
})).toJsonLd({ contextLoader: mockDocumentLoader })).extraRef, "ap+ef61://did:key:z6Mkabc/extra");
|
|
698
|
-
});
|
|
699
|
-
test("fromJsonLd() ignores malformed portable IRIs in extension cache terms", async () => {
|
|
700
|
-
const noteJson = {
|
|
701
|
-
"@context": ["https://www.w3.org/ns/activitystreams", { extraRef: {
|
|
702
|
-
"@id": "https://example.com/ns#extraRef",
|
|
703
|
-
"@type": "@id"
|
|
704
|
-
} }],
|
|
705
|
-
type: "Note",
|
|
706
|
-
id: "https://example.com/notes/1",
|
|
707
|
-
extraRef: "ap://example.com/not-portable"
|
|
708
|
-
};
|
|
709
|
-
deepStrictEqual(await (await Note.fromJsonLd(noteJson, {
|
|
710
|
-
documentLoader: mockDocumentLoader,
|
|
711
|
-
contextLoader: mockDocumentLoader
|
|
712
|
-
})).toJsonLd(), noteJson);
|
|
713
|
-
});
|
|
714
|
-
test("fromJsonLd() preserves portable IRIs in @id typed terms", async () => {
|
|
715
|
-
const jsonLd = await (await Note.fromJsonLd({
|
|
716
|
-
"@context": ["https://www.w3.org/ns/activitystreams", {
|
|
717
|
-
"@vocab": "https://example.com/ns#",
|
|
718
|
-
extraRef: { "@type": "@id" }
|
|
719
|
-
}],
|
|
720
|
-
type: "Note",
|
|
721
|
-
id: "https://example.com/notes/1",
|
|
722
|
-
content: "This text mentions ap://did:key:z6Mkabc/text.",
|
|
723
|
-
extraRef: "ap://did:key:z6Mkabc/extra"
|
|
724
|
-
}, {
|
|
725
|
-
documentLoader: mockDocumentLoader,
|
|
726
|
-
contextLoader: mockDocumentLoader
|
|
727
|
-
})).toJsonLd({ contextLoader: mockDocumentLoader });
|
|
728
|
-
deepStrictEqual(jsonLd.content, "This text mentions ap://did:key:z6Mkabc/text.");
|
|
729
|
-
deepStrictEqual(jsonLd.extraRef, "ap+ef61://did:key:z6Mkabc/extra");
|
|
730
|
-
});
|
|
731
|
-
test("fromJsonLd() preserves portable IRIs hidden behind remote contexts", async () => {
|
|
732
|
-
const contextUrl = "https://example.com/contexts/portable-iris";
|
|
733
|
-
const contextLoader = async (resource, options) => {
|
|
734
|
-
if (resource === contextUrl) return {
|
|
735
|
-
contextUrl: null,
|
|
736
|
-
documentUrl: resource,
|
|
737
|
-
document: { "@context": ["https://www.w3.org/ns/activitystreams", {
|
|
738
|
-
"@vocab": "https://example.com/ns#",
|
|
739
|
-
extra: "https://example.com/ns#extra",
|
|
740
|
-
extraRef: { "@type": "@id" }
|
|
741
|
-
}] }
|
|
742
|
-
};
|
|
743
|
-
return await mockDocumentLoader(resource, options);
|
|
744
|
-
};
|
|
745
|
-
const jsonLd = await (await Note.fromJsonLd({
|
|
746
|
-
"@context": contextUrl,
|
|
747
|
-
type: "Note",
|
|
748
|
-
id: "https://example.com/notes/1",
|
|
749
|
-
content: "This text mentions ap://did:key:z6Mkabc/text.",
|
|
750
|
-
extra: "This extension property should stay cached.",
|
|
751
|
-
extraRef: "ap://did:key:z6Mkabc/extra"
|
|
752
|
-
}, {
|
|
753
|
-
documentLoader: mockDocumentLoader,
|
|
754
|
-
contextLoader
|
|
755
|
-
})).toJsonLd({ contextLoader });
|
|
756
|
-
deepStrictEqual(jsonLd.content, "This text mentions ap://did:key:z6Mkabc/text.");
|
|
757
|
-
deepStrictEqual(jsonLd.extra, "This extension property should stay cached.");
|
|
758
|
-
deepStrictEqual(jsonLd.extraRef, "ap+ef61://did:key:z6Mkabc/extra");
|
|
759
|
-
});
|
|
760
|
-
test("fromJsonLd() formats portable IRIs hidden behind nested remote contexts", async () => {
|
|
761
|
-
const rootContextUrl = "https://example.com/contexts/nested-portable-iris";
|
|
762
|
-
const nestedContextUrl = "https://example.com/contexts/nested-portable-ref";
|
|
763
|
-
const contextLoader = async (resource, options) => {
|
|
764
|
-
if (resource === rootContextUrl) return {
|
|
765
|
-
contextUrl: null,
|
|
766
|
-
documentUrl: resource,
|
|
767
|
-
document: { "@context": ["https://www.w3.org/ns/activitystreams", {
|
|
768
|
-
"@vocab": "https://example.com/ns#",
|
|
769
|
-
extraContainer: "https://example.com/ns#extraContainer"
|
|
770
|
-
}] }
|
|
771
|
-
};
|
|
772
|
-
if (resource === nestedContextUrl) return {
|
|
773
|
-
contextUrl: null,
|
|
774
|
-
documentUrl: resource,
|
|
775
|
-
document: { "@context": {
|
|
776
|
-
"@vocab": "https://example.com/ns#",
|
|
777
|
-
extra: "https://example.com/ns#extra",
|
|
778
|
-
extraRef: { "@type": "@id" }
|
|
779
|
-
} }
|
|
780
|
-
};
|
|
781
|
-
return await mockDocumentLoader(resource, options);
|
|
782
|
-
};
|
|
783
|
-
deepStrictEqual((await (await Note.fromJsonLd({
|
|
784
|
-
"@context": rootContextUrl,
|
|
785
|
-
type: "Note",
|
|
786
|
-
id: "https://example.com/notes/1",
|
|
787
|
-
extraContainer: {
|
|
788
|
-
"@context": nestedContextUrl,
|
|
789
|
-
content: "This text mentions ap://did:key:z6Mkabc/text.",
|
|
790
|
-
extra: "This nested extension object should stay cached.",
|
|
791
|
-
extraRef: "ap://did:key:z6Mkabc/extra"
|
|
792
|
-
}
|
|
793
|
-
}, {
|
|
794
|
-
documentLoader: mockDocumentLoader,
|
|
795
|
-
contextLoader
|
|
796
|
-
})).toJsonLd({ contextLoader })).extraContainer, {
|
|
797
|
-
"@context": nestedContextUrl,
|
|
798
|
-
content: "This text mentions ap://did:key:z6Mkabc/text.",
|
|
799
|
-
extra: "This nested extension object should stay cached.",
|
|
800
|
-
extraRef: { id: "ap+ef61://did:key:z6Mkabc/extra" }
|
|
801
|
-
});
|
|
802
|
-
});
|
|
803
|
-
test("fromJsonLd() formats portable IRIs in sibling remote-context objects", async () => {
|
|
804
|
-
const rootContextUrl = "https://example.com/contexts/sibling-containers";
|
|
805
|
-
const nestedContextUrl = "https://example.com/contexts/sibling-extra-ref";
|
|
806
|
-
const contextLoader = async (resource, options) => {
|
|
807
|
-
if (resource === rootContextUrl) return {
|
|
808
|
-
contextUrl: null,
|
|
809
|
-
documentUrl: resource,
|
|
810
|
-
document: { "@context": ["https://www.w3.org/ns/activitystreams", {
|
|
811
|
-
"@vocab": "https://example.com/ns#",
|
|
812
|
-
firstExtraContainer: "https://example.com/ns#firstExtraContainer",
|
|
813
|
-
secondExtraContainer: "https://example.com/ns#secondExtraContainer"
|
|
814
|
-
}] }
|
|
815
|
-
};
|
|
816
|
-
if (resource === nestedContextUrl) return {
|
|
817
|
-
contextUrl: null,
|
|
818
|
-
documentUrl: resource,
|
|
819
|
-
document: { "@context": {
|
|
820
|
-
"@vocab": "https://example.com/ns#",
|
|
821
|
-
extraRef: { "@type": "@id" }
|
|
822
|
-
} }
|
|
823
|
-
};
|
|
824
|
-
return await mockDocumentLoader(resource, options);
|
|
825
|
-
};
|
|
826
|
-
const jsonLd = await (await Note.fromJsonLd({
|
|
827
|
-
"@context": rootContextUrl,
|
|
828
|
-
type: "Note",
|
|
829
|
-
id: "https://example.com/notes/1",
|
|
830
|
-
firstExtraContainer: {
|
|
831
|
-
"@context": nestedContextUrl,
|
|
832
|
-
content: "No portable IRI here."
|
|
833
|
-
},
|
|
834
|
-
secondExtraContainer: {
|
|
835
|
-
"@context": nestedContextUrl,
|
|
836
|
-
extraRef: "ap://did:key:z6Mkabc/second"
|
|
837
|
-
}
|
|
838
|
-
}, {
|
|
839
|
-
documentLoader: mockDocumentLoader,
|
|
840
|
-
contextLoader
|
|
841
|
-
})).toJsonLd({ contextLoader });
|
|
842
|
-
deepStrictEqual(jsonLd.firstExtraContainer, {
|
|
843
|
-
"@context": nestedContextUrl,
|
|
844
|
-
content: "No portable IRI here."
|
|
845
|
-
});
|
|
846
|
-
deepStrictEqual(jsonLd.secondExtraContainer, {
|
|
847
|
-
"@context": nestedContextUrl,
|
|
848
|
-
extraRef: { id: "ap+ef61://did:key:z6Mkabc/second" }
|
|
849
|
-
});
|
|
850
|
-
});
|
|
851
|
-
test("fromJsonLd() batches unmapped portable IRI term checks", async () => {
|
|
852
|
-
const contextUrl = "https://example.com/contexts/batched-portable-aliases";
|
|
853
|
-
let contextLoads = 0;
|
|
854
|
-
const contextLoader = async (resource, options) => {
|
|
855
|
-
if (resource === contextUrl) {
|
|
856
|
-
contextLoads++;
|
|
857
|
-
return {
|
|
858
|
-
contextUrl: null,
|
|
859
|
-
documentUrl: resource,
|
|
860
|
-
document: { "@context": ["https://www.w3.org/ns/activitystreams", {
|
|
861
|
-
as: {
|
|
862
|
-
"@id": "https://www.w3.org/ns/activitystreams#",
|
|
863
|
-
"@prefix": true
|
|
864
|
-
},
|
|
865
|
-
actorRef0: {
|
|
866
|
-
"@id": "as:actor",
|
|
867
|
-
"@type": "@id"
|
|
868
|
-
},
|
|
869
|
-
actorRef1: {
|
|
870
|
-
"@id": "as:actor",
|
|
871
|
-
"@type": "@id"
|
|
872
|
-
},
|
|
873
|
-
targetRef0: {
|
|
874
|
-
"@id": "as:target",
|
|
875
|
-
"@type": "@id"
|
|
876
|
-
},
|
|
877
|
-
targetRef1: {
|
|
878
|
-
"@id": "as:target",
|
|
879
|
-
"@type": "@id"
|
|
880
|
-
},
|
|
881
|
-
objectRef0: {
|
|
882
|
-
"@id": "as:object",
|
|
883
|
-
"@type": "@id"
|
|
884
|
-
},
|
|
885
|
-
objectRef1: {
|
|
886
|
-
"@id": "as:object",
|
|
887
|
-
"@type": "@id"
|
|
888
|
-
}
|
|
889
|
-
}] }
|
|
890
|
-
};
|
|
891
|
-
}
|
|
892
|
-
return await mockDocumentLoader(resource, options);
|
|
893
|
-
};
|
|
894
|
-
await (await Activity.fromJsonLd({
|
|
895
|
-
"@context": contextUrl,
|
|
896
|
-
type: "Create",
|
|
897
|
-
actorRef0: "ap://did:key:z6Mkabc/actor0",
|
|
898
|
-
actorRef1: "ap://did:key:z6Mkabc/actor1",
|
|
899
|
-
targetRef0: "ap://did:key:z6Mkabc/target0",
|
|
900
|
-
targetRef1: "ap://did:key:z6Mkabc/target1",
|
|
901
|
-
objectRef0: "https://example.com/objects/0",
|
|
902
|
-
objectRef1: "https://example.com/objects/1"
|
|
903
|
-
}, {
|
|
904
|
-
documentLoader: mockDocumentLoader,
|
|
905
|
-
contextLoader
|
|
906
|
-
})).toJsonLd({ contextLoader });
|
|
907
|
-
ok(contextLoads <= 5);
|
|
908
|
-
});
|
|
909
|
-
test("fromJsonLd() falls back when portable IRI cache merge fails", async () => {
|
|
910
|
-
const contextUrl = "https://example.com/contexts/failing-cache-merge";
|
|
911
|
-
let contextLoads = 0;
|
|
912
|
-
const contextLoader = async (resource, options) => {
|
|
913
|
-
if (resource === contextUrl) {
|
|
914
|
-
contextLoads++;
|
|
915
|
-
if (contextLoads > 1) throw new Error("merge context unavailable");
|
|
916
|
-
return {
|
|
917
|
-
contextUrl: null,
|
|
918
|
-
documentUrl: resource,
|
|
919
|
-
document: { "@context": ["https://www.w3.org/ns/activitystreams", {
|
|
920
|
-
"@vocab": "https://example.com/ns#",
|
|
921
|
-
extraRef: { "@type": "@id" }
|
|
922
|
-
}] }
|
|
923
|
-
};
|
|
924
|
-
}
|
|
925
|
-
return await mockDocumentLoader(resource, options);
|
|
926
|
-
};
|
|
927
|
-
const jsonLd = await (await Note.fromJsonLd({
|
|
928
|
-
"@context": contextUrl,
|
|
929
|
-
type: "Note",
|
|
930
|
-
id: "ap://did:key:z6Mkabc/objects/1",
|
|
931
|
-
extraRef: "ap://did:key:z6Mkabc/extra"
|
|
932
|
-
}, {
|
|
933
|
-
documentLoader: mockDocumentLoader,
|
|
934
|
-
contextLoader
|
|
935
|
-
})).toJsonLd({ contextLoader: mockDocumentLoader });
|
|
936
|
-
deepStrictEqual(jsonLd.type, "Note");
|
|
937
|
-
deepStrictEqual(jsonLd.id, "ap+ef61://did:key:z6Mkabc/objects/1");
|
|
938
|
-
});
|
|
939
|
-
test("fromJsonLd() formats portable IRIs in scalar URL values", async () => {
|
|
940
|
-
const note = await Note.fromJsonLd({
|
|
941
|
-
"@context": [
|
|
942
|
-
"https://www.w3.org/ns/activitystreams",
|
|
943
|
-
"https://gotosocial.org/ns",
|
|
944
|
-
{ quoteUrl: "as:quoteUrl" }
|
|
945
|
-
],
|
|
946
|
-
type: "Note",
|
|
947
|
-
id: "https://example.com/notes/1",
|
|
948
|
-
quoteUrl: "ap://did:key:z6Mkabc/objects/1"
|
|
949
|
-
}, {
|
|
950
|
-
documentLoader: mockDocumentLoader,
|
|
951
|
-
contextLoader: mockDocumentLoader
|
|
952
|
-
});
|
|
953
|
-
deepStrictEqual(note.quoteUrl, new URL("ap+ef61://did%3Akey%3Az6Mkabc/objects/1"));
|
|
954
|
-
deepStrictEqual((await note.toJsonLd({ contextLoader: mockDocumentLoader })).quoteUrl, "ap+ef61://did:key:z6Mkabc/objects/1");
|
|
955
|
-
});
|
|
956
|
-
test("fromJsonLd() formats portable IRIs in URL value lists", async () => {
|
|
957
|
-
const note = await Note.fromJsonLd({
|
|
958
|
-
"@context": [
|
|
959
|
-
"https://www.w3.org/ns/activitystreams",
|
|
960
|
-
"https://gotosocial.org/ns",
|
|
961
|
-
{ quoteUrl: "as:quoteUrl" }
|
|
962
|
-
],
|
|
963
|
-
type: "Note",
|
|
964
|
-
id: "https://example.com/notes/1",
|
|
965
|
-
quoteUrl: { "@list": [{ "@value": "ap://did:key:z6Mkabc/objects/1" }] }
|
|
966
|
-
}, {
|
|
967
|
-
documentLoader: mockDocumentLoader,
|
|
968
|
-
contextLoader: mockDocumentLoader
|
|
969
|
-
});
|
|
970
|
-
deepStrictEqual(note.quoteUrl, new URL("ap+ef61://did%3Akey%3Az6Mkabc/objects/1"));
|
|
971
|
-
deepStrictEqual((await note.toJsonLd({ contextLoader: mockDocumentLoader })).quoteUrl, { "@list": ["ap+ef61://did:key:z6Mkabc/objects/1"] });
|
|
972
|
-
});
|
|
973
305
|
test({
|
|
974
306
|
name: "Activity.getObject()",
|
|
975
307
|
permissions: {
|
|
@@ -1025,34 +357,6 @@ test({
|
|
|
1025
357
|
});
|
|
1026
358
|
}
|
|
1027
359
|
});
|
|
1028
|
-
test("Activity.getObject() fetches canonical portable IRIs", async () => {
|
|
1029
|
-
const fetchedUrls = [];
|
|
1030
|
-
const documentLoader = async (url) => {
|
|
1031
|
-
fetchedUrls.push(url);
|
|
1032
|
-
return {
|
|
1033
|
-
contextUrl: null,
|
|
1034
|
-
documentUrl: url,
|
|
1035
|
-
document: {
|
|
1036
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1037
|
-
id: url,
|
|
1038
|
-
type: "Note",
|
|
1039
|
-
content: "Fetched portable object"
|
|
1040
|
-
}
|
|
1041
|
-
};
|
|
1042
|
-
};
|
|
1043
|
-
assertInstanceOf(await (await Activity.fromJsonLd({
|
|
1044
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
1045
|
-
type: "Create",
|
|
1046
|
-
object: "ap://did:key:z6Mkabc/objects/1"
|
|
1047
|
-
}, {
|
|
1048
|
-
documentLoader,
|
|
1049
|
-
contextLoader: mockDocumentLoader
|
|
1050
|
-
})).getObject({
|
|
1051
|
-
documentLoader,
|
|
1052
|
-
contextLoader: mockDocumentLoader
|
|
1053
|
-
}), Note);
|
|
1054
|
-
deepStrictEqual(fetchedUrls, ["ap+ef61://did:key:z6Mkabc/objects/1"]);
|
|
1055
|
-
});
|
|
1056
360
|
test({
|
|
1057
361
|
name: "Activity.getObjects()",
|
|
1058
362
|
permissions: {
|
|
@@ -1161,7 +465,6 @@ test("Person.toJsonLd()", async () => {
|
|
|
1161
465
|
"https://www.w3.org/ns/did/v1",
|
|
1162
466
|
"https://w3id.org/security/multikey/v1",
|
|
1163
467
|
"https://gotosocial.org/ns",
|
|
1164
|
-
"https://w3id.org/fep/7aa9",
|
|
1165
468
|
{
|
|
1166
469
|
PropertyValue: "schema:PropertyValue",
|
|
1167
470
|
alsoKnownAs: {
|
|
@@ -1177,10 +480,6 @@ test("Person.toJsonLd()", async () => {
|
|
|
1177
480
|
"@id": "toot:featured",
|
|
1178
481
|
"@type": "@id"
|
|
1179
482
|
},
|
|
1180
|
-
featuredCollections: {
|
|
1181
|
-
"@id": "https://w3id.org/fep/7aa9#featuredCollections",
|
|
1182
|
-
"@type": "@id"
|
|
1183
|
-
},
|
|
1184
483
|
featuredTags: {
|
|
1185
484
|
"@id": "toot:featuredTags",
|
|
1186
485
|
"@type": "@id"
|
|
@@ -1627,187 +926,6 @@ test("InteractionPolicy.canQuote", async () => {
|
|
|
1627
926
|
contextLoader: mockDocumentLoader
|
|
1628
927
|
})).toJsonLd({ contextLoader: mockDocumentLoader }), expected);
|
|
1629
928
|
});
|
|
1630
|
-
test("InteractionPolicy.canFeature", async () => {
|
|
1631
|
-
const person = new Person({
|
|
1632
|
-
id: new URL("https://example.com/users/alice"),
|
|
1633
|
-
featuredCollections: new URL("https://example.com/users/alice/featured_collections"),
|
|
1634
|
-
interactionPolicy: new InteractionPolicy({ canFeature: new InteractionRule({ automaticApproval: new URL("https://www.w3.org/ns/activitystreams#Public") }) })
|
|
1635
|
-
});
|
|
1636
|
-
const expected = {
|
|
1637
|
-
"@context": [
|
|
1638
|
-
"https://www.w3.org/ns/activitystreams",
|
|
1639
|
-
"https://w3id.org/security/v1",
|
|
1640
|
-
"https://w3id.org/security/data-integrity/v1",
|
|
1641
|
-
"https://www.w3.org/ns/did/v1",
|
|
1642
|
-
"https://w3id.org/security/multikey/v1",
|
|
1643
|
-
"https://gotosocial.org/ns",
|
|
1644
|
-
"https://w3id.org/fep/7aa9",
|
|
1645
|
-
{
|
|
1646
|
-
Emoji: "toot:Emoji",
|
|
1647
|
-
PropertyValue: "schema:PropertyValue",
|
|
1648
|
-
_misskey_followedMessage: "misskey:_misskey_followedMessage",
|
|
1649
|
-
alsoKnownAs: {
|
|
1650
|
-
"@id": "as:alsoKnownAs",
|
|
1651
|
-
"@type": "@id"
|
|
1652
|
-
},
|
|
1653
|
-
discoverable: "toot:discoverable",
|
|
1654
|
-
featured: {
|
|
1655
|
-
"@id": "toot:featured",
|
|
1656
|
-
"@type": "@id"
|
|
1657
|
-
},
|
|
1658
|
-
featuredCollections: {
|
|
1659
|
-
"@id": "https://w3id.org/fep/7aa9#featuredCollections",
|
|
1660
|
-
"@type": "@id"
|
|
1661
|
-
},
|
|
1662
|
-
featuredTags: {
|
|
1663
|
-
"@id": "toot:featuredTags",
|
|
1664
|
-
"@type": "@id"
|
|
1665
|
-
},
|
|
1666
|
-
indexable: "toot:indexable",
|
|
1667
|
-
isCat: "misskey:isCat",
|
|
1668
|
-
manuallyApprovesFollowers: "as:manuallyApprovesFollowers",
|
|
1669
|
-
memorial: "toot:memorial",
|
|
1670
|
-
misskey: "https://misskey-hub.net/ns#",
|
|
1671
|
-
movedTo: {
|
|
1672
|
-
"@id": "as:movedTo",
|
|
1673
|
-
"@type": "@id"
|
|
1674
|
-
},
|
|
1675
|
-
schema: "http://schema.org#",
|
|
1676
|
-
suspended: "toot:suspended",
|
|
1677
|
-
toot: "http://joinmastodon.org/ns#",
|
|
1678
|
-
value: "schema:value"
|
|
1679
|
-
}
|
|
1680
|
-
],
|
|
1681
|
-
type: "Person",
|
|
1682
|
-
id: "https://example.com/users/alice",
|
|
1683
|
-
featuredCollections: "https://example.com/users/alice/featured_collections",
|
|
1684
|
-
interactionPolicy: { canFeature: { automaticApproval: "as:Public" } }
|
|
1685
|
-
};
|
|
1686
|
-
deepStrictEqual(await person.toJsonLd({ contextLoader: mockDocumentLoader }), expected);
|
|
1687
|
-
const loaded = await Person.fromJsonLd(expected, {
|
|
1688
|
-
documentLoader: mockDocumentLoader,
|
|
1689
|
-
contextLoader: mockDocumentLoader
|
|
1690
|
-
});
|
|
1691
|
-
deepStrictEqual(loaded.featuredCollectionsId, new URL("https://example.com/users/alice/featured_collections"));
|
|
1692
|
-
assertInstanceOf(loaded.interactionPolicy, InteractionPolicy);
|
|
1693
|
-
assertInstanceOf(loaded.interactionPolicy.canFeature, InteractionRule);
|
|
1694
|
-
deepStrictEqual(loaded.interactionPolicy.canFeature.automaticApproval, new URL("https://www.w3.org/ns/activitystreams#Public"));
|
|
1695
|
-
deepStrictEqual(await loaded.toJsonLd({ contextLoader: mockDocumentLoader }), expected);
|
|
1696
|
-
deepStrictEqual((await Person.fromJsonLd({
|
|
1697
|
-
"@context": [
|
|
1698
|
-
"https://www.w3.org/ns/activitystreams",
|
|
1699
|
-
"https://gotosocial.org/ns",
|
|
1700
|
-
"https://w3id.org/fep/7aa9",
|
|
1701
|
-
{ featuredCollections: {
|
|
1702
|
-
"@id": "https://w3id.org/fep/7aa9#featuredCollections",
|
|
1703
|
-
"@type": "@id"
|
|
1704
|
-
} }
|
|
1705
|
-
],
|
|
1706
|
-
type: "Person",
|
|
1707
|
-
id: "https://example.com/users/alice",
|
|
1708
|
-
featuredCollections: "https://example.com/users/alice/featured_collections"
|
|
1709
|
-
}, {
|
|
1710
|
-
documentLoader: mockDocumentLoader,
|
|
1711
|
-
contextLoader: mockDocumentLoader
|
|
1712
|
-
})).featuredCollectionsId, new URL("https://example.com/users/alice/featured_collections"));
|
|
1713
|
-
});
|
|
1714
|
-
test("FeaturedCollection.toJsonLd()", async () => {
|
|
1715
|
-
const collection = new FeaturedCollection({
|
|
1716
|
-
id: new URL("https://example.com/users/alice/featured/1"),
|
|
1717
|
-
name: "Cute cats",
|
|
1718
|
-
attribution: new URL("https://example.com/users/alice"),
|
|
1719
|
-
topic: new Hashtag({ name: "#cats" }),
|
|
1720
|
-
discoverable: false,
|
|
1721
|
-
totalItems: 1
|
|
1722
|
-
});
|
|
1723
|
-
const expected = {
|
|
1724
|
-
"@context": FEATURED_COLLECTION_CONTEXT,
|
|
1725
|
-
type: "FeaturedCollection",
|
|
1726
|
-
id: "https://example.com/users/alice/featured/1",
|
|
1727
|
-
attributedTo: "https://example.com/users/alice",
|
|
1728
|
-
name: "Cute cats",
|
|
1729
|
-
totalItems: 1,
|
|
1730
|
-
topic: {
|
|
1731
|
-
type: "Hashtag",
|
|
1732
|
-
name: "#cats"
|
|
1733
|
-
},
|
|
1734
|
-
discoverable: false
|
|
1735
|
-
};
|
|
1736
|
-
deepStrictEqual(await collection.toJsonLd({ contextLoader: mockDocumentLoader }), expected);
|
|
1737
|
-
const loaded = await FeaturedCollection.fromJsonLd(expected, {
|
|
1738
|
-
documentLoader: mockDocumentLoader,
|
|
1739
|
-
contextLoader: mockDocumentLoader
|
|
1740
|
-
});
|
|
1741
|
-
assertInstanceOf(loaded, FeaturedCollection);
|
|
1742
|
-
deepStrictEqual(loaded.name, "Cute cats");
|
|
1743
|
-
deepStrictEqual(loaded.attributionId, new URL("https://example.com/users/alice"));
|
|
1744
|
-
assertInstanceOf(loaded.topic, Hashtag);
|
|
1745
|
-
deepStrictEqual(loaded.topic.name, "#cats");
|
|
1746
|
-
deepStrictEqual(loaded.discoverable, false);
|
|
1747
|
-
deepStrictEqual(loaded.totalItems, 1);
|
|
1748
|
-
deepStrictEqual(await loaded.toJsonLd({ contextLoader: mockDocumentLoader }), expected);
|
|
1749
|
-
});
|
|
1750
|
-
test("FeaturedItem.toJsonLd()", async () => {
|
|
1751
|
-
const item = new FeaturedItem({
|
|
1752
|
-
id: new URL("https://example.com/users/alice/featured/1/items/1"),
|
|
1753
|
-
featuredObject: new URL("https://example.com/users/bob"),
|
|
1754
|
-
featureAuthorization: new URL("https://example.com/users/bob/stamps/1")
|
|
1755
|
-
});
|
|
1756
|
-
const expected = {
|
|
1757
|
-
"@context": FEATURE_CONTEXT,
|
|
1758
|
-
type: "FeaturedItem",
|
|
1759
|
-
id: "https://example.com/users/alice/featured/1/items/1",
|
|
1760
|
-
featuredObject: "https://example.com/users/bob",
|
|
1761
|
-
featureAuthorization: "https://example.com/users/bob/stamps/1"
|
|
1762
|
-
};
|
|
1763
|
-
deepStrictEqual(await item.toJsonLd({ contextLoader: mockDocumentLoader }), expected);
|
|
1764
|
-
const loaded = await FeaturedItem.fromJsonLd(expected, {
|
|
1765
|
-
documentLoader: mockDocumentLoader,
|
|
1766
|
-
contextLoader: mockDocumentLoader
|
|
1767
|
-
});
|
|
1768
|
-
assertInstanceOf(loaded, FeaturedItem);
|
|
1769
|
-
deepStrictEqual(loaded.featuredObjectId, new URL("https://example.com/users/bob"));
|
|
1770
|
-
deepStrictEqual(loaded.featureAuthorizationId, new URL("https://example.com/users/bob/stamps/1"));
|
|
1771
|
-
});
|
|
1772
|
-
test("FeatureAuthorization.fromJsonLd()", async () => {
|
|
1773
|
-
const jsonLd = {
|
|
1774
|
-
"@context": FEATURE_CONTEXT,
|
|
1775
|
-
type: "FeatureAuthorization",
|
|
1776
|
-
id: "https://example.com/users/bob/stamps/1",
|
|
1777
|
-
interactingObject: "https://example.com/users/alice/featured/1",
|
|
1778
|
-
interactionTarget: "https://example.com/users/bob"
|
|
1779
|
-
};
|
|
1780
|
-
const authorization = await FeatureAuthorization.fromJsonLd(jsonLd, {
|
|
1781
|
-
documentLoader: mockDocumentLoader,
|
|
1782
|
-
contextLoader: mockDocumentLoader
|
|
1783
|
-
});
|
|
1784
|
-
assertInstanceOf(authorization, FeatureAuthorization);
|
|
1785
|
-
deepStrictEqual(authorization.interactingObjectId, new URL("https://example.com/users/alice/featured/1"));
|
|
1786
|
-
deepStrictEqual(authorization.interactionTargetId, new URL("https://example.com/users/bob"));
|
|
1787
|
-
deepStrictEqual(await authorization.toJsonLd({ contextLoader: mockDocumentLoader }), jsonLd);
|
|
1788
|
-
});
|
|
1789
|
-
test("FeatureRequest.toJsonLd()", async () => {
|
|
1790
|
-
const request = new FeatureRequest({
|
|
1791
|
-
id: new URL("https://example.com/users/alice/featured/1/requests/1"),
|
|
1792
|
-
object: new URL("https://example.com/users/bob"),
|
|
1793
|
-
instrument: new URL("https://example.com/users/alice/featured/1")
|
|
1794
|
-
});
|
|
1795
|
-
const expected = {
|
|
1796
|
-
"@context": ["https://w3id.org/identity/v1", ...FEATURE_CONTEXT],
|
|
1797
|
-
type: "FeatureRequest",
|
|
1798
|
-
id: "https://example.com/users/alice/featured/1/requests/1",
|
|
1799
|
-
object: "https://example.com/users/bob",
|
|
1800
|
-
instrument: "https://example.com/users/alice/featured/1"
|
|
1801
|
-
};
|
|
1802
|
-
deepStrictEqual(await request.toJsonLd({ contextLoader: mockDocumentLoader }), expected);
|
|
1803
|
-
const loaded = await FeatureRequest.fromJsonLd(expected, {
|
|
1804
|
-
documentLoader: mockDocumentLoader,
|
|
1805
|
-
contextLoader: mockDocumentLoader
|
|
1806
|
-
});
|
|
1807
|
-
assertInstanceOf(loaded, FeatureRequest);
|
|
1808
|
-
deepStrictEqual(loaded.objectId, new URL("https://example.com/users/bob"));
|
|
1809
|
-
deepStrictEqual(loaded.instrumentId, new URL("https://example.com/users/alice/featured/1"));
|
|
1810
|
-
});
|
|
1811
929
|
test("QuoteAuthorization.fromJsonLd()", async () => {
|
|
1812
930
|
const jsonLd = {
|
|
1813
931
|
"@context": [
|
|
@@ -2512,26 +1630,6 @@ test("FEP-fe34: crossOrigin trust behavior", async () => {
|
|
|
2512
1630
|
deepStrictEqual(result?.id, new URL("https://malicious.com/fake-note"));
|
|
2513
1631
|
deepStrictEqual(result?.content, "This is a spoofed note");
|
|
2514
1632
|
});
|
|
2515
|
-
test("FEP-fe34: id-less owners honor crossOrigin trust", async () => {
|
|
2516
|
-
const result = await (await Create.fromJsonLd({
|
|
2517
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
2518
|
-
"@type": "Create",
|
|
2519
|
-
"actor": "https://example.com/actor",
|
|
2520
|
-
"object": {
|
|
2521
|
-
"@type": "Note",
|
|
2522
|
-
"@id": "https://different-origin.com/note",
|
|
2523
|
-
"content": "Embedded note"
|
|
2524
|
-
}
|
|
2525
|
-
})).getObject({
|
|
2526
|
-
crossOrigin: "trust",
|
|
2527
|
-
documentLoader: async (url) => {
|
|
2528
|
-
throw new Error(`Unexpected fetch: ${url}`);
|
|
2529
|
-
}
|
|
2530
|
-
});
|
|
2531
|
-
assertInstanceOf(result, Note);
|
|
2532
|
-
deepStrictEqual(result.id, new URL("https://different-origin.com/note"));
|
|
2533
|
-
deepStrictEqual(result.content, "Embedded note");
|
|
2534
|
-
});
|
|
2535
1633
|
test("FEP-fe34: Same origin objects are trusted", async () => {
|
|
2536
1634
|
const sameOriginDocumentLoader = async (url) => {
|
|
2537
1635
|
if (url === "https://example.com/note") return {
|
|
@@ -2627,35 +1725,6 @@ test("FEP-fe34: Constructor vs JSON-LD parsing trust difference", async () => {
|
|
|
2627
1725
|
};
|
|
2628
1726
|
deepStrictEqual((await jsonLdCreate.getObject({ documentLoader }))?.content, "Fetched from origin");
|
|
2629
1727
|
});
|
|
2630
|
-
test("FEP-fe34: Portable DID authorities are cross-origin boundaries", async () => {
|
|
2631
|
-
const create = await Create.fromJsonLd({
|
|
2632
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
2633
|
-
"@type": "Create",
|
|
2634
|
-
"@id": "ap://did:key:z6MkOwner/create",
|
|
2635
|
-
"actor": "ap://did:key:z6MkOwner/actor",
|
|
2636
|
-
"object": {
|
|
2637
|
-
"@type": "Note",
|
|
2638
|
-
"@id": "ap://did:key:z6MkOther/note",
|
|
2639
|
-
"content": "Embedded portable note"
|
|
2640
|
-
}
|
|
2641
|
-
});
|
|
2642
|
-
const documentLoader = async (url) => {
|
|
2643
|
-
if (url === "ap+ef61://did:key:z6MkOther/note") return {
|
|
2644
|
-
documentUrl: url,
|
|
2645
|
-
contextUrl: null,
|
|
2646
|
-
document: {
|
|
2647
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
2648
|
-
"@type": "Note",
|
|
2649
|
-
"@id": "ap://did:key:z6MkOther/note",
|
|
2650
|
-
"content": "Fetched portable note"
|
|
2651
|
-
}
|
|
2652
|
-
};
|
|
2653
|
-
throw new Error("Document not found");
|
|
2654
|
-
};
|
|
2655
|
-
const result = await create.getObject({ documentLoader });
|
|
2656
|
-
assertInstanceOf(result, Note);
|
|
2657
|
-
deepStrictEqual(result.content, "Fetched portable note");
|
|
2658
|
-
});
|
|
2659
1728
|
test("FEP-fe34: Array properties respect cross-origin policy", async () => {
|
|
2660
1729
|
const crossOriginDocumentLoader = async (url) => {
|
|
2661
1730
|
if (url === "https://different-origin.com/note1") return {
|
|
@@ -2729,60 +1798,6 @@ test("FEP-fe34: Array properties with crossOrigin trust option", async () => {
|
|
|
2729
1798
|
deepStrictEqual(items[0].content, "Fake note 1");
|
|
2730
1799
|
deepStrictEqual(items[1].content, "Legitimate note 2");
|
|
2731
1800
|
});
|
|
2732
|
-
test("FEP-fe34: id-less arrays honor crossOrigin trust", async () => {
|
|
2733
|
-
const collection = await Collection.fromJsonLd({
|
|
2734
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
2735
|
-
"@type": "Collection",
|
|
2736
|
-
"items": [{
|
|
2737
|
-
"@type": "Note",
|
|
2738
|
-
"@id": "https://different-origin.com/note1",
|
|
2739
|
-
"content": "Embedded note 1"
|
|
2740
|
-
}, {
|
|
2741
|
-
"@type": "Note",
|
|
2742
|
-
"@id": "https://different-origin.com/note2",
|
|
2743
|
-
"content": "Embedded note 2"
|
|
2744
|
-
}]
|
|
2745
|
-
});
|
|
2746
|
-
const items = [];
|
|
2747
|
-
for await (const item of collection.getItems({
|
|
2748
|
-
crossOrigin: "trust",
|
|
2749
|
-
documentLoader: async (url) => {
|
|
2750
|
-
throw new Error(`Unexpected fetch: ${url}`);
|
|
2751
|
-
}
|
|
2752
|
-
})) items.push(item);
|
|
2753
|
-
deepStrictEqual(items.length, 2);
|
|
2754
|
-
assertInstanceOf(items[0], Note);
|
|
2755
|
-
assertInstanceOf(items[1], Note);
|
|
2756
|
-
deepStrictEqual(items[0].content, "Embedded note 1");
|
|
2757
|
-
deepStrictEqual(items[1].content, "Embedded note 2");
|
|
2758
|
-
});
|
|
2759
|
-
test("FEP-fe34: Array properties track trust per item", async () => {
|
|
2760
|
-
const collection = new Collection({
|
|
2761
|
-
id: new URL("https://example.com/collection"),
|
|
2762
|
-
items: [new Note({
|
|
2763
|
-
id: new URL("https://malicious.com/fake-note1"),
|
|
2764
|
-
content: "Trusted constructor note 1"
|
|
2765
|
-
}), new URL("https://different-origin.com/note2")]
|
|
2766
|
-
});
|
|
2767
|
-
const documentLoader = async (url) => {
|
|
2768
|
-
if (url === "https://different-origin.com/note2") return {
|
|
2769
|
-
documentUrl: url,
|
|
2770
|
-
contextUrl: null,
|
|
2771
|
-
document: {
|
|
2772
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
2773
|
-
"@type": "Note",
|
|
2774
|
-
"@id": "https://malicious.com/fake-note2",
|
|
2775
|
-
"content": "Untrusted fetched note 2"
|
|
2776
|
-
}
|
|
2777
|
-
};
|
|
2778
|
-
throw new Error("Document not found");
|
|
2779
|
-
};
|
|
2780
|
-
const items = [];
|
|
2781
|
-
for await (const item of collection.getItems({ documentLoader })) items.push(item);
|
|
2782
|
-
deepStrictEqual(items.length, 1);
|
|
2783
|
-
assertInstanceOf(items[0], Note);
|
|
2784
|
-
deepStrictEqual(items[0].content, "Trusted constructor note 1");
|
|
2785
|
-
});
|
|
2786
1801
|
test("FEP-fe34: Embedded objects in arrays from JSON-LD respect cross-origin policy", async () => {
|
|
2787
1802
|
const collectionDocumentLoader = async (url) => {
|
|
2788
1803
|
if (url === "https://example.com/collection") return {
|