SearchCtrlK

  • ↑↓navigate
  • ↵open
  • escclose

sdxc

Take one, or take the set. There are 142 of them, built on fetch, Request, Response and Web Crypto, so most run unchanged on Cloudflare Workers, Bun, Deno and Node.

Terminal
npm install @sdxc/markdown @sdxc/response @sdxc/result
Terminal
pnpm add @sdxc/markdown @sdxc/response @sdxc/result
Terminal
yarn add @sdxc/markdown @sdxc/response @sdxc/result
Terminal
bun add @sdxc/markdown @sdxc/response @sdxc/result
Latest release
import { Markdown } from "@sdxc/markdown";
import { badRequest, ok } from "@sdxc/response";
import { isFailure } from "@sdxc/result";

async function handle(request: Request) {
	let parsed = Markdown.parse(await request.text());
	if (isFailure(parsed)) {
		let line = parsed.error.position?.start.line;
		return badRequest({ error: parsed.error.message, line });
	}

	return ok({ frontmatter: parsed.data.frontmatter });
}

export default { fetch: handle };
import { Markdown } from "@sdxc/markdown";
import { badRequest, ok } from "@sdxc/response";
import { isFailure } from "@sdxc/result";

async function handle(request: Request) {
	let parsed = Markdown.parse(await request.text());
	if (isFailure(parsed)) {
		let line = parsed.error.position?.start.line;
		return badRequest({ error: parsed.error.message, line });
	}

	return ok({ frontmatter: parsed.data.frontmatter });
}

Bun.serve({ port: 3000, fetch: handle });
import { Markdown } from "@sdxc/markdown";
import { badRequest, ok } from "@sdxc/response";
import { isFailure } from "@sdxc/result";

async function handle(request: Request) {
	let parsed = Markdown.parse(await request.text());
	if (isFailure(parsed)) {
		let line = parsed.error.position?.start.line;
		return badRequest({ error: parsed.error.message, line });
	}

	return ok({ frontmatter: parsed.data.frontmatter });
}

Deno.serve({ port: 3000 }, handle);
import { createRequestListener } from "@remix-run/node-fetch-server";
import { createServer } from "node:http";
import { Markdown } from "@sdxc/markdown";
import { badRequest, ok } from "@sdxc/response";
import { isFailure } from "@sdxc/result";

async function handle(request: Request) {
	let parsed = Markdown.parse(await request.text());
	if (isFailure(parsed)) {
		let line = parsed.error.position?.start.line;
		return badRequest({ error: parsed.error.message, line });
	}

	return ok({ frontmatter: parsed.data.frontmatter });
}

createServer(createRequestListener(handle)).listen(3000);
142packages under one scope
103RFCs the packages follow
199components in @sdxc/ui
298styling utilities in @sdxc/u

Everything between the Request and the Response.

Validation, auth, caching, jobs, feeds and the UI on top: 142 packages that already agree on how to fail, so the tenth one you install reads like the first.

Packages that agree with each other

Every package answers the same way, so the second one you pick up already reads like the first. Each example here composes a few of them and never throws.

  • Request and Response in and out, never a framework's own objects

  • Every fallible entry point answers with a Result

  • Standard Schema at every boundary that reads untrusted input

  • Subpath exports, so what you skip stays out of the bundle

app/controllers/board.ts
import { redirect } from "@sdxc/http/response";
import { isFailure } from "@sdxc/result";
import { validate } from "@sdxc/validate";
import { createAction } from "remix/router";

export default createAction(routes.board.create, async (ctx) => {
	let form = await validate(ctx.formData, PostingSchema);
	if (isFailure(form)) return renderBoard(ctx, form.error);

	let posting = await Job.publish(ctx.db, form.data);
	await cache.delete(LISTING_KEY);
	await ctx.jobs.enqueue(jobs.confirm, { id: posting.id });

	ctx.log.set({ posting: { id: posting.id } });
	return redirect(routes.board.index.href(), { status: 303 });
});
app/mcp/cache.ts
import type { ToolMiddleware } from "@sdxc/mcp/server";

import { WorkerKVCache } from "@sdxc/cache/worker-kv";
import { isSuccess } from "@sdxc/result";

