@fedify/vocab 2.4.0-pr.934.40 → 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.
Files changed (58) hide show
  1. package/deno.json +1 -1
  2. package/dist/mod.cjs +31713 -8698
  3. package/dist/mod.d.cts +4708 -7
  4. package/dist/mod.d.ts +4708 -7
  5. package/dist/mod.js +31606 -8598
  6. package/dist-tests/actor-D7_iX4I4.mjs +276 -0
  7. package/dist-tests/actor.test.mjs +55 -49
  8. package/dist-tests/application.yaml +15 -0
  9. package/dist-tests/audio.yaml +2 -1
  10. package/dist-tests/document.yaml +14 -1
  11. package/dist-tests/{actor-CnnyTI7i.mjs → esm-DP2Spt0u.mjs} +15 -211
  12. package/dist-tests/gateway.test.mjs +1906 -0
  13. package/dist-tests/group.yaml +15 -0
  14. package/dist-tests/image.yaml +2 -1
  15. package/dist-tests/link.yaml +14 -1
  16. package/dist-tests/lookup-IiNFWuXL.mjs +480 -0
  17. package/dist-tests/lookup.test.mjs +10 -268
  18. package/dist-tests/object.yaml +15 -0
  19. package/dist-tests/organization.yaml +15 -0
  20. package/dist-tests/page.yaml +2 -1
  21. package/dist-tests/person.yaml +15 -0
  22. package/dist-tests/portable-lookup.test.mjs +653 -0
  23. package/dist-tests/service.yaml +15 -0
  24. package/dist-tests/signed-representation.test.mjs +189 -0
  25. package/dist-tests/translation.test.mjs +404 -0
  26. package/dist-tests/translation.yaml +91 -0
  27. package/dist-tests/type.test.mjs +2 -2
  28. package/dist-tests/video.yaml +2 -1
  29. package/dist-tests/{vocab-OU5GYbNg.mjs → vocab-COZcxf00.mjs} +31227 -8501
  30. package/dist-tests/vocab.test.mjs +824 -6
  31. package/package.json +5 -5
  32. package/src/__snapshots__/vocab.test.ts.snap +345 -30
  33. package/src/actor.test.ts +41 -40
  34. package/src/actor.ts +120 -1
  35. package/src/application.yaml +15 -0
  36. package/src/audio.yaml +2 -1
  37. package/src/document.yaml +14 -1
  38. package/src/gateway.test.ts +2637 -0
  39. package/src/group.yaml +15 -0
  40. package/src/image.yaml +2 -1
  41. package/src/link.yaml +14 -1
  42. package/src/lookup.test.ts +6 -5
  43. package/src/lookup.ts +460 -33
  44. package/src/mod.ts +9 -0
  45. package/src/object.yaml +15 -0
  46. package/src/organization.yaml +15 -0
  47. package/src/page.yaml +2 -1
  48. package/src/person.yaml +15 -0
  49. package/src/portable-lookup.test.ts +889 -0
  50. package/src/preprocessors.ts +4 -2
  51. package/src/service.yaml +15 -0
  52. package/src/signed-representation.test.ts +277 -0
  53. package/src/translation.test.ts +518 -0
  54. package/src/translation.yaml +91 -0
  55. package/src/video.yaml +2 -1
  56. package/src/vocab.test.ts +1155 -2
  57. /package/dist-tests/{type-_BlsfT3S.mjs → type-BMdjm4Eg.mjs} +0 -0
  58. /package/dist-tests/{utils-CQjrpUkt.mjs → utils-Df2odC27.mjs} +0 -0
@@ -1,269 +1,11 @@
1
- import { Temporal } from "temporal-polyfill";
1
+ import "temporal-polyfill";
2
2
  globalThis.addEventListener = () => {};
3
- import { D as Object$1, E as Note, j as Person, o as Collection } from "./vocab-OU5GYbNg.mjs";
4
- import { c as esm_default, i as isActor, o as name, s as version } from "./actor-CnnyTI7i.mjs";
5
- import { t as getTypeId } from "./type-_BlsfT3S.mjs";
6
- import { t as assertInstanceOf } from "./utils-CQjrpUkt.mjs";
3
+ import { A as Object$1, F as Person, k as Note, s as Collection } from "./vocab-COZcxf00.mjs";
4
+ import { t as esm_default } from "./esm-DP2Spt0u.mjs";
5
+ import { n as traverseCollection, t as lookupObject } from "./lookup-IiNFWuXL.mjs";
6
+ import { t as assertInstanceOf } from "./utils-Df2odC27.mjs";
7
7
  import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
