@fedify/fedify 2.5.0-dev.2609 → 2.5.0-dev.2614
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.
- package/dist/{builder-BLCvt0zp.mjs → builder-B2H8GsyI.mjs} +82 -3
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-DNhLvscb.mjs → compound-proof-BavXW-ow.mjs} +2 -2
- package/dist/{context-ByflxsY9.d.cts → context-C1NqcIct.d.cts} +1061 -443
- package/dist/{context-uQUY5fQT.d.ts → context-xrqq4Hrq.d.ts} +1064 -446
- package/dist/{deno-DYkYmKp0.mjs → deno-CRXki9-m.mjs} +1 -1
- package/dist/{docloader-B9RO5nh9.mjs → docloader-Ca6i6ddp.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +3 -3
- package/dist/federation/handler-compound-proof.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +4 -4
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox-actorless.test.mjs +5 -5
- package/dist/federation/inbox-report.test.mjs +220 -5
- package/dist/federation/inbox-view.test.mjs +1 -1
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +7 -7
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +4 -4
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +1 -1
- package/dist/federation/outbound-body.test.d.mts +2 -0
- package/dist/federation/outbound-body.test.mjs +149 -0
- package/dist/federation/outbound-capture.test.d.mts +2 -0
- package/dist/federation/outbound-capture.test.mjs +394 -0
- package/dist/federation/outbound-dispatch.test.d.mts +2 -0
- package/dist/federation/outbound-dispatch.test.mjs +368 -0
- package/dist/federation/outbound-document.test.d.mts +2 -0
- package/dist/federation/outbound-document.test.mjs +171 -0
- package/dist/federation/outbound-provenance.test.d.mts +2 -0
- package/dist/federation/outbound-provenance.test.mjs +543 -0
- package/dist/federation/outbound-report.test.d.mts +2 -0
- package/dist/federation/outbound-report.test.mjs +5475 -0
- package/dist/federation/outbound-workers.test.d.mts +2 -0
- package/dist/federation/outbound-workers.test.mjs +64 -0
- package/dist/federation/outbound-workflow.test.d.mts +2 -0
- package/dist/federation/outbound-workflow.test.mjs +154 -0
- package/dist/federation/outgoing-proof.test.mjs +4 -4
- package/dist/federation/portable-actor.test.mjs +3 -3
- package/dist/federation/portable-chain.test.mjs +3 -3
- package/dist/federation/portable-collection.test.mjs +4 -4
- package/dist/federation/portable-delivery.test.mjs +106 -7
- package/dist/federation/portable-gateway-keys.test.mjs +5 -5
- package/dist/federation/portable-inbox.test.mjs +430 -5
- package/dist/federation/portable-parse-uri.test.mjs +1 -1
- package/dist/federation/portable-route-activity.test.mjs +2 -2
- package/dist/federation/portable.test.mjs +3 -3
- package/dist/federation/send.test.mjs +44 -3
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{federation-DzMdu4zU.cjs → federation-DRl81KbJ.cjs} +2358 -405
- package/dist/{http-EoFzzh1s.js → http-C2H4DT6t.js} +62 -32
- package/dist/{http-BdTJujR0.cjs → http-DHSIMipE.cjs} +62 -32
- package/dist/{http-TgeyuTDM.mjs → http-DqOXkYbX.mjs} +64 -34
- package/dist/{inbox-ChkbRxCN.mjs → inbox-C4iW1g0F.mjs} +90 -3
- package/dist/{inbox-view-BosBZdlt.mjs → inbox-view-Bu_aSwFJ.mjs} +1 -1
- package/dist/{key-DbLNT-vA.d.ts → key-dwwAg5bA.d.ts} +1 -1
- package/dist/{kv-cache-CMgOYiLU.js → kv-cache-B-x_OrBo.js} +1 -1
- package/dist/{kv-cache-B0o18_Ni.cjs → kv-cache-CHR_BVyM.cjs} +1 -1
- package/dist/{kv-cache-CTA2i08a.mjs → kv-cache-DJsxu9nI.mjs} +1 -1
- package/dist/{ld-CZsMAfOw.mjs → ld-C5JOBBFj.mjs} +2 -2
- package/dist/{ld-Inagww4R.d.ts → ld-o4LnhCYY.d.ts} +2 -2
- package/dist/{metrics-Kc9mP1LD.mjs → metrics-CTteRnEr.mjs} +1 -1
- package/dist/{metrics-DoRtI0qU.d.ts → metrics-DVKYY6K1.d.ts} +1 -1
- package/dist/{middleware-B-pjIvQ8.js → middleware-B-tMVSUA.js} +2303 -351
- package/dist/{middleware-CIRoIVSU.mjs → middleware-BkRqMGPU.mjs} +1573 -325
- package/dist/{middleware-C1coPz8l.mjs → middleware-CPigKLnt.mjs} +1 -1
- package/dist/{mod-B7j30IQf.d.ts → mod-CQC9xlLZ.d.ts} +3 -3
- package/dist/{mod-B4kBw1-8.d.ts → mod-D22ZsiKi.d.ts} +2 -2
- package/dist/{mod-BN0TTfyQ.d.ts → mod-DO0epJGH.d.ts} +1 -1
- package/dist/{mod-DF2gbN_d.d.cts → mod-TwiQgEf3.d.cts} +1 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +4 -4
- package/dist/mod.d.ts +8 -8
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/outbound-body-CckPA5gc.mjs +83 -0
- package/dist/outbound-capture-DVgnRzlf.mjs +184 -0
- package/dist/outbound-document-BMgwLXTY.mjs +179 -0
- package/dist/{owner-Dwz4fGUW.mjs → owner-CKlRIkeF.mjs} +3 -3
- package/dist/{portable-collection-Oxfavx8f.mjs → portable-collection-B7RBHVGl.mjs} +2 -2
- package/dist/{portable-collection-BikiijSk.js → portable-collection-CByyPjLc.js} +2 -2
- package/dist/{portable-collection-Ci6vIhbR.cjs → portable-collection-CmLjeKsP.cjs} +2 -2
- package/dist/{portable-key--fWpTNDN.mjs → portable-key-Clg0IH8C.mjs} +1 -1
- package/dist/{proof-D6Kb0Qq9.mjs → proof-tAb5_Dq4.mjs} +3 -3
- package/dist/{send-BFti0L3I.mjs → send-DzfFREML.mjs} +153 -40
- package/dist/sig/compound-proof-discovery.test.mjs +1 -1
- package/dist/sig/compound-proof-producer.test.mjs +1 -1
- package/dist/sig/compound-proof-verification.test.mjs +2 -2
- package/dist/sig/compound-proof.test.mjs +2 -2
- package/dist/sig/http.test.mjs +38 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +1 -1
- package/dist/sig/mitra-receive-interop.test.mjs +4 -4
- package/dist/sig/mitra-send-interop.test.mjs +3 -3
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.ts +3 -3
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +1 -1
- package/dist/sig/portable-interop.test.mjs +3 -3
- package/dist/sig/portable-key.test.mjs +5 -5
- package/dist/sig/proof.test.mjs +3 -3
- package/dist/sig/tootik-request-interop.test.mjs +4 -4
- package/dist/sig/tootik-send-interop.test.mjs +3 -3
- package/dist/sig/verification.test.mjs +3 -3
- package/dist/{temporal-DlYL_raW.mjs → temporal-D9Nt8Fm4.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +1030 -412
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +7 -7
- package/skills/fedify/SKILL.md +60 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { t as projectSenderPayload } from "../outbound-document-BMgwLXTY.mjs";
|
|
5
|
+
import { t as captureResponseBody } from "../outbound-capture-DVgnRzlf.mjs";
|
|
6
|
+
import { test } from "@fedify/fixture";
|
|
7
|
+
import { deepStrictEqual, ok, rejects, strictEqual } from "node:assert/strict";
|
|
8
|
+
//#region src/federation/outbound-workers.test.ts
|
|
9
|
+
test("outbound sender payload privacy on Workers", () => {
|
|
10
|
+
const secret = "https://private.example/recipient";
|
|
11
|
+
const input = {
|
|
12
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
13
|
+
type: "Create",
|
|
14
|
+
bcc: [secret],
|
|
15
|
+
object: {
|
|
16
|
+
"as:bto": [secret],
|
|
17
|
+
content: "visible"
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const result = projectSenderPayload(input);
|
|
21
|
+
ok(result.type === "available");
|
|
22
|
+
strictEqual(result.redacted, true);
|
|
23
|
+
strictEqual(JSON.stringify(result.value).includes(secret), false);
|
|
24
|
+
ok(Object.isFrozen(result.value));
|
|
25
|
+
strictEqual(input.bcc[0], secret);
|
|
26
|
+
deepStrictEqual(projectSenderPayload({
|
|
27
|
+
...input,
|
|
28
|
+
"@context": [input["@context"], { private: "as:bcc" }]
|
|
29
|
+
}), {
|
|
30
|
+
stage: "sender-payload",
|
|
31
|
+
type: "unavailable",
|
|
32
|
+
reason: "privacy-filter-unavailable"
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
test("outbound body capture revocation on Workers", async () => {
|
|
36
|
+
let cancelled = 0;
|
|
37
|
+
let reader;
|
|
38
|
+
let signal;
|
|
39
|
+
const source = new ReadableStream({
|
|
40
|
+
pull() {},
|
|
41
|
+
cancel() {
|
|
42
|
+
cancelled++;
|
|
43
|
+
return new Promise(() => {});
|
|
44
|
+
}
|
|
45
|
+
}, { highWaterMark: 0 });
|
|
46
|
+
deepStrictEqual(await captureResponseBody(new Response(source), {
|
|
47
|
+
maxBytes: 16,
|
|
48
|
+
timeoutMs: 10
|
|
49
|
+
}, async (body, current) => {
|
|
50
|
+
signal = current;
|
|
51
|
+
reader = body.getReader();
|
|
52
|
+
await rejects(reader.read());
|
|
53
|
+
}), {
|
|
54
|
+
type: "deadline",
|
|
55
|
+
bytesExposed: 0
|
|
56
|
+
});
|
|
57
|
+
ok(signal.aborted);
|
|
58
|
+
strictEqual(cancelled, 1);
|
|
59
|
+
strictEqual(source.locked, false);
|
|
60
|
+
await rejects(reader.read());
|
|
61
|
+
reader.releaseLock();
|
|
62
|
+
});
|
|
63
|
+
//#endregion
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
5
|
+
import { i as rsaPrivateKey2, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
6
|
+
import { o as createFederation } from "../middleware-BkRqMGPU.mjs";
|
|
7
|
+
import { Create, Person } from "@fedify/vocab";
|
|
8
|
+
import { test } from "@fedify/fixture";
|
|
9
|
+
import { deepStrictEqual, ok, strictEqual } from "node:assert/strict";
|
|
10
|
+
import { once } from "node:events";
|
|
11
|
+
import { createServer } from "node:http";
|
|
12
|
+
//#region src/federation/outbound-workflow.test.ts
|
|
13
|
+
const { Deno: deno } = globalThis;
|
|
14
|
+
test({
|
|
15
|
+
name: "outbound recorder archives real HTTP exchanges across a queued retry",
|
|
16
|
+
ignore: deno != null && (await deno.permissions.query({ name: "net" })).state !== "granted",
|
|
17
|
+
async fn() {
|
|
18
|
+
let accepting = false;
|
|
19
|
+
const received = [];
|
|
20
|
+
const receiverErrors = [];
|
|
21
|
+
const server = createServer((request, response) => {
|
|
22
|
+
(async () => {
|
|
23
|
+
let body = "";
|
|
24
|
+
for await (const chunk of request) body += chunk.toString();
|
|
25
|
+
const status = accepting ? 202 : 503;
|
|
26
|
+
received.push({
|
|
27
|
+
status,
|
|
28
|
+
activity: JSON.parse(body)
|
|
29
|
+
});
|
|
30
|
+
response.writeHead(status, {
|
|
31
|
+
"Content-Type": "text/plain",
|
|
32
|
+
"Connection": "close"
|
|
33
|
+
});
|
|
34
|
+
response.end(accepting ? "accepted" : "retry later");
|
|
35
|
+
})().catch((error) => {
|
|
36
|
+
receiverErrors.push(error);
|
|
37
|
+
response.writeHead(500);
|
|
38
|
+
response.end();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
server.listen(0, "127.0.0.1");
|
|
42
|
+
await once(server, "listening");
|
|
43
|
+
const inbox = new URL(`http://127.0.0.1:${server.address().port}/inbox`);
|
|
44
|
+
const queued = [];
|
|
45
|
+
const attempts = [];
|
|
46
|
+
const exchanges = [];
|
|
47
|
+
const workers = [];
|
|
48
|
+
const archive = /* @__PURE__ */ new Map();
|
|
49
|
+
const federation = createFederation({
|
|
50
|
+
kv: new MemoryKvStore(),
|
|
51
|
+
allowPrivateAddress: true,
|
|
52
|
+
manuallyStartQueue: true,
|
|
53
|
+
circuitBreaker: false,
|
|
54
|
+
outboxRetryPolicy: () => Temporal.Duration.from({ milliseconds: 0 }),
|
|
55
|
+
queue: { outbox: {
|
|
56
|
+
enqueue(message) {
|
|
57
|
+
queued.push(message);
|
|
58
|
+
return Promise.resolve();
|
|
59
|
+
},
|
|
60
|
+
listen: () => Promise.resolve()
|
|
61
|
+
} }
|
|
62
|
+
});
|
|
63
|
+
federation.setOutboundObservers({
|
|
64
|
+
senderPayload: true,
|
|
65
|
+
responseBodyCapture: {
|
|
66
|
+
maxBytes: 4096,
|
|
67
|
+
timeoutMs: 1e3,
|
|
68
|
+
async onBody(_ctx, event) {
|
|
69
|
+
const key = `${event.attemptId}:${event.exchangeIndex}`;
|
|
70
|
+
const chunks = [];
|
|
71
|
+
archive.set(key, chunks);
|
|
72
|
+
const reader = event.body.getReader();
|
|
73
|
+
try {
|
|
74
|
+
while (true) {
|
|
75
|
+
const chunk = await reader.read();
|
|
76
|
+
if (chunk.done) break;
|
|
77
|
+
chunks.push(chunk.value.slice());
|
|
78
|
+
}
|
|
79
|
+
} finally {
|
|
80
|
+
reader.releaseLock();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
onAttempt(_ctx, report) {
|
|
85
|
+
attempts.push(report);
|
|
86
|
+
},
|
|
87
|
+
onHttpExchange(_ctx, report) {
|
|
88
|
+
exchanges.push(report);
|
|
89
|
+
},
|
|
90
|
+
onWorker(_ctx, report) {
|
|
91
|
+
workers.push(report);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
try {
|
|
95
|
+
await federation.createContext(new URL("https://example.com"), void 0).sendActivity({
|
|
96
|
+
keyId: rsaPublicKey2.id,
|
|
97
|
+
privateKey: rsaPrivateKey2
|
|
98
|
+
}, new Person({
|
|
99
|
+
id: new URL(inbox.origin + "/actor"),
|
|
100
|
+
inbox
|
|
101
|
+
}), new Create({
|
|
102
|
+
id: new URL("https://example.com/workflow/activity"),
|
|
103
|
+
actor: new URL("https://example.com/person2")
|
|
104
|
+
}), {
|
|
105
|
+
fanout: "skip",
|
|
106
|
+
dispatchId: "saved-workflow-dispatch"
|
|
107
|
+
});
|
|
108
|
+
strictEqual(received.length, 0);
|
|
109
|
+
strictEqual(queued.length, 1);
|
|
110
|
+
await federation.processQueuedTask(void 0, queued.shift());
|
|
111
|
+
strictEqual(attempts.length, 1);
|
|
112
|
+
strictEqual(attempts[0].outcome.type, "failed");
|
|
113
|
+
ok(attempts[0].outcome.response.type === "final");
|
|
114
|
+
strictEqual(attempts[0].outcome.response.metadata.status, 503);
|
|
115
|
+
strictEqual(queued.length, 1);
|
|
116
|
+
const retry = workers[0].actions.find((action) => action.type === "scheduled" && action.action === "retry");
|
|
117
|
+
ok(retry?.type === "scheduled");
|
|
118
|
+
strictEqual(retry.queue.queueAttempt, 1);
|
|
119
|
+
accepting = true;
|
|
120
|
+
await federation.processQueuedTask(void 0, queued.shift());
|
|
121
|
+
strictEqual(queued.length, 0);
|
|
122
|
+
strictEqual(attempts.length, 2);
|
|
123
|
+
strictEqual(attempts[1].outcome.type, "succeeded");
|
|
124
|
+
strictEqual(attempts[0].deliveryId, attempts[1].deliveryId);
|
|
125
|
+
ok(attempts[0].attemptId !== attempts[1].attemptId);
|
|
126
|
+
deepStrictEqual(attempts[0].broadcast, attempts[1].broadcast);
|
|
127
|
+
strictEqual(attempts[1].queue?.queueAttempt, 1);
|
|
128
|
+
for (const report of attempts) {
|
|
129
|
+
strictEqual(report.provenance.dispatchId, "saved-workflow-dispatch");
|
|
130
|
+
strictEqual(report.senderPayload.type, "available");
|
|
131
|
+
strictEqual(report.httpExchangeCount, exchanges.filter((exchange) => exchange.attemptId === report.attemptId).length);
|
|
132
|
+
}
|
|
133
|
+
strictEqual(received.length, exchanges.length);
|
|
134
|
+
strictEqual(archive.size, exchanges.length);
|
|
135
|
+
for (const [index, exchange] of exchanges.entries()) {
|
|
136
|
+
deepStrictEqual(exchange.bodyCapture, {
|
|
137
|
+
type: "complete",
|
|
138
|
+
bytesExposed: received[index].status === 503 ? 11 : 8
|
|
139
|
+
});
|
|
140
|
+
const chunks = archive.get(`${exchange.attemptId}:${exchange.exchangeIndex}`);
|
|
141
|
+
const decoder = new TextDecoder();
|
|
142
|
+
strictEqual(chunks.map((chunk) => decoder.decode(chunk)).join(""), received[index].status === 503 ? "retry later" : "accepted");
|
|
143
|
+
}
|
|
144
|
+
deepStrictEqual(workers.map((worker) => worker.attemptsStarted), [1, 1]);
|
|
145
|
+
deepStrictEqual(receiverErrors, []);
|
|
146
|
+
} finally {
|
|
147
|
+
const closed = new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
|
148
|
+
server.closeAllConnections();
|
|
149
|
+
await closed;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
//#endregion
|
|
154
|
+
export {};
|
|
@@ -7,11 +7,11 @@ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
|
7
7
|
import "../std__assert-DvMDRPes.mjs";
|
|
8
8
|
import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
|
|
9
9
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
10
|
-
import { s as detachSignature, x as exportJwk } from "../ld-
|
|
11
|
-
import { s as signObject } from "../proof-
|
|
10
|
+
import { s as detachSignature, x as exportJwk } from "../ld-C5JOBBFj.mjs";
|
|
11
|
+
import { s as signObject } from "../proof-tAb5_Dq4.mjs";
|
|
12
12
|
import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
13
|
-
import { C as getEmbeddedFirstGateway, S as getCompactRootId, c as assertPortableActorActivity, n as FederationImpl, o as createFederation, w as warnCompatibleIdsInJson } from "../middleware-
|
|
14
|
-
import { o as verifyCompoundPortableObjectProofs } from "../compound-proof-
|
|
13
|
+
import { C as getEmbeddedFirstGateway, S as getCompactRootId, c as assertPortableActorActivity, n as FederationImpl, o as createFederation, w as warnCompatibleIdsInJson } from "../middleware-BkRqMGPU.mjs";
|
|
14
|
+
import { o as verifyCompoundPortableObjectProofs } from "../compound-proof-BavXW-ow.mjs";
|
|
15
15
|
import { configure, reset } from "@logtape/logtape";
|
|
16
16
|
import { Accept, Announce, Create, Follow, Note, Person } from "@fedify/vocab";
|
|
17
17
|
import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
|
|
@@ -8,10 +8,10 @@ import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
|
|
|
8
8
|
import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
|
|
9
9
|
import "../std__assert-DvMDRPes.mjs";
|
|
10
10
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
11
|
-
import { d as verifyPortableObjectProof, s as signObject } from "../proof-
|
|
12
|
-
import { p as verifyRequest, u as signRequest } from "../http-
|
|
11
|
+
import { d as verifyPortableObjectProof, s as signObject } from "../proof-tAb5_Dq4.mjs";
|
|
12
|
+
import { p as verifyRequest, u as signRequest } from "../http-DqOXkYbX.mjs";
|
|
13
13
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
14
|
-
import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-
|
|
14
|
+
import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-BkRqMGPU.mjs";
|
|
15
15
|
import { n as RouterError } from "../router-u5O5KCQr.mjs";
|
|
16
16
|
import { configure, reset } from "@logtape/logtape";
|
|
17
17
|
import { Note, Person, Tombstone, lookupObject } from "@fedify/vocab";
|
|
@@ -7,10 +7,10 @@ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
|
7
7
|
import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
|
|
8
8
|
import "../std__assert-DvMDRPes.mjs";
|
|
9
9
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
10
|
-
import { x as exportJwk } from "../ld-
|
|
11
|
-
import { d as verifyPortableObjectProof, l as verifyObject } from "../proof-
|
|
10
|
+
import { x as exportJwk } from "../ld-C5JOBBFj.mjs";
|
|
11
|
+
import { d as verifyPortableObjectProof, l as verifyObject } from "../proof-tAb5_Dq4.mjs";
|
|
12
12
|
import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
13
|
-
import { n as FederationImpl } from "../middleware-
|
|
13
|
+
import { n as FederationImpl } from "../middleware-BkRqMGPU.mjs";
|
|
14
14
|
import { Create, DataIntegrityProof, Note, OrderedCollection, Person, traverseCollection } from "@fedify/vocab";
|
|
15
15
|
import { FetchError, encodeMultibase, exportDidKey } from "@fedify/vocab-runtime";
|
|
16
16
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
@@ -7,11 +7,11 @@ import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
|
|
|
7
7
|
import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
|
|
8
8
|
import "../std__assert-DvMDRPes.mjs";
|
|
9
9
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
10
|
-
import { d as verifyPortableObjectProof, s as signObject } from "../proof-
|
|
10
|
+
import { d as verifyPortableObjectProof, s as signObject } from "../proof-tAb5_Dq4.mjs";
|
|
11
11
|
import { n as ed25519PrivateKey, r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
|
|
12
|
-
import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-
|
|
13
|
-
import { t as verifyPortableObject } from "../portable-collection-
|
|
14
|
-
import { c as verifyServedPortableObjects } from "../compound-proof-
|
|
12
|
+
import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-BkRqMGPU.mjs";
|
|
13
|
+
import { t as verifyPortableObject } from "../portable-collection-B7RBHVGl.mjs";
|
|
14
|
+
import { c as verifyServedPortableObjects } from "../compound-proof-BavXW-ow.mjs";
|
|
15
15
|
import { n as RouterError } from "../router-u5O5KCQr.mjs";
|
|
16
16
|
import { configure, reset } from "@logtape/logtape";
|
|
17
17
|
import { Create, Note, Object as Object$1, OrderedCollection, PUBLIC_COLLECTION, Person, Tombstone, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
@@ -4,12 +4,12 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
|
|
5
5
|
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
6
6
|
import { i as rsaPrivateKey2, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
7
|
-
import { n as FederationImpl, r as InboxContextImpl } from "../middleware-
|
|
8
|
-
import { n as extractInboxes, t as SendActivityError } from "../send-
|
|
7
|
+
import { n as FederationImpl, r as InboxContextImpl, t as ContextImpl } from "../middleware-BkRqMGPU.mjs";
|
|
8
|
+
import { n as extractInboxes, t as SendActivityError } from "../send-DzfFREML.mjs";
|
|
9
9
|
import { Create, Endpoints, Note, Person } from "@fedify/vocab";
|
|
10
10
|
import { parseIri } from "@fedify/vocab-runtime";
|
|
11
11
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
12
|
-
import { deepStrictEqual, ok, rejects, strictEqual } from "node:assert/strict";
|
|
12
|
+
import { deepStrictEqual, notStrictEqual, ok, rejects, strictEqual } from "node:assert/strict";
|
|
13
13
|
//#region src/federation/portable-delivery.test.ts
|
|
14
14
|
const DID = "did:key:z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2";
|
|
15
15
|
const OTHER_DID = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
|
|
@@ -344,6 +344,77 @@ test("Context.sendActivity() to a portable inbox", async (t) => {
|
|
|
344
344
|
});
|
|
345
345
|
esm_default.hardReset();
|
|
346
346
|
});
|
|
347
|
+
test("portable fallback attempts retain one delivery across concrete gateway sends", async (t) => {
|
|
348
|
+
for (const operation of [
|
|
349
|
+
"send",
|
|
350
|
+
"inbox-forward",
|
|
351
|
+
"outbox-forward"
|
|
352
|
+
]) for (const accepted of [true, false]) await t.step(`${operation}: ${accepted ? "accepted fallback" : "exhausted gateways"}`, async () => {
|
|
353
|
+
const raw = {
|
|
354
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
355
|
+
type: "Create",
|
|
356
|
+
id: "https://example.com/activities/forwarded",
|
|
357
|
+
actor: "https://example.com/person2",
|
|
358
|
+
extra: "preserve received payload"
|
|
359
|
+
};
|
|
360
|
+
const reports = [];
|
|
361
|
+
const deliveries = [];
|
|
362
|
+
const submitted = [];
|
|
363
|
+
esm_default.mockGlobal().post("begin:https://gw1.example/", (call) => {
|
|
364
|
+
submitted.push(JSON.parse(String(call.options.body)));
|
|
365
|
+
return 503;
|
|
366
|
+
}).post("begin:https://gw2.example/", (call) => {
|
|
367
|
+
submitted.push(JSON.parse(String(call.options.body)));
|
|
368
|
+
return accepted ? 202 : 502;
|
|
369
|
+
});
|
|
370
|
+
try {
|
|
371
|
+
const federation = new FederationImpl({
|
|
372
|
+
kv: new MemoryKvStore(),
|
|
373
|
+
allowPrivateAddress: true
|
|
374
|
+
});
|
|
375
|
+
federation.setOutboundObservers({
|
|
376
|
+
onDelivery(_ctx, event) {
|
|
377
|
+
deliveries.push(event);
|
|
378
|
+
},
|
|
379
|
+
onAttempt(_ctx, report) {
|
|
380
|
+
reports.push(report);
|
|
381
|
+
throw new Error("attempt observer cannot stop gateway fallback");
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
const ctx = federation.createContext(new URL("https://example.com/"));
|
|
385
|
+
ok(ctx instanceof ContextImpl);
|
|
386
|
+
const target = portableActor({ gateways: ["https://gw1.example", "https://gw2.example"] });
|
|
387
|
+
const pending = operation === "send" ? ctx.sendActivity(sender, target, createActivity(), { immediate: true }) : (operation === "inbox-forward" ? ctx.toInboxContext(null, raw, raw.id, Create.typeId.href) : ctx.toOutboxContext("alice", raw, raw.id, Create.typeId.href)).forwardActivity(sender, target, {
|
|
388
|
+
immediate: true,
|
|
389
|
+
skipIfUnsigned: false
|
|
390
|
+
});
|
|
391
|
+
if (accepted) await pending;
|
|
392
|
+
else await rejects(pending, (error) => error instanceof SendActivityError && error.statusCode === 502);
|
|
393
|
+
strictEqual(reports.length, 2);
|
|
394
|
+
deepStrictEqual(reports.map((report) => report.inboxUrl), [inboxOn("https://gw1.example"), inboxOn("https://gw2.example")]);
|
|
395
|
+
notStrictEqual(reports[0].attemptId, reports[1].attemptId);
|
|
396
|
+
strictEqual(reports[0].deliveryId, reports[1].deliveryId);
|
|
397
|
+
deepStrictEqual(reports[0].broadcast, reports[1].broadcast);
|
|
398
|
+
for (const report of reports) {
|
|
399
|
+
strictEqual(report.destinationInbox, inboxOn("https://gw1.example"));
|
|
400
|
+
strictEqual(report.portableInbox, PORTABLE_INBOX);
|
|
401
|
+
strictEqual(report.deliveryId, deliveries[0].deliveryId);
|
|
402
|
+
}
|
|
403
|
+
strictEqual(reports[0].outcome.type, "failed");
|
|
404
|
+
strictEqual(reports[1].outcome.type, accepted ? "succeeded" : "failed");
|
|
405
|
+
for (const [index, report] of reports.entries()) {
|
|
406
|
+
const response = report.outcome.response;
|
|
407
|
+
ok(response.type === "final");
|
|
408
|
+
strictEqual(response.metadata.status, index === 0 ? 503 : accepted ? 202 : 502);
|
|
409
|
+
strictEqual(report.httpExchangeCount, index === 1 && accepted ? 1 : 2);
|
|
410
|
+
strictEqual(response.exchangeIndex, report.httpExchangeCount - 1);
|
|
411
|
+
}
|
|
412
|
+
if (operation !== "send") for (const payload of submitted) deepStrictEqual(payload, raw);
|
|
413
|
+
} finally {
|
|
414
|
+
esm_default.hardReset();
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
});
|
|
347
418
|
test("InboxContext.forwardActivity() to a portable inbox", async (t) => {
|
|
348
419
|
esm_default.spyGlobal();
|
|
349
420
|
const activity = {
|
|
@@ -374,25 +445,44 @@ test("InboxContext.forwardActivity() to a portable inbox", async (t) => {
|
|
|
374
445
|
requests.push(call.url);
|
|
375
446
|
return 202;
|
|
376
447
|
});
|
|
377
|
-
|
|
448
|
+
const federation = new FederationImpl({
|
|
378
449
|
kv: new MemoryKvStore(),
|
|
379
450
|
allowPrivateAddress: true
|
|
380
|
-
})
|
|
451
|
+
});
|
|
452
|
+
const events = [];
|
|
453
|
+
federation.setOutboundObservers({ onDelivery(_ctx, event) {
|
|
454
|
+
events.push(event);
|
|
455
|
+
} });
|
|
456
|
+
await createInboxContext(federation).forwardActivity(sender, recipient, {
|
|
381
457
|
immediate: true,
|
|
382
458
|
skipIfUnsigned: false
|
|
383
459
|
});
|
|
384
460
|
deepStrictEqual([...new Set(requests)], [inboxOn("https://gw1.example"), inboxOn("https://gw2.example")]);
|
|
461
|
+
deepStrictEqual(events.map((event) => event.stage), ["planned", "handoff"]);
|
|
462
|
+
strictEqual(events[0].deliveryId, events[1].deliveryId);
|
|
463
|
+
for (const event of events) {
|
|
464
|
+
strictEqual(event.destinationInbox, inboxOn("https://gw1.example"));
|
|
465
|
+
strictEqual(event.portableInbox, PORTABLE_INBOX);
|
|
466
|
+
}
|
|
467
|
+
const planned = events[0];
|
|
468
|
+
ok(planned.stage === "planned");
|
|
469
|
+
strictEqual(planned.mode, "portable-fallback");
|
|
385
470
|
});
|
|
386
471
|
await t.step("through the queue", async () => {
|
|
387
472
|
esm_default.hardReset();
|
|
388
473
|
esm_default.spyGlobal();
|
|
389
474
|
const { queue, queued } = createQueue();
|
|
390
|
-
|
|
475
|
+
const federation = new FederationImpl({
|
|
391
476
|
kv: new MemoryKvStore(),
|
|
392
477
|
queue,
|
|
393
478
|
manuallyStartQueue: true,
|
|
394
479
|
allowPrivateAddress: true
|
|
395
|
-
})
|
|
480
|
+
});
|
|
481
|
+
const events = [];
|
|
482
|
+
federation.setOutboundObservers({ onDelivery(_ctx, event) {
|
|
483
|
+
events.push(event);
|
|
484
|
+
} });
|
|
485
|
+
await createInboxContext(federation).forwardActivity(sender, recipient, {
|
|
396
486
|
orderingKey: "key",
|
|
397
487
|
skipIfUnsigned: false
|
|
398
488
|
});
|
|
@@ -402,6 +492,15 @@ test("InboxContext.forwardActivity() to a portable inbox", async (t) => {
|
|
|
402
492
|
strictEqual(message.portableInbox, PORTABLE_INBOX);
|
|
403
493
|
deepStrictEqual(message.gatewayInboxes, [inboxOn("https://gw1.example"), inboxOn("https://gw2.example")]);
|
|
404
494
|
strictEqual(queued[0].options?.orderingKey, `key\n${DID}`);
|
|
495
|
+
deepStrictEqual(events.map((event) => event.stage), ["planned", "handoff"]);
|
|
496
|
+
strictEqual(events[0].deliveryId, events[1].deliveryId);
|
|
497
|
+
for (const event of events) {
|
|
498
|
+
strictEqual(event.destinationInbox, inboxOn("https://gw1.example"));
|
|
499
|
+
strictEqual(event.portableInbox, PORTABLE_INBOX);
|
|
500
|
+
}
|
|
501
|
+
const planned = events[0];
|
|
502
|
+
ok(planned.stage === "planned");
|
|
503
|
+
strictEqual(planned.mode, "portable-fallback");
|
|
405
504
|
});
|
|
406
505
|
esm_default.hardReset();
|
|
407
506
|
});
|
|
@@ -8,12 +8,12 @@ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
|
8
8
|
import "../std__assert-DvMDRPes.mjs";
|
|
9
9
|
import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
|
|
10
10
|
import { r as getVerifiedKeyOwnerEvidence } from "../key-owner-evidence-APB6NpSk.mjs";
|
|
11
|
-
import { m as signJsonLd } from "../ld-
|
|
12
|
-
import { m as verifyRequestDetailed, p as verifyRequest, u as signRequest } from "../http-
|
|
11
|
+
import { m as signJsonLd } from "../ld-C5JOBBFj.mjs";
|
|
12
|
+
import { m as verifyRequestDetailed, p as verifyRequest, u as signRequest } from "../http-DqOXkYbX.mjs";
|
|
13
13
|
import { a as rsaPrivateKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
|
|
14
|
-
import { n as getAuthenticatedDocumentLoader } from "../docloader-
|
|
15
|
-
import { f as handleInbox, n as FederationImpl, o as createFederation } from "../middleware-
|
|
16
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-
|
|
14
|
+
import { n as getAuthenticatedDocumentLoader } from "../docloader-Ca6i6ddp.mjs";
|
|
15
|
+
import { f as handleInbox, n as FederationImpl, o as createFederation } from "../middleware-BkRqMGPU.mjs";
|
|
16
|
+
import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-CKlRIkeF.mjs";
|
|
17
17
|
import { t as ActivityListenerSet } from "../activity-listener-CELFAodc.mjs";
|
|
18
18
|
import { t as KvKeyCache } from "../keycache-N2opu4-3.mjs";
|
|
19
19
|
import { Create, CryptographicKey, Person } from "@fedify/vocab";
|