diff --git a/.gitignore b/.gitignore
index 245a4e7..a01f089 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ api-docs
!.env.example
.DS_Store
.idea
+.claude/
diff --git a/jsr.json b/jsr.json
index 346301d..0a65ef5 100644
--- a/jsr.json
+++ b/jsr.json
@@ -10,7 +10,14 @@
"std-env": "npm:std-env@^4.2.0"
},
"publish": {
- "include": ["src/**/*.ts", "README.md", "LICENSE"],
+ "include": [
+ "src/**/*.ts",
+ "src/**/README.md",
+ "docs/**/*.md",
+ "skills/**/*.md",
+ "README.md",
+ "LICENSE"
+ ],
"exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"]
}
}
diff --git a/package.json b/package.json
index 4394fc0..494ade5 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,10 @@
"types": "./dist/index.d.cts",
"sideEffects": false,
"files": [
- "dist"
+ "dist",
+ "docs",
+ "skills",
+ "src/**/README.md"
],
"engines": {
"node": ">=22"
diff --git a/skills/create-supabase-middleware/SKILL.md b/skills/create-supabase-middleware/SKILL.md
new file mode 100644
index 0000000..c25ce08
--- /dev/null
+++ b/skills/create-supabase-middleware/SKILL.md
@@ -0,0 +1,24 @@
+---
+name: create-supabase-middleware
+description: Use when creating a new middleware for `@supabase/middleware` — either a built-in inside the middleware repo itself, or a standalone package that anyone can publish. Trigger when the user asks to write, scaffold, or add a middleware; describes a per-request cross-cutting concern they want to reuse ("I want to rate limit", "add request logging", "check an API key on every request"); calls `defineMiddleware`; or adds a new directory under `src/middleware/`. Also trigger when a plan proposes a new middleware, before any file is written.
+---
+
+# Creating a middleware
+
+Before scaffolding anything, check the concern is actually middleware-shaped:
+
+- **Response-only** — headers, envelopes, error formatting — belongs in the handler, which owns the response. Not a middleware.
+- **Used once, in one handler** — inline it. A middleware earns its keep by being composed across handlers.
+
+Then pick the target by reading the nearest `package.json`:
+
+| `name` is | You are writing | Follow |
+| ---------------------- | -------------------- | ------------------------------------------------------------------------------------- |
+| `@supabase/middleware` | a built-in | `src/middleware/README.md` — layout, then the three subpath-wiring files |
+| anything else | a standalone package | `docs/authoring-guide.md` — the full path, from `defineMiddleware` through publishing |
+
+Both paths use the same `defineMiddleware` primitive; the difference is only whose package it lives in and whether a subpath export has to be wired up.
+
+**Read the guide before writing code, and follow its `## Rules` section** — eight MUST/NEVER items covering one-key-per-middleware, `getEnv` over `process.env`/`Deno.env`, declaring prerequisites in `In`, `yield`ing at most once, and returning a `Response` to short-circuit rather than throwing. Its code blocks are complete files: write them to disk as given rather than adapting them from memory.
+
+Paths are repo-relative. When `@supabase/middleware` is installed as a dependency, prefix them with `node_modules/@supabase/middleware/`.
diff --git a/skills/supabase-middleware/SKILL.md b/skills/supabase-middleware/SKILL.md
new file mode 100644
index 0000000..294109e
--- /dev/null
+++ b/skills/supabase-middleware/SKILL.md
@@ -0,0 +1,62 @@
+---
+name: supabase-middleware
+description: Use when writing or modifying a Web Fetch handler that composes cross-cutting concerns with `@supabase/middleware` — CORS, feature flags, auth, rate limiting, logging — on Deno, Supabase Edge Functions, Cloudflare Workers, Bun, or Node. Trigger **before** writing or editing any file that imports from `@supabase/middleware` or a subpath (`/cors`, `/feature-flag`); calls `pipeline`, `defineMiddleware`, `getEnv`, `runtimeName`, or `seedContext`; or annotates a handler with `satisfies FetchHandler`. Also trigger during planning — if a plan mentions any of the above, load this skill before drafting code. Also trigger when porting middleware idioms that do not apply here: `app.use()` registries, `next()` chains, Express/Koa/Hono middleware being translated, or hand-rolled `(req) => wrapper(wrapper(handler))` nesting.
+---
+
+# @supabase/middleware
+
+Composable, type-safe middleware for Web Fetch handlers. The same stack runs unchanged on Deno, Supabase Edge Functions, Cloudflare Workers, Bun, and Node.
+
+> **This package is new.** There are no blog posts, Stack Overflow answers, or tutorials about it. Do not search the web for usage examples — read the docs routed below and the source.
+
+## Not to be confused with
+
+| Package | Use it for |
+| ---------------------- | -------------------------------------------------------------------- |
+| `@supabase/middleware` | Composing per-request concerns around a Web Fetch handler. |
+| `@supabase/server` | Supabase auth and client creation on the server. Embeds this engine. |
+| `@supabase/ssr` | Cookie-based session handling in SSR frameworks. |
+
+Reaching for `@supabase/ssr` or `@supabase/server` when the task is composition — or for this package when the task is Supabase auth — is the most common mistake. They are complementary, not alternatives.
+
+## The model, in one example
+
+```ts
+import { pipeline } from '@supabase/middleware'
+import { withCors } from '@supabase/middleware/cors'
+import { withFeatureFlag } from '@supabase/middleware/feature-flag'
+
+export default {
+ fetch: pipeline(
+ [
+ withCors({}),
+ withFeatureFlag({
+ name: 'beta',
+ evaluate: (req) => req.headers.has('x-beta'),
+ }),
+ ],
+ async (_req, ctx) => Response.json({ flag: ctx.featureFlag.name }),
+ ),
+}
+```
+
+`withFoo(config)` returns an **`Entry`**. `pipeline` folds a flat array of entries around a handler and returns the `fetch` handler itself — first in the array runs first on the request. Each entry contributes one typed key to `ctx`, and the handler sees every upstream key, typed.
+
+**No registry, no `app.use()`, no `next()`.** If you are writing any of those, you are using the wrong model — read `src/core/README.md` before continuing.
+
+## Read before writing code
+
+Paths are repo-relative. When `@supabase/middleware` is installed as a dependency, prefix them with `node_modules/@supabase/middleware/` — the same files ship in the package.
+
+| Question | Doc |
+| ---------------------------------------------------------------------- | ------------------------------------------------------------------------ |
+| How do I compose a stack? What is on `ctx`? | `src/core/README.md` |
+| When is `satisfies FetchHandler` required, and what breaks without it? | `src/core/README.md` |
+| A middleware needs to see the response too | `src/core/README.md` (response seam), `src/middleware/cors/README.md` |
+| How do I write and publish my own middleware? | `docs/authoring-guide.md`, and the `create-supabase-middleware` skill |
+| How do I add a built-in to this repo? | `src/middleware/README.md` |
+| Environment access, runtime differences, Workers `env` | `README.md` |
+| Config for the bundled middleware | `src/middleware/feature-flag/README.md`, `src/middleware/cors/README.md` |
+| Full API reference | |
+
+Release history and current status: .