@fedify/vocab-runtime 2.4.0-dev.2035 → 2.4.0-dev.2054

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 (41) hide show
  1. package/deno.json +2 -1
  2. package/dist/internal/portable-dereference.cjs +315 -0
  3. package/dist/internal/portable-dereference.d.cts +87 -0
  4. package/dist/internal/portable-dereference.d.ts +87 -0
  5. package/dist/internal/portable-dereference.js +311 -0
  6. package/dist/mod.cjs +8 -2
  7. package/dist/mod.d.cts +2 -1
  8. package/dist/mod.d.ts +2 -1
  9. package/dist/mod.js +8 -2
  10. package/dist/portable-CduElToy.d.ts +52 -0
  11. package/dist/portable-DxF2Ut-Z.d.cts +52 -0
  12. package/dist/tests/{body-DRpkDnEg.cjs → body-B-qUpCl3.cjs} +1 -1
  13. package/dist/tests/{body-BEIDQFV6.mjs → body-hdMfBU1p.mjs} +1 -1
  14. package/dist/tests/body.test.cjs +1 -1
  15. package/dist/tests/body.test.mjs +1 -1
  16. package/dist/tests/decimal.test.cjs +1 -1
  17. package/dist/tests/decimal.test.mjs +1 -1
  18. package/dist/tests/{docloader-M8t8o-GD.cjs → docloader-BLHl6D4V.cjs} +2 -2
  19. package/dist/tests/{docloader-DHux1ztB.mjs → docloader-C_fQRm09.mjs} +2 -2
  20. package/dist/tests/docloader.test.cjs +2 -2
  21. package/dist/tests/docloader.test.mjs +2 -2
  22. package/dist/tests/internal/portable-dereference.test.cjs +123 -0
  23. package/dist/tests/internal/portable-dereference.test.d.cts +1 -0
  24. package/dist/tests/internal/portable-dereference.test.d.mts +1 -0
  25. package/dist/tests/internal/portable-dereference.test.mjs +124 -0
  26. package/dist/tests/jsonld-CmDhHNTx.cjs +12 -0
  27. package/dist/tests/jsonld-DweIJ07b.mjs +5 -0
  28. package/dist/tests/jsonld-cache.test.cjs +17 -22
  29. package/dist/tests/jsonld-cache.test.mjs +1 -4
  30. package/dist/tests/{request-eP_YLVjb.mjs → request-Bbg1-1C3.mjs} +8 -2
  31. package/dist/tests/{request-BbczASfn.cjs → request-CcifKcqU.cjs} +8 -2
  32. package/dist/tests/request.test.cjs +4 -1
  33. package/dist/tests/request.test.mjs +4 -1
  34. package/package.json +11 -1
  35. package/src/internal/portable-dereference.test.ts +74 -0
  36. package/src/internal/portable-dereference.ts +426 -0
  37. package/src/mod.ts +4 -0
  38. package/src/portable.ts +52 -0
  39. package/src/request.test.ts +10 -1
  40. package/src/request.ts +9 -1
  41. package/tsdown.config.ts +1 -0