8
8
  import { deepStrictEqual, equal, ok, rejects } from "node:assert/strict";
9
- import { lookupWebFinger } from "@fedify/webfinger";
10
- import { SpanStatusCode, trace } from "@opentelemetry/api";
11
- import { getLogger } from "@logtape/logtape";
12
- import { getDocumentLoader, haveSameFe34Origin, haveSameIriOrigin, parseIri } from "@fedify/vocab-runtime";
13
- import { delay } from "es-toolkit";
14
- //#region src/handle.ts
15
- /**
16
- * Regular expression to match a fediverse handle in the format `@user@server`
17
- * or `user@server`. The `user` part can contain alphanumeric characters and
18
- * some special characters except `@`. The `server` part is all characters
19
- * after the `@` symbol in the middle.
20
- */
21
- const handleRegexp = /^@?((?:[-A-Za-z0-9._~!$&'()*+,;=]|%[A-Fa-f0-9]{2})+)@([^@]+)$/;
22
- /**
23
- * Parses a fediverse handle in the format `@user@server` or `user@server`.
24
- * The `user` part can contain alphanumeric characters and some special
25
- * characters except `@`. The `server` part is all characters after the `@`
26
- * symbol in the middle.
27
- *
28
- * @example
29
- * ```typescript
30
- * const handle = parseFediverseHandle("@username@example.com");
31
- * console.log(handle?.username); // "username"
32
- * console.log(handle?.host); // "example.com"
33
- * ```
34
- *
35
- * @param handle - The fediverse handle string to parse.
36
- * @returns A {@link FediverseHandle} object with `username` and `host`
37
- * if the input is valid; otherwise `null`.
38
- * @since 1.8.0
39
- */
40
- function parseFediverseHandle(handle) {
41
- const match = handleRegexp.exec(handle);
42
- if (match) return {
43
- username: match[1],
44
- host: match[2]
45
- };
46
- return null;
47
- }
48
- /**
49
- * Converts a fediverse handle in the format `@user@server` or `user@server`
50
- * to an `acct:` URI, which is a URL-like identifier for ActivityPub actors.
51
- *
52
- * @example
53
- * ```typescript
54
- * const identifier = toAcctUrl("@username@example.com");
55
- * console.log(identifier?.href); // "acct:username@example.com"
56
- * ```
57
- *
58
- * @param handle - The fediverse handle string to convert.
59
- * @returns A `URL` object representing the `acct:` URI if conversion succeeds;
60
- * otherwise `null`.
61
- * @since 1.8.0
62
- */
63
- function toAcctUrl(handle) {
64
- const parsed = parseFediverseHandle(handle);
65
- if (!parsed) return null;
66
- return new URL(`acct:${parsed.username}@${parsed.host}`);
67
- }
68
- //#endregion
69
- //#region src/lookup.ts
70
- const objectLookupCounters = /* @__PURE__ */ new WeakMap();
71
- function getObjectLookupCounter(meterProvider) {
72
- let counter = objectLookupCounters.get(meterProvider);
73
- if (counter == null) {
74
- counter = meterProvider.getMeter(name, version).createCounter("activitypub.object.lookup", {
75
- description: "ActivityStreams object lookups via lookupObject(), classified by whether the resolved value is an Actor.",
76
- unit: "{lookup}"
77
- });
78
- objectLookupCounters.set(meterProvider, counter);
79
- }
80
- return counter;
81
- }
82
- function getLookupRemoteHost(identifier) {
83
- let url;
84
- if (identifier instanceof URL) url = identifier;
85
- else try {
86
- url = new URL(identifier);
87
- } catch {
88
- return extractHandleHost(identifier.startsWith("@") ? identifier.slice(1) : identifier);
89
- }
90
- if (url.host !== "") return url.host;
91
- if (url.protocol === "acct:") return extractHandleHost(url.pathname);
92
- }
93
- function extractHandleHost(handle) {
94
- const at = handle.lastIndexOf("@");
95
- if (at < 0 || at >= handle.length - 1) return void 0;
96
- const candidate = handle.slice(at + 1);
97
- if (/[/?#\s]/.test(candidate)) return void 0;
98
- try {
99
- return new URL(`https://${candidate}`).host || void 0;
100
- } catch {
101
- return;
102
- }
103
- }
104
- const logger = getLogger([
105
- "fedify",
106
- "vocab",
107
- "lookup"
108
- ]);
109
- /**
110
- * Looks up an ActivityStreams object by its URI (including `acct:` URIs)
111
- * or a fediverse handle (e.g., `@user@server` or `user@server`).
112
- *
113
- * @example
114
- * ``` typescript
115
- * // Look up an actor by its fediverse handle:
116
- * await lookupObject("@hongminhee@fosstodon.org");
117
- * // returning a `Person` object.
118
- *
119
- * // A fediverse handle can omit the leading '@':
120
- * await lookupObject("hongminhee@fosstodon.org");
121
- * // returning a `Person` object.
122
- *
123
- * // A `acct:` URI can be used as well:
124
- * await lookupObject("acct:hongminhee@fosstodon.org");
125
- * // returning a `Person` object.
126
- *
127
- * // Look up an object by its URI:
128
- * await lookupObject("https://todon.eu/@hongminhee/112060633798771581");
129
- * // returning a `Note` object.
130
- *
131
- * // It can be a `URL` object as well:
132
- * await lookupObject(new URL("https://todon.eu/@hongminhee/112060633798771581"));
133
- * // returning a `Note` object.
134
- * ```
135
- *
136
- * @param identifier The URI or fediverse handle to look up.
137
- * @param options Lookup options.
138
- * @returns The object, or `null` if not found.
139
- * @since 0.2.0
140
- */
141
- async function lookupObject(identifier, options = {}) {
142
- return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("activitypub.lookup_object", async (span) => {
143
- let kind = "other";
144
- try {
145
- const result = await lookupObjectInternal(identifier, options);
146
- if (result != null) kind = isActor(result) ? "actor" : "object";
147
- if (result == null) span.setStatus({ code: SpanStatusCode.ERROR });
148
- else {
149
- if (result.id != null) span.setAttribute("activitypub.object.id", result.id.href);
150
- span.setAttribute("activitypub.object.type", getTypeId(result).href);
151
- if (result.replyTargetIds.length > 0) span.setAttribute("activitypub.object.in_reply_to", result.replyTargetIds.map((id) => id.href));
152
- span.addEvent("activitypub.object.fetched", {
153
- "activitypub.object.type": getTypeId(result).href,
154
- "activitypub.object.json": JSON.stringify(await result.toJsonLd(options))
155
- });
156
- }
157
- return result;
158
- } catch (error) {
159
- span.recordException(error);
160
- span.setStatus({
161
- code: SpanStatusCode.ERROR,
162
- message: String(error)
163
- });
164
- throw error;
165
- } finally {
166
- if (options.meterProvider != null) {
167
- const attributes = { "activitypub.lookup.kind": kind };
168
- const host = getLookupRemoteHost(identifier);
169
- if (host != null) attributes["activitypub.remote.host"] = host;
170
- getObjectLookupCounter(options.meterProvider).add(1, attributes);
171
- }
172
- span.end();
173
- }
174
- });
175
- }
176
- async function lookupObjectInternal(identifier, options = {}) {
177
- const documentLoader = options.documentLoader ?? getDocumentLoader({ userAgent: options.userAgent });
178
- if (typeof identifier === "string") identifier = toAcctUrl(identifier) ?? new URL(identifier);
179
- let remoteDoc = null;
180
- if (identifier.protocol === "http:" || identifier.protocol === "https:") try {
181
- remoteDoc = await documentLoader(identifier.href, { signal: options.signal });
182
- } catch (error) {
183
- logger.debug("Failed to fetch remote document:\n{error}", { error });
184
- }
185
- if (remoteDoc == null) {
186
- const jrd = await lookupWebFinger(identifier, {
187
- userAgent: options.userAgent,
188
- tracerProvider: options.tracerProvider,
189
- meterProvider: options.meterProvider,
190
- allowPrivateAddress: "allowPrivateAddress" in options && options.allowPrivateAddress === true,
191
- signal: options.signal
192
- });
193
- if (jrd?.links == null) return null;
194
- for (const l of jrd.links) {
195
- if (l.type !== "application/activity+json" && !l.type?.match(/application\/ld\+json;\s*profile="https:\/\/www.w3.org\/ns\/activitystreams"/) || l.rel !== "self" || l.href == null) continue;
196
- try {
197
- remoteDoc = await documentLoader(l.href, { signal: options.signal });
198
- break;
199
- } catch (error) {
200
- logger.debug("Failed to fetch remote document:\n{error}", { error });
201
- continue;
202
- }
203
- }
204
- }
205
- if (remoteDoc == null) return null;
206
- let object;
207
- let documentUrl;
208
- try {
209
- documentUrl = parseIri(remoteDoc.documentUrl);
210
- object = await Object$1.fromJsonLd(remoteDoc.document, {
211
- documentLoader,
212
- contextLoader: options.contextLoader,
213
- tracerProvider: options.tracerProvider,
214
- baseUrl: documentUrl
215
- });
216
- } catch (error) {
217
- if (error instanceof TypeError) {
218
- logger.debug("Failed to parse JSON-LD document: {error}\n{document}", {
219
- ...remoteDoc,
220
- error
221
- });
222
- return null;
223
- }
224
- throw error;
225
- }
226
- if (options.crossOrigin !== "trust" && object.id != null && !haveSameIriOrigin(object.id, documentUrl) && !haveSameFe34Origin(object.id, documentUrl)) {
227
- if (options.crossOrigin === "throw") throw new Error(`The object's @id (${object.id.href}) has a different origin than the document URL (${remoteDoc.documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to "trust".`);
228
- logger.warn("The object's @id ({objectId}) has a different origin than the document URL ({documentUrl}); refusing to return the object. If you want to bypass this check and are aware of the security implications, set the crossOrigin option to \"trust\".", {
229
- ...remoteDoc,
230
- objectId: object.id.href
231
- });
232
- return null;
233
- }
234
- return object;
235
- }
236
- /**
237
- * Traverses a collection, yielding each item in the collection.
238
- * If the collection is paginated, it will fetch the next page
239
- * automatically.
240
- *
241
- * @example
242
- * ``` typescript
243
- * const collection = await lookupObject(collectionUrl);
244
- * if (collection instanceof Collection) {
245
- * for await (const item of traverseCollection(collection)) {
246
- * console.log(item.id?.href);
247
- * }
248
- * }
249
- * ```
250
- *
251
- * @param collection The collection to traverse.
252
- * @param options Options for traversing the collection.
253
- * @returns An async iterable of each item in the collection.
254
- * @since 1.1.0
255
- */
256
- async function* traverseCollection(collection, options = {}) {
257
- const interval = Temporal.Duration.from(options.interval ?? { seconds: 0 }).total("millisecond");
258
- let page = await collection.getFirst(options);
259
- if (page == null) for await (const item of collection.getItems(options)) yield item;
260
- else while (page != null) {
261
- for await (const item of page.getItems(options)) yield item;
262
- if (interval > 0) await delay(interval);
263
- page = await page.getNext(options);
264
- }
265
- }
266
- //#endregion
267
9
  //#region src/lookup.test.ts
268
10
  test("lookupObject()", {
269
11
  sanitizeResources: false,
@@ -316,7 +58,7 @@ test("lookupObject()", {
316
58
  name: "Hong Gildong"
317
59
  }));
318
60
  });
319
- esm_default.removeRoutes();
61
+ esm_default.removeRoutes({ includeFallback: false });
320
62
  esm_default.get("begin:https://example.com/.well-known/webfinger", {
321
63
  subject: "acct:janedoe@example.com",
322
64
  links: [{
@@ -329,7 +71,7 @@ test("lookupObject()", {
329
71
  deepStrictEqual(await lookupObject("janedoe@example.com", options), null);
330
72
  deepStrictEqual(await lookupObject("https://example.com/404", options), null);
331
73
  });
332
- esm_default.removeRoutes();
74
+ esm_default.removeRoutes({ includeFallback: false });
333
75
  esm_default.get("begin:https://example.com/.well-known/webfinger", () => new Promise((resolve) => {
334
76
  setTimeout(() => {
335
77
  resolve({
@@ -351,7 +93,7 @@ test("lookupObject()", {
351
93
  controller.abort();
352
94
  deepStrictEqual(await promise, null);
353
95
  });
354
- esm_default.removeRoutes();
96
+ esm_default.removeRoutes({ includeFallback: false });
355
97
  esm_default.get("begin:https://example.com/.well-known/webfinger", {
356
98
  subject: "acct:johndoe@example.com",
357
99
  links: [{
@@ -369,7 +111,7 @@ test("lookupObject()", {
369
111
  assertInstanceOf(person, Person);
370
112
  deepStrictEqual(person.id, new URL("https://example.com/person"));
371
113
  });
372
- esm_default.removeRoutes();
114
+ esm_default.removeRoutes({ includeFallback: false });
373
115
  esm_default.get("begin:https://example.com/.well-known/webfinger", () => new Promise((resolve) => {
374
116
  setTimeout(() => {
375
117
  resolve({
@@ -390,7 +132,7 @@ test("lookupObject()", {
390
132
  signal: controller.signal
391
133
  }), null);
392
134
  });
393
- esm_default.removeRoutes();
135
+ esm_default.removeRoutes({ includeFallback: false });
394
136
  esm_default.get("https://example.com/slow-object", () => new Promise((resolve) => {
395
137
  setTimeout(() => {
396
138
  resolve({
@@ -469,3 +469,18 @@ properties:
469
469
  (boost) was approved by the target post's author.
470
470
  range:
471
471
  - "https://gotosocial.org/ns#AnnounceAuthorization"
472
+
473
+ - pluralName: translations
474
+ singularName: translation
475
+ compactName: translations
476
+ uri: "https://w3id.org/fep/22cd#translations"
477
+ extraContext: "https://w3id.org/fep/22cd"
478
+ description: |
479
+ Translation metadata for the language versions in this object's content,
480
+ name, or summary maps, as defined by the draft
481
+ [FEP-22cd](https://w3id.org/fep/22cd). Each entry describes one translated
482
+ language; directly authored languages have no entry. The entries share
483
+ this object's identity, replies, and reactions. Translator credit grants
484
+ no authority to update the object.
485
+ range:
486
+ - "https://w3id.org/fep/22cd#Translation"
@@ -7,6 +7,7 @@ entity: true
7
7
  description: Represents an organization.
8
8
  defaultContext:
9
9
  - "https://www.w3.org/ns/activitystreams"
10
+ - "https://w3id.org/fep/ef61"
10
11
  - "https://w3id.org/security/v1"
11
12
  - "https://w3id.org/security/data-integrity/v1"
12
13
  - "https://www.w3.org/ns/did/v1"
@@ -78,6 +79,20 @@ properties:
78
79
  range:
79
80
  - "https://w3id.org/security#Multikey"
80
81
 
82
+ - pluralName: gateways
83
+ singularName: gateway
84
+ singularAccessor: true
85
+ compactName: gateways
86
+ uri: "https://w3id.org/fep/ef61/gateways"
87
+ container: list
88
+ description: |
89
+ Gateways where the latest version of this portable actor object can be
90
+ retrieved.
91
+
92
+ See [FEP-ef61](https://w3id.org/fep/ef61) for details.
93
+ range:
94
+ - "fedify:gatewayUrl"
95
+
81
96
  - singularName: manuallyApprovesFollowers
82
97
  functional: true
83
98
  compactName: manuallyApprovesFollowers
@@ -7,6 +7,7 @@ entity: true
7
7
  description: Represents a Web Page.
8
8
  defaultContext:
9
9
  - "https://www.w3.org/ns/activitystreams"
10
- - "https://w3id.org/security/data-integrity/v1"
10
+ - "https://w3id.org/security/data-integrity/v2"
11
+ - digestMultibase: "https://www.w3.org/ns/credentials/v2#digestMultibase"
11
12
  - "https://gotosocial.org/ns"
12
13
  properties: []
@@ -7,6 +7,7 @@ entity: true
7
7
  description: Represents an individual person.
8
8
  defaultContext:
9
9
  - "https://www.w3.org/ns/activitystreams"
10
+ - "https://w3id.org/fep/ef61"
10
11
  - "https://w3id.org/security/v1"
11
12
  - "https://w3id.org/security/data-integrity/v1"
12
13
  - "https://www.w3.org/ns/did/v1"
@@ -78,6 +79,20 @@ properties:
78
79
  range:
79
80
  - "https://w3id.org/security#Multikey"
80
81
 
82
+ - pluralName: gateways
83
+ singularName: gateway
84
+ singularAccessor: true
85
+ compactName: gateways
86
+ uri: "https://w3id.org/fep/ef61/gateways"
87
+ container: list
88
+ description: |
89
+ Gateways where the latest version of this portable actor object can be
90
+ retrieved.
91
+
92
+ See [FEP-ef61](https://w3id.org/fep/ef61) for details.
93
+ range:
94
+ - "fedify:gatewayUrl"
95
+
81
96
  - singularName: manuallyApprovesFollowers
82
97
  functional: true
83
98
  compactName: manuallyApprovesFollowers