@fedify/vocab-runtime 2.4.0-dev.2233 → 2.4.0-dev.2245

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 (55) hide show
  1. package/deno.json +2 -2
  2. package/dist/internal/jsonld-cache.cjs +1 -1
  3. package/dist/internal/jsonld-cache.js +1 -1
  4. package/dist/internal/portable-dereference.cjs +3 -6
  5. package/dist/internal/portable-dereference.js +3 -6
  6. package/dist/mod.cjs +2 -2
  7. package/dist/mod.d.cts +14 -3
  8. package/dist/mod.d.ts +14 -3
  9. package/dist/mod.js +2 -2
  10. package/dist/tests/{body-DQKTFMsH.cjs → body-C7LfsQJ3.cjs} +1 -1
  11. package/dist/tests/{body-BEYUpKvX.mjs → body-D50XSh7s.mjs} +1 -1
  12. package/dist/tests/body.test.cjs +1 -1
  13. package/dist/tests/body.test.mjs +1 -1
  14. package/dist/tests/decimal.test.cjs +3 -3
  15. package/dist/tests/decimal.test.mjs +3 -3
  16. package/dist/tests/{docloader-ra1lbBfP.mjs → docloader-BdcRw8S9.mjs} +3 -3
  17. package/dist/tests/{docloader-DHCYdBXf.cjs → docloader-oUrWeeUz.cjs} +3 -3
  18. package/dist/tests/docloader.test.cjs +59 -65
  19. package/dist/tests/docloader.test.mjs +38 -44
  20. package/dist/tests/internal/portable-dereference.test.cjs +21 -149
  21. package/dist/tests/internal/portable-dereference.test.mjs +3 -131
  22. package/dist/tests/{jsonld-cache-BKrsWPCQ.mjs → jsonld-cache-CKfgtxPB.mjs} +1 -1
  23. package/dist/tests/{jsonld-cache-BTgizq9A.cjs → jsonld-cache-DzxT9IXH.cjs} +1 -1
  24. package/dist/tests/jsonld-cache.test.cjs +2 -2
  25. package/dist/tests/jsonld-cache.test.mjs +2 -2
  26. package/dist/tests/multibase/multibase.test.cjs +7 -7
  27. package/dist/tests/multibase/multibase.test.mjs +5 -5
  28. package/dist/tests/portable-dereference-C_cwGjz6.mjs +132 -0
  29. package/dist/tests/portable-dereference-CyFYTBnr.cjs +149 -0
  30. package/dist/tests/{portable-media-DHaAEpaQ.cjs → portable-media-Bj5FXpUr.cjs} +3 -3
  31. package/dist/tests/{portable-media-1NjUQari.mjs → portable-media-D4NJcqkI.mjs} +3 -3
  32. package/dist/tests/portable-media.test.cjs +1 -1
  33. package/dist/tests/portable-media.test.mjs +1 -1
  34. package/dist/tests/portable-workers.test.cjs +36 -0
  35. package/dist/tests/portable-workers.test.d.cts +2 -0
  36. package/dist/tests/portable-workers.test.d.mts +2 -0
  37. package/dist/tests/portable-workers.test.mjs +35 -0
  38. package/dist/tests/{request-BQlkB8du.cjs → request-CBCk-e0R.cjs} +1 -1
  39. package/dist/tests/{request-D3elVRyB.mjs → request-wKyc91L5.mjs} +1 -1
  40. package/dist/tests/request.test.cjs +1 -1
  41. package/dist/tests/request.test.mjs +1 -1
  42. package/dist/tests/{url-Cdu4ASgJ.mjs → url-CWC-NI3a.mjs} +63 -7
  43. package/dist/tests/{url-CtHyzMR4.cjs → url-DsMBGqCc.cjs} +63 -7
  44. package/dist/tests/url.test.cjs +74 -1
  45. package/dist/tests/url.test.mjs +74 -1
  46. package/dist/{url-BHfQuRdy.cjs → url-BJMXv2kE.cjs} +63 -7
  47. package/dist/{url-DqqDZY9i.js → url-CZm2-7ZO.js} +63 -7
  48. package/package.json +6 -3
  49. package/scripts/test-bun.mjs +17 -0
  50. package/src/docloader.test.ts +82 -74
  51. package/src/internal/portable-dereference.ts +2 -5
  52. package/src/multibase/multibase.test.ts +5 -5
  53. package/src/portable-workers.test.ts +81 -0
  54. package/src/url.test.ts +90 -0
  55. package/src/url.ts +115 -13
@@ -1,136 +1,8 @@
1
- import { t as preloadedContexts } from "../contexts-CIKsin4e.mjs";
2
- import { S as withGatewayHints, _ as parseIri, h as parseGatewayOrigin, o as formatIri, t as GATEWAY_HINT_PARAMETER } from "../url-Cdu4ASgJ.mjs";
3
- import { n as createScopedContextLoader, o as registerDocumentLoaderWrapper, s as unwrapReleasedDocumentLoader } from "../jsonld-cache-BKrsWPCQ.mjs";
1
+ import { S as withGatewayHints, _ as parseIri } from "../url-CWC-NI3a.mjs";
2
+ import { n as createScopedContextLoader } from "../jsonld-cache-CKfgtxPB.mjs";
3
+ import { n as getPortableGatewayCandidates, r as isPortableIri, t as createSnapshotContextLoader } from "../portable-dereference-C_cwGjz6.mjs";
4
4
  import { deepStrictEqual, ok, throws } from "node:assert/strict";
