@fedify/vocab-runtime 2.4.0-pr.936.41 → 2.5.0-dev.2271

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 (138) hide show
  1. package/deno.json +4 -2
  2. package/dist/{tests/docloader-Ck8bcBir.mjs → contexts-BK1CqBR5.cjs} +164 -275
  3. package/dist/{tests/docloader-S_gQZ2tt.cjs → contexts-DPuJ4UYL.js} +160 -294
  4. package/dist/{docloader-C_dir7Xb.d.ts → docloader-CYwqh5Df.d.cts} +66 -2
  5. package/dist/{docloader-D2DTRiyA.d.cts → docloader-CYwqh5Df.d.ts} +66 -2
  6. package/dist/internal/jsonld-cache.cjs +70 -1
  7. package/dist/internal/jsonld-cache.d.cts +35 -2
  8. package/dist/internal/jsonld-cache.d.ts +35 -2
  9. package/dist/internal/jsonld-cache.js +67 -2
  10. package/dist/internal/portable-dereference.cjs +721 -0
  11. package/dist/internal/portable-dereference.d.cts +349 -0
  12. package/dist/internal/portable-dereference.d.ts +349 -0
  13. package/dist/internal/portable-dereference.js +702 -0
  14. package/dist/internal/signed-representation.cjs +378 -0
  15. package/dist/internal/signed-representation.d.cts +144 -0
  16. package/dist/internal/signed-representation.d.ts +144 -0
  17. package/dist/internal/signed-representation.js +371 -0
  18. package/dist/jsonld.cjs +2 -2
  19. package/dist/mod.cjs +711 -4527
  20. package/dist/mod.d.cts +400 -8
  21. package/dist/mod.d.ts +400 -9
  22. package/dist/mod.js +692 -4522
  23. package/dist/portable-DqtfLy_1.d.cts +160 -0
  24. package/dist/portable-DtWsu2yU.d.ts +160 -0
  25. package/dist/tests/body-CVR1bABr.mjs +130 -0
  26. package/dist/tests/body-T_yQWql3.cjs +153 -0
  27. package/dist/tests/body.test.cjs +89 -0
  28. package/dist/tests/body.test.d.cts +1 -0
  29. package/dist/tests/body.test.d.mts +1 -0
  30. package/dist/tests/body.test.mjs +90 -0
  31. package/dist/tests/contexts-CIKsin4e.mjs +4512 -0
  32. package/dist/tests/contexts-DizzBjz4.cjs +4523 -0
  33. package/dist/tests/decimal.test.cjs +8 -7
  34. package/dist/tests/decimal.test.mjs +8 -6
  35. package/dist/tests/digest-3FeH2Y-Q.cjs +176 -0
  36. package/dist/tests/digest-COC7xDiQ.mjs +141 -0
  37. package/dist/tests/digest.test.cjs +102 -0
  38. package/dist/tests/digest.test.d.cts +1 -0
  39. package/dist/tests/digest.test.d.mts +1 -0
  40. package/dist/tests/digest.test.mjs +103 -0
  41. package/dist/tests/docloader-C6yApEts.mjs +373 -0
  42. package/dist/tests/docloader-CJK2w3l-.cjs +396 -0
  43. package/dist/tests/docloader.test.cjs +692 -39
  44. package/dist/tests/docloader.test.mjs +686 -34
  45. package/dist/tests/internal/multicodec.test.cjs +2 -3
  46. package/dist/tests/internal/multicodec.test.mjs +2 -2
  47. package/dist/tests/internal/portable-dereference.test.cjs +172 -0
  48. package/dist/tests/internal/portable-dereference.test.d.cts +1 -0
  49. package/dist/tests/internal/portable-dereference.test.d.mts +1 -0
  50. package/dist/tests/internal/portable-dereference.test.mjs +173 -0
  51. package/dist/tests/jsonld-cache-BPQmOZWD.mjs +342 -0
  52. package/dist/tests/jsonld-cache-C07AyNOY.cjs +397 -0
  53. package/dist/tests/jsonld-cache.test.cjs +101 -298
  54. package/dist/tests/jsonld-cache.test.mjs +94 -289
  55. package/dist/tests/{key-_wXwomh_.cjs → key-C-AYkdJJ.cjs} +11 -4
  56. package/dist/tests/{key-CDGDH_vC.mjs → key-C2Db_TAJ.mjs} +11 -3
  57. package/dist/tests/key.test.cjs +6 -5
  58. package/dist/tests/key.test.mjs +6 -4
  59. package/dist/tests/langstr.test.cjs +4 -4
  60. package/dist/tests/langstr.test.mjs +2 -2
  61. package/dist/tests/link.test.cjs +2 -3
  62. package/dist/tests/link.test.mjs +2 -2
  63. package/dist/tests/multibase/multibase.test.cjs +8 -9
  64. package/dist/tests/multibase/multibase.test.mjs +6 -6
  65. package/dist/tests/{multibase-Bz_UUDtL.cjs → multibase-B5Mea7Ip.cjs} +19 -2
  66. package/dist/tests/{multibase-B4bvakyA.mjs → multibase-BPnF_L4e.mjs} +12 -1
  67. package/dist/tests/portable-dereference-BNXtgg5U.cjs +278 -0
  68. package/dist/tests/portable-dereference-DRE5bz-l.mjs +249 -0
  69. package/dist/tests/portable-media-C4LEXJk4.mjs +148 -0
  70. package/dist/tests/portable-media-D05JJIJg.cjs +153 -0
  71. package/dist/tests/portable-media.test.cjs +222 -0
  72. package/dist/tests/portable-media.test.d.cts +1 -0
  73. package/dist/tests/portable-media.test.d.mts +1 -0
  74. package/dist/tests/portable-media.test.mjs +223 -0
  75. package/dist/tests/portable-workers.test.cjs +36 -0
  76. package/dist/tests/portable-workers.test.d.cts +2 -0
  77. package/dist/tests/portable-workers.test.d.mts +2 -0
  78. package/dist/tests/portable-workers.test.mjs +35 -0
  79. package/dist/tests/{request-uk51rkhO.cjs → request-C-VZ9TLj.cjs} +10 -4
  80. package/dist/tests/{request-C8CaGwtt.mjs → request-Cf-A3DU-.mjs} +8 -2
  81. package/dist/tests/request.test.cjs +7 -4
  82. package/dist/tests/request.test.mjs +5 -2
  83. package/dist/tests/signed-representation.test.cjs +588 -0
  84. package/dist/tests/signed-representation.test.d.cts +1 -0
  85. package/dist/tests/signed-representation.test.d.mts +1 -0
  86. package/dist/tests/signed-representation.test.mjs +589 -0
  87. package/dist/tests/temporal.test.cjs +1 -2
  88. package/dist/tests/temporal.test.mjs +1 -1
  89. package/dist/tests/url-BNakuZ8k.cjs +998 -0
  90. package/dist/tests/url-DMxmp7ZG.mjs +859 -0
  91. package/dist/tests/url.test.cjs +489 -6
  92. package/dist/tests/url.test.mjs +489 -5
  93. package/dist/url-DrGTR8yv.cjs +993 -0
  94. package/dist/url-Dzyp-NsC.js +860 -0
  95. package/package.json +27 -4
  96. package/scripts/test-bun.mjs +17 -0
  97. package/src/body.test.ts +125 -0
  98. package/src/body.ts +152 -0
  99. package/src/contexts/cid-v1.json +114 -0
  100. package/src/contexts/fep-22cd.json +21 -0
  101. package/src/contexts/miscellany.json +17 -0
  102. package/src/contexts.ts +39 -1
  103. package/src/digest.test.ts +220 -0
  104. package/src/digest.ts +229 -0
  105. package/src/docloader.test.ts +918 -27
  106. package/src/docloader.ts +320 -105
  107. package/src/internal/jsonld-cache.ts +97 -1
  108. package/src/internal/portable-dereference.test.ts +254 -0
  109. package/src/internal/portable-dereference.ts +1000 -0
  110. package/src/internal/signed-representation.ts +565 -0
  111. package/src/jsonld-cache.test.ts +89 -0
  112. package/src/key.test.ts +9 -0
  113. package/src/key.ts +11 -1
  114. package/src/mod.ts +28 -0
  115. package/src/multibase/multibase.test.ts +5 -5
  116. package/src/portable-media.test.ts +293 -0
  117. package/src/portable-media.ts +264 -0
  118. package/src/portable-workers.test.ts +81 -0
  119. package/src/portable.ts +180 -0
  120. package/src/preprocessor.ts +7 -0
  121. package/src/request.test.ts +10 -1
  122. package/src/request.ts +9 -1
  123. package/src/signed-representation.test.ts +338 -0
  124. package/src/url.test.ts +823 -3
  125. package/src/url.ts +700 -23
  126. package/tsdown.config.ts +2 -0
  127. package/dist/tests/url-CsOV_B_P.cjs +0 -482
  128. package/dist/tests/url-Du7RQQgP.mjs +0 -392
  129. package/dist/url-DD4F0ULf.cjs +0 -483
  130. package/dist/url-DGVbSVVi.js +0 -393
  131. /package/dist/{chunk-M78iaK0I.cjs → rolldown-runtime-B7lfambq.cjs} +0 -0
  132. /package/dist/tests/{langstr-CbAxaeEZ.cjs → langstr-C4Fl80ae.cjs} +0 -0
  133. /package/dist/tests/{langstr-Di5AvKpB.mjs → langstr-CQ26J_L7.mjs} +0 -0
  134. /package/dist/tests/{link-NUUWCdnK.mjs → link-Cevmc87v.mjs} +0 -0
  135. /package/dist/tests/{link-FguCydMA.cjs → link-DlKm8bEr.cjs} +0 -0
  136. /package/dist/tests/{multicodec-CxGVGa91.cjs → multicodec-CLRPeW4N.cjs} +0 -0
  137. /package/dist/tests/{multicodec-CyFp54fI.mjs → multicodec-CRIj_05H.mjs} +0 -0
  138. /package/dist/tests/{chunk-C2EiDwsr.cjs → rolldown-runtime-emK7D4bc.cjs} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab-runtime",
