Skip to content

Commit

Permalink
feat: header utils (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
LeeCheneler authored Jan 4, 2025
1 parent 4c6fb53 commit bbea08f
Show file tree
Hide file tree
Showing 12 changed files with 747 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .dvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.6
2.1.4
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ git commit -m "docs: update documentation"
...
```

### Releasing
### Releasing

Releases are automatically created when a pull request is merged if the version
in [deno.json](./deno.json) is updated. The version should be updated following
Expand Down
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,6 @@ Serve a file from the file system.
await context.serveFile("path/to/file");
```

####

### Cookies

You can read cookies from the request.
Expand Down Expand Up @@ -362,3 +360,30 @@ To run your app, you can use the `Deno.serve` function:
```tsx
Deno.serve(app.build());
```

## Header utilities

Some utility methods are available to configure common complex response headers.

### `cacheControl`

Set the `Cache-Control` header.

```tsx
cacheControl(context, {
maxAge: 60,
});
```

### `contentSecurityPolicy`

Set the `Content-Security-Policy` header.

```tsx
contentSecurityPolicy(context, {
directives: {
defaultSrc: "'self'",
scriptSrc: ["'self'", "https://example.com"],
},
});
```
4 changes: 2 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mage/server",
"version": "0.9.0",
"version": "0.10.0",
"license": "MIT",
"exports": "./mod.ts",
"tasks": {
Expand All @@ -16,7 +16,7 @@
"@std/fs": "jsr:@std/fs@^1.0.5",
"@std/http": "jsr:@std/http@^1.0.10",
"@std/path": "jsr:@std/path@^1.0.8",
"@std/testing": "jsr:@std/testing@^1.0.5",
"@std/testing": "jsr:@std/testing@^1.0.8",
"preact": "npm:preact@^10.25.0",
"preact-render-to-string": "npm:preact-render-to-string@^6.5.11"
},
Expand Down
76 changes: 58 additions & 18 deletions deno.lock

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

6 changes: 5 additions & 1 deletion mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ export type { MageMiddleware } from "./src/router.ts";
export { HttpMethod, StatusCode, StatusText } from "./src/http.ts";
export type { CookieOptions } from "./src/cookies.ts";

//middleware
// middleware
export { useCors } from "./src/middleware/cors.ts";
export { useMethodNotAllowed } from "./src/middleware/method-not-allowed.ts";
export { useNotFound } from "./src/middleware/not-found.ts";
export { useOptions } from "./src/middleware/options.ts";
export { useSecurityHeaders } from "./src/middleware/security-headers.ts";
export { useServeFiles } from "./src/middleware/serve-files.ts";

// headers
export { cacheControl } from "./src/headers/cache-control.ts";
export { contentSecurityPolicy } from "./src/headers/content-security-policy.ts";
122 changes: 122 additions & 0 deletions src/headers/cache-control.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import type { MageContext } from "../context.ts";

interface CacheControlOptions {
/**
* The number of seconds the response can be cached until it is stale.
*/
maxAge?: number;
/**
* The number of seconds the response can be cached in shared cache until it is stale.
*/
sMaxAge?: number;
/**
* Can be cached but origin server must be checked befor use.
*/
noCache?: boolean;
/**
* No caches of the response can be stored.
*/
noStore?: boolean;
/**
* Do not transform response contents when caching.
*/
noTransform?: boolean;
/**
* Must revalidate stale cache entry before using it.
*/
mustRevalidate?: boolean;
/**
* Must revalidate stale cache entry before using it (shared cache only).
*/
proxyRevalidate?: boolean;
/**
* Responses can be cached only if store understands caching requirements based on status code.
*/
mustUnderstand?: boolean;
/**
* Can be stored in private caches only (non-shared, ie browser cache).
*/
private?: boolean;
/**
* Can be stored in public caches.
*/
public?: boolean;
/**
* The cache response should not be updated while the cache is stll fresh.
*/
immutable?: boolean;
/**
* The number of seconds a stale cache response can be used while the cache is being revalidated.
*/
staleWhileRevalidate?: number;
/**
* The number of seconds a stale cache response can be used if the origin server is not available.
*/
staleIfError?: number;
}

/**
* Apply a Cache-Control header based on the provided options.
*
* @returns string
*/
export const cacheControl = (
context: MageContext,
options: CacheControlOptions,
): void => {
const values = [];

if (options.maxAge) {
values.push(`max-age=${options.maxAge}`);
}

if (options.sMaxAge) {
values.push(`s-maxage=${options.sMaxAge}`);
}

if (options.noCache) {
values.push("no-cache");
}

if (options.noStore) {
values.push("no-store");
}

if (options.noTransform) {
values.push("no-transform");
}

if (options.mustRevalidate) {
values.push("must-revalidate");
}

if (options.proxyRevalidate) {
values.push("proxy-revalidate");
}

if (options.mustUnderstand) {
values.push("must-understand");
}

if (options.private) {
values.push("private");
}

if (options.public) {
values.push("public");
}

if (options.immutable) {
values.push("immutable");
}

if (options.staleWhileRevalidate) {
values.push(`stale-while-revalidate=${options.staleWhileRevalidate}`);
}

if (options.staleIfError) {
values.push(`stale-if-error=${options.staleIfError}`);
}

context.response.headers.set("Cache-Control", values.join(", "));
};
Loading

0 comments on commit bbea08f

Please sign in to comment.