Skip to content

Commit 26776ee

Browse files
authored
Merge branch 'main' into main
2 parents 8572315 + 53d8840 commit 26776ee

File tree

4 files changed

+164
-2
lines changed

4 files changed

+164
-2
lines changed

CHANGES.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,35 @@ To be released.
5959
[#656]: https://github.com/fedify-dev/fedify/pull/656
6060

6161

62+
Version 2.1.4
63+
-------------
64+
65+
Released on April 7, 2026.
66+
67+
### @fedify/fedify
68+
69+
- Fixed `sendActivity()` not awaiting `fanoutQueue.enqueue()` in the fanout
70+
path, which could cause fanout messages to be silently dropped on runtimes
71+
like Cloudflare Workers that may terminate an isolate as soon as the
72+
response is sent. [[#661]]
73+
74+
[#661]: https://github.com/fedify-dev/fedify/issues/661
75+
76+
### @fedify/cfworkers
77+
78+
- Fixed a TypeScript type mismatch that occurred when passing
79+
`wrangler types`-generated binding types (e.g. `KVNamespace`, `Queue`)
80+
to `WorkersKvStore` and `WorkersMessageQueue` constructors. The package
81+
previously imported these types from
82+
`@cloudflare/workers-types/experimental`, which includes extra members
83+
(such as `KVNamespace.deleteBulk()`) absent from types generated by
84+
`wrangler types`, causing TypeScript assignment errors at the call site.
85+
The import now uses the stable `@cloudflare/workers-types` entrypoint,
86+
whose definitions match what `wrangler types` generates. [[#662]]
87+
88+
[#662]: https://github.com/fedify-dev/fedify/issues/662
89+
90+
6291
Version 2.1.3
6392
-------------
6493

@@ -320,6 +349,31 @@ Released on March 24, 2026.
320349
[#599]: https://github.com/fedify-dev/fedify/pull/599
321350

322351

352+
Version 2.0.11
353+
--------------
354+
355+
Released on April 7, 2026.
356+
357+
### @fedify/fedify
358+
359+
- Fixed `sendActivity()` not awaiting `fanoutQueue.enqueue()` in the fanout
360+
path, which could cause fanout messages to be silently dropped on runtimes
361+
like Cloudflare Workers that may terminate an isolate as soon as the
362+
response is sent. [[#661]]
363+
364+
### @fedify/cfworkers
365+
366+
- Fixed a TypeScript type mismatch that occurred when passing
367+
`wrangler types`-generated binding types (e.g. `KVNamespace`, `Queue`)
368+
to `WorkersKvStore` and `WorkersMessageQueue` constructors. The package
369+
previously imported these types from
370+
`@cloudflare/workers-types/experimental`, which includes extra members
371+
(such as `KVNamespace.deleteBulk()`) absent from types generated by
372+
`wrangler types`, causing TypeScript assignment errors at the call site.
373+
The import now uses the stable `@cloudflare/workers-types` entrypoint,
374+
whose definitions match what `wrangler types` generates. [[#662]]
375+
376+
323377
Version 2.0.10
324378
--------------
325379

@@ -1097,6 +1151,19 @@ Released on February 22, 2026.
10971151
[#351]: https://github.com/fedify-dev/fedify/issues/351
10981152

10991153

1154+
Version 1.10.7
1155+
--------------
1156+
1157+
Released on April 7, 2026.
1158+
1159+
### @fedify/fedify
1160+
1161+
- Fixed `sendActivity()` not awaiting `fanoutQueue.enqueue()` in the fanout
1162+
path, which could cause fanout messages to be silently dropped on runtimes
1163+
like Cloudflare Workers that may terminate an isolate as soon as the
1164+
response is sent. [[#661]]
1165+
1166+
11001167
Version 1.10.6
11011168
--------------
11021169

@@ -1284,6 +1351,19 @@ Released on December 24, 2025.
12841351
- Implemented `list()` method in `WorkersKvStore`. [[#498], [#500]]
12851352

12861353

1354+
Version 1.9.8
1355+
-------------
1356+
1357+
Released on April 7, 2026.
1358+
1359+
### @fedify/fedify
1360+
1361+
- Fixed `sendActivity()` not awaiting `fanoutQueue.enqueue()` in the fanout
1362+
path, which could cause fanout messages to be silently dropped on runtimes
1363+
like Cloudflare Workers that may terminate an isolate as soon as the
1364+
response is sent. [[#661]]
1365+
1366+
12871367
Version 1.9.7
12881368
-------------
12891369

packages/cfworkers/src/mod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type {
1212
KVNamespace,
1313
MessageSendRequest,
1414
Queue,
15-
} from "@cloudflare/workers-types/experimental";
15+
} from "@cloudflare/workers-types";
1616
import type {
1717
KvKey,
1818
KvStore,

packages/fedify/src/federation/middleware.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3001,6 +3001,88 @@ test("ContextImpl.sendActivity()", async (t) => {
30013001
]);
30023002
});
30033003

3004+
await t.step(
3005+
"fanout: fanoutQueue.enqueue() is awaited before sendActivity() returns",
3006+
async () => {
3007+
// Regression test for <https://github.com/fedify-dev/fedify/issues/661>.
3008+
// The fanout branch of sendActivityInternal() must await
3009+
// fanoutQueue.enqueue() so that the message is guaranteed to be
3010+
// enqueued before sendActivity() returns. On runtimes like Cloudflare
3011+
// Workers that may terminate an isolate as soon as the response is sent,
3012+
// a floating (non-awaited) enqueue() promise can be silently dropped,
3013+
// causing fanout messages to be lost.
3014+
//
3015+
// This test uses a queue whose enqueue() resolves only after a
3016+
// macro-task delay (setTimeout 0). If enqueue() is not awaited,
3017+
// sendActivity() will return before the message is recorded, and the
3018+
// assertion below will fail.
3019+
const asyncEnqueued: Message[] = [];
3020+
const asyncQueue: MessageQueue = {
3021+
enqueue(message: Message): Promise<void> {
3022+
return new Promise<void>((resolve) => {
3023+
setTimeout(() => {
3024+
asyncEnqueued.push(message);
3025+
resolve();
3026+
}, 0);
3027+
});
3028+
},
3029+
async listen(): Promise<void> {},
3030+
};
3031+
const fed = new FederationImpl<void>({
3032+
kv,
3033+
contextLoaderFactory: () => mockDocumentLoader,
3034+
queue: asyncQueue,
3035+
manuallyStartQueue: true,
3036+
});
3037+
fed
3038+
.setActorDispatcher("/{identifier}", async (ctx, identifier) => {
3039+
if (identifier !== "john") return null;
3040+
const keys = await ctx.getActorKeyPairs(identifier);
3041+
return new vocab.Person({
3042+
id: ctx.getActorUri(identifier),
3043+
preferredUsername: "john",
3044+
publicKey: keys[0].cryptographicKey,
3045+
assertionMethods: keys.map((k) => k.multikey),
3046+
});
3047+
})
3048+
.setKeyPairsDispatcher((_ctx, identifier) => {
3049+
if (identifier !== "john") return [];
3050+
return [
3051+
{ privateKey: rsaPrivateKey2, publicKey: rsaPublicKey2.publicKey! },
3052+
{
3053+
privateKey: ed25519PrivateKey,
3054+
publicKey: ed25519PublicKey.publicKey!,
3055+
},
3056+
];
3057+
});
3058+
const ctx3 = new ContextImpl({
3059+
data: undefined,
3060+
federation: fed,
3061+
url: new URL("https://example.com/"),
3062+
documentLoader: mockDocumentLoader,
3063+
contextLoader: mockDocumentLoader,
3064+
});
3065+
const activity = new vocab.Create({
3066+
id: new URL("https://example.com/activity/1"),
3067+
actor: new URL("https://example.com/person"),
3068+
});
3069+
await ctx3.sendActivity(
3070+
{ username: "john" },
3071+
{
3072+
id: new URL("https://example.com/recipient"),
3073+
inboxId: new URL("https://example.com/inbox"),
3074+
},
3075+
activity,
3076+
{ fanout: "force" },
3077+
);
3078+
assertEquals(
3079+
asyncEnqueued.length,
3080+
1,
3081+
"fanoutQueue.enqueue() must be awaited before sendActivity() returns",
3082+
);
3083+
},
3084+
);
3085+
30043086
collectionSyncHeader = null;
30053087

30063088
await t.step("followers collection without syncCollection", async () => {

packages/fedify/src/federation/middleware.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2363,7 +2363,7 @@ export class ContextImpl<TContextData> implements Context<TContextData> {
23632363
if (!this.federation.manuallyStartQueue) {
23642364
this.federation._startQueueInternal(this.data);
23652365
}
2366-
this.federation.fanoutQueue.enqueue(
2366+
await this.federation.fanoutQueue.enqueue(
23672367
message,
23682368
{ orderingKey: options.orderingKey },
23692369
);

0 commit comments

Comments
 (0)