export function cacheToolResults(): ToolMiddleware {
	return async (ctx, next) => {
		let key = await keyFor(ctx.tool.name, ctx.input);
		if (key === null) return next();

		let cache = new WorkerKVCache(env.CACHE, { waitUntil });
		let hit = await cache.read<CallToolResult>(key);
		if (isSuccess(hit) && hit.data !== null) return hit.data;

		let result = await next();
		if (!result.isError) {
			await cache.write(key, result, { ttl: "5 minutes" });
		}
		return result;
	};
}
app/jobs/send-confirmation.ts
import { createJobHandler } from "@sdxc/jobs";
import { isFailure } from "@sdxc/result";

export default createJobHandler(jobs.confirm, async (ctx) => {
	let posting = await Job.find(ctx.db, ctx.input.id);
	if (!posting) {
		ctx.log.note("posting.missing");
		return;
	}

	let sent = await mailer.send(new PublishedEmail(posting));
	if (isFailure(sent)) {
		ctx.log.fail(sent.error);
		return;
	}

	ctx.log.set({ confirmation: { posting: posting.id } });
});
app/controllers/auth.tsx
import { contextOf } from "@sdxc/auth/remix/context";
import { redirect } from "@sdxc/http/response";
import { Location } from "@sdxc/location";
import { isFailure, wrap } from "@sdxc/result";
import { createAction } from "remix/router";

export default createAction(routes.auth.callback, async (ctx) => {
	let party = relyingParty(ctx.url);
	let grant = await wrap(() => party.callback(contextOf(ctx)));
	if (isFailure(grant)) {
		return ctx.render(<LoginView error={grant.error} />);
	}

	let user = await User.fromProfile(ctx.db, grant.data.profile);
	ctx.log.set({ user: { id: user.id } }).note("auth.signed_in");

	let returnTo = Location.safe(grant.data.returnTo, {
		fallback: routes.dashboard.href(),
	});
	return redirect(returnTo, { status: 303 });
});
app/webhooks/invoices.ts
import { ok, unauthorized } from "@sdxc/http/response/json";
import { isFailure } from "@sdxc/result";
import * as Webhooks from "@sdxc/webhooks";
import * as s from "remix/data-schema";
import { createAction } from "remix/router";

let InvoiceEvent = s.object({
	type: s.string(),
	amount: s.number(),
});

export default createAction(routes.webhook, async (ctx) => {
	let delivery = await Webhooks.verify(ctx.request, {
		secret: env.WEBHOOK_SECRET,
		schema: InvoiceEvent,
	});
	if (isFailure(delivery)) {
		return unauthorized({ error: "invalid_signature" });
	}

	let { payload } = delivery.data;
	await ctx.jobs.enqueue(jobs.recordInvoice, payload);
	return ok({ received: true });
});
app/controllers/rss.ts
import { text, xml } from "@sdxc/http/response";
import { isFailure } from "@sdxc/result";
import { RSS } from "@sdxc/rss";
import { links } from "@sdxc/websub/publisher";
import { createAction } from "remix/router";

export default createAction(routes.rss, async (ctx) => {
	let self = new URL("/rss.xml", ctx.url).href;
	let articles = await Article.published(ctx.db);
	let rss = RSS.stringify({
		channel: {
			title: "Articles",
			description: "Everything published here.",
			link: new URL("/articles", ctx.url).href,
			atomLink: [
				{ rel: "self", href: self },
				{ rel: "hub", href: HUB },
			],
		},
		items: articles.map((article) => ({
			guid: article.id,
			title: article.title,
			description: article.excerpt,
			link: article.url,
			pubDate: article.publishedAt.toUTCString(),
		})),
	});
	if (isFailure(rss)) return text("Internal Server Error", { status: 500 });

	let headers = { link: links({ hubs: [HUB], self }) };
	return xml(rss.data, { headers });
});
app/services/rate-limit.ts
import type { Adapter } from "@sdxc/rate-limit";

import { currentLog } from "@sdxc/logger";
import { tooManyRequests } from "@sdxc/rate-limit";
import { isFailure } from "@sdxc/result";

export async function spend(adapter: Adapter, key: string) {
	let result = await adapter.consume(key);
	if (isFailure(result)) {
		currentLog()?.warn("rate_limit.unavailable", { key });
		return null;
	}
	if (result.data.allowed) return null;

	return tooManyRequests(result.data, adapter, JSON.stringify({ error: "rate_limited" }), {
		headers: { "Content-Type": "application/json" },
	});
}
[ Principles ]

