Skip to content

Commit

Permalink
Documentation website (#20)
Browse files Browse the repository at this point in the history
* feat: starting documentation website

* docs: progress

* docs: .gitignore

* docs: progress

* docs: prepare for wip release

* fix: tests
  • Loading branch information
magne4000 authored Aug 23, 2024
1 parent e9989c4 commit fda7a5c
Show file tree
Hide file tree
Showing 30 changed files with 2,027 additions and 56 deletions.
65 changes: 65 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Sample workflow for building and deploying a VitePress site to GitHub Pages
#
name: Deploy VitePress site to Pages

on:
# Runs on pushes targeting the `main` branch. Change this to `master` if you're
# using the `master` branch as the default branch.
push:
branches: [main]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: pages
cancel-in-progress: false

jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled
- uses: pnpm/action-setup@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Install dependencies
run: pnpm i
- name: Build with VitePress
run: pnpm run build-doc
with:
path: docs
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ dist
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
# vuepress/vitepress build output
.vuepress/dist
*/.vitepress/dist
*/.vitepress/cache

# vuepress v2.x temp and cache directory
.temp
Expand Down
3 changes: 3 additions & 0 deletions .idea/universal-handler.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 54 additions & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { defineConfig } from "vitepress";
import { transformerTwoslash } from "@shikijs/vitepress-twoslash";
import type { ModuleResolutionKind } from "typescript";

// https://vitepress.dev/reference/site-config
export default defineConfig({
cleanUrls: true,
markdown: {
codeTransformers: [
transformerTwoslash({
twoslashOptions: {
compilerOptions: {
verbatimModuleSyntax: true,
moduleResolution: 100 satisfies ModuleResolutionKind.Bundler,
},
},
}),
],
},
title: "universal-middleware",
description:
"Write universal middlewares and handlers once, target all supported servers",
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: "Guide", link: "/guide/what-is-universal-middleware" },
{ text: "Examples", link: "/examples/context-middleware" },
],

sidebar: [
{
text: "Middlewares",
items: [
{
text: "Updating the context",
link: "/examples/context-middleware",
},
{
text: "Updating the headers",
link: "/examples/headers-middleware",
},
{
text: "Return an early response",
link: "/examples/guard-middleware",
},
],
},
],

socialLinks: [
{ icon: "github", link: "https://github.com/vuejs/vitepress" },
],
},
});
11 changes: 11 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import DefaultTheme from "vitepress/theme";
import TwoslashFloatingVue from "@shikijs/vitepress-twoslash/client";
import "@shikijs/vitepress-twoslash/style.css";
import type { EnhanceAppContext } from "vitepress";

export default {
extends: DefaultTheme,
enhanceApp({ app }: EnhanceAppContext) {
app.use(TwoslashFloatingVue);
},
};
49 changes: 49 additions & 0 deletions docs/definitions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Definitions

## Handler
A function that returns a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response).

```ts twoslash
export interface UniversalHandler<Context> {
(request: Request, context: Context): Response | Promise<Response>;
}
```

