Skip to content

Commit 4da1921

Browse files
committed
Merge branch 'fix/redismq' of https://github.com/sij411/fedify into fix/redismq
2 parents 952bcd5 + cf88fa7 commit 4da1921

8 files changed

Lines changed: 343 additions & 450 deletions

File tree

.hongdown.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ include = ["*.md", "**/*.md"]
33
exclude = [
44
"**/node_modules/**",
55
"**/dist/**",
6+
".github/copilot-instructions.md",
67
"AGENT.md",
78
"CLAUDE.md",
89
"GEMINI.md",

AGENTS.md

Lines changed: 112 additions & 438 deletions
Large diffs are not rendered by default.

CHANGES.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,21 @@ To be released.
313313
[#532]: https://github.com/fedify-dev/fedify/pull/532
314314

315315

316+
Version 1.10.2
317+
--------------
318+
319+
Released on January 23, 2026.
320+
321+
### @fedify/testing
322+
323+
- Fixed `TestContext.getActorKeyPairs()` returning empty array instead of
324+
calling registered key pairs dispatcher. The method now properly invokes
325+
the key pairs dispatcher when it is registered via
326+
`setKeyPairsDispatcher()`. [[#530]]
327+
328+
[#530]: https://github.com/fedify-dev/fedify/issues/530
329+
330+
316331
Version 1.10.1
317332
--------------
318333

@@ -325,8 +340,6 @@ Released on January 22, 2026.
325340
invoke actor and object dispatchers when they are registered via
326341
`setActorDispatcher()` and `setObjectDispatcher()`. [[#530]]
327342

328-
[#530]: https://github.com/fedify-dev/fedify/issues/530
329-
330343

331344
Version 1.10.0
332345
--------------
@@ -425,6 +438,19 @@ Released on December 24, 2025.
425438
- Implemented `list()` method in `WorkersKvStore`. [[#498], [#500]]
426439

427440

441+
Version 1.9.4
442+
-------------
443+
444+
Released on January 23, 2026.
445+
446+
### @fedify/testing
447+
448+
- Fixed `TestContext.getActorKeyPairs()` returning empty array instead of
449+
calling registered key pairs dispatcher. The method now properly invokes
450+
the key pairs dispatcher when it is registered via
451+
`setKeyPairsDispatcher()`. [[#530]]
452+
453+
428454
Version 1.9.3
429455
-------------
430456

CONTRIBUTING.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,9 @@ The repository is organized as a monorepo with the following packages:
321321
322322
- *packages/fedify/*: The main Fedify library (@fedify/fedify). The library
323323
is built with Deno, and tested with Deno, Node.js, and [Bun].
324-
- *src/codegen/*: The code generation scripts.
325-
- *packages/cli/*: The Fedify CLI (@fedify/cli). The CLI is built with
326-
[Deno].
324+
- *packages/cli/*: The Fedify CLI (@fedify/cli). Built with [Deno] and
325+
tested with Deno, Node.js, and [Bun]. Uses `deno compile` to create
326+
standalone executables.
327327
- *packages/amqp/*: AMQP/RabbitMQ driver (@fedify/amqp) for Fedify.
328328
- *packages/cfworkers/*: Cloudflare Workers integration (@fedify/cfworkers) for
329329
Fedify.
@@ -344,10 +344,15 @@ The repository is organized as a monorepo with the following packages:
344344
- *packages/sqlite/*: SQLite driver (@fedify/sqlite) for Fedify.
345345
- *packages/sveltekit/*: SvelteKit integration (@fedify/sveltekit) for Fedify.
346346
- *packages/testing/*: Testing utilities (@fedify/testing) for Fedify.
347+
- *packages/vocab/*: Activity Vocabulary library (@fedify/vocab) for Fedify.
347348
- *packages/vocab-runtime/*: Runtime library for code-generated vocab
348349
(@fedify/vocab-runtime) for Fedify.
349350
- *packages/vocab-tools/*: Code generation tools for Activity Vocabulary
350351
(@fedify/vocab-tools) for Fedify.
352+
- *packages/webfinger/*: WebFinger client library (@fedify/webfinger) for
353+
ActivityPub.
354+
- *packages/fixture/*: Testing utilities (@fedify/fixture) providing
355+
runtime-agnostic test adapters.
351356
- *docs/*: The Fedify docs. The docs are built with [Node.js] and
352357
[VitePress].
353358
- *examples/*: The example projects. Some examples are built with Deno, and
@@ -434,10 +439,11 @@ the `fedify lookup` subcommand, you can run the following command:
434439
mise run cli -- lookup @fedify@hollo.social
435440
~~~~
436441

437-
> [!TIP]
442+
> [!NOTE]
438443
>
439-
> Unlike the Fedify library, the Fedify CLI does not have to be tested with
440-
> Node.js and Bun; you can test the CLI with Deno only.
444+
> The Fedify CLI is tested with Deno, Node.js, and Bun like other packages.
445+
> However, for quick local testing during development, `mise run cli` uses
446+
> Deno directly without requiring a full multi-runtime test run.
441447
442448
#### Running the tests
443449

docs/README.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,84 @@ to *http://localhost:5173/* to view the docs.
1919
[VitePress]: https://vitepress.dev/
2020
[Node.js]: https://nodejs.org/
2121
[pnpm]: https://pnpm.io/
22+
23+
24+
VitePress documentation conventions
25+
-----------------------------------
26+
27+
The *docs/* directory uses VitePress with additional features beyond standard
28+
Markdown.
29+
30+
### Twoslash code blocks
31+
32+
Use the `twoslash` modifier to enable TypeScript type checking and hover
33+
information in code blocks:
34+
35+
~~~~~ markdown
36+
~~~~ typescript twoslash
37+
import { createFederation } from "@fedify/fedify";
38+
39+
const federation = createFederation({ kv: undefined! });
40+
~~~~
41+
~~~~~
42+
43+
### Fixture variables
44+
45+
When code examples need variables that shouldn't be shown to readers,
46+
declare them *before* the `// ---cut-before---` directive. Content before
47+
this directive is compiled but hidden from display:
48+
49+
~~~~~ markdown
50+
~~~~ typescript twoslash
51+
import type { KvStore } from "@fedify/fedify";
52+
declare const kv: KvStore;
53+
// ---cut-before---
54+
import { createFederation } from "@fedify/fedify";
55+
56+
const federation = createFederation({ kv });
57+
~~~~
58+
~~~~~
59+
60+
The reader sees only the code after `---cut-before---`, but TypeScript
61+
checks the entire block including the hidden fixture.
62+
63+
### Code groups
64+
65+
Use code groups to show the same content for different package managers
66+
or environments:
67+
68+
~~~~~ markdown
69+
::: code-group
70+
71+
~~~~ bash [Deno]
72+
deno add jsr:@fedify/fedify
73+
~~~~
74+
75+
~~~~ bash [npm]
76+
npm add @fedify/fedify
77+
~~~~
78+
79+
~~~~ bash [pnpm]
80+
pnpm add @fedify/fedify
81+
~~~~
82+
83+
:::
84+
~~~~~
85+
86+
### Internal links
87+
88+
- When linking to other VitePress documents within the *docs/* directory,
89+
use inline link syntax (e.g., `[text](./path/to/file.md)`) instead of
90+
reference-style links.
91+
- Always use relative paths for internal links.
92+
- Include the `.md` extension in internal link paths.
93+
94+
### Building documentation
95+
96+
~~~~ bash
97+
mise run docs:build # Build for production (runs Twoslash type checking)
98+
mise run docs # Start development server
99+
~~~~
100+
101+
Always run `mise run docs:build` before committing to catch Twoslash type
102+
errors.

mise.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,13 @@ done
4040
# Code quality
4141
[tasks.check]
4242
description = "Check code formatting, linting, and type checking"
43-
depends = ["check:fmt", "check:lint", "check:types", "check:md", "check-versions"]
43+
depends = [
44+
"check:fmt",
45+
"check:lint",
46+
"check:types",
47+
"check:md",
48+
"check-versions",
49+
]
4450

4551
[tasks."check:fmt"]
4652
description = "Check code formatting"

packages/testing/src/mock.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,73 @@ test("MockContext.getObject() returns null when no dispatcher registered", async
440440
});
441441
assertEquals(note, null);
442442
});
443+
444+
test("MockContext.getActorKeyPairs() calls registered key pairs dispatcher", async () => {
445+
const mockFederation = createFederation<void>();
446+
447+
// Generate a test RSA key pair
448+
const keyPair = await crypto.subtle.generateKey(
449+
{
450+
name: "RSASSA-PKCS1-v1_5",
451+
modulusLength: 2048,
452+
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
453+
hash: "SHA-256",
454+
},
455+
true,
456+
["sign", "verify"],
457+
);
458+
459+
// Register actor dispatcher with key pairs dispatcher
460+
mockFederation
461+
.setActorDispatcher("/users/{identifier}", (ctx, identifier) => {
462+
return new Person({
463+
id: ctx.getActorUri(identifier),
464+
preferredUsername: identifier,
465+
});
466+
})
467+
.setKeyPairsDispatcher((ctx, identifier) => {
468+
return [
469+
{
470+
keyId: new URL(`${ctx.getActorUri(identifier).href}#main-key`),
471+
privateKey: keyPair.privateKey,
472+
publicKey: keyPair.publicKey,
473+
},
474+
];
475+
});
476+
477+
const context = mockFederation.createContext(
478+
new URL("https://example.com"),
479+
undefined,
480+
);
481+
482+
const keyPairs = await context.getActorKeyPairs("alice");
483+
484+
assertEquals(keyPairs.length, 1);
485+
assertEquals(
486+
keyPairs[0].keyId.href,
487+
"https://example.com/users/alice#main-key",
488+
);
489+
assertEquals(keyPairs[0].privateKey, keyPair.privateKey);
490+
assertEquals(keyPairs[0].publicKey, keyPair.publicKey);
491+
assertEquals(keyPairs[0].cryptographicKey.id?.href, keyPairs[0].keyId.href);
492+
assertEquals(
493+
keyPairs[0].cryptographicKey.ownerId?.href,
494+
"https://example.com/users/alice",
495+
);
496+
assertEquals(keyPairs[0].multikey.id?.href, keyPairs[0].keyId.href);
497+
assertEquals(
498+
keyPairs[0].multikey.controllerId?.href,
499+
"https://example.com/users/alice",
500+
);
501+
});
502+
503+
test("MockContext.getActorKeyPairs() returns empty array when no dispatcher registered", async () => {
504+
const mockFederation = createFederation<void>();
505+
const context = mockFederation.createContext(
506+
new URL("https://example.com"),
507+
undefined,
508+
);
509+
510+
const keyPairs = await context.getActorKeyPairs("alice");
511+
assertEquals(keyPairs, []);
512+
});

packages/testing/src/mock.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
RequestContext,
1111
RouteActivityOptions,
1212
} from "@fedify/fedify/federation";
13+
import { CryptographicKey, Multikey } from "@fedify/vocab";
1314
import type {
1415
Activity,
1516
Collection,
@@ -179,6 +180,7 @@ class MockFederation<TContextData> implements Federation<TContextData> {
179180
// types present in Context.tracerProvider (issue #468).
180181
private webFingerDispatcher?: any;
181182
public actorDispatchers: Map<string, any> = new Map();
183+
public actorKeyPairsDispatcher?: any;
182184
public actorPath?: string;
183185
public inboxPath?: string;
184186
public outboxPath?: string;
@@ -229,7 +231,10 @@ class MockFederation<TContextData> implements Federation<TContextData> {
229231
this.actorDispatchers.set(path, dispatcher);
230232
this.actorPath = path;
231233
return {
232-
setKeyPairsDispatcher: () => this as any,
234+
setKeyPairsDispatcher: (keyPairsDispatcher: any) => {
235+
this.actorKeyPairsDispatcher = keyPairsDispatcher;
236+
return this as any;
237+
},
233238
mapHandle: () => this as any,
234239
mapAlias: () => this as any,
235240
authorize: () => this as any,
@@ -836,8 +841,32 @@ class MockContext<TContextData> implements Context<TContextData> {
836841
return null;
837842
}
838843

839-
getActorKeyPairs(_identifier: string): Promise<ActorKeyPair[]> {
840-
return Promise.resolve([]);
844+
async getActorKeyPairs(identifier: string): Promise<ActorKeyPair[]> {
845+
if (
846+
this.federation instanceof MockFederation &&
847+
this.federation.actorKeyPairsDispatcher
848+
) {
849+
const keyPairs = await this.federation.actorKeyPairsDispatcher(
850+
this,
851+
identifier,
852+
);
853+
// Wrap with CryptographicKey and Multikey objects like real implementation
854+
const owner = this.getActorUri(identifier);
855+
return keyPairs.map((kp: any) => ({
856+
...kp,
857+
cryptographicKey: new CryptographicKey({
858+
id: kp.keyId,
859+
owner,
860+
publicKey: kp.publicKey,
861+
}),
862+
multikey: new Multikey({
863+
id: kp.keyId,
864+
controller: owner,
865+
publicKey: kp.publicKey,
866+
}),
867+
}));
868+
}
869+
return [];
841870
}
842871

843872
getDocumentLoader(params: any): any {

0 commit comments

Comments
 (0)