@fedify/fedify 2.2.0-pr.715.28 → 2.2.0-pr.731.34

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 (87) hide show
  1. package/dist/assert_strict_equals-Dmjbg-bA.mjs +41 -0
  2. package/dist/{builder-BfmqkrIE.mjs → builder-OFkoAQ85.mjs} +3 -3
  3. package/dist/compat/mod.d.cts +1 -1
  4. package/dist/compat/mod.d.ts +1 -1
  5. package/dist/compat/outgoing-jsonld.test.d.mts +2 -0
  6. package/dist/compat/outgoing-jsonld.test.mjs +189 -0
  7. package/dist/compat/public-audience.test.d.mts +2 -0
  8. package/dist/compat/public-audience.test.mjs +178 -0
  9. package/dist/compat/transformers.test.mjs +3 -3
  10. package/dist/{context-BGrYMSTk.d.ts → context-BzH2-ajs.d.ts} +22 -0
  11. package/dist/{context-CMUd4wy0.d.cts → context-DJGagtNd.d.cts} +22 -0
  12. package/dist/{deno-CjZGHpS5.mjs → deno-BYRjHaeb.mjs} +1 -1
  13. package/dist/{docloader-Du9_2PyA.mjs → docloader-CuVh02a1.mjs} +2 -2
  14. package/dist/federation/builder.test.mjs +3 -3
  15. package/dist/federation/collection.test.mjs +2 -2
  16. package/dist/federation/handler.test.mjs +8 -8
  17. package/dist/federation/idempotency.test.mjs +5 -5
  18. package/dist/federation/inbox.test.mjs +1 -1
  19. package/dist/federation/keycache.test.mjs +3 -3
  20. package/dist/federation/kv.test.mjs +2 -2
  21. package/dist/federation/middleware.test.mjs +160 -10
  22. package/dist/federation/mod.cjs +1 -1
  23. package/dist/federation/mod.d.cts +2 -2
  24. package/dist/federation/mod.d.ts +2 -2
  25. package/dist/federation/mod.js +1 -1
  26. package/dist/federation/mq.test.mjs +17 -10
  27. package/dist/federation/negotiation.test.mjs +3 -3
  28. package/dist/federation/retry.test.mjs +1 -1
  29. package/dist/federation/router.test.mjs +2 -2
  30. package/dist/federation/send.test.mjs +6 -6
  31. package/dist/federation/webfinger.test.mjs +3 -3
  32. package/dist/{http-9zjtsC0n.mjs → http-BrVfkREl.mjs} +3 -3
  33. package/dist/{http-Wm7tvhRa.cjs → http-H-4FzBb3.cjs} +1 -1
  34. package/dist/{http-C8puyZ4Z.js → http-pZce7PcA.js} +1 -1
  35. package/dist/{key-CMhIdO1-.mjs → key-cK-YTNKa.mjs} +1 -1
  36. package/dist/{kv-cache-BTq6qqgJ.js → kv-cache-CjweM0Am.js} +1 -1
  37. package/dist/{kv-cache-DyRjARFV.cjs → kv-cache-NgzTkTjv.cjs} +1 -1
  38. package/dist/{ld-BH-6muxq.mjs → ld-SXDkzuUo.mjs} +2 -2
  39. package/dist/{middleware-CO5RpqOP.mjs → middleware-AqjYcDmu.mjs} +1 -1
  40. package/dist/{middleware-DBgitte6.mjs → middleware-BMePykoo.mjs} +34 -22
  41. package/dist/{middleware-D3S_Ctwx.cjs → middleware-CXhCkBG4.cjs} +1 -1
  42. package/dist/{middleware-C_CRggCg.cjs → middleware-TZHT1gKk.cjs} +20 -9
  43. package/dist/{middleware-Da-j6H9U.js → middleware-YcQIUUHs.js} +19 -8
  44. package/dist/{mod-CJXfyw7v.d.ts → mod-2d12ffz3.d.ts} +1 -1
  45. package/dist/{mod-BcJHeuv1.d.cts → mod-D35TRn09.d.cts} +1 -1
  46. package/dist/mod.cjs +4 -4
  47. package/dist/mod.d.cts +2 -2
  48. package/dist/mod.d.ts +2 -2
  49. package/dist/mod.js +4 -4
  50. package/dist/nodeinfo/client.test.mjs +2 -2
  51. package/dist/nodeinfo/handler.test.mjs +3 -3
  52. package/dist/nodeinfo/types.test.mjs +2 -2
  53. package/dist/otel/exporter.test.mjs +2 -2
  54. package/dist/outgoing-jsonld-CNmZLixq.mjs +203 -0
  55. package/dist/{owner-CE5FwvNR.mjs → owner-Cudh-ej0.mjs} +2 -2
  56. package/dist/{proof-jHDv7IKD.js → proof-BJrEACyu.js} +425 -41
  57. package/dist/{proof-D-HuDCQe.cjs → proof-CFERN43j.cjs} +428 -38
  58. package/dist/{proof-Ds3T1-sE.mjs → proof-DC69vtxY.mjs} +38 -31
  59. package/dist/public-audience-DYFHzm_c.mjs +192 -0
  60. package/dist/{send-TicMA6nJ.mjs → send-BBRG1CKC.mjs} +2 -2
  61. package/dist/sig/accept.test.mjs +1 -1
  62. package/dist/sig/http.test.mjs +5 -5
  63. package/dist/sig/key.test.mjs +3 -3
  64. package/dist/sig/ld.test.mjs +4 -4
  65. package/dist/sig/mod.cjs +2 -2
  66. package/dist/sig/mod.js +2 -2
  67. package/dist/sig/owner.test.mjs +4 -4
  68. package/dist/sig/proof.test.mjs +78 -5
  69. package/dist/{std__assert-Duiq_YC9.mjs → std__assert-CRDpx_HF.mjs} +3 -38
  70. package/dist/testing/mod.d.mts +22 -0
  71. package/dist/utils/docloader.test.mjs +4 -4
  72. package/dist/utils/kv-cache.test.mjs +1 -1
  73. package/dist/utils/mod.cjs +1 -1
  74. package/dist/utils/mod.js +1 -1
  75. package/package.json +5 -5
  76. /package/dist/{accept-Dd__NiUL.mjs → accept-CPkZzmGN.mjs} +0 -0
  77. /package/dist/{activity-listener-Ck3JZ_hR.mjs → activity-listener-ell7W1s9.mjs} +0 -0
  78. /package/dist/{assert-ddO5KLpe.mjs → assert-DikXweDx.mjs} +0 -0
  79. /package/dist/{client-DEpOVgY1.mjs → client-D_1QpnWt.mjs} +0 -0
  80. /package/dist/{collection-BD6-SZ6O.mjs → collection-D-HqUuA2.mjs} +0 -0
  81. /package/dist/{keycache-CCSwkQcY.mjs → keycache-EGATflN-.mjs} +0 -0
  82. /package/dist/{keys-BAK-tUlf.mjs → keys-DGu1NFwu.mjs} +0 -0
  83. /package/dist/{kv-cache-B01V7s3h.mjs → kv-cache-U__xU4qR.mjs} +0 -0
  84. /package/dist/{kv-tL2TOE9X.mjs → kv-rV3vodCc.mjs} +0 -0
  85. /package/dist/{negotiation-DnsfFF8I.mjs → negotiation-SQvQgUqe.mjs} +0 -0
  86. /package/dist/{retry-B_E3V_Dx.mjs → retry-bMXBL97A.mjs} +0 -0
  87. /package/dist/{types-DCP0WLdt.mjs → types-J53Kw7so.mjs} +0 -0