@@ -0,0 +1,123 @@
1
+ const require_url = require("../url-BqlldzT8.cjs");
2
+ require("../jsonld-CmDhHNTx.cjs");
3
+ let node_assert_strict = require("node:assert/strict");
4
+ let node_test = require("node:test");
5
+ let _logtape_logtape = require("@logtape/logtape");
6
+ require("@opentelemetry/api");
7
+ //#region src/internal/portable-dereference.ts
8
+ const logger = (0, _logtape_logtape.getLogger)([
9
+ "fedify",
10
+ "vocab",
11
+ "gateway"
12
+ ]);
13
+ /**
14
+ * The maximum number of `@gateway` location hints to try for one reference.
15
+ * Hints come from possibly untrusted documents, so they are bounded to keep
16
+ * a single accessor call from fanning out to many servers.
17
+ */
18
+ const MAX_GATEWAY_HINTS = 5;
19
+ const LOCATION_HINT_PARAMETER = "@gateway";
20
+ /**
21
+ * Checks whether a URL is an FEP-ef61 portable ActivityPub IRI.
22
+ *
23
+ * @internal Technically exported for generated vocabulary classes, but not
24
+ * part of the public API contract. This is not considered public API for
25
+ * Semantic Versioning decisions.
26
+ */
27
+ function isPortableIri(url) {
28
+ return url.protocol === "ap+ef61:" || url.protocol === "ap:";
29
+ }
30
+ /**
31
+ * Picks the ordered list of FEP-ef61 gateways to fetch a portable IRI from.
32
+ *
33
+ * If `gateways` is given, it is used as is (even when empty), and `@gateway`
34
+ * location hints in the IRI are ignored. Otherwise, up to
35
+ * {@link MAX_GATEWAY_HINTS} valid `@gateway` hints are used. Duplicate
36
+ * gateways are dropped in both cases.
37
+ *
38
+ * @throws {TypeError} If an explicit gateway is not an HTTP(S) origin.
39
+ * @internal
40
+ */
41
+ function getPortableGatewayCandidates(url, gateways) {
42
+ const candidates = [];
43
+ const seen = /* @__PURE__ */ new Set();
44
+ const add = (gateway) => {
45
+ if (seen.has(gateway.href)) return;
46
+ seen.add(gateway.href);
47
+ candidates.push(gateway);
48
+ };
49
+ if (gateways != null) {
50
+ for (const gateway of gateways) {
51
+ const parsed = parseGatewayOrigin(gateway);
52
+ if (parsed == null) throw new TypeError("FEP-ef61 gateways must be HTTP(S) origins with no credentials, path, query, or fragment: " + String(gateway));
53
+ add(parsed);
54
+ }
55
+ return candidates;
56
+ }
57
+ for (const hint of new URLSearchParams(url.search).getAll(LOCATION_HINT_PARAMETER)) {
58
+ if (candidates.length >= MAX_GATEWAY_HINTS) break;
59
+ const parsed = parseGatewayOrigin(hint);
60
+ if (parsed == null) {
61
+ logger.debug("Ignoring an invalid FEP-ef61 gateway hint {hint} in {url}.", {
62
+ hint,
63
+ url: require_url.formatIri(url)
64
+ });
65
+ continue;
66
+ }
67
+ add(parsed);
68
+ }
69
+ return candidates;
70
+ }
71
+ function parseGatewayOrigin(gateway) {
72
+ let url;
73
+ if (gateway instanceof URL) url = new URL(gateway.href);
74
+ else if (typeof gateway === "string" && URL.canParse(gateway)) url = new URL(gateway);
75
+ else return null;
76
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.href !== `${url.origin}/`) return null;
77
+ return url;
78
+ }
79
+ //#endregion
80
+ //#region src/internal/portable-dereference.test.ts
81
+ const hrefs = (urls) => urls.map((url) => url.href);
82
+ (0, node_test.test)("isPortableIri()", () => {
83
+ (0, node_assert_strict.ok)(isPortableIri(require_url.parseIri("ap://did:key:z6Mkabc/actor")));
84
+ (0, node_assert_strict.ok)(isPortableIri(require_url.parseIri("ap+ef61://did:key:z6Mkabc/actor")));
85
+ (0, node_assert_strict.ok)(isPortableIri(new URL("ap://did%3Akey%3Az6Mkabc/actor")));
86
+ (0, node_assert_strict.ok)(!isPortableIri(new URL("https://example.com/actor")));
87
+ (0, node_assert_strict.ok)(!isPortableIri(new URL("did:key:z6Mkabc")));
88
+ });
89
+ (0, node_test.test)("getPortableGatewayCandidates() uses explicit gateways", () => {
90
+ const url = require_url.parseIri("ap://did:key:z6Mkabc/actor?@gateway=https%3A%2F%2Fhint.example");
91
+ (0, node_assert_strict.deepStrictEqual)(hrefs(getPortableGatewayCandidates(url, [
92
+ "https://b.example",
93
+ new URL("https://a.example/"),
94
+ "https://b.example/",
95
+ "http://c.example:8080"
96
+ ])), [
97
+ "https://b.example/",
98
+ "https://a.example/",
99
+ "http://c.example:8080/"
100
+ ]);
101
+ (0, node_assert_strict.deepStrictEqual)(getPortableGatewayCandidates(url, []), []);
102
+ for (const gateway of [
103
+ "https://a.example/path",
104
+ "https://a.example/?",
105
+ "https://a.example/#",
106
+ "https://user:pass@a.example",
107
+ "ftp://a.example",
108
+ "a.example"
109
+ ]) (0, node_assert_strict.throws)(() => getPortableGatewayCandidates(url, [gateway]), TypeError);
110
+ });
111
+ (0, node_test.test)("getPortableGatewayCandidates() reads @gateway hints", () => {
112
+ (0, node_assert_strict.deepStrictEqual)(hrefs(getPortableGatewayCandidates(require_url.parseIri("ap://did:key:z6Mkabc/actor?page=1&@gateway=https%3A%2F%2Fa.example&gateways=https%3A%2F%2Flegacy.example&@gateway=not%20a%20URL&@gateway=https%3A%2F%2Fb.example%2Fpath&%40gateway=https%3A%2F%2Fb.example&@gateway=https%3A%2F%2Fa.example%2F"))), ["https://a.example/", "https://b.example/"]);
113
+ (0, node_assert_strict.deepStrictEqual)(getPortableGatewayCandidates(require_url.parseIri("ap://did:key:z6Mkabc/actor")), []);
114
+ const hints = Array.from({ length: 7 }, (_, i) => `@gateway=https%3A%2F%2Fg${i}.example`);
115
+ (0, node_assert_strict.deepStrictEqual)(hrefs(getPortableGatewayCandidates(require_url.parseIri(`ap://did:key:z6Mkabc/actor?${hints.join("&")}`))), [
116
+ 0,
117
+ 1,
118
+ 2,
119
+ 3,
120
+ 4
121
+ ].map((i) => `https://g${i}.example/`));
122
+ });
123
+ //#endregion
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,124 @@
1
+ import { a as formatIri, m as parseIri } from "../url-DgJM9WJn.mjs";
2
+ import "../jsonld-DweIJ07b.mjs";
3
+ import { deepStrictEqual, ok, throws } from "node:assert/strict";
4
+ import { test } from "node:test";
5
+ import { getLogger } from "@logtape/logtape";
6
+ import "@opentelemetry/api";
7
+ //#region src/internal/portable-dereference.ts
8
+ const logger = getLogger([
9
+ "fedify",
10
+ "vocab",
11
+ "gateway"
12
+ ]);
13
+ /**
14
+ * The maximum number of `@gateway` location hints to try for one reference.
15
+ * Hints come from possibly untrusted documents, so they are bounded to keep
16
+ * a single accessor call from fanning out to many servers.
17
+ */
18
+ const MAX_GATEWAY_HINTS = 5;
19
+ const LOCATION_HINT_PARAMETER = "@gateway";
20
+ /**
21
+ * Checks whether a URL is an FEP-ef61 portable ActivityPub IRI.
22
+ *
23
+ * @internal Technically exported for generated vocabulary classes, but not
24
+ * part of the public API contract. This is not considered public API for
25
+ * Semantic Versioning decisions.
26
+ */
27
+ function isPortableIri(url) {
28
+ return url.protocol === "ap+ef61:" || url.protocol === "ap:";
29
+ }
30
+ /**
31
+ * Picks the ordered list of FEP-ef61 gateways to fetch a portable IRI from.
32
+ *
33
+ * If `gateways` is given, it is used as is (even when empty), and `@gateway`
34
+ * location hints in the IRI are ignored. Otherwise, up to
35
+ * {@link MAX_GATEWAY_HINTS} valid `@gateway` hints are used. Duplicate
36
+ * gateways are dropped in both cases.
37
+ *
38
+ * @throws {TypeError} If an explicit gateway is not an HTTP(S) origin.
39
+ * @internal
40
+ */
41
+ function getPortableGatewayCandidates(url, gateways) {
42
+ const candidates = [];
43
+ const seen = /* @__PURE__ */ new Set();
44
+ const add = (gateway) => {
45
+ if (seen.has(gateway.href)) return;
46
+ seen.add(gateway.href);
47
+ candidates.push(gateway);
48
+ };
49
+ if (gateways != null) {
50
+ for (const gateway of gateways) {
51
+ const parsed = parseGatewayOrigin(gateway);
52
+ if (parsed == null) throw new TypeError("FEP-ef61 gateways must be HTTP(S) origins with no credentials, path, query, or fragment: " + String(gateway));
53
+ add(parsed);
54
+ }
55
+ return candidates;
56
+ }
57
+ for (const hint of new URLSearchParams(url.search).getAll(LOCATION_HINT_PARAMETER)) {
58
+ if (candidates.length >= MAX_GATEWAY_HINTS) break;
59
+ const parsed = parseGatewayOrigin(hint);
60
+ if (parsed == null) {
61
+ logger.debug("Ignoring an invalid FEP-ef61 gateway hint {hint} in {url}.", {
62
+ hint,
63
+ url: formatIri(url)
64
+ });
65
+ continue;
66
+ }
67
+ add(parsed);
68
+ }
69
+ return candidates;
70
+ }
71
+ function parseGatewayOrigin(gateway) {
72
+ let url;
73
+ if (gateway instanceof URL) url = new URL(gateway.href);
74
+ else if (typeof gateway === "string" && URL.canParse(gateway)) url = new URL(gateway);
75
+ else return null;
76
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.href !== `${url.origin}/`) return null;
77
+ return url;
78
+ }
79
+ //#endregion
80
+ //#region src/internal/portable-dereference.test.ts
81
+ const hrefs = (urls) => urls.map((url) => url.href);
82
+ test("isPortableIri()", () => {
83
+ ok(isPortableIri(parseIri("ap://did:key:z6Mkabc/actor")));
84
+ ok(isPortableIri(parseIri("ap+ef61://did:key:z6Mkabc/actor")));
85
+ ok(isPortableIri(new URL("ap://did%3Akey%3Az6Mkabc/actor")));
86
+ ok(!isPortableIri(new URL("https://example.com/actor")));
87
+ ok(!isPortableIri(new URL("did:key:z6Mkabc")));
88
+ });
89
+ test("getPortableGatewayCandidates() uses explicit gateways", () => {
90
+ const url = parseIri("ap://did:key:z6Mkabc/actor?@gateway=https%3A%2F%2Fhint.example");
91
+ deepStrictEqual(hrefs(getPortableGatewayCandidates(url, [
92
+ "https://b.example",
93
+ new URL("https://a.example/"),
94
+ "https://b.example/",
95
+ "http://c.example:8080"
96
+ ])), [
97
+ "https://b.example/",
98
+ "https://a.example/",
99
+ "http://c.example:8080/"
100
+ ]);
101
+ deepStrictEqual(getPortableGatewayCandidates(url, []), []);
102
+ for (const gateway of [
103
+ "https://a.example/path",
104
+ "https://a.example/?",
105
+ "https://a.example/#",
106
+ "https://user:pass@a.example",
107
+ "ftp://a.example",
108
+ "a.example"
109
+ ]) throws(() => getPortableGatewayCandidates(url, [gateway]), TypeError);
110
+ });
111
+ test("getPortableGatewayCandidates() reads @gateway hints", () => {
112
+ deepStrictEqual(hrefs(getPortableGatewayCandidates(parseIri("ap://did:key:z6Mkabc/actor?page=1&@gateway=https%3A%2F%2Fa.example&gateways=https%3A%2F%2Flegacy.example&@gateway=not%20a%20URL&@gateway=https%3A%2F%2Fb.example%2Fpath&%40gateway=https%3A%2F%2Fb.example&@gateway=https%3A%2F%2Fa.example%2F"))), ["https://a.example/", "https://b.example/"]);
113
+ deepStrictEqual(getPortableGatewayCandidates(parseIri("ap://did:key:z6Mkabc/actor")), []);
114
+ const hints = Array.from({ length: 7 }, (_, i) => `@gateway=https%3A%2F%2Fg${i}.example`);
115
+ deepStrictEqual(hrefs(getPortableGatewayCandidates(parseIri(`ap://did:key:z6Mkabc/actor?${hints.join("&")}`))), [
116
+ 0,
117
+ 1,
118
+ 2,
119
+ 3,
120
+ 4
121
+ ].map((i) => `https://g${i}.example/`));
122
+ });
123
+ //#endregion
124
+ export {};
@@ -0,0 +1,12 @@
1
+ const require_rolldown_runtime = require("./rolldown-runtime-emK7D4bc.cjs");
2
+ let jsonld_dist_jsonld_esm_js = require("jsonld/dist/jsonld.esm.js");
3
+ jsonld_dist_jsonld_esm_js = require_rolldown_runtime.__toESM(jsonld_dist_jsonld_esm_js, 1);
4
+ //#region src/jsonld.ts
5
+ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
6
+ //#endregion
7
+ Object.defineProperty(exports, "jsonld_default", {
8
+ enumerable: true,
9
+ get: function() {
10
+ return jsonld_default;
11
+ }
12
+ });
@@ -0,0 +1,5 @@
1
+ import jsonld from "jsonld/dist/jsonld.esm.js";
2
+ //#region src/jsonld.ts
3
+ var jsonld_default = jsonld;
4
+ //#endregion
5
+ export { jsonld_default as t };
@@ -1,12 +1,7 @@
1
- const require_rolldown_runtime = require("./rolldown-runtime-emK7D4bc.cjs");
2
1
  const require_url = require("./url-BqlldzT8.cjs");
