@fedify/vocab 2.4.0-dev.2155 → 2.4.0-dev.2159

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.
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
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-CVEaA48h.mjs";
4
- import { t as esm_default } from "./esm-CxYTzURx.mjs";
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-D3C791xF.mjs";
4
+ import { t as esm_default } from "./esm-2_Uqq_my.mjs";
5
5
  import { t as assertInstanceOf } from "./utils-Df2odC27.mjs";
6
6
  import { mockDocumentLoader, test } from "@fedify/fixture";
7
7
  import { deepStrictEqual, notDeepStrictEqual, ok, rejects, throws } from "node:assert/strict";
@@ -1311,6 +1311,72 @@ test({
1311
1311
  deepStrictEqual(objects2[0].name, "Second object");
1312
1312
  }
1313
1313
  });
1314
+ function embeddedPropertyDocument(property) {
1315
+ const context = "https://www.w3.org/ns/activitystreams";
1316
+ return {
1317
+ "@context": [context, { CustomImage: `${context}#Image` }],
1318
+ type: property === "attachment" ? "Note" : "Create",
1319
+ [property]: [
1320
+ {
1321
+ "@context": [context, { "@vocab": "https://example.com/" }],
1322
+ type: "CustomImage",
1323
+ name: "Malformed when reparsed"
1324
+ },
1325
+ {
1326
+ "@context": context,
1327
+ type: "Image",
1328
+ name: "Cached image"
1329
+ },
1330
+ {
1331
+ type: "Image",
1332
+ name: "Decoded image"
1333
+ }
1334
+ ]
1335
+ };
1336
+ }
1337
+ for (const suppressError of [
1338
+ void 0,
1339
+ false,
1340
+ true
1341
+ ]) {
1342
+ test(`Note.getAttachments() cached parse failure (suppressError: ${suppressError})`, async () => {
1343
+ const jsonLd = embeddedPropertyDocument("attachment");
1344
+ const note = await Note.fromJsonLd(jsonLd);
1345
+ if (suppressError) {
1346
+ for (let i = 0; i < 2; i++) deepStrictEqual((await Array.fromAsync(note.getAttachments({ suppressError }))).map((attachment) => attachment.name), ["Cached image", "Decoded image"]);
1347
+ await rejects(() => Array.fromAsync(note.getAttachments()), TypeError);
1348
+ } else await rejects(() => Array.fromAsync(note.getAttachments({ suppressError })), TypeError);
1349
+ deepStrictEqual(await note.toJsonLd(), jsonLd);
1350
+ });
1351
+ test(`Activity.getObject() cached parse failure (suppressError: ${suppressError})`, async () => {
1352
+ const jsonLd = embeddedPropertyDocument("object");
1353
+ const activity = await Activity.fromJsonLd(jsonLd);
1354
+ if (suppressError) {
1355
+ deepStrictEqual(await activity.getObject({ suppressError }), null);
1356
+ deepStrictEqual(await activity.getObject({ suppressError }), null);
1357
+ await rejects(() => activity.getObject(), TypeError);
1358
+ } else await rejects(() => activity.getObject({ suppressError }), TypeError);
1359
+ deepStrictEqual(await activity.toJsonLd(), jsonLd);
1360
+ });
1361
+ }
1362
+ test("Note.getAttachments() suppresses cached context loader failures", async () => {
1363
+ const note = await Note.fromJsonLd(embeddedPropertyDocument("attachment"));
1364
+ const contextLoader = () => Promise.reject(/* @__PURE__ */ new Error("Context loader failed"));
1365
+ await rejects(() => Array.fromAsync(note.getAttachments({ contextLoader })), (error) => error instanceof Error && !(error instanceof TypeError));
1366
+ deepStrictEqual((await Array.fromAsync(note.getAttachments({
1367
+ contextLoader,
1368
+ suppressError: true
1369
+ }))).map((attachment) => attachment.name), ["Decoded image"]);
1370
+ });
1371
+ test("Activity.getObject() suppresses cached context loader failures", async () => {
1372
+ const activity = await Activity.fromJsonLd(embeddedPropertyDocument("object"));
1373
+ const contextLoader = () => Promise.reject(/* @__PURE__ */ new Error("Context loader failed"));
1374
+ await rejects(() => activity.getObject({ contextLoader }), (error) => error instanceof Error && !(error instanceof TypeError));
1375
+ deepStrictEqual(await activity.getObject({
1376
+ contextLoader,
1377
+ suppressError: true
1378
+ }), null);
1379
+ });
1314
1380
  test("Activity.clone()", async () => {
1315
1381
  const activity = new Activity({
1316
1382
  actor: new Person({ name: "John Doe" }),
@@ -2188,6 +2254,83 @@ test("Delete.toJsonLd() compacts embedded QuoteRequest", async () => {
2188
2254
  type: "Delete"
2189
2255
  });
2190
2256
  });
2257
+ test("Note.quoteUrl (IRI-typed alias terms)", async () => {
2258
+ const jsonLd = {
2259
+ "@context": ["https://www.w3.org/ns/activitystreams", {
2260
+ fedibird: "http://fedibird.com/ns#",
2261
+ misskey: "https://misskey-hub.net/ns#",
2262
+ _misskey_quote: {
2263
+ "@id": "misskey:_misskey_quote",
2264
+ "@type": "@id"
2265
+ },
2266
+ quoteUri: {
2267
+ "@id": "fedibird:quoteUri",
2268
+ "@type": "@id"
2269
+ }
2270
+ }],
2271
+ id: "https://example.com/notes/1",
2272
+ type: "Note",
2273
+ _misskey_quote: "https://example.com/notes/quoted",
2274
+ quoteUri: "https://example.com/notes/quoted2"
2275
+ };
2276
+ deepStrictEqual((await Note.fromJsonLd(jsonLd)).quoteUrl, new URL("https://example.com/notes/quoted"));
2277
+ delete jsonLd._misskey_quote;
2278
+ deepStrictEqual((await Note.fromJsonLd(jsonLd)).quoteUrl, new URL("https://example.com/notes/quoted2"));
2279
+ });
2280
+ test("Note.quoteUrl (IRI-typed primary term)", async () => {
2281
+ const context = ["https://www.w3.org/ns/activitystreams", {
2282
+ misskey: "https://misskey-hub.net/ns#",
2283
+ quoteUrl: {
2284
+ "@id": "as:quoteUrl",
2285
+ "@type": "@id"
2286
+ },
2287
+ _misskey_quote: "misskey:_misskey_quote"
2288
+ }];
2289
+ deepStrictEqual((await Note.fromJsonLd({
2290
+ "@context": context,
2291
+ type: "Note",
2292
+ quoteUrl: "https://example.com/object"
2293
+ })).quoteUrl, new URL("https://example.com/object"));
2294
+ deepStrictEqual((await Note.fromJsonLd({
2295
+ "@context": context,
2296
+ type: "Note",
2297
+ quoteUrl: "https://example.com/object",
2298
+ _misskey_quote: "https://example.com/object2"
2299
+ })).quoteUrl, new URL("https://example.com/object"));
2300
+ });
2301
+ test("Note.quoteUrl (unparsable IRI-valued terms)", async () => {
2302
+ const context = ["https://www.w3.org/ns/activitystreams", {
2303
+ misskey: "https://misskey-hub.net/ns#",
2304
+ _misskey_quote: {
2305
+ "@id": "misskey:_misskey_quote",
2306
+ "@type": "@id"
2307
+ }
2308
+ }];
2309
+ for (const quote of [
2310
+ {
2311
+ type: "Note",
2312
+ content: "An inlined quote without an id"
2313
+ },
2314
+ "_:b0",
2315
+ "",
2316
+ "notes/relative"
2317
+ ]) {
2318
+ const loaded = await Note.fromJsonLd({
2319
+ "@context": context,
2320
+ id: "https://example.com/notes/1",
2321
+ type: "Note",
2322
+ content: "Hello",
2323
+ _misskey_quote: quote
2324
+ });
2325
+ deepStrictEqual(loaded.quoteUrl, null, JSON.stringify(quote));
2326
+ deepStrictEqual(loaded.content, "Hello");
2327
+ }
2328
+ deepStrictEqual((await Note.fromJsonLd({
2329
+ "@context": context,
2330
+ type: "Note",
2331
+ _misskey_quote: "at://did:plc:abc/app.bsky.feed.post/xyz"
2332
+ })).quoteUrl, new URL("at://" + encodeURIComponent("did:plc:abc/app.bsky.feed.post/xyz")));
2333
+ });
2191
2334
  test("Key.publicKey", async () => {
2192
2335
  const jwk = {
2193
2336
  kty: "RSA",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab",
3
- "version": "2.4.0-dev.2155+2ccc5085",
3
+ "version": "2.4.0-dev.2159+bb072eda",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,9 +43,9 @@
43
43
  "jsonld": "^9.0.0",
44
44
  "pkijs": "^3.3.3",
45
45
  "temporal-polyfill": "^1.0.1",
46
- "@fedify/webfinger": "2.4.0-dev.2155+2ccc5085",
47
- "@fedify/vocab-tools": "2.4.0-dev.2155+2ccc5085",
48
- "@fedify/vocab-runtime": "2.4.0-dev.2155+2ccc5085"
46
+ "@fedify/vocab-tools": "2.4.0-dev.2159+bb072eda",
47
+ "@fedify/webfinger": "2.4.0-dev.2159+bb072eda",
48
+ "@fedify/vocab-runtime": "2.4.0-dev.2159+bb072eda"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^24.12.2",
@@ -80,7 +80,8 @@ test("lookupObject()", {
80
80
  );
81
81
  });
82
82
 
83
- fetchMock.removeRoutes();
83
+ // Workers load fixtures through fetch(), so keep the spy fallback between steps.
84
+ fetchMock.removeRoutes({ includeFallback: false });
84
85
  fetchMock.get("begin:https://example.com/.well-known/webfinger", {
85
86
  subject: "acct:janedoe@example.com",
86
87
  links: [
@@ -100,7 +101,7 @@ test("lookupObject()", {
100
101
  );
101
102
  });
102
103
 
103
- fetchMock.removeRoutes();
104
+ fetchMock.removeRoutes({ includeFallback: false });
104
105
  fetchMock.get(
105
106
  "begin:https://example.com/.well-known/webfinger",
106
107
  () =>
@@ -131,7 +132,7 @@ test("lookupObject()", {
131
132
  deepStrictEqual(await promise, null);
132
133
  });
133
134
 
134
- fetchMock.removeRoutes();
135
+ fetchMock.removeRoutes({ includeFallback: false });
135
136
  fetchMock.get(
136
137
  "begin:https://example.com/.well-known/webfinger",
137
138
  {
@@ -156,7 +157,7 @@ test("lookupObject()", {
156
157
  deepStrictEqual(person.id, new URL("https://example.com/person"));
157
158
  });
158
159
 
159
- fetchMock.removeRoutes();
160
+ fetchMock.removeRoutes({ includeFallback: false });
160
161
  fetchMock.get(
161
162
  "begin:https://example.com/.well-known/webfinger",
162
163
  () =>
@@ -187,7 +188,7 @@ test("lookupObject()", {
187
188
  deepStrictEqual(result, null);
188
189
  });
189
190
 
190
- fetchMock.removeRoutes();
191
+ fetchMock.removeRoutes({ includeFallback: false });
191
192
  fetchMock.get(
192
193
  "https://example.com/slow-object",
193
194
  () =>
package/src/vocab.test.ts CHANGED
@@ -2007,6 +2007,104 @@ test({
2007
2007
  },
2008
2008
  });
2009
2009
 
2010
+ // The alias is inherited during initial expansion, but is unavailable when
2011
+ // an embedded value is reparsed standalone from the cached JSON-LD.
2012
+ function embeddedPropertyDocument(property: "attachment" | "object") {
2013
+ const context = "https://www.w3.org/ns/activitystreams";
2014
+ return {
2015
+ "@context": [context, { CustomImage: `${context}#Image` }],
2016
+ type: property === "attachment" ? "Note" : "Create",
2017
+ [property]: [
2018
+ {
2019
+ "@context": [context, { "@vocab": "https://example.com/" }],
2020
+ type: "CustomImage",
2021
+ name: "Malformed when reparsed",
2022
+ },
2023
+ { "@context": context, type: "Image", name: "Cached image" },
2024
+ { type: "Image", name: "Decoded image" },
2025
+ ],
2026
+ };
2027
+ }
2028
+
2029
+ for (const suppressError of [undefined, false, true]) {
2030
+ test(
2031
+ `Note.getAttachments() cached parse failure (suppressError: ${suppressError})`,
2032
+ async () => {
2033
+ const jsonLd = embeddedPropertyDocument("attachment");
2034
+ const note = await Note.fromJsonLd(jsonLd);
2035
+ if (suppressError) {
2036
+ for (let i = 0; i < 2; i++) {
2037
+ const attachments: (Object | Link | PropertyValue)[] = await Array
2038
+ .fromAsync(
2039
+ note.getAttachments({ suppressError }),
2040
+ );
2041
+ deepStrictEqual(
2042
+ attachments.map((attachment) => attachment.name),
2043
+ ["Cached image", "Decoded image"],
2044
+ );
2045
+ }
2046
+ // Suppression must not remove or replace the cached malformed value.
2047
+ await rejects(() => Array.fromAsync(note.getAttachments()), TypeError);
2048
+ } else {
2049
+ await rejects(
2050
+ () => Array.fromAsync(note.getAttachments({ suppressError })),
2051
+ TypeError,
2052
+ );
2053
+ }
2054
+ deepStrictEqual(await note.toJsonLd(), jsonLd);
2055
+ },
2056
+ );
2057
+
2058
+ test(
2059
+ `Activity.getObject() cached parse failure (suppressError: ${suppressError})`,
2060
+ async () => {
2061
+ const jsonLd = embeddedPropertyDocument("object");
2062
+ const activity = await Activity.fromJsonLd(jsonLd);
2063
+ if (suppressError) {
2064
+ deepStrictEqual(await activity.getObject({ suppressError }), null);
2065
+ deepStrictEqual(await activity.getObject({ suppressError }), null);
2066
+ await rejects(() => activity.getObject(), TypeError);
2067
+ } else {
2068
+ await rejects(() => activity.getObject({ suppressError }), TypeError);
2069
+ }
2070
+ deepStrictEqual(await activity.toJsonLd(), jsonLd);
2071
+ },
2072
+ );
2073
+ }
2074
+
2075
+ test("Note.getAttachments() suppresses cached context loader failures", async () => {
2076
+ const note = await Note.fromJsonLd(embeddedPropertyDocument("attachment"));
2077
+ const contextLoader = () =>
2078
+ Promise.reject(new Error("Context loader failed"));
2079
+ await rejects(
2080
+ () => Array.fromAsync(note.getAttachments({ contextLoader })),
2081
+ (error: unknown) => error instanceof Error && !(error instanceof TypeError),
2082
+ );
2083
+ // Both cached values need the failing loader; the decoded value remains usable.
2084
+ const attachments: (Object | Link | PropertyValue)[] = await Array.fromAsync(
2085
+ note.getAttachments({ contextLoader, suppressError: true }),
2086
+ );
2087
+ deepStrictEqual(attachments.map((attachment) => attachment.name), [
2088
+ "Decoded image",
2089
+ ]);
2090
+ });
2091
+
2092
+ test("Activity.getObject() suppresses cached context loader failures", async () => {
2093
+ const activity = await Activity.fromJsonLd(
2094
+ embeddedPropertyDocument("object"),
2095
+ );
2096
+ const contextLoader = () =>
2097
+ Promise.reject(new Error("Context loader failed"));
2098
+ await rejects(
2099
+ () => activity.getObject({ contextLoader }),
2100
+ (error: unknown) => error instanceof Error && !(error instanceof TypeError),
2101
+ );
2102
+ deepStrictEqual(
2103
+ await activity.getObject({ contextLoader, suppressError: true }),
2104
+ null,
2105
+ );
2106
+ });
2107
+
2010
2108
  test("Activity.clone()", async () => {
2011
2109
  const activity = new Activity({
2012
2110
  actor: new Person({
@@ -3354,6 +3452,105 @@ test("Delete.toJsonLd() compacts embedded QuoteRequest", async () => {
3354
3452
  );
3355
3453
  });
3356
3454
 
3455
+ test("Note.quoteUrl (IRI-typed alias terms)", async () => {
3456
+ const jsonLd: Record<string, unknown> = {
3457
+ "@context": [
3458
+ "https://www.w3.org/ns/activitystreams",
3459
+ {
3460
+ fedibird: "http://fedibird.com/ns#",
3461
+ misskey: "https://misskey-hub.net/ns#",
3462
+ _misskey_quote: {
3463
+ "@id": "misskey:_misskey_quote",
3464
+ "@type": "@id",
3465
+ },
3466
+ quoteUri: {
3467
+ "@id": "fedibird:quoteUri",
3468
+ "@type": "@id",
3469
+ },
3470
+ },
3471
+ ],
3472
+ id: "https://example.com/notes/1",
3473
+ type: "Note",
3474
+ _misskey_quote: "https://example.com/notes/quoted",
3475
+ quoteUri: "https://example.com/notes/quoted2",
3476
+ };
3477
+
3478
+ const loaded = await Note.fromJsonLd(jsonLd);
3479
+ deepStrictEqual(loaded.quoteUrl, new URL("https://example.com/notes/quoted"));
3480
+
3481
+ delete jsonLd._misskey_quote;
3482
+ const loaded2 = await Note.fromJsonLd(jsonLd);
3483
+ deepStrictEqual(
3484
+ loaded2.quoteUrl,
3485
+ new URL("https://example.com/notes/quoted2"),
3486
+ );
3487
+ });
3488
+
3489
+ test("Note.quoteUrl (IRI-typed primary term)", async () => {
3490
+ const context = [
3491
+ "https://www.w3.org/ns/activitystreams",
3492
+ {
3493
+ misskey: "https://misskey-hub.net/ns#",
3494
+ quoteUrl: { "@id": "as:quoteUrl", "@type": "@id" },
3495
+ _misskey_quote: "misskey:_misskey_quote",
3496
+ },
3497
+ ];
3498
+
3499
+ const loaded = await Note.fromJsonLd({
3500
+ "@context": context,
3501
+ type: "Note",
3502
+ quoteUrl: "https://example.com/object",
3503
+ });
3504
+ deepStrictEqual(loaded.quoteUrl, new URL("https://example.com/object"));
3505
+
3506
+ // An IRI-valued quoteUrl still takes precedence over a plain-string alias:
3507
+ const loaded2 = await Note.fromJsonLd({
3508
+ "@context": context,
3509
+ type: "Note",
3510
+ quoteUrl: "https://example.com/object",
3511
+ _misskey_quote: "https://example.com/object2",
3512
+ });
3513
+ deepStrictEqual(loaded2.quoteUrl, new URL("https://example.com/object"));
3514
+ });
3515
+
3516
+ test("Note.quoteUrl (unparsable IRI-valued terms)", async () => {
3517
+ const context = [
3518
+ "https://www.w3.org/ns/activitystreams",
3519
+ {
3520
+ misskey: "https://misskey-hub.net/ns#",
3521
+ _misskey_quote: { "@id": "misskey:_misskey_quote", "@type": "@id" },
3522
+ },
3523
+ ];
3524
+ for (
3525
+ const quote of [
3526
+ { type: "Note", content: "An inlined quote without an id" },
3527
+ "_:b0",
3528
+ "",
3529
+ "notes/relative",
3530
+ ]
3531
+ ) {
3532
+ const loaded = await Note.fromJsonLd({
3533
+ "@context": context,
3534
+ id: "https://example.com/notes/1",
3535
+ type: "Note",
3536
+ content: "Hello",
3537
+ _misskey_quote: quote,
3538
+ });
3539
+ deepStrictEqual(loaded.quoteUrl, null, JSON.stringify(quote));
3540
+ deepStrictEqual(loaded.content, "Hello");
3541
+ }
3542
+
3543
+ const atUri = await Note.fromJsonLd({
3544
+ "@context": context,
3545
+ type: "Note",
3546
+ _misskey_quote: "at://did:plc:abc/app.bsky.feed.post/xyz",
3547
+ });
3548
+ deepStrictEqual(
3549
+ atUri.quoteUrl,
3550
+ new URL("at://" + encodeURIComponent("did:plc:abc/app.bsky.feed.post/xyz")),
3551
+ );
3552
+ });
3553
+
3357
3554
  test("Key.publicKey", async () => {
3358
3555
  const jwk = {
3359
3556
  kty: "RSA",