Loading CHANGES.md +41 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,21 @@ Version 1.9.1 To be released. ### @fedify/testing - Fixed JSR publishing hanging indefinitely at the *processing* stage. The issue was caused by TypeScript function overload signatures in `MockContext` and `MockFederation` classes that triggered a bug in JSR's type analyzer. All method overloads have been removed and simplified to use `any` types where necessary. [[#468], [#470]] ### @fedify/cli - Fixed `fedify` command failing on Windows with `PermissionDenied` error when trying to locate or execute package managers during initialization. The CLI now properly handles _\*.cmd_ and _\*.bat_ files on Windows by invoking them through `cmd /c`. [[#463]] Version 1.9.0 ------------- Loading Loading @@ -298,6 +313,32 @@ Released on October 14, 2025. CommonJS-based Node.js applications. [[#429], [#431]] Version 1.8.14 -------------- Released on October 19, 2025. ### @fedify/testing - Fixed JSR publishing hanging indefinitely at the *processing* stage. The issue was caused by TypeScript function overload signatures in `MockContext` and `MockFederation` classes that triggered a bug in JSR's type analyzer. All method overloads have been removed and simplified to use `any` types where necessary. [[#468], [#470]] [#468]: https://github.com/fedify-dev/fedify/issues/468 [#470]: https://github.com/fedify-dev/fedify/pull/470 ### @fedify/cli - Fixed `fedify` command failing on Windows with `PermissionDenied` error when trying to locate or execute package managers during initialization. The CLI now properly handles _\*.cmd_ and _\*.bat_ files on Windows by invoking them through `cmd /c`. [[#463]] [#463]: https://github.com/fedify-dev/fedify/issues/463 Version 1.8.13 -------------- Loading deno.lock +0 −10 Original line number Diff line number Diff line Loading @@ -4838,16 +4838,6 @@ "jsr:@std/assert@^1.0.13", "npm:@sveltejs/kit@2" ] }, "packages/testing": { "dependencies": [ "npm:@opentelemetry/api@^1.9.0" ], "packageJson": { "dependencies": [ "npm:@opentelemetry/api@^1.9.0" ] } } } } Loading docs/manual/test.md +35 −37 Original line number Diff line number Diff line Loading @@ -230,23 +230,22 @@ await federation.receiveActivity(activity); console.log("Sent activities:", federation.sentActivities); ~~~~ ### `MockContext` ### Creating mock contexts The `MockContext` class provides a mock implementation of the `Context` interface that tracks sent activities and provides mock implementations of URI generation methods: You can create mock contexts using `MockFederation.createContext()` method, which provides a mock implementation of the `Context` interface that tracks sent activities and provides mock implementations of URI generation methods: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; import { Create, Note, Person } from "@fedify/fedify/vocab"; // Create a mock federation and context const federation = new MockFederation<{ userId: string }>(); const context = new MockContext({ url: new URL("https://example.com"), data: { userId: "test-user" }, federation: federation }); const context = federation.createContext( new URL("https://example.com"), { userId: "test-user" } ); // Send an activity const activity = new Create({ Loading Loading @@ -274,11 +273,12 @@ console.log("Federation sent activities:", federation.sentActivities); ### Testing URI generation `MockContext` provides mock implementations of all URI generation methods that work with the paths configured in your `MockFederation`: Mock contexts created with `MockFederation.createContext()` provide mock implementations of all URI generation methods that work with the paths configured in your `MockFederation`: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; import { Note } from "@fedify/fedify/vocab"; const federation = new MockFederation(); Loading @@ -303,37 +303,36 @@ console.log(context.getObjectUri(Note, { id: "123" })); // https://example.com/n ### Tracking sent activities Both `MockFederation` and `MockContext` track sent activities with detailed Both `MockFederation` and mock contexts track sent activities with detailed metadata: ~~~~ typescript twoslash // @noErrors: 2554 import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); const context = new MockContext({ federation, url: new URL("https://example.com/"), data: undefined, }); const context = federation.createContext( new URL("https://example.com/"), undefined ); // Send some activities... await context.sendActivity(/* ... */); // Check federation-level tracking federation.sentActivities.forEach(sent => { for (const sent of federation.sentActivities) { console.log("Activity:", sent.activity.id); console.log("Queued:", sent.queued); console.log("Queue type:", sent.queue); console.log("Send order:", sent.sentOrder); }); } // Check context-level tracking context.getSentActivities().forEach(sent => { for (const sent of context.getSentActivities()) { console.log("Sender:", sent.sender); console.log("Recipients:", sent.recipients); console.log("Activity:", sent.activity); }); } ~~~~ ### Simulating queue processing Loading @@ -342,7 +341,7 @@ You can test queue-based activity processing by starting the mock queue: ~~~~ typescript twoslash // @noErrors: 2554 import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); Loading @@ -350,11 +349,10 @@ const federation = new MockFederation(); await federation.startQueue({ contextData: { userId: "test" } }); // Now sent activities will be marked as queued const context = new MockContext({ federation, url: new URL("https://example.com/"), data: { userId: "test" }, }) const context = federation.createContext( new URL("https://example.com/"), { userId: "test" } ); await context.sendActivity(/* ... */); // Check if activities were queued Loading @@ -364,16 +362,16 @@ console.log("Queued activities:", queued.length); ### Resetting mock state Both mock classes provide `reset()` methods to clear tracked activities: Both `MockFederation` and mock contexts provide `reset()` methods to clear tracked activities: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); const context = new MockContext({ federation, url: new URL("https://example.com/"), data: undefined, }); const context = federation.createContext( new URL("https://example.com/"), undefined ); // ---cut-before--- // Clear all sent activities federation.reset(); Loading packages/cli/scripts/pack.ts +5 −0 Original line number Diff line number Diff line Loading @@ -51,10 +51,15 @@ async function pack(os: OS, arch: Arch): Promise<void> { } } const osFilter = Deno.env.get("OS")?.toLowerCase(); const archFilter = Deno.env.get("ARCH")?.toLowerCase(); const promises: Promise<void>[] = []; for (const osKey in triplets) { const os = osKey as OS; if (osFilter != null && osFilter !== os) continue; for (const arch in triplets[os]) { if (archFilter != null && archFilter !== arch) continue; const promise = pack(os, arch as Arch); promises.push(promise); } Loading packages/cli/src/init.ts +42 −18 Original line number Diff line number Diff line Loading @@ -1521,7 +1521,12 @@ async function isCommandAvailable( "The command {command} failed with the error: {error}", { command: checkCommand, error }, ); if (error instanceof Deno.errors.NotFound) return false; if ( error instanceof Deno.errors.NotFound || error instanceof Deno.errors.PermissionDenied ) { return false; } throw error; } } Loading @@ -1538,11 +1543,13 @@ async function locatePackageManager( } if (Deno.build.os !== "windows") return undefined; const cmd: [string, ...string[]] = [ "cmd", "/c", packageManagers[pm].checkCommand[0] + ".cmd", ...packageManagers[pm].checkCommand.slice(1), ]; if (await isCommandAvailable({ ...packageManagers[pm], checkCommand: cmd })) { return cmd[0]; return cmd[2]; } return undefined; } Loading @@ -1569,7 +1576,24 @@ async function addDependencies( }` ); if (deps.length < 1) return; const cmd = new Deno.Command( const cmd = runtime === "node" && packageManagerLocations[pm]?.match(/\.cmd$/i) ? new Deno.Command( "cmd", { args: [ "/c", `${packageManagerLocations[pm]} add ${ dev ? (pm === "yarn" ? "--dev" : "--save-dev") : "" } ${uniqueArray(deps).join(" ")}`, ], cwd: dir, stdin: "inherit", stdout: "inherit", stderr: "inherit", }, ) : new Deno.Command( runtime === "node" ? (packageManagerLocations[pm] ?? pm) : runtime, { args: [ Loading Loading
CHANGES.md +41 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,21 @@ Version 1.9.1 To be released. ### @fedify/testing - Fixed JSR publishing hanging indefinitely at the *processing* stage. The issue was caused by TypeScript function overload signatures in `MockContext` and `MockFederation` classes that triggered a bug in JSR's type analyzer. All method overloads have been removed and simplified to use `any` types where necessary. [[#468], [#470]] ### @fedify/cli - Fixed `fedify` command failing on Windows with `PermissionDenied` error when trying to locate or execute package managers during initialization. The CLI now properly handles _\*.cmd_ and _\*.bat_ files on Windows by invoking them through `cmd /c`. [[#463]] Version 1.9.0 ------------- Loading Loading @@ -298,6 +313,32 @@ Released on October 14, 2025. CommonJS-based Node.js applications. [[#429], [#431]] Version 1.8.14 -------------- Released on October 19, 2025. ### @fedify/testing - Fixed JSR publishing hanging indefinitely at the *processing* stage. The issue was caused by TypeScript function overload signatures in `MockContext` and `MockFederation` classes that triggered a bug in JSR's type analyzer. All method overloads have been removed and simplified to use `any` types where necessary. [[#468], [#470]] [#468]: https://github.com/fedify-dev/fedify/issues/468 [#470]: https://github.com/fedify-dev/fedify/pull/470 ### @fedify/cli - Fixed `fedify` command failing on Windows with `PermissionDenied` error when trying to locate or execute package managers during initialization. The CLI now properly handles _\*.cmd_ and _\*.bat_ files on Windows by invoking them through `cmd /c`. [[#463]] [#463]: https://github.com/fedify-dev/fedify/issues/463 Version 1.8.13 -------------- Loading
deno.lock +0 −10 Original line number Diff line number Diff line Loading @@ -4838,16 +4838,6 @@ "jsr:@std/assert@^1.0.13", "npm:@sveltejs/kit@2" ] }, "packages/testing": { "dependencies": [ "npm:@opentelemetry/api@^1.9.0" ], "packageJson": { "dependencies": [ "npm:@opentelemetry/api@^1.9.0" ] } } } } Loading
docs/manual/test.md +35 −37 Original line number Diff line number Diff line Loading @@ -230,23 +230,22 @@ await federation.receiveActivity(activity); console.log("Sent activities:", federation.sentActivities); ~~~~ ### `MockContext` ### Creating mock contexts The `MockContext` class provides a mock implementation of the `Context` interface that tracks sent activities and provides mock implementations of URI generation methods: You can create mock contexts using `MockFederation.createContext()` method, which provides a mock implementation of the `Context` interface that tracks sent activities and provides mock implementations of URI generation methods: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; import { Create, Note, Person } from "@fedify/fedify/vocab"; // Create a mock federation and context const federation = new MockFederation<{ userId: string }>(); const context = new MockContext({ url: new URL("https://example.com"), data: { userId: "test-user" }, federation: federation }); const context = federation.createContext( new URL("https://example.com"), { userId: "test-user" } ); // Send an activity const activity = new Create({ Loading Loading @@ -274,11 +273,12 @@ console.log("Federation sent activities:", federation.sentActivities); ### Testing URI generation `MockContext` provides mock implementations of all URI generation methods that work with the paths configured in your `MockFederation`: Mock contexts created with `MockFederation.createContext()` provide mock implementations of all URI generation methods that work with the paths configured in your `MockFederation`: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; import { Note } from "@fedify/fedify/vocab"; const federation = new MockFederation(); Loading @@ -303,37 +303,36 @@ console.log(context.getObjectUri(Note, { id: "123" })); // https://example.com/n ### Tracking sent activities Both `MockFederation` and `MockContext` track sent activities with detailed Both `MockFederation` and mock contexts track sent activities with detailed metadata: ~~~~ typescript twoslash // @noErrors: 2554 import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); const context = new MockContext({ federation, url: new URL("https://example.com/"), data: undefined, }); const context = federation.createContext( new URL("https://example.com/"), undefined ); // Send some activities... await context.sendActivity(/* ... */); // Check federation-level tracking federation.sentActivities.forEach(sent => { for (const sent of federation.sentActivities) { console.log("Activity:", sent.activity.id); console.log("Queued:", sent.queued); console.log("Queue type:", sent.queue); console.log("Send order:", sent.sentOrder); }); } // Check context-level tracking context.getSentActivities().forEach(sent => { for (const sent of context.getSentActivities()) { console.log("Sender:", sent.sender); console.log("Recipients:", sent.recipients); console.log("Activity:", sent.activity); }); } ~~~~ ### Simulating queue processing Loading @@ -342,7 +341,7 @@ You can test queue-based activity processing by starting the mock queue: ~~~~ typescript twoslash // @noErrors: 2554 import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); Loading @@ -350,11 +349,10 @@ const federation = new MockFederation(); await federation.startQueue({ contextData: { userId: "test" } }); // Now sent activities will be marked as queued const context = new MockContext({ federation, url: new URL("https://example.com/"), data: { userId: "test" }, }) const context = federation.createContext( new URL("https://example.com/"), { userId: "test" } ); await context.sendActivity(/* ... */); // Check if activities were queued Loading @@ -364,16 +362,16 @@ console.log("Queued activities:", queued.length); ### Resetting mock state Both mock classes provide `reset()` methods to clear tracked activities: Both `MockFederation` and mock contexts provide `reset()` methods to clear tracked activities: ~~~~ typescript twoslash import { MockContext, MockFederation } from "@fedify/testing"; import { MockFederation } from "@fedify/testing"; const federation = new MockFederation(); const context = new MockContext({ federation, url: new URL("https://example.com/"), data: undefined, }); const context = federation.createContext( new URL("https://example.com/"), undefined ); // ---cut-before--- // Clear all sent activities federation.reset(); Loading
packages/cli/scripts/pack.ts +5 −0 Original line number Diff line number Diff line Loading @@ -51,10 +51,15 @@ async function pack(os: OS, arch: Arch): Promise<void> { } } const osFilter = Deno.env.get("OS")?.toLowerCase(); const archFilter = Deno.env.get("ARCH")?.toLowerCase(); const promises: Promise<void>[] = []; for (const osKey in triplets) { const os = osKey as OS; if (osFilter != null && osFilter !== os) continue; for (const arch in triplets[os]) { if (archFilter != null && archFilter !== arch) continue; const promise = pack(os, arch as Arch); promises.push(promise); } Loading
packages/cli/src/init.ts +42 −18 Original line number Diff line number Diff line Loading @@ -1521,7 +1521,12 @@ async function isCommandAvailable( "The command {command} failed with the error: {error}", { command: checkCommand, error }, ); if (error instanceof Deno.errors.NotFound) return false; if ( error instanceof Deno.errors.NotFound || error instanceof Deno.errors.PermissionDenied ) { return false; } throw error; } } Loading @@ -1538,11 +1543,13 @@ async function locatePackageManager( } if (Deno.build.os !== "windows") return undefined; const cmd: [string, ...string[]] = [ "cmd", "/c", packageManagers[pm].checkCommand[0] + ".cmd", ...packageManagers[pm].checkCommand.slice(1), ]; if (await isCommandAvailable({ ...packageManagers[pm], checkCommand: cmd })) { return cmd[0]; return cmd[2]; } return undefined; } Loading @@ -1569,7 +1576,24 @@ async function addDependencies( }` ); if (deps.length < 1) return; const cmd = new Deno.Command( const cmd = runtime === "node" && packageManagerLocations[pm]?.match(/\.cmd$/i) ? new Deno.Command( "cmd", { args: [ "/c", `${packageManagerLocations[pm]} add ${ dev ? (pm === "yarn" ? "--dev" : "--save-dev") : "" } ${uniqueArray(deps).join(" ")}`, ], cwd: dir, stdin: "inherit", stdout: "inherit", stderr: "inherit", }, ) : new Deno.Command( runtime === "node" ? (packageManagerLocations[pm] ?? pm) : runtime, { args: [ Loading