2
+ const require_jsonld = require("./jsonld-CmDhHNTx.cjs");
3
3
  let node_test = require("node:test");
4
4
  let node_assert = require("node:assert");
5
- let jsonld_dist_jsonld_esm_js = require("jsonld/dist/jsonld.esm.js");
6
- jsonld_dist_jsonld_esm_js = require_rolldown_runtime.__toESM(jsonld_dist_jsonld_esm_js, 1);
7
- //#region src/jsonld.ts
8
- var jsonld_default = jsonld_dist_jsonld_esm_js.default;
9
- //#endregion
10
5
  //#region src/internal/jsonld-cache.ts
11
6
  const noJsonLdContext = Symbol("noJsonLdContext");
12
7
  /**
@@ -121,7 +116,7 @@ async function compactJsonLdCache(normalized, original, documentLoader, depth =
121
116
  const ownContext = getOwnJsonLdContext(original);
122
117
  const context = ownContext === noJsonLdContext ? inheritedContext : ownContext;
123
118
  if (context == null) return preserveNoContextJsonLdShape(normalized, original, depth);
124
- return await preserveJsonLdShape(await mergeUnmappedTerms(await jsonld_default.compact(Array.isArray(normalized) && normalized.length === 1 ? normalized[0] : normalized, context, { documentLoader }), original, context, documentLoader), original, context, documentLoader, depth);
119
+ return await preserveJsonLdShape(await mergeUnmappedTerms(await require_jsonld.jsonld_default.compact(Array.isArray(normalized) && normalized.length === 1 ? normalized[0] : normalized, context, { documentLoader }), original, context, documentLoader), original, context, documentLoader, depth);
125
120
  }
126
121
  function preserveNoContextJsonLdShape(normalized, original, depth = 0) {
127
122
  if (depth > 32) return normalized;
@@ -184,11 +179,11 @@ async function mergeUnmappedTerms(compacted, original, context, documentLoader)
184
179
  "@context": context,
185
180
  ...compacted
186
181
  } : compacted;
187
- const compactedTerms = getTopLevelTerms(await jsonld_default.expand(compactedWithContext, { documentLoader }));
182
+ const compactedTerms = getTopLevelTerms(await require_jsonld.jsonld_default.expand(compactedWithContext, { documentLoader }));
188
183
  const dummyPrefix = "urn:fedify:dummy:";
189
184
  const dummy = { "@context": context };
190
185
  for (let i = 0; i < unmappedKeys.length; i++) defineJsonLdProperty(dummy, unmappedKeys[i], `${dummyPrefix}${i}`);
191
- const expanded = await jsonld_default.expand(dummy, { documentLoader });
186
+ const expanded = await require_jsonld.jsonld_default.expand(dummy, { documentLoader });
192
187
  const representedKeys = /* @__PURE__ */ new Set();