> [!NOTE]
> In some frameworks, a Response can be anything from a web Response, a string, an object, a ServerResponse, etc.
> Only web Response are supported by `universal-middleware`, as this is part of the [WinterCG standard](https://fetch.spec.wintercg.org/#responses).
## Middleware
A function that alters the [Context](#context) or Response.
Check the [examples](/examples/context-middleware) for details.

```ts twoslash
export interface UniversalMiddleware<InContext, OutContext> {
(request: Request, context: InContext):
| Response | Promise<Response> // Can return an early Response
| void | Promise<void> // Can return nothing
| OutContext | Promise<OutContext> // Can return a new context. Ensures type-safe context representation
| ((response: Response) => Response | Promise<Response>); // Can return a function that manipulates the Response
}
```

## Context
Some data, usually added by a middleware, with the same lifespan as a Request.

For instance, a `{ user?: User }` context can be set by a middleware, then accessed by others.

> [!NOTE]
> Each framework as its own way to attach data related to the request,
> either by attaching values to their internal request representation (like Express),
> or by having their own context encapsulating the request (like Hono).
>
> _On the other hand, a universal context is just an object, detached from the request,
> but with the same lifespan._
> [!TIP]
> Each adapter provides a `getContext` helper to retrieve the universal context
> from any non-universal middleware or handler.
> [!NOTE]
> TODO `Universal.Context`
122 changes: 122 additions & 0 deletions docs/examples/context-middleware.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Updating the Context

In this example, we're creating a middleware that adds a `hello` property to the context.
This property will then be accessible to any subsequent middleware or handler.

<<< @/../examples/tool/src/middlewares/context.middleware.ts

After bundling and publishing this middleware, one can then use this middleware as follows:

::: code-group

```ts twoslash [hono-entry.ts]
import { Hono } from "hono";
import contextMiddleware from "@universal-middleware-examples/tool/middlewares/context-middleware-hono";
import { getContext } from "@universal-middleware/hono";

const app = new Hono();

// Now the universal context contains `{ hello: "world" }`
app.use(contextMiddleware("world"));

app.get("/", (honoCtx) => {
// The universal context can be retrieved through `getContext` helper
// outside of universal middlewares and handlers
const universalCtx = getContext<{ hello: string }>(honoCtx)!;
return new Response(`Hello ${universalCtx.hello}`);
});

export default app;
```

```ts twoslash [h3-entry.ts]
import { createApp, createRouter, defineEventHandler } from "h3";
import contextMiddleware from "@universal-middleware-examples/tool/middlewares/context-middleware-h3";
import { getContext, universalOnBeforeResponse } from "@universal-middleware/h3";

const app = createApp({
// /!\ This is required for universal-middleware to operate properly
onBeforeResponse: universalOnBeforeResponse,
});

// Now the universal context contains `{ hello: "world" }`.
app.use(contextMiddleware("world"));

const router = createRouter();

router.get("/", defineEventHandler((event) => {
// The universal context can be retrieved through `getContext` helper
// outside of universal middlewares and handlers
const universalCtx = getContext<{ hello: string }>(event)!;

return `Hello ${universalCtx.hello}`;
}));

app.use(router);

export default app;
```

```ts twoslash [hattip-entry.ts]
import { createRouter } from "@hattip/router";
import contextMiddleware from "@universal-middleware-examples/tool/middlewares/context-middleware-hattip";
import { getContext } from "@universal-middleware/hattip";

const app = createRouter();

// Now the universal context contains `{ hello: "world" }`.
app.use(contextMiddleware("world"));

app.get("/", (honoCtx) => {
// The universal context can be retrieved through `getContext` helper
// outside of universal middlewares and handlers
const universalCtx = getContext<{ hello: string }>(honoCtx)!;
return new Response(`Hello ${universalCtx.hello}`);
});

const hattipHandler = app.buildHandler();

export default hattipHandler;
```

```ts twoslash [express-entry.ts]
import contextMiddleware from "@universal-middleware-examples/tool/middlewares/context-middleware-express";
import express from "express";
import { getContext } from "@universal-middleware/express";

const app = express();

// Now the universal context contains `{ hello: "world" }`.
app.use(contextMiddleware("world"));

app.get("/", (req, res) => {
// The universal context can be retrieved through `getContext` helper
// outside of universal middlewares and handlers
const universalCtx = getContext<{ hello: string }>(req)!;
res.send(`Hello ${universalCtx.hello}`);
});

export default app;
```

```ts twoslash [fastify-entry.ts]
import contextMiddleware from "@universal-middleware-examples/tool/middlewares/context-middleware-fastify";
import fastify from "fastify";
import { getContext } from "@universal-middleware/fastify";

const app = fastify();

// Now the universal context contains `{ hello: "world" }`.
app.register(contextMiddleware("world"));

app.get("/", (req, reply) => {
// The universal context can be retrieved through `getContext` helper
// outside of universal middlewares and handlers
const universalCtx = getContext<{ hello: string }>(req)!;
reply.send(`Hello ${universalCtx.hello}`);
});

export default app;
```

:::
Loading

0 comments on commit fda7a5c

Please sign in to comment.