3
- "version": "2.4.0-pr.936.41+59c7432c",
3
+ "version": "2.5.0-dev.2271+49d937e2",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -52,6 +52,26 @@
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
+ },
65
+ "./internal/signed-representation": {
66
+ "types": {
67
+ "import": "./dist/internal/signed-representation.d.ts",
68
+ "require": "./dist/internal/signed-representation.d.cts",
69
+ "default": "./dist/internal/signed-representation.d.ts"
70
+ },
71
+ "import": "./dist/internal/signed-representation.js",
72
+ "require": "./dist/internal/signed-representation.cjs",
73
+ "default": "./dist/internal/signed-representation.js"
74
+ },
55
75
  "./temporal": {
56
76
  "types": {
57
77
  "import": "./dist/temporal.d.ts",
@@ -76,10 +96,11 @@
76
96
  },
77
97
  "license": "MIT",
78
98
  "devDependencies": {
79
- "@types/node": "^24.2.1",
99
+ "@types/node": "^24.12.2",
80
100
  "fetch-mock": "^12.5.4",
81
101
  "tsdown": "^0.22.0",
82
- "typescript": "^6.0.0"
102
+ "typescript": "^6.0.0",
103
+ "@fedify/fixture": "2.0.0"
83
104
  },
84
105
  "dependencies": {
85
106
  "@js-temporal/polyfill": "^0.5.1",
@@ -96,6 +117,8 @@
96
117
  "build": "pnpm --filter @fedify/vocab-runtime... run build:self",
97
118
  "prepublish": "pnpm build",
98
119
  "pretest": "pnpm build",
99
- "test": "cd dist/tests && node --test"
120
+ "test": "cd dist/tests && node --test",
121
+ "pretest:bun": "pnpm build",
122
+ "test:bun": "node scripts/test-bun.mjs"
100
123
  }
101
124
  }