5
5
  import { test } from "node:test";
6
- import { getLogger } from "@logtape/logtape";
7
- import "@opentelemetry/api";
8
- //#region src/internal/portable-dereference.ts
9
- const logger = getLogger([
10
- "fedify",
11
- "vocab",
12
- "gateway"
13
- ]);
14
- /**
15
- * The maximum number of `@gateway` location hints to try for one reference.
16
- * Hints come from possibly untrusted documents, so they are bounded to keep
17
- * a single accessor call from fanning out to many servers.
18
- */
19
- const MAX_GATEWAY_HINTS = 5;
20
- const BASELINE_CONTEXT_URLS = /* @__PURE__ */ new Set([
21
- "https://w3id.org/identity/v1",
22
- "https://www.w3.org/ns/activitystreams",
23
- "https://w3id.org/security/v1",
24
- "https://w3id.org/security/data-integrity/v1"
25
- ]);
26
- /**
27
- * Checks whether a URL is an FEP-ef61 portable ActivityPub IRI.
28
- *
29
- * @internal Technically exported for generated vocabulary classes, but not
30
- * part of the public API contract. This is not considered public API for
31
- * Semantic Versioning decisions.
32
- */
33
- function isPortableIri(url) {
34
- return url.protocol === "ap+ef61:" || url.protocol === "ap:";
35
- }
36
- /**
37
- * Picks the ordered list of FEP-ef61 gateways to fetch a portable IRI from.
38
- *
39
- * If `gateways` is given, it is used as is (even when empty), and `@gateway`
40
- * location hints in the IRI are ignored. Otherwise, up to
41
- * {@link MAX_GATEWAY_HINTS} valid `@gateway` hints are used. Duplicate
42
- * gateways are dropped in both cases.
43
- *
44
- * @throws {TypeError} If an explicit gateway is not an HTTP(S) origin.
45
- * @internal
46
- */
47
- function getPortableGatewayCandidates(url, gateways) {
48
- const candidates = [];
49
- const seen = /* @__PURE__ */ new Set();
50
- const add = (gateway) => {
51
- if (seen.has(gateway.href)) return;
52
- seen.add(gateway.href);
53
- candidates.push(gateway);
54
- };
55
- if (gateways != null) {
56
- for (const gateway of gateways) {
57
- const parsed = parseGatewayOrigin(gateway);
58
- if (parsed == null) throw new TypeError("FEP-ef61 gateways must be HTTP(S) origins with no credentials, path, query, or fragment: " + String(gateway));
59
- add(parsed);
60
- }
61
- return candidates;
62
- }
63
- for (const hint of new URLSearchParams(url.search).getAll(GATEWAY_HINT_PARAMETER)) {
64
- if (candidates.length >= MAX_GATEWAY_HINTS) break;
65
- const parsed = parseGatewayOrigin(hint);
66
- if (parsed == null) {
67
- logger.debug("Ignoring an invalid FEP-ef61 gateway hint {hint} in {url}.", {
68
- hint,
69
- url: formatIri(url)
70
- });
71
- continue;
72
- }
73
- add(parsed);
74
- }
75
- return candidates;
76
- }
77
- /**
78
- * Creates a context loader that returns the same context documents for the
79
- * whole dereference operation, so that the identity check, the proof
80
- * verifier, and the parser interpret the fetched document identically even
81
- * if the underlying loader is nondeterministic. Failed loads are not
82
- * remembered, so a transient failure does not affect the next gateway.
83
- *
84
- * The parsed object keeps the loader for its own later dereferences, so
85
- * `release()` turns it into a plain pass-through to the underlying loader
86
- * once the operation is over.
87
- *
88
- * @internal Technically exported for generated vocabulary classes, but not
89
- * part of the public API contract. This is not considered public API for
90
- * Semantic Versioning decisions.
91
- */
92
- function createSnapshotContextLoader(contextLoader, suppressError) {
93
- contextLoader = unwrapReleasedDocumentLoader(contextLoader);
94
- const cache = /* @__PURE__ */ new Map();
95
- let released = false;
96
- const release = () => {
97
- released = true;
98
- state.released = true;
99
- cache.clear();
100
- };
101
- const loader = async (url, options) => {
102
- if (released) return await contextLoader(url, options);
103
- const key = URL.canParse(url) ? new URL(url).href : url;
104
- if (BASELINE_CONTEXT_URLS.has(key)) return {
105
- contextUrl: null,
106
- document: structuredClone(preloadedContexts[key]),
107
- documentUrl: key
108
- };
109
- let promise = cache.get(key);
110
- if (promise == null) {
111
- const loading = contextLoader(url, suppressError ? {
112
- ...options,
113
- suppressError: true
114
- } : options).then((document) => structuredClone(document));
115
- promise = loading;
116
- cache.set(key, loading);
117
- loading.catch(() => {
118
- if (cache.get(key) === loading) cache.delete(key);
119
- });
120
- }
121
- return structuredClone(await promise);
122
- };
123
- const state = {
124
- base: contextLoader,
125
- released: false
126
- };
127
- registerDocumentLoaderWrapper(loader, state);
128
- return {
129
- loader,
130
- release
131
- };
132
- }
133
- //#endregion
134
6
  //#region src/internal/portable-dereference.test.ts
