Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,15 @@ To be released.
[#597]: https://github.com/fedify-dev/fedify/pull/597
[#599]: https://github.com/fedify-dev/fedify/pull/599

### @fedify/solidstart

- Added `@fedify/solidstart` package for integrating Fedify with
[SolidStart]. It provides `fedifyMiddleware()` for request handling
with SolidStart's middleware system. [[#476]]

[SolidStart]: https://start.solidjs.com/
[#476]: https://github.com/fedify-dev/fedify/issues/476


Version 2.0.3
-------------
Expand Down
3 changes: 3 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"./packages/postgres",
"./packages/redis",
"./packages/relay",
"./packages/solidstart",
"./packages/sqlite",
"./packages/sveltekit",
"./packages/testing",
Expand Down Expand Up @@ -56,6 +57,8 @@
"@std/yaml": "jsr:@std/yaml@^1.0.8",
"@types/node": "npm:@types/node@^22.16.0",
"amqplib": "npm:amqplib@^0.10.9",
"@solidjs/start/middleware": "npm:@solidjs/start@^1.3.0/middleware",
"@solidjs/start/server": "npm:@solidjs/start@^1.3.0/server",
"astro": "npm:astro@^5.17.3",
"byte-encodings": "npm:byte-encodings@^1.0.11",
"chalk": "npm:chalk@^5.6.2",
Expand Down
1,968 changes: 1,892 additions & 76 deletions deno.lock

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions docs/manual/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,8 @@ an exceptional developer experience. Powered by Bun, it delivers high
performance and modern tooling. The *@fedify/elysia* package provides
a seamless plugin for integrating Fedify with Elysia:

::: code-group

~~~~ sh [Bun]
bun add @fedify/elysia
~~~~
Expand All @@ -611,6 +613,8 @@ pnpm add @fedify/elysia
yarn add @fedify/elysia
~~~~

:::

~~~~ typescript
import { fedify } from "@fedify/elysia";
import { federation } from "./federation.ts"; // Your `Federation` instance
Expand Down Expand Up @@ -868,6 +872,65 @@ instead of `astro`:
~~~~


Solidstart
----------

Comment on lines +875 to +877
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Section heading is written as “Solidstart”, but the framework name is “SolidStart” elsewhere in the doc and in upstream branding. Please fix the capitalization for consistency/searchability.

Copilot uses AI. Check for mistakes.
*This API is available since Fedify 2.1.0.*

[SolidStart] is a JavaScript framework built on top of [Solid] for building
full-stack web applications. The *@fedify/solidstart* package provides
a middleware to integrate Fedify with SolidStart:

::: code-group

~~~~ sh [Deno]
deno add jsr:@fedify/solidstart
~~~~

~~~~ sh [npm]
npm add @fedify/solidstart
~~~~

~~~~ sh [pnpm]
pnpm add @fedify/solidstart
~~~~

~~~~ sh [Yarn]
yarn add @fedify/solidstart
~~~~

~~~~ sh [Bun]
bun add @fedify/solidstart
~~~~

:::

First, set up the middleware entry point in your *app.config.ts*:

~~~~ typescript
import { defineConfig } from "@solidjs/start/config";

export default defineConfig({
middleware: "src/middleware/index.ts", // [!code highlight]
});
~~~~

Then, create your middleware in *src/middleware/index.ts*:

~~~~ typescript
import { createFederation } from "@fedify/fedify";
import { fedifyMiddleware } from "@fedify/solidstart";

const federation = createFederation<void>({
// Omitted for brevity; see the related section for details.
});

export default fedifyMiddleware(federation, (event) => undefined); // [!code highlight]
~~~~

[Solid]: https://www.solidjs.com/


Fresh
-----

Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ added in the future.[^1]
- [Fedify–Next.js integration example using `@fedify/next`](./next-integration/)
- [Fedify–Next.js 14 integration example](./next14-app-router/)
- [Fedify–Next.js 15 integration example](./next15-app-router/)
- [Fedify–SolidStart integration example](./solidstart/)
- [Fedify on Cloudflare Workers example](./cloudflare-workers/)

[^1]: Contributions are welcome! If you have built an application with the
Expand Down
4 changes: 4 additions & 0 deletions examples/solidstart/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
dist
.output
.vinxi
8 changes: 8 additions & 0 deletions examples/solidstart/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from "@solidjs/start/config";

export default defineConfig({
middleware: "src/middleware/index.ts",
server: {
preset: "node-server",
},
});
24 changes: 24 additions & 0 deletions examples/solidstart/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "solidstart-example",
"version": "0.0.1",
"private": true,
"type": "module",
"description": "Fedify app with SolidStart integration",
"scripts": {
"dev": "vinxi dev",
"build": "vinxi build",
"start": "vinxi start"
},
"dependencies": {
"@fedify/fedify": "workspace:^",
"@fedify/solidstart": "workspace:^",
"@fedify/vocab": "workspace:^",
"@solidjs/router": "^0.15.4",
"@solidjs/start": "^1.3.2",
"solid-js": "^1.9.11",
"vinxi": "^0.5.11"
},
"devDependencies": {
"typescript": "^5.5.4"
}
}
26 changes: 26 additions & 0 deletions examples/solidstart/src/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
body {
font-family: system-ui, -apple-system, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
line-height: 1.6;
}

h1 {
border-bottom: 1px solid #ccc;
padding-bottom: 0.5rem;
}

.profile {
padding: 1rem;
border: 1px solid #ddd;
border-radius: 8px;
}

.profile h2 {
margin-top: 0;
}

.followers {
margin-top: 1rem;
}
12 changes: 12 additions & 0 deletions examples/solidstart/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
import { Suspense } from "solid-js";
import "./app.css";

export default function App() {
return (
<Router root={(props) => <Suspense>{props.children}</Suspense>}>
<FileRoutes />
</Router>
);
}
4 changes: 4 additions & 0 deletions examples/solidstart/src/entry-client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @refresh reload
import { mount, StartClient } from "@solidjs/start/client";

mount(() => <StartClient />, document.getElementById("app")!);
20 changes: 20 additions & 0 deletions examples/solidstart/src/entry-server.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// @refresh reload
import { createHandler, StartServer } from "@solidjs/start/server";

export default createHandler(() => (
<StartServer
document={({ assets, children, scripts }) => (
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{assets}
</head>
<body>
<div id="app">{children}</div>
{scripts}
</body>
</html>
)}
/>
));
141 changes: 141 additions & 0 deletions examples/solidstart/src/lib/federation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import {
createFederation,
generateCryptoKeyPair,
MemoryKvStore,
} from "@fedify/fedify";
import {
Accept,
Endpoints,
Follow,
Note,
Person,
type Recipient,
Undo,
} from "@fedify/vocab";
import { keyPairsStore, relationStore } from "./store";

const federation = createFederation<void>({
kv: new MemoryKvStore(),
});

const IDENTIFIER = "demo";

federation.setNodeInfoDispatcher("/nodeinfo/2.1", (_ctx) => {
return {
software: {
name: "fedify-solidstart",
version: "0.0.1",
},
protocols: ["activitypub"],
usage: {
users: { total: 1, activeHalfyear: 1, activeMonth: 1 },
localPosts: 0,
localComments: 0,
},
};
});

federation
.setActorDispatcher("/users/{identifier}", async (context, identifier) => {
if (identifier !== IDENTIFIER) {
return null;
}
const keyPairs = await context.getActorKeyPairs(identifier);
return new Person({
id: context.getActorUri(identifier),
name: "Fedify Demo",
summary: "This is a Fedify Demo account on SolidStart.",
preferredUsername: identifier,
url: new URL("/", context.url),
inbox: context.getInboxUri(identifier),
endpoints: new Endpoints({ sharedInbox: context.getInboxUri() }),
publicKey: keyPairs[0].cryptographicKey,
assertionMethods: keyPairs.map((keyPair) => keyPair.multikey),
});
})
.setKeyPairsDispatcher(async (_, identifier) => {
if (identifier !== IDENTIFIER) {
return [];
}
const keyPairs = keyPairsStore.get(identifier);
if (keyPairs) {
return keyPairs;
}
const { privateKey, publicKey } = await generateCryptoKeyPair();
keyPairsStore.set(identifier, [{ privateKey, publicKey }]);
return [{ privateKey, publicKey }];
});

federation
.setInboxListeners("/users/{identifier}/inbox", "/inbox")
.on(Follow, async (context, follow) => {
if (
follow.id == null ||
follow.actorId == null ||
follow.objectId == null
) {
return;
}
const result = context.parseUri(follow.objectId);
if (result?.type !== "actor" || result.identifier !== IDENTIFIER) {
return;
}
const follower = (await follow.getActor(context)) as Person;
if (!follower?.id || follower.id === null) {
throw new Error("follower is null");
}
await context.sendActivity(
{ identifier: result.identifier },
follower,
new Accept({
id: new URL(
`#accepts/${follower.id.href}`,
context.getActorUri(IDENTIFIER),
),
actor: follow.objectId,
object: follow,
}),
);
relationStore.set(follower.id.href, follower);
})
.on(Undo, async (context, undo) => {
const activity = await undo.getObject(context);
if (activity instanceof Follow) {
if (activity.id == null) {
return;
}
if (undo.actorId == null) {
return;
}
relationStore.delete(undo.actorId.href);
} else {
console.debug(undo);
}
});

federation.setObjectDispatcher(
Note,
"/users/{identifier}/posts/{id}",
(ctx, values) => {
return new Note({
id: ctx.getObjectUri(Note, values),
attribution: ctx.getActorUri(values.identifier),
name: values.id,
});
},
);

federation.setFollowersDispatcher(
"/users/{identifier}/followers",
() => {
const followers = Array.from(relationStore.values());
const items: Recipient[] = followers.map((f) => ({
id: f.id,
inboxId: f.inboxId,
endpoints: f.endpoints,
}));
return { items };
},
);

export default federation;
16 changes: 16 additions & 0 deletions examples/solidstart/src/lib/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Person } from "@fedify/vocab";

declare global {
var keyPairsStore: Map<string, CryptoKeyPair[]>;
var relationStore: Map<string, Person>;
}

export const keyPairsStore: Map<string, CryptoKeyPair[]> =
globalThis.keyPairsStore ?? new Map();
export const relationStore: Map<string, Person> = globalThis.relationStore ??
new Map();

// This is just a hack for the demo.
// Never do this in production; use safe and secure storage.
globalThis.keyPairsStore = keyPairsStore;
globalThis.relationStore = relationStore;
4 changes: 4 additions & 0 deletions examples/solidstart/src/middleware/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { fedifyMiddleware } from "@fedify/solidstart";
import federation from "../lib/federation";

export default fedifyMiddleware(federation);
5 changes: 5 additions & 0 deletions examples/solidstart/src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Navigate } from "@solidjs/router";

export default function Home() {
return <Navigate href="/users/demo" />;
}
Loading
Loading