@@ -0,0 +1,17 @@
1
+ import { access, glob } from "node:fs/promises";
2
+ import { spawnSync } from "node:child_process";
3
+
4
+ const sources = await Array.fromAsync(glob("src/**/*.test.ts"));
5
+ if (sources.length === 0) throw new Error("No vocab-runtime tests found.");
6
+
7
+ const tests = sources.map((source) =>
8
+ source.replaceAll("\\", "/").replace(/^src\//, "dist/tests/")
9
+ .replace(/\.ts$/, ".mjs")
10
+ ).sort();
11
+ for (const test of tests) await access(test);
12
+
13
+ const result = spawnSync("bun", ["test", "--timeout", "60000", ...tests], {
14
+ stdio: "inherit",
15
+ });
16
+ if (result.error) throw result.error;
17
+ process.exitCode = result.status ?? 1;
@@ -0,0 +1,125 @@
1
+ import { deepStrictEqual, ok, rejects } from "node:assert/strict";
2
+ import { test } from "node:test";
3
+ import { gunzipSync, gzipSync } from "node:zlib";
4
+ import { BodyTooLargeError, readBoundedText } from "./body.ts";
5
+
6
+ const url = "https://example.com/document";
7
+
8
+ test("readBoundedText handles empty bodies and flushes incomplete UTF-8", async () => {
9
+ deepStrictEqual(await readBoundedText(new Response(null), 1, url), "");
10
+ deepStrictEqual(await readBoundedText(new Response(""), 1, url), "");
11
+ deepStrictEqual(
12
+ await readBoundedText(new Response(new Uint8Array([0xe2, 0x82])), 2, url),
13
+ "\ufffd",
14
+ );
15
+ });
16
+
17
+ test("readBoundedText counts bytes and preserves split UTF-8", async () => {
18
+ const bytes = new TextEncoder().encode('"안녕"');
19
+ const stream = new ReadableStream<Uint8Array>({
20
+ start(controller) {
21
+ for (const byte of bytes) controller.enqueue(new Uint8Array([byte]));
22
+ controller.close();
23
+ },
24
+ });
25
+ deepStrictEqual(
26
+ await readBoundedText(new Response(stream), bytes.length, url),
27
+ '"안녕"',
28
+ );
29
+ await rejects(
30
+ readBoundedText(new Response(bytes), bytes.length - 1, url),
31
+ BodyTooLargeError,
32
+ );
33
+ });
34
+
35
+ test("readBoundedText rejects a declared oversized body without pulling", async () => {
36
+ let pulled = false;
37
+ let canceled = false;
38
+ const response = new Response(
39
+ new ReadableStream({
40
+ pull() {
41
+ pulled = true;
42
+ },
43
+ cancel() {
44
+ canceled = true;
45
+ },
46
+ }, { highWaterMark: 0 }),
47
+ { headers: { "Content-Length": "100" } },
48
+ );
49
+ await rejects(readBoundedText(response, 10, url), BodyTooLargeError);
50
+ deepStrictEqual(pulled, false);
51
+ deepStrictEqual(canceled, true);
52
+ deepStrictEqual(response.body?.locked, false);
53
+ });
54
+
55
+ test("readBoundedText cancels chunked bodies despite absent or false lengths", async () => {
56
+ for (const length of [undefined, "1", "invalid"]) {
57
+ let pulls = 0;
58
+ let canceled = false;
59
+ const stream = new ReadableStream<Uint8Array>({
60
+ pull(controller) {
61
+ pulls++;
62
+ controller.enqueue(new Uint8Array(4));
63
+ },
64
+ cancel() {
65
+ canceled = true;
66
+ },
67
+ }, { highWaterMark: 0 });
68
+ const response = new Response(stream, {
69
+ headers: length == null ? {} : { "Content-Length": length },
70
+ });
71
+ await rejects(readBoundedText(response, 8, url), BodyTooLargeError);
72
+ deepStrictEqual(pulls, 3);
73
+ deepStrictEqual(canceled, true);
74
+ deepStrictEqual(response.body?.locked, false);
75
+ }
76
+ });
77
+
78
+ test("readBoundedText limits decoded gzip bytes, not the compressed length", async () => {
79
+ const compressed = gzipSync('"' + "a".repeat(4096) + '"');
80
+ ok(compressed.length < 128);
81
+ const decoded = gunzipSync(compressed);
82
+ await rejects(
83
+ readBoundedText(
84
+ new Response(decoded, {
85
+ headers: {
86
+ "Content-Encoding": "gzip",
87
+ "Content-Length": String(compressed.length),
88
+ },
89
+ }),
90
+ 128,
91
+ url,
92
+ ),
93
+ BodyTooLargeError,
94
+ );
95
+ // The encoded representation can also be larger than a valid decoded body.
96
+ deepStrictEqual(
97
+ await readBoundedText(
98
+ new Response("{}", {
99
+ headers: { "Content-Encoding": "gzip", "Content-Length": "22" },
100
+ }),
101
+ 2,
102
+ url,
103
+ ),
104
+ "{}",
105
+ );
106
+ });
107
+
108
+ test("readBoundedText rejects invalid limits and releases failed streams", async () => {
109
+ for (const limit of [0, -1, NaN, Infinity, 1.5]) {
110
+ await rejects(readBoundedText(new Response("{}"), limit, url), RangeError);
111
+ }
112
+ const failure = new Error("broken stream");
113
+ const response = new Response(
114
+ new ReadableStream({
115
+ pull(controller) {
116
+ controller.error(failure);
117
+ },
118
+ }),
119
+ );
120
+ await rejects(
121
+ readBoundedText(response, 10, url),
122
+ (error) => error === failure,
123
+ );
124
+ deepStrictEqual(response.body?.locked, false);
125
+ });
package/src/body.ts ADDED
@@ -0,0 +1,152 @@
1
+ // Kept private in each package to avoid adding public API in a patch release.
2
+ // TODO(Jiwon Kwon): Consolidate the duplicated body helpers in @fedify/vocab-runtime,
3
+ // @fedify/webfinger, and @fedify/fedify into a shared implementation.
4
+ import { getLogger } from "@logtape/logtape";
5
+ import { FetchError } from "./request.ts";
6
+
7
+ /** Decoded JSON body limit: 16 MiB. @internal */
8
+ export const MAX_BODY_SIZE: number = 16 * 1024 * 1024;
9
+
10
+ /** A body exceeded the byte limit. @internal */
11
+ export class BodyTooLargeError extends FetchError {
12
+ /** Creates an error for a body exceeding the given limit. */
13
+ constructor(url: string | URL, maxBytes: number) {
14
+ super(url, `Body exceeds the limit of ${maxBytes} bytes`);
15
+ this.name = "BodyTooLargeError";
16
+ }
17
+ }
18
+
19
+ /** Validates a finite, positive byte limit. @internal */
20
+ function validateBodySizeLimit(maxBytes: number): void {
21
+ if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
22
+ throw new RangeError(
23
+ "The body size limit must be a positive safe integer.",
24
+ );
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Reads UTF-8 text while limiting bytes received from the body stream.
30
+ * Fetch responses are already decompressed, so the stream count is authoritative.
31
+ * @param message The response or request to read.
32
+ * @param maxBytes The maximum number of decoded bytes to read.
33
+ * @param url The URL to include in errors and logs.
34
+ * @returns The decoded body text.
35
+ * @throws {BodyTooLargeError} If the body exceeds the limit.
36
+ * @internal
37
+ */
38
+ export async function readBoundedText(
39
+ message: Pick<Request, "body" | "headers">,
40
+ maxBytes: number,
41
+ url: string | URL,
42
+ ): Promise<string> {
43
+ validateBodySizeLimit(maxBytes);
44
+ const reader = message.body?.getReader();
45
+ const tooLarge = (): never => {
46
+ getLogger(["fedify", "runtime", "body"]).warn(
47
+ "Body from {url} exceeds the limit of {maxBytes} bytes.",
48
+ { url: url.toString(), maxBytes },
49
+ );
50
+ throw new BodyTooLargeError(url, maxBytes);
51
+ };
52
+ try {
53
+ // Content-Length describes the encoded body when compression is used.
54
+ // Only use it as an early rejection for an unencoded body.
55
+ const length = message.headers.get("Content-Length");
56
+ const encoding = message.headers.get("Content-Encoding");
57
+ if (
58
+ (encoding == null || encoding.toLowerCase() === "identity") &&
59
+ length != null && /^\d+$/.test(length) && Number(length) > maxBytes
60
+ ) tooLarge();
61
+ if (reader == null) return "";
62
+ let size = 0;
63
+ const bounded = new ReadableStream<BufferSource>({
64
+ async pull(controller) {
65
+ const { done, value } = await reader.read();
66
+ if (done) {
67
+ controller.close();
68
+ return;
69
+ }
70
+ size += value.byteLength;
71
+ if (size > maxBytes) tooLarge();
72
+ controller.enqueue(value);
73
+ },
74
+ }, { highWaterMark: 0 });
75
+ const textReader = bounded.pipeThrough(new TextDecoderStream()).getReader();
76
+ try {
77
+ const chunks: string[] = [];
78
+ while (true) {
79
+ const { done, value } = await textReader.read();
80
+ if (done) break;
81
+ chunks.push(value);
82
+ }
83
+ return chunks.join("");
84
+ } finally {
85
+ textReader.releaseLock();
86
+ }
87
+ } catch (error) {
88
+ // A cloned inbox request is a tee. Awaiting one branch's cancellation
89
+ // would deadlock until the other branch is canceled by the inbox handler.
90
+ if (reader != null) void reader.cancel(error).catch(() => {});
91
+ throw error;
92
+ } finally {
93
+ reader?.releaseLock();
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Reads raw bytes while limiting bytes received from the body stream.
99
+ * Unlike {@link readBoundedText}, the bytes are returned as they were
100
+ * received, without decoding.
101
+ * @param message The response or request to read.
102
+ * @param maxBytes The maximum number of decoded bytes to read.
103
+ * @param url The URL to include in errors and logs.
104
+ * @returns The body bytes.
105
+ * @throws {BodyTooLargeError} If the body exceeds the limit.
106
+ * @internal
107
+ */
108
+ export async function readBoundedBytes(
109
+ message: Pick<Request, "body" | "headers">,
110
+ maxBytes: number,
111
+ url: string | URL,
112
+ ): Promise<Uint8Array<ArrayBuffer>> {
113
+ validateBodySizeLimit(maxBytes);
114
+ const reader = message.body?.getReader();
115
+ const tooLarge = (): never => {
116
+ getLogger(["fedify", "runtime", "body"]).warn(
117
+ "Body from {url} exceeds the limit of {maxBytes} bytes.",
118
+ { url: url.toString(), maxBytes },
119
+ );
120
+ throw new BodyTooLargeError(url, maxBytes);
121
+ };
122
+ try {
123
+ const length = message.headers.get("Content-Length");
124
+ const encoding = message.headers.get("Content-Encoding");
125
+ if (
126
+ (encoding == null || encoding.toLowerCase() === "identity") &&
127
+ length != null && /^\d+$/.test(length) && Number(length) > maxBytes
128
+ ) tooLarge();
129
+ if (reader == null) return new Uint8Array(0);
130
+ const chunks: Uint8Array[] = [];
131
+ let size = 0;
132
+ while (true) {
133
+ const { done, value } = await reader.read();
134
+ if (done) break;
135
+ size += value.byteLength;
136
+ if (size > maxBytes) tooLarge();
137
+ chunks.push(value);
138
+ }
139
+ const bytes = new Uint8Array(size);
140
+ let offset = 0;
141
+ for (const chunk of chunks) {
142
+ bytes.set(chunk, offset);
143
+ offset += chunk.byteLength;
144
+ }
145
+ return bytes;
146
+ } catch (error) {
147
+ if (reader != null) void reader.cancel(error).catch(() => {});
148
+ throw error;
149
+ } finally {
150
+ reader?.releaseLock();
151
+ }
152
+ }
@@ -0,0 +1,114 @@
1
+ {
2
+ "@context": {
3
+ "@protected": true,
4
+ "id": "@id",
5
+ "type": "@type",
6
+
7
+ "alsoKnownAs": {
8
+ "@id": "https://www.w3.org/ns/activitystreams#alsoKnownAs",
9
+ "@type": "@id"
10
+ },
11
+ "assertionMethod": {
12
+ "@id": "https://w3id.org/security#assertionMethod",
13
+ "@type": "@id",
14
+ "@container": "@set"
15
+ },
16
+ "authentication": {
17
+ "@id": "https://w3id.org/security#authenticationMethod",
18
+ "@type": "@id",
19
+ "@container": "@set"
20
+ },
21
+ "capabilityDelegation": {
22
+ "@id": "https://w3id.org/security#capabilityDelegationMethod",
23
+ "@type": "@id",
24
+ "@container": "@set"
25
+ },
26
+ "capabilityInvocation": {
27
+ "@id": "https://w3id.org/security#capabilityInvocationMethod",
28
+ "@type": "@id",
29
+ "@container": "@set"
30
+ },
31
+ "controller": {
32
+ "@id": "https://w3id.org/security#controller",
33
+ "@type": "@id"
34
+ },
35
+ "keyAgreement": {
36
+ "@id": "https://w3id.org/security#keyAgreementMethod",
37
+ "@type": "@id",
38
+ "@container": "@set"
39
+ },
40
+ "service": {
41
+ "@id": "https://www.w3.org/ns/did#service",
42
+ "@type": "@id",
43
+ "@context": {
44
+ "@protected": true,
45
+ "id": "@id",
46
+ "type": "@type",
47
+ "serviceEndpoint": {
48
+ "@id": "https://www.w3.org/ns/did#serviceEndpoint",
49
+ "@type": "@id"
50
+ }
51
+ }
52
+ },
53
+ "verificationMethod": {
54
+ "@id": "https://w3id.org/security#verificationMethod",
55
+ "@type": "@id"
56
+ },
57
+ "Multikey": {
58
+ "@id": "https://w3id.org/security#Multikey",
59
+ "@context": {
60
+ "@protected": true,
61
+ "id": "@id",
62
+ "type": "@type",
63
+ "controller": {
64
+ "@id": "https://w3id.org/security#controller",
65
+ "@type": "@id"
66
+ },
67
+ "revoked": {
68
+ "@id": "https://w3id.org/security#revoked",
69
+ "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
70
+ },
71
+ "expires": {
72
+ "@id": "https://w3id.org/security#expiration",
73
+ "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
74
+ },
75
+ "publicKeyMultibase": {
76
+ "@id": "https://w3id.org/security#publicKeyMultibase",
77
+ "@type": "https://w3id.org/security#multibase"
78
+ },
79
+ "secretKeyMultibase": {
80
+ "@id": "https://w3id.org/security#secretKeyMultibase",
81
+ "@type": "https://w3id.org/security#multibase"
82
+ }
83
+ }
84
+ },
85
+ "JsonWebKey": {
86
+ "@id": "https://w3id.org/security#JsonWebKey",
87
+ "@context": {
88
+ "@protected": true,
89
+ "id": "@id",
90
+ "type": "@type",
91
+ "controller": {
92
+ "@id": "https://w3id.org/security#controller",
93
+ "@type": "@id"
94
+ },
95
+ "revoked": {
96
+ "@id": "https://w3id.org/security#revoked",
97
+ "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
98
+ },
99
+ "expires": {
100
+ "@id": "https://w3id.org/security#expiration",
101
+ "@type": "http://www.w3.org/2001/XMLSchema#dateTime"
102
+ },
103
+ "publicKeyJwk": {
104
+ "@id": "https://w3id.org/security#publicKeyJwk",
105
+ "@type": "@json"
106
+ },
107
+ "secretKeyJwk": {
108
+ "@id": "https://w3id.org/security#secretKeyJwk",
109
+ "@type": "@json"
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "@context": {
3
+ "fep-22cd": "https://w3id.org/fep/22cd#",
4
+ "schema": "https://schema.org/",
5
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
6
+ "translations": { "@id": "fep-22cd:translations", "@container": "@set" },
7
+ "Translation": "fep-22cd:Translation",
8
+ "sourceUpdated": {
9
+ "@id": "fep-22cd:sourceUpdated",
10
+ "@type": "xsd:dateTime"
11
+ },
12
+ "translator": {
13
+ "@id": "schema:translator",
14
+ "@type": "@id",
15
+ "@container": "@set"
16
+ },
17
+ "inLanguage": "schema:inLanguage",
18
+ "translationOfWork": { "@id": "schema:translationOfWork", "@type": "@id" },
19
+ "isBasedOn": { "@id": "schema:isBasedOn", "@type": "@id" }
20
+ }
21
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "@context": {
3
+ "as": "https://www.w3.org/ns/activitystreams#",
4
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
5
+ "Hashtag": "as:Hashtag",
6
+ "manuallyApprovesFollowers": {
7
+ "@id": "as:manuallyApprovesFollowers"
8
+ },
9
+ "movedTo": {
10
+ "@id": "as:movedTo",
11
+ "@type": "@id"
12
+ },
13
+ "sensitive": {
14
+ "@id": "as:sensitive"
15
+ }
16
+ }
17
+ }
package/src/contexts.ts CHANGED
@@ -5,7 +5,9 @@
5
5
  import activitystreams from "./contexts/activitystreams.json" with {
6
6
  type: "json",
7
7
  };
8
+ import cidV1Context from "./contexts/cid-v1.json" with { type: "json" };
8
9
  import didV1 from "./contexts/did-v1.json" with { type: "json" };
10
+ import fep22cd from "./contexts/fep-22cd.json" with { type: "json" };
9
11
  import fep5711 from "./contexts/fep-5711.json" with { type: "json" };
10
12
  import fep7aa9 from "./contexts/fep-7aa9.json" with { type: "json" };
11
13
  import fepEf61 from "./contexts/fep-ef61.json" with { type: "json" };
@@ -13,6 +15,7 @@ import gotosocial from "./contexts/gotosocial.json" with { type: "json" };
13
15
  import identityV1 from "./contexts/identity-v1.json" with { type: "json" };
14
16
  import joinLemmyContext from "./contexts/join-lemmy.json" with { type: "json" };
15
17
  import joinmastodon from "./contexts/joinmastodon.json" with { type: "json" };
18
+ import miscellany from "./contexts/miscellany.json" with { type: "json" };
16
19
  import schemaorg from "./contexts/schemaorg.json" with { type: "json" };
17
20
  import securityDataIntegrityV1 from "./contexts/security-data-integrity-v1.json" with {
18
21
  type: "json",
@@ -36,10 +39,33 @@ const preloadedContexts: Record<string, unknown> = {
36
39
  "https://purl.archive.org/socialweb/webfinger": webfinger,
37
40
  "http://schema.org/": schemaorg,
38
41
  "https://gotosocial.org/ns": gotosocial,
42
+ "https://w3id.org/fep/22cd": fep22cd,
39
43
  "https://w3id.org/fep/5711": fep5711,
40
- "https://w3id.org/fep/7aa9": fep7aa9,
44
+
45
+ // Controlled Identifiers v1.0 requires JSON-LD processors to treat this
46
+ // context URL as already resolved. We ship the official W3C context so that
47
+ // controlled identifier documents can be processed without depending on the
48
+ // availability of the remote context server.
49
+ // See: https://www.w3.org/TR/cid-1.0/#json-ld-context
50
+ // https://github.com/fedify-dev/fedify/issues/932
51
+ "https://www.w3.org/ns/cid/v1": cidV1Context,
52
+
53
+ // The FEP-ef61 context. The w3id.org URL redirects to Codeberg Pages, which
54
+ // suffers recurring outages; while it is unreachable, every document
55
+ // referencing this URL fails JSON-LD expansion before application handlers
56
+ // run. We ship a built-in copy so that portable objects can be processed
57
+ // without depending on the availability of the remote context server.
58
+ // See: https://w3id.org/fep/ef61
59
+ // https://github.com/fedify-dev/fedify/issues/982
41
60
  "https://w3id.org/fep/ef61": fepEf61,
42
61
 
62
+ // The FEP-7aa9 context is hosted on Codeberg Pages, whose outages can
63
+ // prevent actor documents from being parsed. Resolve it locally so that
64
+ // parsing does not depend on the remote context server's availability.
65
+ // See: https://w3id.org/fep/7aa9
66
+ // https://github.com/fedify-dev/fedify/issues/1078
67
+ "https://w3id.org/fep/7aa9": fep7aa9,
68
+
43
69
  // Lemmy's context document is served as application/json without the JSON-LD
44
70
  // context Link header. The default document loader treats that as a regular
45
71
  // JSON response instead of a JSON-LD context, so every Lemmy activity that
@@ -59,6 +85,18 @@ const preloadedContexts: Record<string, unknown> = {
59
85
  // See: https://github.com/mastodon/joinmastodon/issues/148
60
86
  // https://github.com/fedify-dev/fedify/issues/630
61
87
  "http://joinmastodon.org/ns": joinmastodon,
88
+
89
+ // The SWICG "ActivityPub Miscellaneous Terms" context. Bridgy Fed (via
90
+ // granary) references this URL in the @context of every activity it sends,
91
+ // and other implementations use it as the recommended home for widely
92
+ // deployed extension terms like as:Hashtag and as:sensitive. It is served
93
+ // through purl.archive.org (Internet Archive's PURL service), which suffers
94
+ // recurring outages; while it is unreachable, every activity referencing
95
+ // this URL fails JSON-LD expansion before application handlers run. The
96
+ // document is a deliberately stable, versioned SWICG deliverable, so we
97
+ // ship a built-in copy.
98
+ // See: https://swicg.github.io/miscellany/
99
+ "https://purl.archive.org/miscellany": miscellany,
62
100
  };
63
101
 
64
102
  export default preloadedContexts;