135
7
  const hrefs = (urls) => urls.map((url) => url.href);
136
8
  test("isPortableIri()", () => {
@@ -1,4 +1,4 @@
1
- import { d as haveSameIriOrigin, o as formatIri, u as haveSameFe34Origin } from "./url-Cdu4ASgJ.mjs";
1
+ import { d as haveSameIriOrigin, o as formatIri, u as haveSameFe34Origin } from "./url-CWC-NI3a.mjs";
2
2
  import jsonld from "jsonld/dist/jsonld.esm.js";
3
3
  //#region src/jsonld.ts
4
4
  var jsonld_default = jsonld;
@@ -1,5 +1,5 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-emK7D4bc.cjs");
2
- const require_url = require("./url-CtHyzMR4.cjs");
2
+ const require_url = require("./url-DsMBGqCc.cjs");
3
3
  let jsonld_dist_jsonld_esm_js = require("jsonld/dist/jsonld.esm.js");
4
4
  jsonld_dist_jsonld_esm_js = require_rolldown_runtime.__toESM(jsonld_dist_jsonld_esm_js, 1);
5
5
  //#region src/jsonld.ts
@@ -1,5 +1,5 @@
1
- const require_url = require("./url-CtHyzMR4.cjs");
2
- const require_jsonld_cache = require("./jsonld-cache-BTgizq9A.cjs");
1
+ const require_url = require("./url-DsMBGqCc.cjs");
2
+ const require_jsonld_cache = require("./jsonld-cache-DzxT9IXH.cjs");
3
3
  let node_test = require("node:test");
4
4
  let node_assert = require("node:assert");
5
5
  //#region src/jsonld-cache.test.ts
@@ -1,5 +1,5 @@
1
- import { _ as parseIri } from "./url-Cdu4ASgJ.mjs";
2
- import { a as normalizeJsonLdIris, c as jsonld_default, i as isTrustedIriOrigin, r as getJsonLdContext, t as compactJsonLdCache } from "./jsonld-cache-BKrsWPCQ.mjs";
1
+ import { _ as parseIri } from "./url-CWC-NI3a.mjs";
2
+ import { a as normalizeJsonLdIris, c as jsonld_default, i as isTrustedIriOrigin, r as getJsonLdContext, t as compactJsonLdCache } from "./jsonld-cache-CKfgtxPB.mjs";
3
3
  import { test } from "node:test";
4
4
  import { deepStrictEqual, ok, strictEqual } from "node:assert";
5
5
  //#region src/jsonld-cache.test.ts
@@ -1,8 +1,8 @@
1
1
  const require_multibase = require("../multibase-B5Mea7Ip.cjs");
2
- let node_test = require("node:test");
3
2
  let node_assert = require("node:assert");
3
+ let _fedify_fixture = require("@fedify/fixture");
4
4
  //#region src/multibase/multibase.test.ts
5
- (0, node_test.test)("multibase.encode and decode", async (t) => {
5
+ (0, _fedify_fixture.test)("multibase.encode and decode", async (t) => {
6
6
  const testCases = [
7
7
  [
8
8
  "base16",
@@ -315,22 +315,22 @@ let node_assert = require("node:assert");
315
315
  "Uw7fDr8O_8J-lsMO3w6_Dv_CfmI7wn6W28J-krw=="
316
316
  ]
317
317
  ];
318
- for (const [name, input, expectedOutput] of testCases) await t.test(`Encoding/Decoding ${name} with ${input}`, () => {
318
+ for (const [name, input, expectedOutput] of testCases) await t.step(`Encoding/Decoding ${name} with ${input}`, () => {
319
319
  (0, node_assert.deepStrictEqual)(require_multibase.decodeText(require_multibase.encodeMultibase(name, require_multibase.encodeText(input))), expectedOutput, `Encoding ${name} failed`);
320
320
  (0, node_assert.deepStrictEqual)(require_multibase.decodeMultibase(expectedOutput), require_multibase.encodeText(input), `Decoding ${name} failed`);
321
321
  (0, node_assert.deepStrictEqual)(require_multibase.decodeMultibase(require_multibase.encodeText(expectedOutput)), require_multibase.encodeText(input), `Decoding buffer of ${name} failed`);
322
322
  });
323
- await t.test("should allow base32pad full alphabet", () => {
323
+ await t.step("should allow base32pad full alphabet", () => {
324
324
  const decoded = require_multibase.decodeMultibase("ctimaq4ygg2iegci7");
325
325
  (0, node_assert.deepStrictEqual)(require_multibase.decodeMultibase(require_multibase.encodeMultibase("c", decoded)), decoded);
326
326
  });
327
327
  });
328
- (0, node_test.test)("constants", async (t) => {
329
- await t.test("constants indexed by name", () => {
328
+ (0, _fedify_fixture.test)("constants", async (t) => {
329
+ await t.step("constants indexed by name", () => {
330
330
  const names$1 = require_multibase.names;
331
331
  (0, node_assert.deepStrictEqual)(Object.keys(names$1).length, 23);
332
332
  });
333
- await t.test("constants indexed by code", () => {
333
+ await t.step("constants indexed by code", () => {
334
334
  const codes$1 = require_multibase.codes;
335
335
  (0, node_assert.deepStrictEqual)(Object.keys(codes$1).length, 23);
336
336
  });
@@ -1,6 +1,6 @@
1
1
  import { a as names, i as codes, n as encodeMultibase, o as decodeText, s as encodeText, t as decodeMultibase } from "../multibase-BPnF_L4e.mjs";
2
- import { test } from "node:test";
3
2
  import { deepStrictEqual } from "node:assert";
3
+ import { test } from "@fedify/fixture";
4
4
  //#region src/multibase/multibase.test.ts
5
5
  test("multibase.encode and decode", async (t) => {
6
6
  const testCases = [
@@ -315,21 +315,21 @@ test("multibase.encode and decode", async (t) => {
315
315
  "Uw7fDr8O_8J-lsMO3w6_Dv_CfmI7wn6W28J-krw=="
316
316
  ]
317
317
  ];
318
- for (const [name, input, expectedOutput] of testCases) await t.test(`Encoding/Decoding ${name} with ${input}`, () => {
318
+ for (const [name, input, expectedOutput] of testCases) await t.step(`Encoding/Decoding ${name} with ${input}`, () => {
319
319
  deepStrictEqual(decodeText(encodeMultibase(name, encodeText(input))), expectedOutput, `Encoding ${name} failed`);
320
320
  deepStrictEqual(decodeMultibase(expectedOutput), encodeText(input), `Decoding ${name} failed`);
321
321
  deepStrictEqual(decodeMultibase(encodeText(expectedOutput)), encodeText(input), `Decoding buffer of ${name} failed`);
322
322
  });
323
- await t.test("should allow base32pad full alphabet", () => {
323
+ await t.step("should allow base32pad full alphabet", () => {
324
324
  const decoded = decodeMultibase("ctimaq4ygg2iegci7");
325
325
  deepStrictEqual(decodeMultibase(encodeMultibase("c", decoded)), decoded);
326
326
  });
327
327
  });
328
328
  test("constants", async (t) => {
329
- await t.test("constants indexed by name", () => {
329
+ await t.step("constants indexed by name", () => {
330
330
  deepStrictEqual(Object.keys(names).length, 23);
331
331
  });
332
- await t.test("constants indexed by code", () => {
332
+ await t.step("constants indexed by code", () => {
333
333
  deepStrictEqual(Object.keys(codes).length, 23);
334
334
  });
335
335
  });
@@ -0,0 +1,132 @@
1
+ import { t as preloadedContexts } from "./contexts-CIKsin4e.mjs";
2
+ import { h as parseGatewayOrigin, o as formatIri, t as GATEWAY_HINT_PARAMETER } from "./url-CWC-NI3a.mjs";
3
+ import { o as registerDocumentLoaderWrapper, s as unwrapReleasedDocumentLoader } from "./jsonld-cache-CKfgtxPB.mjs";
4
+ import { getLogger } from "@logtape/logtape";
5
+ import "@opentelemetry/api";
6
+ //#region src/internal/portable-dereference.ts
7
+ const logger = getLogger([
8
+ "fedify",
9
+ "vocab",
10
+ "gateway"
11
+ ]);
12
+ /**
13
+ * The maximum number of `@gateway` location hints to try for one reference.
14
+ * Hints come from possibly untrusted documents, so they are bounded to keep
15
+ * a single accessor call from fanning out to many servers.
16
+ */
17
+ const MAX_GATEWAY_HINTS = 5;
18
+ const BASELINE_CONTEXT_URLS = /* @__PURE__ */ new Set([
19
+ "https://w3id.org/identity/v1",
20
+ "https://www.w3.org/ns/activitystreams",
21
+ "https://w3id.org/security/v1",
22
+ "https://w3id.org/security/data-integrity/v1"
23
+ ]);
24
+ /**
25
+ * Checks whether a URL is an FEP-ef61 portable ActivityPub IRI.
26
+ *
27
+ * @internal Technically exported for generated vocabulary classes, but not
28
+ * part of the public API contract. This is not considered public API for
29
+ * Semantic Versioning decisions.
30
+ */
31
+ function isPortableIri(url) {
32
+ return url.protocol === "ap+ef61:" || url.protocol === "ap:";
33
+ }
34
+ /**
35
+ * Picks the ordered list of FEP-ef61 gateways to fetch a portable IRI from.
36
+ *
37
+ * If `gateways` is given, it is used as is (even when empty), and `@gateway`
38
+ * location hints in the IRI are ignored. Otherwise, up to
39
+ * {@link MAX_GATEWAY_HINTS} valid `@gateway` hints are used. Duplicate
40
+ * gateways are dropped in both cases.
41
+ *
42
+ * @throws {TypeError} If an explicit gateway is not an HTTP(S) origin.
43
+ * @internal
44
+ */
45
+ function getPortableGatewayCandidates(url, gateways) {
46
+ const candidates = [];
47
+ const seen = /* @__PURE__ */ new Set();
48
+ const add = (gateway) => {
49
+ if (seen.has(gateway.href)) return;
50
+ seen.add(gateway.href);
51
+ candidates.push(gateway);
52
+ };
53
+ if (gateways != null) {
54
+ for (const gateway of gateways) {
55
+ const parsed = parseGatewayOrigin(gateway);
56
+ if (parsed == null) throw new TypeError("FEP-ef61 gateways must be HTTP(S) origins with no credentials, path, query, or fragment: " + String(gateway));
57
+ add(parsed);
58
+ }
59
+ return candidates;
60
+ }
61
+ for (const hint of new URLSearchParams(url.search).getAll(GATEWAY_HINT_PARAMETER)) {
62
+ if (candidates.length >= MAX_GATEWAY_HINTS) break;
63
+ const parsed = parseGatewayOrigin(hint);
64
+ if (parsed == null) {
65
+ logger.debug("Ignoring an invalid FEP-ef61 gateway hint {hint} in {url}.", {
66
+ hint,
67
+ url: formatIri(url)
68
+ });
69
+ continue;
70
+ }
71
+ add(parsed);
72
+ }
73
+ return candidates;
74
+ }
75
+ /**
76
+ * Creates a context loader that returns the same context documents for the
77
+ * whole dereference operation, so that the identity check, the proof
78
+ * verifier, and the parser interpret the fetched document identically even
79
+ * if the underlying loader is nondeterministic. Failed loads are not
80
+ * remembered, so a transient failure does not affect the next gateway.
81
+ *
82
+ * The parsed object keeps the loader for its own later dereferences, so
83
+ * `release()` turns it into a plain pass-through to the underlying loader
84
+ * once the operation is over.
85
+ *
86
+ * @internal Technically exported for generated vocabulary classes, but not
87
+ * part of the public API contract. This is not considered public API for
88
+ * Semantic Versioning decisions.
89
+ */
90
+ function createSnapshotContextLoader(contextLoader, suppressError) {
91
+ contextLoader = unwrapReleasedDocumentLoader(contextLoader);
92
+ const cache = /* @__PURE__ */ new Map();
93
+ let released = false;
94
+ const release = () => {
95
+ released = true;
96
+ state.released = true;
97
+ cache.clear();
98
+ };
99
+ const loader = async (url, options) => {
100
+ if (released) return await contextLoader(url, options);
101
+ const key = URL.canParse(url) ? new URL(url).href : url;
102
+ if (BASELINE_CONTEXT_URLS.has(key)) return {
103
+ contextUrl: null,
104
+ document: structuredClone(preloadedContexts[key]),
105
+ documentUrl: key
106
+ };
107
+ let promise = cache.get(key);
108
+ if (promise == null) {
109
+ const loading = contextLoader(url, suppressError ? {
110
+ ...options,
111
+ suppressError: true
112
+ } : options).then((document) => structuredClone(document));
113
+ promise = loading;
114
+ cache.set(key, loading);
115
+ loading.catch(() => {
116
+ if (cache.get(key) === loading) cache.delete(key);
117
+ });
118
+ }
119
+ return structuredClone(await promise);
120
+ };
121
+ const state = {
122
+ base: contextLoader,
123
+ released: false
124
+ };
125
+ registerDocumentLoaderWrapper(loader, state);
126
+ return {
127
+ loader,
128
+ release
129
+ };
130
+ }
131
+ //#endregion
132
+ export { getPortableGatewayCandidates as n, isPortableIri as r, createSnapshotContextLoader as t };
@@ -0,0 +1,149 @@
1
+ const require_contexts = require("./contexts-DizzBjz4.cjs");
2
+ const require_url = require("./url-DsMBGqCc.cjs");
3
+ const require_jsonld_cache = require("./jsonld-cache-DzxT9IXH.cjs");
4
+ let _logtape_logtape = require("@logtape/logtape");
5
+ require("@opentelemetry/api");
6
+ //#region src/internal/portable-dereference.ts
7
+ const logger = (0, _logtape_logtape.getLogger)([
8
+ "fedify",
9
+ "vocab",
10
+ "gateway"
11
+ ]);
12
+ /**
13
+ * The maximum number of `@gateway` location hints to try for one reference.
14
+ * Hints come from possibly untrusted documents, so they are bounded to keep
15
+ * a single accessor call from fanning out to many servers.
16
+ */
17
+ const MAX_GATEWAY_HINTS = 5;
18
+ const BASELINE_CONTEXT_URLS = /* @__PURE__ */ new Set([
19
+ "https://w3id.org/identity/v1",
20
+ "https://www.w3.org/ns/activitystreams",
21
+ "https://w3id.org/security/v1",
22
+ "https://w3id.org/security/data-integrity/v1"
23
+ ]);
24
+ /**
25
+ * Checks whether a URL is an FEP-ef61 portable ActivityPub IRI.
26
+ *
27
+ * @internal Technically exported for generated vocabulary classes, but not
28
+ * part of the public API contract. This is not considered public API for
29
+ * Semantic Versioning decisions.
30
+ */
31
+ function isPortableIri(url) {
32
+ return url.protocol === "ap+ef61:" || url.protocol === "ap:";
33
+ }
34
+ /**
35
+ * Picks the ordered list of FEP-ef61 gateways to fetch a portable IRI from.
36
+ *
37
+ * If `gateways` is given, it is used as is (even when empty), and `@gateway`
38
+ * location hints in the IRI are ignored. Otherwise, up to
39
+ * {@link MAX_GATEWAY_HINTS} valid `@gateway` hints are used. Duplicate
40
+ * gateways are dropped in both cases.
41
+ *
42
+ * @throws {TypeError} If an explicit gateway is not an HTTP(S) origin.
43
+ * @internal
44
+ */
45
+ function getPortableGatewayCandidates(url, gateways) {
46
+ const candidates = [];
47
+ const seen = /* @__PURE__ */ new Set();
48
+ const add = (gateway) => {
49
+ if (seen.has(gateway.href)) return;
50
+ seen.add(gateway.href);
51
+ candidates.push(gateway);
52
+ };
53
+ if (gateways != null) {
54
+ for (const gateway of gateways) {
55
+ const parsed = require_url.parseGatewayOrigin(gateway);
56
+ if (parsed == null) throw new TypeError("FEP-ef61 gateways must be HTTP(S) origins with no credentials, path, query, or fragment: " + String(gateway));
57
+ add(parsed);
58
+ }
59
+ return candidates;
60
+ }
61
+ for (const hint of new URLSearchParams(url.search).getAll(require_url.GATEWAY_HINT_PARAMETER)) {
62
+ if (candidates.length >= MAX_GATEWAY_HINTS) break;
63
+ const parsed = require_url.parseGatewayOrigin(hint);
64
+ if (parsed == null) {
65
+ logger.debug("Ignoring an invalid FEP-ef61 gateway hint {hint} in {url}.", {
66
+ hint,
67
+ url: require_url.formatIri(url)
68
+ });
69
+ continue;
70
+ }
71
+ add(parsed);
72
+ }
73
+ return candidates;
74
+ }
75
+ /**
76
+ * Creates a context loader that returns the same context documents for the
77
+ * whole dereference operation, so that the identity check, the proof
78
+ * verifier, and the parser interpret the fetched document identically even
79
+ * if the underlying loader is nondeterministic. Failed loads are not
80
+ * remembered, so a transient failure does not affect the next gateway.
81
+ *
82
+ * The parsed object keeps the loader for its own later dereferences, so
83
+ * `release()` turns it into a plain pass-through to the underlying loader
84
+ * once the operation is over.
85
+ *
86
+ * @internal Technically exported for generated vocabulary classes, but not
87
+ * part of the public API contract. This is not considered public API for
88
+ * Semantic Versioning decisions.
89
+ */
90
+ function createSnapshotContextLoader(contextLoader, suppressError) {
91
+ contextLoader = require_jsonld_cache.unwrapReleasedDocumentLoader(contextLoader);
92
+ const cache = /* @__PURE__ */ new Map();
93
+ let released = false;
94
+ const release = () => {
95
+ released = true;
96
+ state.released = true;
97
+ cache.clear();
98
+ };
99
+ const loader = async (url, options) => {
100
+ if (released) return await contextLoader(url, options);
101
+ const key = URL.canParse(url) ? new URL(url).href : url;
102
+ if (BASELINE_CONTEXT_URLS.has(key)) return {
103
+ contextUrl: null,
104
+ document: structuredClone(require_contexts.preloadedContexts[key]),
105
+ documentUrl: key
106
+ };
107
+ let promise = cache.get(key);
108
+ if (promise == null) {
109
+ const loading = contextLoader(url, suppressError ? {
110
+ ...options,
111
+ suppressError: true
112
+ } : options).then((document) => structuredClone(document));
113
+ promise = loading;
114
+ cache.set(key, loading);
115
+ loading.catch(() => {
116
+ if (cache.get(key) === loading) cache.delete(key);
117
+ });
118
+ }
119
+ return structuredClone(await promise);
120
+ };
121
+ const state = {
122
+ base: contextLoader,
123
+ released: false
124
+ };
125
+ require_jsonld_cache.registerDocumentLoaderWrapper(loader, state);
126
+ return {
127
+ loader,
128
+ release
129
+ };
130
+ }
131
+ //#endregion
132
+ Object.defineProperty(exports, "createSnapshotContextLoader", {
133
+ enumerable: true,
134
+ get: function() {
135
+ return createSnapshotContextLoader;
136
+ }
137
+ });
138
+ Object.defineProperty(exports, "getPortableGatewayCandidates", {
139
+ enumerable: true,
140
+ get: function() {
141
+ return getPortableGatewayCandidates;
142
+ }
143
+ });
144
+ Object.defineProperty(exports, "isPortableIri", {
145
+ enumerable: true,
146
+ get: function() {
147
+ return isPortableIri;
148
+ }
149
+ });
@@ -1,6 +1,6 @@
1
- const require_request = require("./request-BQlkB8du.cjs");
2
- const require_body = require("./body-DQKTFMsH.cjs");
3
- const require_url = require("./url-CtHyzMR4.cjs");
1
+ const require_request = require("./request-CBCk-e0R.cjs");
2
+ const require_body = require("./body-C7LfsQJ3.cjs");
3
+ const require_url = require("./url-DsMBGqCc.cjs");
4
4
  const require_digest = require("./digest-3FeH2Y-Q.cjs");
5
5
  //#region src/portable-media.ts
6
6
  function resolveTimeout(value, fallback) {
@@ -1,6 +1,6 @@
1
- import { r as getUserAgent } from "./request-D3elVRyB.mjs";
2
- import { r as readBoundedBytes } from "./body-BEYUpKvX.mjs";
3
- import { g as parseGatewayUrl, x as validatePublicUrl } from "./url-Cdu4ASgJ.mjs";
1
+ import { r as getUserAgent } from "./request-wKyc91L5.mjs";
2
+ import { r as readBoundedBytes } from "./body-D50XSh7s.mjs";
3
+ import { g as parseGatewayUrl, x as validatePublicUrl } from "./url-CWC-NI3a.mjs";
4
4
  import { a as verifyDigestMultibase, i as parseHashlink, r as parseDigestMultibase } from "./digest-COC7xDiQ.mjs";
5
5
  //#region src/portable-media.ts
6
6
  function resolveTimeout(value, fallback) {
@@ -1,5 +1,5 @@
1
1
  const require_digest = require("./digest-3FeH2Y-Q.cjs");
2
- const require_portable_media = require("./portable-media-DHaAEpaQ.cjs");
2
+ const require_portable_media = require("./portable-media-Bj5FXpUr.cjs");
3
3
  let node_assert_strict = require("node:assert/strict");
4
4
  let node_test = require("node:test");
5
5
  let node_http = require("node:http");
@@ -1,5 +1,5 @@
1
1
  import { n as createHashlink, t as computeDigestMultibase } from "./digest-COC7xDiQ.mjs";
2
- import { t as fetchPortableMedia } from "./portable-media-1NjUQari.mjs";
2
+ import { t as fetchPortableMedia } from "./portable-media-D4NJcqkI.mjs";
3
3
  import { deepStrictEqual, equal, rejects } from "node:assert/strict";
4
4
  import { test } from "node:test";
5
5
  import { createServer } from "node:http";
@@ -0,0 +1,36 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_url = require("./url-DsMBGqCc.cjs");
3
+ const require_key = require("./key-C-AYkdJJ.cjs");
4
+ const require_digest = require("./digest-3FeH2Y-Q.cjs");
5
+ const require_portable_dereference = require("./portable-dereference-CyFYTBnr.cjs");
6
+ let node_assert_strict = require("node:assert/strict");
7
+ let _fedify_fixture = require("@fedify/fixture");
8
+ //#region src/portable-workers.test.ts
9
+ const portableId = "ap://did:key:z6Mkabc/actor";
10
+ (0, _fedify_fixture.test)("vocab-runtime portable URI formats on Workers", () => {
11
+ (0, node_assert_strict.equal)(require_url.formatIri(require_url.parseIri(portableId)), "ap+ef61://did:key:z6Mkabc/actor");
12
+ (0, node_assert_strict.equal)(require_url.canonicalizePortableUri(`${portableId}?@gateway=https%3A%2F%2Fa.example`), "ap+ef61://did:key:z6Mkabc/actor");
13
+ });
14
+ (0, _fedify_fixture.test)("vocab-runtime compatible IDs on Workers", () => {
15
+ const compatible = require_url.toCompatibleEf61Id(portableId, "https://gw.example");
16
+ (0, node_assert_strict.equal)(compatible.href, "https://gw.example/.well-known/apgateway/did:key:z6Mkabc/actor");
17
+ (0, node_assert_strict.equal)(require_url.formatIri(require_url.fromCompatibleEf61Id(compatible)), "ap+ef61://did:key:z6Mkabc/actor");
18
+ });
19
+ (0, _fedify_fixture.test)("vocab-runtime did:key on Workers", async () => {
20
+ const keys = await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
21
+ const imported = await require_key.importDidKey(await require_key.exportDidKey(keys.publicKey));
22
+ (0, node_assert_strict.deepStrictEqual)(new Uint8Array(await crypto.subtle.exportKey("raw", imported)), new Uint8Array(await crypto.subtle.exportKey("raw", keys.publicKey)));
23
+ });
24
+ (0, _fedify_fixture.test)("vocab-runtime hashlink on Workers", async () => {
25
+ const bytes = new TextEncoder().encode("portable media");
26
+ const digest = await require_digest.computeDigestMultibase(bytes);
27
+ (0, node_assert_strict.equal)(await require_digest.verifyHashlink(bytes, require_digest.createHashlink(digest)), true);
28
+ (0, node_assert_strict.equal)(await require_digest.verifyHashlink(new TextEncoder().encode("tampered"), require_digest.createHashlink(digest)), false);
29
+ });
30
+ (0, _fedify_fixture.test)("vocab-runtime gateway candidates on Workers", () => {
31
+ const hinted = require_url.parseIri(`${portableId}?@gateway=https%3A%2F%2Fhint.example`);
32
+ (0, node_assert_strict.deepStrictEqual)(require_portable_dereference.getPortableGatewayCandidates(hinted, ["https://first.example"]).map((url) => url.href), ["https://first.example/"]);
33
+ (0, node_assert_strict.deepStrictEqual)(require_portable_dereference.getPortableGatewayCandidates(hinted).map((url) => url.href), ["https://hint.example/"]);
34
+ });
35
+ //#endregion
36
+ exports.testDefinitions = _fedify_fixture.testDefinitions;
@@ -0,0 +1,2 @@
1
+ import { testDefinitions } from "@fedify/fixture";
2
+ export { testDefinitions };
@@ -0,0 +1,2 @@
1
+ import { testDefinitions } from "@fedify/fixture";
2
+ export { testDefinitions };
@@ -0,0 +1,35 @@
1
+ import { _ as parseIri, i as canonicalizePortableUri, o as formatIri, s as fromCompatibleEf61Id, y as toCompatibleEf61Id } from "./url-CWC-NI3a.mjs";
2
+ import { i as importDidKey, t as exportDidKey } from "./key-C2Db_TAJ.mjs";
3
+ import { n as createHashlink, o as verifyHashlink, t as computeDigestMultibase } from "./digest-COC7xDiQ.mjs";
4
+ import { n as getPortableGatewayCandidates } from "./portable-dereference-C_cwGjz6.mjs";
5
+ import { deepStrictEqual, equal } from "node:assert/strict";
6
+ import { test, testDefinitions } from "@fedify/fixture";
7
+ //#region src/portable-workers.test.ts
8
+ const portableId = "ap://did:key:z6Mkabc/actor";
9
+ test("vocab-runtime portable URI formats on Workers", () => {
10
+ equal(formatIri(parseIri(portableId)), "ap+ef61://did:key:z6Mkabc/actor");
11
+ equal(canonicalizePortableUri(`${portableId}?@gateway=https%3A%2F%2Fa.example`), "ap+ef61://did:key:z6Mkabc/actor");
12
+ });
13
+ test("vocab-runtime compatible IDs on Workers", () => {
14
+ const compatible = toCompatibleEf61Id(portableId, "https://gw.example");
15
+ equal(compatible.href, "https://gw.example/.well-known/apgateway/did:key:z6Mkabc/actor");
16
+ equal(formatIri(fromCompatibleEf61Id(compatible)), "ap+ef61://did:key:z6Mkabc/actor");
17
+ });
18
+ test("vocab-runtime did:key on Workers", async () => {
19
+ const keys = await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
20
+ const imported = await importDidKey(await exportDidKey(keys.publicKey));
21
+ deepStrictEqual(new Uint8Array(await crypto.subtle.exportKey("raw", imported)), new Uint8Array(await crypto.subtle.exportKey("raw", keys.publicKey)));
22
+ });
23
+ test("vocab-runtime hashlink on Workers", async () => {
24
+ const bytes = new TextEncoder().encode("portable media");
25
+ const digest = await computeDigestMultibase(bytes);
26
+ equal(await verifyHashlink(bytes, createHashlink(digest)), true);
27
+ equal(await verifyHashlink(new TextEncoder().encode("tampered"), createHashlink(digest)), false);
28
+ });
29
+ test("vocab-runtime gateway candidates on Workers", () => {
30
+ const hinted = parseIri(`${portableId}?@gateway=https%3A%2F%2Fhint.example`);
31
+ deepStrictEqual(getPortableGatewayCandidates(hinted, ["https://first.example"]).map((url) => url.href), ["https://first.example/"]);
32
+ deepStrictEqual(getPortableGatewayCandidates(hinted).map((url) => url.href), ["https://hint.example/"]);
33
+ });
34
+ //#endregion
35
+ export { testDefinitions };
@@ -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.2233+abb4a6b2";
6
+ var version = "2.4.0-dev.2245+042b9587";
7
7
  //#endregion
8
8
  //#region src/request.ts
9
9
  /**