@@ -0,0 +1,41 @@
1
+ import "@js-temporal/polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { a as red, i as buildMessage, l as AssertionError, n as diffStr, r as diff, s as format } from "./assert_equals-Ew3jOFa3.mjs";
5
+ //#region ../../node_modules/.pnpm/@jsr+std__assert@0.226.0/node_modules/@jsr/std__assert/assert_strict_equals.js
6
+ /**
7
+ * Make an assertion that `actual` and `expected` are equal using
8
+ * {@linkcode Object.is} for equality comparison. If not, then throw.
9
+ *
10
+ * @example Usage
11
+ * ```ts no-eval
12
+ * import { assertStrictEquals } from "@std/assert/assert-strict-equals";
13
+ *
14
+ * const a = {};
15
+ * const b = a;
16
+ * assertStrictEquals(a, b); // Doesn't throw
17
+ *
18
+ * const c = {};
19
+ * const d = {};
20
+ * assertStrictEquals(c, d); // Throws
21
+ * ```
22
+ *
23
+ * @typeParam T The type of the expected value.
24
+ * @param actual The actual value to compare.
25
+ * @param expected The expected value to compare.
26
+ * @param msg The optional message to display if the assertion fails.
27
+ */ function assertStrictEquals(actual, expected, msg) {
28
+ if (Object.is(actual, expected)) return;
29
+ const msgSuffix = msg ? `: ${msg}` : ".";
30
+ let message;
31
+ const actualString = format(actual);
32
+ const expectedString = format(expected);
33
+ if (actualString === expectedString) message = `Values have the same structure but are not reference-equal${msgSuffix}\n\n${red(actualString.split("\n").map((l) => ` ${l}`).join("\n"))}\n`;
34
+ else {
35
+ const stringDiff = typeof actual === "string" && typeof expected === "string";
36
+ message = `Values are not strictly equal${msgSuffix}\n${buildMessage(stringDiff ? diffStr(actual, expected) : diff(actualString.split("\n"), expectedString.split("\n")), { stringDiff }).join("\n")}`;
37
+ }
38
+ throw new AssertionError(message);
39
+ }
40
+ //#endregion
41
+ export { assertStrictEquals as t };
@@ -2,8 +2,8 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as RouterError, t as Router } from "./router-CrMLXoOr.mjs";
5
- import { n as version, t as name } from "./deno-CjZGHpS5.mjs";
6
- import { t as ActivityListenerSet } from "./activity-listener-Ck3JZ_hR.mjs";
5
+ import { n as version, t as name } from "./deno-BYRjHaeb.mjs";
6
+ import { t as ActivityListenerSet } from "./activity-listener-ell7W1s9.mjs";
7
7
  import { Tombstone, getTypeId } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