What holds the set together

Web standards first

Request, Response, Web Crypto, Intl and Standard Schema — the platform, not a shim over it.

Errors are values

Every fallible entry point answers with a Result, so nothing throws past you and the failure carries where it happened.

Nothing you didn't ask for

Subpath exports keep what you skip out of your bundle, and 50 packages pull in nothing from outside the collection at all.

Framework-free at the core

49 depend on no framework and run anywhere fetch does. The other 93 add Remix middleware and bindings on top, as exports you opt into.

[ Packages ]

The packages, by the problem they solve

Package inspector142 packages

The sdxc command

1 package

The sdxc command line: start a Remix v3 app, generate its files, copy UI components into it and upgrade it

Install
npm add @sdxc/kit
Entry points
@sdxc/kit@sdxc/kit/doctor@sdxc/kit/config.schema.json@sdxc/kit/lock.schema.json
From the collection
12 packages
From outside it
oxc-parser
Peers
remix
Licence
MIT
Read the @sdxc/kit reference

HTTP & responses

18 packages · 8 standalone · 8 framework-free

Content & formats

23 packages · 10 standalone · 11 framework-free

Operations

24 packages · 5 standalone · 3 framework-free

Three worth the detour

Each of these does something you cannot get by reaching for another package: specs you can run, components that work before any JavaScript loads, and a markdown parser this very page is rendered through.

  • @sdxc/spec runs specifications under permissions you grant by name

  • @sdxc/ui builds on <dialog>, the Popover API and Invoker Commands

  • @sdxc/markdown gives you a typed tree to walk, transform and write back

spec/greeting.spec
use fs
use cli

test "the script prints its greeting" {
	given {
		write "index.js" "console.log(\"hello from the workspace\")"
	}
	when {
		let result = run "node" "index.js"
	}
	then {
		expect result.exit_code 0
		expect result.stdout "hello from the workspace\n"
	}
}
invite-teammate.tsx
import { Button, Dialog, TextField } from "@sdxc/ui";

<Button commandfor="invite" command="show-modal">
	Invite a teammate
</Button>

<Dialog id="invite" aria-labelledby="invite-title">
	<form method="post" action="/team/invites">
		<Dialog.Header>
			<Dialog.Title id="invite-title">
				Invite a teammate
			</Dialog.Title>
			<Dialog.Description>
				They get an email with a link to join.
			</Dialog.Description>
		</Dialog.Header>

		<TextField
			label="Email"
			type="email"
			name="email"
			required
		/>

		<Dialog.Footer>
			<Button
				type="button"
				commandfor="invite"
				command="close"
				variant="outline"
			>
				Cancel
			</Button>
			<Button type="submit">Send invite</Button>
		</Dialog.Footer>
	</form>
</Dialog>;
app/posts/render.ts
import { highlight } from "@sdxc/highlight/markdown";
import { Markdown } from "@sdxc/markdown";
import { toHTML } from "@sdxc/markdown/html";
import { isFailure } from "@sdxc/result";
import * as s from "remix/data-schema";
import * as coerce from "remix/data-schema/coerce";

let Frontmatter = s.object({
	title: s.string(),
	publishedAt: coerce.date(),
});

let parsed = Markdown.parse(source, { frontmatter: Frontmatter });
if (isFailure(parsed)) return parsed;

let walked = Markdown.walk(parsed.data.document, {
	...highlight,
	link(node) {
		if (!node.href.startsWith("/")) return;
		return { ...node, href: new URL(node.href, origin).href };
	},
});
if (isFailure(walked)) return walked;

return { ...parsed.data.frontmatter, html: toHTML(walked.data) };

Versions are dates

A release published on the 21st of September 2026 is 2026.9.21. There is at most one a day, and a later date means a later release and nothing more: it promises no compatibility with the one before it.

So pin exactly, and move when you are ready to read what changed.

package.json
{
	"dependencies": {
		"@sdxc/result": "2026.9.21"
	}
}
[ Sponsors ]

People who fund this work

The collection is written and maintained in the open, funded through GitHub Sponsors by the people and teams below.

Take one, or take the set.

Start with the guides, or go straight to the package you came for.