193
188
  const nodes = Array.isArray(expanded) ? expanded : [expanded];
194
189
  for (const node of nodes) {
@@ -267,8 +262,8 @@ async function compactWithEmptyContext(compacted, context, documentLoader) {
267
262
  "@context": context,
268
263
  ...compacted
269
264
  } : compacted;
270
- const expanded = await jsonld_default.expand(compactedWithContext, { documentLoader });
271
- return await jsonld_default.compact(expanded, {}, { documentLoader });
265
+ const expanded = await require_jsonld.jsonld_default.expand(compactedWithContext, { documentLoader });
266
+ return await require_jsonld.jsonld_default.compact(expanded, {}, { documentLoader });
272
267
  }
273
268
  function combineContexts(inheritedContext, ownContext) {
274
269
  if (ownContext === noJsonLdContext || ownContext === inheritedContext) return inheritedContext;
@@ -322,7 +317,7 @@ function combineContexts(inheritedContext, ownContext) {
322
317
  "https://www.w3.org/ns/activitystreams#attributedTo": [{ "@id": "ap://did:key:z6Mkabc/actor" }],
323
318
  "https://www.w3.org/ns/activitystreams#content": [{ "@value": "No-context object shape should stay cached." }]
324
319
  };
325
- (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id", "https://www.w3.org/ns/activitystreams#attributedTo"])), original), {
320
+ (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await require_jsonld.jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id", "https://www.w3.org/ns/activitystreams#attributedTo"])), original), {
326
321
  "@id": "ap+ef61://did:key:z6Mkabc/objects/1",
327
322
  "@type": ["https://www.w3.org/ns/activitystreams#Note"],
328
323
  "https://www.w3.org/ns/activitystreams#attributedTo": [{ "@id": "ap+ef61://did:key:z6Mkabc/actor" }],
@@ -343,7 +338,7 @@ function combineContexts(inheritedContext, ownContext) {
343
338
  name: "Nested object"
344
339
  }]
345
340
  };
346
- (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
341
+ (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await require_jsonld.jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
347
342
  "@id": "ap+ef61://did:key:z6Mkabc/objects/1",
348
343
  "@type": ["https://www.w3.org/ns/activitystreams#Note"],
349
344
  "https://www.w3.org/ns/activitystreams#attachment": [{
@@ -390,7 +385,7 @@ function combineContexts(inheritedContext, ownContext) {
390
385
  extra: "This nested unmapped property should stay cached."
391
386
  }
392
387
  };
393
- (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
388
+ (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await require_jsonld.jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
394
389
  "@context": context,
395
390
  type: "as:Note",
396
391
  id: "ap+ef61://did:key:z6Mkabc/objects/1",
@@ -422,7 +417,7 @@ function combineContexts(inheritedContext, ownContext) {
422
417
  extra
423
418
  }
424
419
  };
425
- const compacted = await compactJsonLdCache(normalizeJsonLdIris(await jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original);
420
+ const compacted = await compactJsonLdCache(normalizeJsonLdIris(await require_jsonld.jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original);
426
421
  (0, node_assert.strictEqual)(compacted.rootExtra, rootExtra);
427
422
  (0, node_assert.strictEqual)(compacted.attachment.extra, extra);
428
423
  });
@@ -443,7 +438,7 @@ function combineContexts(inheritedContext, ownContext) {
443
438
  configurable: true,
444
439
  writable: true
445
440
  });
446
- const compacted = await compactJsonLdCache(normalizeJsonLdIris(await jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original);
441
+ const compacted = await compactJsonLdCache(normalizeJsonLdIris(await require_jsonld.jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original);
447
442
  (0, node_assert.strictEqual)(globalThis.Object.getOwnPropertyDescriptor(compacted, "__proto__")?.value, protoValue);
448
443
  (0, node_assert.strictEqual)(globalThis.Object.getPrototypeOf(compacted), Object.prototype);
449
444
  });
@@ -470,7 +465,7 @@ function combineContexts(inheritedContext, ownContext) {
470
465
  configurable: true,
471
466
  writable: true
472
467
  });
473
- const compacted = await compactJsonLdCache(normalizeJsonLdIris(await jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original);
468
+ const compacted = await compactJsonLdCache(normalizeJsonLdIris(await require_jsonld.jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original);
474
469
  const protoDescriptor = globalThis.Object.getOwnPropertyDescriptor(compacted, "__proto__");
475
470
  (0, node_assert.ok)(protoDescriptor?.value === "Alias represented by the compacted term." || Array.isArray(protoDescriptor?.value) && protoDescriptor.value.includes("Alias represented by the compacted term."));
476
471
  (0, node_assert.strictEqual)(protoDescriptor?.enumerable, true);
@@ -500,7 +495,7 @@ function combineContexts(inheritedContext, ownContext) {
500
495
  title: "Nested title."
501
496
  }
502
497
  };
503
- (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
498
+ (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await require_jsonld.jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
504
499
  "@context": context,
505
500
  type: "as:Note",
506
501
  id: "ap+ef61://did:key:z6Mkabc/objects/1",
@@ -532,7 +527,7 @@ function combineContexts(inheritedContext, ownContext) {
532
527
  title: "Nested title."
533
528
  }
534
529
  };
535
- (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
530
+ (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await require_jsonld.jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
536
531
  "@context": context,
537
532
  type: "as:Note",
538
533
  id: "ap+ef61://did:key:z6Mkabc/objects/1",
@@ -560,7 +555,7 @@ function combineContexts(inheritedContext, ownContext) {
560
555
  "https://example.com/ns#title": "Absolute title."
561
556
  }
562
557
  };
563
- (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
558
+ (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await require_jsonld.jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
564
559
  "@context": context,
565
560
  type: "as:Note",
566
561
  id: "ap+ef61://did:key:z6Mkabc/objects/1",
@@ -599,7 +594,7 @@ function combineContexts(inheritedContext, ownContext) {
599
594
  actorRef: "ap://did:key:z6Mkabc/actor"
600
595
  }
601
596
  };
602
- (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id", "https://www.w3.org/ns/activitystreams#actor"])), original), {
597
+ (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await require_jsonld.jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id", "https://www.w3.org/ns/activitystreams#actor"])), original), {
603
598
  "@context": context,
604
599
  type: "as:Note",
605
600
  id: "ap+ef61://did:key:z6Mkabc/objects/1",
@@ -633,7 +628,7 @@ function combineContexts(inheritedContext, ownContext) {
633
628
  key9: "Extension 9",
634
629
  key10: "Alias represented by the compacted term."
635
630
  };
636
- (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
631
+ (0, node_assert.deepStrictEqual)(await compactJsonLdCache(normalizeJsonLdIris(await require_jsonld.jsonld_default.expand(original), /* @__PURE__ */ new Set(["@id"])), original), {
637
632
  "@context": context,
638
633
  id: "ap+ef61://did:key:z6Mkabc/objects/1",
639
634
  key0: "Extension 0",
@@ -1,10 +1,7 @@
1
1
  import { a as formatIri, c as haveSameFe34Origin, l as haveSameIriOrigin, m as parseIri } from "./url-DgJM9WJn.mjs";
2
+ import { t as jsonld_default } from "./jsonld-DweIJ07b.mjs";
2
3
  import { test } from "node:test";
3
4
  import { deepStrictEqual, ok, strictEqual } from "node:assert";
4
- import jsonld from "jsonld/dist/jsonld.esm.js";
5
- //#region src/jsonld.ts
6
- var jsonld_default = jsonld;
7
- //#endregion
8
5
  //#region src/internal/jsonld-cache.ts
9
6
  const noJsonLdContext = Symbol("noJsonLdContext");
10
7
  /**
@@ -1,7 +1,7 @@
1
1
  import process from "node:process";
2
2
  //#region deno.json
3
3
  var name = "@fedify/vocab-runtime";
4
- var version = "2.4.0-dev.2035+d1f8f8e2";
4
+ var version = "2.4.0-dev.2054+1756e190";
5
5
  //#endregion
6
6
  //#region src/request.ts
7
7
  /**
@@ -31,6 +31,12 @@ var FetchError = class extends Error {
31
31
  }
32
32
  };
33
33
  /**
34
+ * The `Accept` header value for fetching ActivityPub objects. ActivityPub
35
+ * and FEP-ef61 gateways require the ActivityStreams profile on the JSON-LD
36
+ * media type.
37
+ */
38
+ const ACTIVITYPUB_ACCEPT = "application/activity+json, application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
39
+ /**
34
40
  * Creates a request for the given URL.
35
41
  * @param url The URL to create the request for.
36
42
  * @param options The options for the request.
@@ -40,7 +46,7 @@ var FetchError = class extends Error {
40
46
  function createActivityPubRequest(url, options = {}) {
41
47
  return new Request(url, {
42
48
  headers: {
43
- Accept: "application/activity+json, application/ld+json",
49
+ Accept: ACTIVITYPUB_ACCEPT,
44
50
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
45
51
  },
46
52
  redirect: "manual"
@@ -3,7 +3,7 @@ let node_process = require("node:process");
3
3
  node_process = require_rolldown_runtime.__toESM(node_process, 1);
4
4
  //#region deno.json
5
5
  var name = "@fedify/vocab-runtime";
6
- var version = "2.4.0-dev.2035+d1f8f8e2";
6
+ var version = "2.4.0-dev.2054+1756e190";
7
7
  //#endregion
8
8
  //#region src/request.ts
9
9
  /**
@@ -33,6 +33,12 @@ var FetchError = class extends Error {
33
33
  }
34
34
  };
35
35
  /**
36
+ * The `Accept` header value for fetching ActivityPub objects. ActivityPub
37
+ * and FEP-ef61 gateways require the ActivityStreams profile on the JSON-LD
38
+ * media type.
39
+ */
40
+ const ACTIVITYPUB_ACCEPT = "application/activity+json, application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
41
+ /**
36
42
  * Creates a request for the given URL.
37
43
  * @param url The URL to create the request for.
38
44
  * @param options The options for the request.
@@ -42,7 +48,7 @@ var FetchError = class extends Error {
42
48
  function createActivityPubRequest(url, options = {}) {
43
49
  return new Request(url, {
44
50
  headers: {
45
- Accept: "application/activity+json, application/ld+json",
51
+ Accept: ACTIVITYPUB_ACCEPT,
46
52
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
47
53
  },
48
54
  redirect: "manual"
@@ -1,5 +1,5 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-emK7D4bc.cjs");
2
- const require_request = require("./request-BbczASfn.cjs");
2
+ const require_request = require("./request-CcifKcqU.cjs");
3
3
  let node_test = require("node:test");
4
4
  let node_process = require("node:process");
5
5
  node_process = require_rolldown_runtime.__toESM(node_process, 1);
@@ -40,4 +40,7 @@ let node_assert = require("node:assert");
40
40
  }), `MyApp/1.0.0 (Fedify/${require_request.version}; Node.js/${node_process.default.versions.node}; +https://example.com/)`);
41
41
  }
42
42
  });
43
+ (0, node_test.test)("createActivityPubRequest() asks for the ActivityStreams profile", () => {
44
+ (0, node_assert.deepStrictEqual)(require_request.createActivityPubRequest("https://example.com/object").headers.get("Accept"), "application/activity+json, application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"");
45
+ });
43
46
  //#endregion
@@ -1,4 +1,4 @@
1
- import { o as version, r as getUserAgent } from "./request-eP_YLVjb.mjs";
1
+ import { n as createActivityPubRequest, o as version, r as getUserAgent } from "./request-Bbg1-1C3.mjs";
2
2
  import { test } from "node:test";
3
3
  import process from "node:process";
4
4
  import { deepStrictEqual } from "node:assert";
@@ -38,5 +38,8 @@ test("getUserAgent()", () => {
38
38
  }), `MyApp/1.0.0 (Fedify/${version}; Node.js/${process.versions.node}; +https://example.com/)`);
39
39
  }
40
40
  });
41
+ test("createActivityPubRequest() asks for the ActivityStreams profile", () => {
42
+ deepStrictEqual(createActivityPubRequest("https://example.com/object").headers.get("Accept"), "application/activity+json, application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"");
43
+ });
41
44
  //#endregion
42
45
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab-runtime",
3
- "version": "2.4.0-dev.2035+d1f8f8e2",
3
+ "version": "2.4.0-dev.2054+1756e190",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -52,6 +52,16 @@
52
52
  "require": "./dist/internal/jsonld-cache.cjs",
53
53
  "default": "./dist/internal/jsonld-cache.js"
54
54
  },
55
+ "./internal/portable-dereference": {
56
+ "types": {
57
+ "import": "./dist/internal/portable-dereference.d.ts",
58
+ "require": "./dist/internal/portable-dereference.d.cts",
59
+ "default": "./dist/internal/portable-dereference.d.ts"
60
+ },
61
+ "import": "./dist/internal/portable-dereference.js",
62
+ "require": "./dist/internal/portable-dereference.cjs",
63
+ "default": "./dist/internal/portable-dereference.js"
64
+ },
55
65
  "./internal/signed-representation": {
56
66
  "types": {
57
67
  "import": "./dist/internal/signed-representation.d.ts",
@@ -0,0 +1,74 @@
1
+ import { deepStrictEqual, ok, throws } from "node:assert/strict";
2
+ import { test } from "node:test";
3
+ import { parseIri } from "../url.ts";
4
+ import {
5
+ getPortableGatewayCandidates,
6
+ isPortableIri,
7
+ } from "./portable-dereference.ts";
8
+
9
+ const hrefs = (urls: URL[]) => urls.map((url) => url.href);
10
+
11
+ test("isPortableIri()", () => {
12
+ ok(isPortableIri(parseIri("ap://did:key:z6Mkabc/actor")));
13
+ ok(isPortableIri(parseIri("ap+ef61://did:key:z6Mkabc/actor")));
14
+ ok(isPortableIri(new URL("ap://did%3Akey%3Az6Mkabc/actor")));
15
+ ok(!isPortableIri(new URL("https://example.com/actor")));
16
+ ok(!isPortableIri(new URL("did:key:z6Mkabc")));
17
+ });
18
+
19
+ test("getPortableGatewayCandidates() uses explicit gateways", () => {
20
+ const url = parseIri(
21
+ "ap://did:key:z6Mkabc/actor?@gateway=https%3A%2F%2Fhint.example",
22
+ );
23
+ deepStrictEqual(
24
+ hrefs(getPortableGatewayCandidates(url, [
25
+ "https://b.example",
26
+ new URL("https://a.example/"),
27
+ "https://b.example/",
28
+ "http://c.example:8080",
29
+ ])),
30
+ ["https://b.example/", "https://a.example/", "http://c.example:8080/"],
31
+ );
32
+ deepStrictEqual(getPortableGatewayCandidates(url, []), []);
33
+ for (
34
+ const gateway of [
35
+ "https://a.example/path",
36
+ "https://a.example/?",
37
+ "https://a.example/#",
38
+ "https://user:pass@a.example",
39
+ "ftp://a.example",
40
+ "a.example",
41
+ ]
42
+ ) {
43
+ throws(() => getPortableGatewayCandidates(url, [gateway]), TypeError);
44
+ }
45
+ });
46
+
47
+ test("getPortableGatewayCandidates() reads @gateway hints", () => {
48
+ deepStrictEqual(
49
+ hrefs(getPortableGatewayCandidates(parseIri(
50
+ "ap://did:key:z6Mkabc/actor?page=1" +
51
+ "&@gateway=https%3A%2F%2Fa.example" +
52
+ "&gateways=https%3A%2F%2Flegacy.example" +
53
+ "&@gateway=not%20a%20URL" +
54
+ "&@gateway=https%3A%2F%2Fb.example%2Fpath" +
55
+ "&%40gateway=https%3A%2F%2Fb.example" +
56
+ "&@gateway=https%3A%2F%2Fa.example%2F",
57
+ ))),
58
+ ["https://a.example/", "https://b.example/"],
59
+ );
60
+ deepStrictEqual(
61
+ getPortableGatewayCandidates(parseIri("ap://did:key:z6Mkabc/actor")),
62
+ [],
63
+ );
64
+ const hints = Array.from(
65
+ { length: 7 },
66
+ (_, i) => `@gateway=https%3A%2F%2Fg${i}.example`,
67
+ );
68
+ deepStrictEqual(
69
+ hrefs(getPortableGatewayCandidates(
70
+ parseIri(`ap://did:key:z6Mkabc/actor?${hints.join("&")}`),
71
+ )),
72
+ [0, 1, 2, 3, 4].map((i) => `https://g${i}.example/`),
73
+ );
74
+ });