9
9
  import { getLogger } from "@logtape/logtape";
@@ -58,7 +58,7 @@ var FederationBuilderImpl = class {
58
58
  this.collectionTypeIds = {};
59
59
  }
60
60
  async build(options) {
61
- const { FederationImpl } = await import("./middleware-CO5RpqOP.mjs");
61
+ const { FederationImpl } = await import("./middleware-AqjYcDmu.mjs");
62
62
  const f = new FederationImpl(options);
63
63
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
64
64
  f.router = this.router.clone();
@@ -1,4 +1,4 @@
1
- import { Ot as ActivityTransformer, n as Context } from "../context-CMUd4wy0.cjs";
1
+ import { Ot as ActivityTransformer, n as Context } from "../context-DJGagtNd.cjs";
2
2
  import { Activity } from "@fedify/vocab";
3
3
 
4
4
  //#region src/compat/transformers.d.ts
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { Ot as ActivityTransformer, n as Context } from "../context-BGrYMSTk.js";
3
+ import { Ot as ActivityTransformer, n as Context } from "../context-BzH2-ajs.js";
4
4
  import { Activity } from "@fedify/vocab";
5
5
 
6
6
  //#region src/compat/transformers.d.ts
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";
@@ -0,0 +1,189 @@
1
+ import "@js-temporal/polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
+ import { t as assertStrictEquals } from "../assert_strict_equals-Dmjbg-bA.mjs";
6
+ import { n as normalizeAttachmentArrays, r as normalizeOutgoingActivityJsonLd, t as isPreloadedContextAttachmentSafe } from "../outgoing-jsonld-CNmZLixq.mjs";
7
+ import { mockDocumentLoader, test } from "@fedify/fixture";
8
+ import { Create, Document, Note, PUBLIC_COLLECTION } from "@fedify/vocab";
9
+ //#region src/compat/outgoing-jsonld.test.ts
10
+ test("normalizeAttachmentArrays() wraps scalar attachments", async () => {
11
+ const object = (await normalizeAttachmentArrays({
12
+ "@context": "https://www.w3.org/ns/activitystreams",
13
+ type: "Create",
14
+ object: {
15
+ type: "Note",
16
+ attachment: {
17
+ type: "Document",
18
+ mediaType: "image/png",
19
+ url: "https://example.com/image.png"
20
+ }
21
+ }
22
+ })).object;
23
+ assertEquals(object.attachment, [{
24
+ type: "Document",
25
+ mediaType: "image/png",
26
+ url: "https://example.com/image.png"
27
+ }]);
28
+ });
29
+ test("normalizeAttachmentArrays() skips canonicalization for known-safe contexts", async () => {
30
+ assertEquals((await normalizeAttachmentArrays({
31
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
32
+ type: "Note",
33
+ attachment: {
34
+ type: "Document",
35
+ mediaType: "image/png",
36
+ url: "https://example.com/image.png"
37
+ }
38
+ }, () => {
39
+ throw new Error("context loader should not be called");
40
+ })).attachment, [{
41
+ type: "Document",
42
+ mediaType: "image/png",
43
+ url: "https://example.com/image.png"
44
+ }]);
45
+ });
46
+ test("isPreloadedContextAttachmentSafe() checks scoped contexts", () => {
47
+ assertEquals(isPreloadedContextAttachmentSafe({ "@context": {
48
+ attachment: {
49
+ "@id": "as:attachment",
50
+ "@type": "@id"
51
+ },
52
+ Example: { "@context": { attachment: {
53
+ "@id": "as:attachment",
54
+ "@type": "@id"
55
+ } } }
56
+ } }), true);
57
+ assertEquals(isPreloadedContextAttachmentSafe({ "@context": { Example: { "@context": { attachment: "https://example.com/custom-attachment" } } } }), false);
58
+ });
59
+ test("normalizeAttachmentArrays() does not wrap JSON-LD list objects", async () => {
60
+ const attachment = { "@list": [{
61
+ type: "Document",
62
+ url: "https://example.com/image.png"
63
+ }] };
64
+ assertEquals((await normalizeAttachmentArrays({
65
+ "@context": "https://www.w3.org/ns/activitystreams",
66
+ type: "Note",
67
+ attachment
68
+ }, () => {
69
+ throw new Error("context loader should not be called");
70
+ })).attachment, attachment);
71
+ });
72
+ test("normalizeAttachmentArrays() does not traverse JSON-LD value payloads", async () => {
73
+ const output = await normalizeAttachmentArrays({
74
+ "@context": "https://www.w3.org/ns/activitystreams",
75
+ type: "Note",
76
+ attachment: { type: "Document" },
77
+ content: {
78
+ "@type": "@json",
79
+ "@value": {
80
+ "@context": { attachment: "https://example.com/custom-attachment" },
81
+ attachment: "https://example.com/metadata"
82
+ }
83
+ }
84
+ }, () => {
85
+ throw new Error("context loader should not be called");
86
+ });
87
+ assertEquals(output.attachment, [{ type: "Document" }]);
88
+ assertEquals(output.content, {
89
+ "@type": "@json",
90
+ "@value": {
91
+ "@context": { attachment: "https://example.com/custom-attachment" },
92
+ attachment: "https://example.com/metadata"
93
+ }
94
+ });
95
+ });
96
+ test("normalizeAttachmentArrays() leaves attachment arrays unchanged", async () => {
97
+ const attachment = [{
98
+ type: "Document",
99
+ mediaType: "image/png",
100
+ url: "https://example.com/image.png"
101
+ }];
102
+ assertEquals((await normalizeAttachmentArrays({
103
+ "@context": "https://www.w3.org/ns/activitystreams",
104
+ type: "Note",
105
+ attachment
106
+ })).attachment, attachment);
107
+ });
108
+ test("normalizeAttachmentArrays() leaves documents without attachments unchanged", async () => {
109
+ const input = {
110
+ "@context": "https://www.w3.org/ns/activitystreams",
111
+ type: "Note",
112
+ content: "Hello"
113
+ };
114
+ assertEquals(await normalizeAttachmentArrays(input), input);
115
+ });
116
+ test("normalizeAttachmentArrays() leaves @context subtrees untouched", async () => {
117
+ const output = await normalizeAttachmentArrays({
118
+ "@context": ["https://www.w3.org/ns/activitystreams", { attachment: "https://example.com/custom-attachment" }],
119
+ type: "Note",
120
+ attachment: "https://example.com/attachment"
121
+ });
122
+ const context = output["@context"];
123
+ assertEquals(context[1], { attachment: "https://example.com/custom-attachment" });
124
+ assertEquals(output.attachment, ["https://example.com/attachment"]);
125
+ });
126
+ test("normalizeAttachmentArrays() bails out when wrapping changes semantics", async () => {
127
+ assertEquals((await normalizeAttachmentArrays({
128
+ "@context": { attachment: {
129
+ "@id": "https://example.com/custom-attachment",
130
+ "@type": "@json"
131
+ } },
132
+ attachment: { custom: true }
133
+ })).attachment, { custom: true });
134
+ });
135
+ test("normalizeAttachmentArrays() does not poison the global prototype via a __proto__ key", async () => {
136
+ await normalizeAttachmentArrays(JSON.parse(`{
137
+ "@context": "https://www.w3.org/ns/activitystreams",
138
+ "type": "Note",
139
+ "attachment": { "type": "Document" },
140
+ "__proto__": { "polluted": true }
141
+ }`));
142
+ assertEquals(Object.prototype.polluted, void 0);
143
+ });
144
+ test("normalizeAttachmentArrays() stops before blowing the stack on pathological nesting", async () => {
145
+ let deep = { attachment: { type: "Document" } };
146
+ for (let i = 0; i < 256; i++) deep = { object: deep };
147
+ const input = {
148
+ "@context": "https://www.w3.org/ns/activitystreams",
149
+ type: "Create",
150
+ object: deep
151
+ };
152
+ assertStrictEquals(await normalizeAttachmentArrays(input), input);
153
+ });
154
+ test("normalizeAttachmentArrays() skips canonicalization for pathological nesting", async () => {
155
+ let deep = { type: "Note" };
156
+ for (let i = 0; i < 256; i++) deep = { object: deep };
157
+ assertEquals((await normalizeAttachmentArrays({
158
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://example.com/context"],
159
+ type: "Note",
160
+ attachment: { type: "Document" },
161
+ object: deep
162
+ }, () => {
163
+ throw new Error("context loader should not be called");
164
+ })).attachment, { type: "Document" });
165
+ });
166
+ test("normalizeOutgoingActivityJsonLd() applies outgoing JSON-LD workarounds", async () => {
167
+ const compact = await new Create({
168
+ id: new URL("https://example.com/activities/1"),
169
+ actor: new URL("https://example.com/alice"),
170
+ object: new Note({
171
+ id: new URL("https://example.com/notes/1"),
172
+ tos: [PUBLIC_COLLECTION],
173
+ attachments: [new Document({
174
+ mediaType: "image/png",
175
+ url: new URL("https://example.com/image.png")
176
+ })]
177
+ }),
178
+ tos: [PUBLIC_COLLECTION]
179
+ }).toJsonLd({ format: "compact" });
180
+ assertEquals(compact.to, "as:Public");
181
+ const compactObject = compact.object;
182
+ assertEquals(Array.isArray(compactObject.attachment), false);
183
+ const normalized = await normalizeOutgoingActivityJsonLd(compact, mockDocumentLoader);
184
+ assertEquals(normalized.to, PUBLIC_COLLECTION.href);
185
+ const normalizedObject = normalized.object;
186
+ assertEquals(Array.isArray(normalizedObject.attachment), true);
187
+ });
188
+ //#endregion
189
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";
@@ -0,0 +1,178 @@
1
+ import "@js-temporal/polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
+ import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
6
+ import { t as normalizePublicAudience } from "../public-audience-DYFHzm_c.mjs";
7
+ import { test } from "@fedify/fixture";
8
+ import { Create, Note, PUBLIC_COLLECTION } from "@fedify/vocab";
9
+ import { getDocumentLoader, preloadedContexts } from "@fedify/vocab-runtime";
10
+ //#region src/compat/public-audience.test.ts
11
+ const PUBLIC_URI = PUBLIC_COLLECTION.href;
12
+ const AS_CONTEXT = "https://www.w3.org/ns/activitystreams";
13
+ test("normalizePublicAudience() rewrites every addressing field and both CURIE forms", async () => {
14
+ const output = await normalizePublicAudience({
15
+ "@context": AS_CONTEXT,
16
+ type: "Note",
17
+ id: "https://example.com/notes/1",
18
+ to: "as:Public",
19
+ cc: ["as:Public", "https://example.com/bob"],
20
+ bto: "Public",
21
+ bcc: ["Public"],
22
+ audience: ["as:Public", "Public"]
23
+ });
24
+ assertEquals(output.to, PUBLIC_URI);
25
+ assertEquals(output.cc, [PUBLIC_URI, "https://example.com/bob"]);
26
+ assertEquals(output.bto, PUBLIC_URI);
27
+ assertEquals(output.bcc, [PUBLIC_URI]);
28
+ assertEquals(output.audience, [PUBLIC_URI, PUBLIC_URI]);
29
+ });
30
+ test("normalizePublicAudience() normalises activities serialized by @fedify/vocab", async () => {
31
+ const compact = await new Create({
32
+ id: new URL("https://example.com/activities/1"),
33
+ actor: new URL("https://example.com/alice"),
34
+ object: new Note({
35
+ id: new URL("https://example.com/notes/1"),
36
+ content: "Hello, world!",
37
+ tos: [PUBLIC_COLLECTION]
38
+ }),
39
+ tos: [PUBLIC_COLLECTION],
40
+ ccs: [new URL("https://example.com/followers")]
41
+ }).toJsonLd({ format: "compact" });
42
+ assertEquals(compact.to, "as:Public");
43
+ const normalized = await normalizePublicAudience(compact, getDocumentLoader());
44
+ assertEquals(normalized.to, PUBLIC_URI);
45
+ const nestedObject = normalized.object;
46
+ assertEquals(nestedObject.to, PUBLIC_URI);
47
+ });
48
+ test("normalizePublicAudience() is a no-op without the CURIE", async () => {
49
+ const input = {
50
+ "@context": AS_CONTEXT,
51
+ type: "Note",
52
+ id: "https://example.com/notes/3",
53
+ to: PUBLIC_URI
54
+ };
55
+ assertEquals(await normalizePublicAudience(input), input);
56
+ });
57
+ test("normalizePublicAudience() leaves non-addressing fields untouched", async () => {
58
+ const output = await normalizePublicAudience({
59
+ "@context": AS_CONTEXT,
60
+ type: "Note",
61
+ id: "https://example.com/notes/4",
62
+ name: "as:Public",
63
+ to: "as:Public"
64
+ });
65
+ assertEquals(output.name, "as:Public");
66
+ assertEquals(output.to, PUBLIC_URI);
67
+ });
68
+ test("normalizePublicAudience() rewrites without canonicalization for known-safe contexts", async () => {
69
+ const rejecting = () => {
70
+ throw new Error("contextLoader should not be called for a known-safe @context");
71
+ };
72
+ assertEquals((await normalizePublicAudience({
73
+ "@context": AS_CONTEXT,
74
+ type: "Note",
75
+ id: "https://example.com/notes/fast1",
76
+ to: "as:Public"
77
+ }, rejecting)).to, PUBLIC_URI);
78
+ assertEquals((await normalizePublicAudience({
79
+ "@context": [AS_CONTEXT, "https://w3id.org/security/data-integrity/v1"],
80
+ type: "Note",
81
+ id: "https://example.com/notes/fast2",
82
+ to: "as:Public"
83
+ }, rejecting)).to, PUBLIC_URI);
84
+ });
85
+ test("normalizePublicAudience() falls back to canonicalization for unknown-URL contexts", async () => {
86
+ let loaderCalls = 0;
87
+ const loader = (url) => {
88
+ loaderCalls++;
89
+ return Promise.resolve({
90
+ contextUrl: null,
91
+ documentUrl: url,
92
+ document: preloadedContexts[AS_CONTEXT]
93
+ });
94
+ };
95
+ assertEquals((await normalizePublicAudience({
96
+ "@context": [AS_CONTEXT, "https://custom.example/ctx"],
97
+ type: "Note",
98
+ id: "https://example.com/notes/unknown",
99
+ to: "as:Public"
100
+ }, loader)).to, PUBLIC_URI);
101
+ assertEquals(loaderCalls > 0, true);
102
+ });
103
+ test("normalizePublicAudience() leaves @context subtrees untouched", async () => {
104
+ const inlineCtx = (await normalizePublicAudience({
105
+ "@context": [AS_CONTEXT, { "customTerm": "as:Public" }],
106
+ type: "Note",
107
+ id: "https://example.com/notes/context",
108
+ to: PUBLIC_URI
109
+ }))["@context"][1];
110
+ assertEquals(inlineCtx.customTerm, "as:Public");
111
+ });
112
+ test("normalizePublicAudience() does not traverse prototype-polluted keys", async () => {
113
+ const polluted = Object.create({ to: "as:Public" });
114
+ polluted["@context"] = AS_CONTEXT;
115
+ polluted.type = "Note";
116
+ polluted.id = "https://example.com/notes/proto";
117
+ const output = await normalizePublicAudience(polluted);
118
+ assertEquals(Object.hasOwn(output, "to"), false);
119
+ });
120
+ test("normalizePublicAudience() stops before blowing the stack on pathological nesting", async () => {
121
+ let deep = { to: "as:Public" };
122
+ for (let i = 0; i < 256; i++) deep = { nested: deep };
123
+ assertEquals(typeof await normalizePublicAudience({
124
+ "@context": AS_CONTEXT,
125
+ type: "Note",
126
+ id: "https://example.com/notes/deep",
127
+ to: "as:Public",
128
+ object: deep
129
+ }), "object");
130
+ });
131
+ test("normalizePublicAudience() does not poison the global prototype via a __proto__ key", async () => {
132
+ await normalizePublicAudience(JSON.parse(`{
133
+ "@context": "https://www.w3.org/ns/activitystreams",
134
+ "type": "Note",
135
+ "id": "https://example.com/notes/proto-pollution",
136
+ "to": "as:Public",
137
+ "__proto__": { "polluted": true }
138
+ }`));
139
+ assertEquals(Object.prototype.polluted, void 0);
140
+ });
141
+ test("normalizePublicAudience() bails out on nested @context that redefines as:", async () => {
142
+ const output = await normalizePublicAudience({
143
+ "@context": AS_CONTEXT,
144
+ type: "Create",
145
+ id: "https://example.com/activities/nested",
146
+ actor: "https://example.com/alice",
147
+ to: "as:Public",
148
+ object: {
149
+ "@context": { "as": "https://not-activitystreams.example/" },
150
+ type: "https://www.w3.org/ns/activitystreams#Note",
151
+ id: "https://example.com/objects/nested",
152
+ to: "as:Public"
153
+ }
154
+ });
155
+ assertEquals(output.to, "as:Public");
156
+ const nested = output.object;
157
+ assertEquals(nested.to, "as:Public");
158
+ });
159
+ test("normalizePublicAudience() bails out when the rewrite changes semantics", async () => {
160
+ const output = await normalizePublicAudience({
161
+ "@context": {
162
+ "as": "https://not-activitystreams.example/",
163
+ "to": {
164
+ "@id": "https://www.w3.org/ns/activitystreams#to",
165
+ "@type": "@id"
166
+ },
167
+ "type": "@type",
168
+ "id": "@id"
169
+ },
170
+ type: "https://www.w3.org/ns/activitystreams#Note",
171
+ id: "https://example.com/notes/5",
172
+ to: "as:Public"
173
+ });
174
+ assertEquals(output.to, "as:Public");
175
+ assertNotEquals(output.to, PUBLIC_URI);
176
+ });
177
+ //#endregion
178
+ export {};
@@ -3,9 +3,9 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
6
- import { t as assert } from "../assert-ddO5KLpe.mjs";
7
- import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
8
- import { n as FederationImpl, v as actorDehydrator, y as autoIdAssigner } from "../middleware-DBgitte6.mjs";
6
+ import { t as assert } from "../assert-DikXweDx.mjs";
7
+ import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
8
+ import { n as FederationImpl, v as actorDehydrator, y as autoIdAssigner } from "../middleware-BMePykoo.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Follow, Person } from "@fedify/vocab";
11
11
  //#region src/compat/transformers.test.ts
@@ -702,6 +702,15 @@ interface FanoutMessage {
702
702
  readonly activityType: string;
703
703
  readonly collectionSync?: string;
704
704
  readonly orderingKey?: string;
705
+ /**
706
+ * Whether to apply outgoing JSON-LD wire-format normalization to queued
707
+ * activities that already carry Object Integrity Proofs.
708
+ *
709
+ * `true` is used for proofs Fedify created before fanout, or when callers
710
+ * explicitly request normalization for locally pre-signed activities.
711
+ * `false`/`undefined` preserves existing proofs as-is.
712
+ */
713
+ readonly normalizeExistingProofs?: boolean;
705
714
  readonly traceContext: Readonly<Record<string, string>>;
706
715
  }
707
716
  interface OutboxMessage {
@@ -2668,6 +2677,19 @@ interface SendActivityOptions {
2668
2677
  */
2669
2678
  readonly fanout?: "auto" | "skip" | "force";
2670
2679
  /**
2680
+ * Whether to apply Fedify's outgoing JSON-LD wire-format compatibility fixes
2681
+ * to activities that already carry Object Integrity Proofs.
2682
+ *
2683
+ * By default, Fedify preserves existing proofs byte-for-byte because it
2684
+ * cannot know whether they were created for the normalized outgoing wire
2685
+ * form. Set this to `true` when sending an activity that was pre-signed
2686
+ * locally with `signObject()` or `createProof()`, so the emitted
2687
+ * compact JSON-LD matches the bytes covered by the proof.
2688
+ *
2689
+ * @since 2.2.0
2690
+ */
2691
+ readonly normalizeExistingProofs?: boolean;
2692
+ /**
2671
2693
  * The base URIs to exclude from the recipients' inboxes. It is useful
2672
2694
  * for excluding the recipients having the same shared inbox with the sender.
2673
2695
  *
@@ -700,6 +700,15 @@ interface FanoutMessage {
700
700
  readonly activityType: string;
701
701
  readonly collectionSync?: string;
702
702
  readonly orderingKey?: string;
703
+ /**
704
+ * Whether to apply outgoing JSON-LD wire-format normalization to queued
705
+ * activities that already carry Object Integrity Proofs.
706
+ *
707
+ * `true` is used for proofs Fedify created before fanout, or when callers
708
+ * explicitly request normalization for locally pre-signed activities.
709
+ * `false`/`undefined` preserves existing proofs as-is.
710
+ */
711
+ readonly normalizeExistingProofs?: boolean;
703
712
  readonly traceContext: Readonly<Record<string, string>>;
704
713
  }
705
714
  interface OutboxMessage {
@@ -2666,6 +2675,19 @@ interface SendActivityOptions {
2666
2675
  */
2667
2676
  readonly fanout?: "auto" | "skip" | "force";
2668
2677
  /**
2678
+ * Whether to apply Fedify's outgoing JSON-LD wire-format compatibility fixes
2679
+ * to activities that already carry Object Integrity Proofs.
2680
+ *
2681
+ * By default, Fedify preserves existing proofs byte-for-byte because it
2682
+ * cannot know whether they were created for the normalized outgoing wire
2683
+ * form. Set this to `true` when sending an activity that was pre-signed
2684
+ * locally with `signObject()` or `createProof()`, so the emitted
2685
+ * compact JSON-LD matches the bytes covered by the proof.
2686
+ *
2687
+ * @since 2.2.0
2688
+ */
2689
+ readonly normalizeExistingProofs?: boolean;
2690
+ /**
2669
2691
  * The base URIs to exclude from the recipients' inboxes. It is useful
2670
2692
  * for excluding the recipients having the same shared inbox with the sender.
2671
2693
  *
@@ -3,6 +3,6 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  //#region deno.json
5
5
  var name = "@fedify/fedify";
6
- var version = "2.2.0-pr.715.28+06979f5f";
6
+ var version = "2.2.0-pr.731.34+1e1bb860";
7
7
  //#endregion
8
8
  export { version as n, name as t };
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { o as validateCryptoKey } from "./key-CMhIdO1-.mjs";
5
- import { n as doubleKnock } from "./http-9zjtsC0n.mjs";
4
+ import { o as validateCryptoKey } from "./key-cK-YTNKa.mjs";
5
+ import { n as doubleKnock } from "./http-BrVfkREl.mjs";
6
6
  import { curry } from "es-toolkit";
7
7
  import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
8
8
  import { getLogger } from "@logtape/logtape";
@@ -3,10 +3,10 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as RouterError } from "../router-CrMLXoOr.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
- import { a as assertExists } from "../std__assert-Duiq_YC9.mjs";
6
+ import { i as assertExists } from "../std__assert-CRDpx_HF.mjs";
7
7
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
8
- import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
9
- import { n as createFederationBuilder } from "../builder-BfmqkrIE.mjs";
8
+ import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
9
+ import { n as createFederationBuilder } from "../builder-OFkoAQ85.mjs";
10
10
  import { test } from "@fedify/fixture";
11
11
  import { Activity, Note, Person } from "@fedify/vocab";
12
12
  //#region src/federation/builder.test.ts
@@ -2,8 +2,8 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
- import "../std__assert-Duiq_YC9.mjs";
6
- import { n as digest, t as buildCollectionSynchronizationHeader } from "../collection-BD6-SZ6O.mjs";
5
+ import "../std__assert-CRDpx_HF.mjs";
6
+ import { n as digest, t as buildCollectionSynchronizationHeader } from "../collection-D-HqUuA2.mjs";
7
7
  import { test } from "@fedify/fixture";
8
8
  import { decodeHex } from "byte-encodings/hex";
9
9
  //#region src/federation/collection.test.ts
@@ -3,15 +3,15 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-Dk_tacqz.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
- import "../std__assert-Duiq_YC9.mjs";
6
+ import "../std__assert-CRDpx_HF.mjs";
7
7
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
8
- import { t as assert } from "../assert-ddO5KLpe.mjs";
9
- import { r as parseAcceptSignature } from "../accept-Dd__NiUL.mjs";
10
- import { s as signRequest } from "../http-9zjtsC0n.mjs";
11
- import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-BAK-tUlf.mjs";
12
- import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
13
- import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-DBgitte6.mjs";
14
- import { t as ActivityListenerSet } from "../activity-listener-Ck3JZ_hR.mjs";
8
+ import { t as assert } from "../assert-DikXweDx.mjs";
9
+ import { r as parseAcceptSignature } from "../accept-CPkZzmGN.mjs";
10
+ import { s as signRequest } from "../http-BrVfkREl.mjs";
11
+ import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-DGu1NFwu.mjs";
12
+ import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
13
+ import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-BMePykoo.mjs";
14
+ import { t as ActivityListenerSet } from "../activity-listener-ell7W1s9.mjs";
15
15
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
16
16
  import { Activity, Create, Note, Person, Tombstone } from "@fedify/vocab";
17
17
  import { FetchError } from "@fedify/vocab-runtime";
@@ -2,11 +2,11 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
- import "../std__assert-Duiq_YC9.mjs";
6
- import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
7
- import { r as signObject } from "../proof-Ds3T1-sE.mjs";
8
- import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
9
- import { o as createFederation } from "../middleware-DBgitte6.mjs";
5
+ import "../std__assert-CRDpx_HF.mjs";
6
+ import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-DGu1NFwu.mjs";
7
+ import { r as signObject } from "../proof-DC69vtxY.mjs";
8
+ import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
9
+ import { o as createFederation } from "../middleware-BMePykoo.mjs";
10
10
  import { mockDocumentLoader, test } from "@fedify/fixture";
11
11
  import { Create, Follow, Person } from "@fedify/vocab";
12
12
  //#region src/federation/idempotency.test.ts
@@ -3,7 +3,7 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
6
- import { t as ActivityListenerSet } from "../activity-listener-Ck3JZ_hR.mjs";
6
+ import { t as ActivityListenerSet } from "../activity-listener-ell7W1s9.mjs";
7
7
  import { test } from "@fedify/fixture";
8
8
  import { Activity, Create, Invite, Offer, Update } from "@fedify/vocab";
9
9
  //#region src/federation/inbox.test.ts
@@ -3,9 +3,9 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
6
- import { t as assert } from "../assert-ddO5KLpe.mjs";
7
- import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
8
- import { t as KvKeyCache } from "../keycache-CCSwkQcY.mjs";
6
+ import { t as assert } from "../assert-DikXweDx.mjs";
7
+ import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
8
+ import { t as KvKeyCache } from "../keycache-EGATflN-.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { CryptographicKey, Multikey } from "@fedify/vocab";
11
11
  //#region src/federation/keycache.test.ts