Skip to content
Merged
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
File renamed without changes.
1 change: 1 addition & 0 deletions .commands/test-types.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ tsc -p tests/plugins/openApiGenerator/tsconfig.json
tsc -p tests/plugins/cacheController/tsconfig.json
tsc -p tests/plugins/static/tsconfig.json
tsc -p tests/plugins/cors/tsconfig.json
tsc -p tests/plugins/cookie/tsconfig.json
# integration
npm -w integration run test:types
# documentation
Expand Down
18 changes: 15 additions & 3 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ export default pipe(
"// @filename: @duplojs/http/cacheController.ts",
`export * from "@v${namedGroups?.version ?? ""}/cacheController";`,

"// @filename: @duplojs/http/cookie.ts",
`export * from "@v${namedGroups?.version ?? ""}/cookie";`,

"// @filename: index.ts",
"// ---cut---",
],
Expand Down Expand Up @@ -145,6 +148,7 @@ export default pipe(
"@v0/static": ["libs/v0/plugins/static/index"],
"@v0/cors": ["libs/v0/plugins/cors/index"],
"@v0/cacheController": ["libs/v0/plugins/cacheController/index"],
"@v0/cookie": ["libs/v0/plugins/cookie/index"],
},
},
},
Expand Down Expand Up @@ -288,6 +292,10 @@ export default pipe(
text: "Contrôle du cache",
link: "/fr/v0/guide/plugins/cacheController",
},
{
text: "Gestion Cookie",
link: "/fr/v0/guide/plugins/cookie",
},
],
},
{
Expand Down Expand Up @@ -434,15 +442,19 @@ export default pipe(
},
{
text: "Create a static entry point",
link: "/fr/v0/guide/plugins/static",
link: "/en/v0/guide/plugins/static",
},
{
text: "CORS Management",
link: "/fr/v0/guide/plugins/cors",
link: "/en/v0/guide/plugins/cors",
},
{
text: "Cache Control",
link: "/fr/v0/guide/plugins/cacheController",
link: "/en/v0/guide/plugins/cacheController",
},
{
text: "Cookie Management",
link: "/en/v0/guide/plugins/cookie",
},
],
},
Expand Down
4 changes: 2 additions & 2 deletions docs/en/v0/guide/features/formData.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
description: "Handle complex FormData payloads."
prev:
text: "Cache control"
link: "/en/v0/guide/plugins/cacheController"
text: "Cookie handling"
link: "/en/v0/guide/plugins/cookie"
next:
text: "Server-Sent Events (SSE)"
link: "/en/v0/guide/features/SSE"
Expand Down
4 changes: 2 additions & 2 deletions docs/en/v0/guide/plugins/cacheController.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ prev:
text: "CORS handling"
link: "/en/v0/guide/plugins/cors"
next:
text: "Advanced FormData"
link: "/en/v0/guide/features/formData"
text: "Cookie handling"
link: "/en/v0/guide/plugins/cookie"
---

# Cache control
Expand Down
68 changes: 68 additions & 0 deletions docs/en/v0/guide/plugins/cookie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
description: "Parse and serialize HTTP cookies"
prev:
text: "Cache control"
link: "/en/v0/guide/plugins/cacheController"
next:
text: "Advanced FormData"
link: "/en/v0/guide/features/formData"
---

# Cookie handling

`@duplojs/http/cookie` lets you read cookies sent by the client and send them back easily in responses.

It is useful when you want to:

- read a value from incoming cookies
- set a new cookie in a response
- ask the client to delete an existing cookie

## With `cookiePlugin`

```ts twoslash {2,7-9,22-24}
// @version: 0
<!--@include: @/examples/v0/guide/plugins/cookie/plugin.ts-->
```

In this example:

- the plugin is registered once on the `Hub`
- every route registered in this `Hub` then benefits from input parsing and output serialization
- the route extracts `session` directly from `cookies`
- the handler also sends back a new cookie with `setCookie`

This is the simplest approach when cookie support should be available across your whole application.

You can also pass your own `parser` and `serializer` to the plugin.
This can be useful for signed cookies, custom encoding rules, or any project-specific format.

If you use `cookiePlugin` globally, you can exclude a specific route with `IgnoreRouteCookieMetadata`.
This lets you keep the plugin enabled everywhere while automatically removing cookie hooks from some routes.

## With hooks directly on one route

On a route, there are three ways to register cookie hooks:

- let `cookiePlugin` add them automatically
- add `cookieHooks` to get input parsing and output serialization at once
- add `parseRequestCookieHook` and `serializeResponseCookieHook` separately if you want more targeted behavior

If you want both behaviors directly on a route without using the plugin, `cookieHooks` is the simplest form.

```ts twoslash {2,6-9}
// @version: 0
<!--@include: @/examples/v0/guide/plugins/cookie/cookieHooks.ts-->
```

Here, the route gets the standard plugin behavior, but only for itself.
This is often the most practical form when you want to enable cookies route by route.

## With the two hooks separately on one route
```ts twoslash {2,5-12}
// @version: 0
<!--@include: @/examples/v0/guide/plugins/cookie/routeHooks.ts-->
```

This form is the most flexible.
It is useful when you only want one hook, or when input parsing and output serialization should be handled separately.
35 changes: 35 additions & 0 deletions docs/examples/v0/guide/plugins/cookie/cookieHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { createHub, ResponseContract, useRouteBuilder } from "@duplojs/http";
import { cookieHooks } from "@duplojs/http/cookie";
import { DPE } from "@duplojs/utils";

const route = useRouteBuilder("GET", "/admin/session", {
hooks: [
// defaultParser and defaultSerializer are use if nothing is given
cookieHooks(),
],
})
.extract({
cookies: {
session: DPE.string(),
},
})
.handler(
ResponseContract.ok(
"admin.session.read",
DPE.object({
session: DPE.string(),
}),
),
({ session }, { response }) => response(
"admin.session.read",
{
session,
},
).setCookie("admin-session", session, {
httpOnly: true,
path: "/admin",
}),
);

const hub = createHub({ environment: "DEV" })
.register(route);
25 changes: 25 additions & 0 deletions docs/examples/v0/guide/plugins/cookie/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createHub, ResponseContract, useRouteBuilder } from "@duplojs/http";
import { cookiePlugin } from "@duplojs/http/cookie";
import { DPE } from "@duplojs/utils";

const route = useRouteBuilder("GET", "/profile")
.extract({
cookies: {
session: DPE.string(),
},
})
.handler(
ResponseContract.noContent("profile.read"),
(floor, { response }) => response("profile.read")
.setCookie("last-route", "profile", {
httpOnly: true,
path: "/",
sameSite: "lax",
}),
);

const hub = createHub({ environment: "DEV" })
.plug(
cookiePlugin(),
)
.register(route);
13 changes: 13 additions & 0 deletions docs/examples/v0/guide/plugins/cookie/routeHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useRouteBuilder } from "@duplojs/http";
import { defaultParser, defaultSerializer, parseRequestCookieHook, serializeResponseCookieHook } from "@duplojs/http/cookie";

const route = useRouteBuilder("GET", "/admin/session", {
hooks: [
parseRequestCookieHook({
parser: defaultParser, // or custom parser
}),
serializeResponseCookieHook({
serializer: defaultSerializer, // or custom serializer
}),
],
}); // ...
4 changes: 2 additions & 2 deletions docs/fr/v0/guide/features/formData.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
description: "Gérer des FormData à structure complexe."
prev:
text: "Contrôle du cache"
link: "/fr/v0/guide/plugins/cacheController"
text: "Gestion cookie"
link: "/fr/v0/guide/plugins/cookie"
next:
text: "Server-Sent Events (SSE)"
link: "/fr/v0/guide/features/SSE"
Expand Down
4 changes: 2 additions & 2 deletions docs/fr/v0/guide/plugins/cacheController.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ prev:
text: "Gestion CORS"
link: "/fr/v0/guide/plugins/cors"
next:
text: "FormData avancés"
link: "/fr/v0/guide/features/formData"
text: "Gestion cookie"
link: "/fr/v0/guide/plugins/cookie"
---

# Contrôle du cache
Expand Down
68 changes: 68 additions & 0 deletions docs/fr/v0/guide/plugins/cookie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
description: "Parser et sérialiser les cookies HTTP"
prev:
text: "Contrôle du cache"
link: "/fr/v0/guide/plugins/cacheController"
next:
text: "FormData avancés"
link: "/fr/v0/guide/features/formData"
---

# Gestion cookie

`@duplojs/http/cookie` permet de lire les cookies envoyés par le client et d'en renvoyer facilement dans les réponses.

Il est utile si vous voulez :

- récupérer une valeur depuis les cookies d'entrée
- poser un nouveau cookie dans une réponse
- demander au client de supprimer un cookie existant

## Avec `cookiePlugin`

```ts twoslash {2,7-9,22-24}
// @version: 0
<!--@include: @/examples/v0/guide/plugins/cookie/plugin.ts-->
```

Dans cet exemple :

- le plugin est branché une fois sur le `Hub`
- toutes les routes enregistrées dans ce `Hub` bénéficient alors du parsing d'entrée et de la sérialisation de sortie
- la route extrait directement `session` depuis `cookies`
- le handler renvoie aussi un nouveau cookie avec `setCookie`

Cette approche est la plus simple si le support des cookies doit être disponible partout dans votre application.

Vous pouvez aussi passer votre propre `parser` et votre propre `serializer` au plugin.
Cela permet par exemple de gérer des cookies signés, de centraliser une logique d'encodage particulière, ou d'adapter le format à une contrainte métier.

Si vous utilisez `cookiePlugin` globalement, vous pouvez exclure une route précise avec `IgnoreRouteCookieMetadata`.
Cela permet de garder le plugin activé partout, tout en retirant automatiquement les hooks cookie sur certaines routes.

## Avec les hooks directement sur une route

Sur une route, il existe trois façons de brancher les hooks cookie :

- laisser `cookiePlugin` les ajouter automatiquement
- ajouter `cookieHooks` pour récupérer le parsing d'entrée et la sérialisation de sortie d'un coup
- ajouter `parseRequestCookieHook` et `serializeResponseCookieHook` séparément si vous voulez un comportement plus ciblé

Si vous voulez brancher les deux comportements directement sur une route sans passer par le plugin, `cookieHooks` est la forme la plus simple.

```ts twoslash {2,6-9}
// @version: 0
<!--@include: @/examples/v0/guide/plugins/cookie/cookieHooks.ts-->
```

Ici, la route récupère le comportement standard du plugin, mais seulement pour elle.
C'est souvent la forme la plus pratique si vous voulez activer les cookies route par route.

## Avec les deux hooks séparés sur une route
```ts twoslash {2,5-12}
// @version: 0
<!--@include: @/examples/v0/guide/plugins/cookie/routeHooks.ts-->
```

Cette forme est la plus flexible.
Elle est utile si vous voulez n'ajouter qu'un seul hook, ou traiter séparément le parsing d'entrée et la sérialisation de sortie.
2 changes: 1 addition & 1 deletion docs/libs/v0/core/functionsBuilders/router/create.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { MaybePromise } from "@duplojs/utils";
import { type Request } from "../../request";
import { type BuildedRouter } from "../../router";
import { type RouterElementSystem } from "../../router/types/routerElementSystem";
import { type RouterElementWrapper } from "../../router/types/routerElementWrapper";
import { type Environment } from "../../types";
import { type MaybePromise } from "@duplojs/utils";
export interface RouterFunctionBuilderParams {
readonly environment: Environment;
readonly routerElementWrapper: RouterElementWrapper;
Expand Down
15 changes: 15 additions & 0 deletions docs/libs/v0/plugins/cookie/hooks/cookieHooks.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

var parser = require('../parser.cjs');
var serialize = require('../serialize.cjs');
var parseRequestCookie = require('./parseRequestCookie.cjs');
var serializeResponseCookie = require('./serializeResponseCookie.cjs');

function cookieHooks({ parser: parser$1 = parser.defaultParser, serializer = serialize.defaultSerializer, } = {}) {
return {
...parseRequestCookie.parseRequestCookieHook({ parser: parser$1 }),
...serializeResponseCookie.serializeResponseCookieHook({ serializer }),
};
}

exports.cookieHooks = cookieHooks;
11 changes: 11 additions & 0 deletions docs/libs/v0/plugins/cookie/hooks/cookieHooks.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { type Parser } from "../parser";
import { type Serializer } from "../serialize";
interface CookieHooksParams {
parser?: Parser;
serializer?: Serializer;
}
export declare function cookieHooks({ parser, serializer, }?: CookieHooksParams): {
beforeSendResponse: ({ currentResponse, next }: import("../../../core/route").RouteHookParamsAfter) => import("../../../core/route").RouteHookNext;
beforeRouteExecution: ({ request, next }: import("../../../core/route").RouteHookParams) => import("../../../core/route").RouteHookNext;
};
export {};
13 changes: 13 additions & 0 deletions docs/libs/v0/plugins/cookie/hooks/cookieHooks.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defaultParser } from '../parser.mjs';
import { defaultSerializer } from '../serialize.mjs';
import { parseRequestCookieHook } from './parseRequestCookie.mjs';
import { serializeResponseCookieHook } from './serializeResponseCookie.mjs';

function cookieHooks({ parser = defaultParser, serializer = defaultSerializer, } = {}) {
return {
...parseRequestCookieHook({ parser }),
...serializeResponseCookieHook({ serializer }),
};
}

export { cookieHooks };
11 changes: 11 additions & 0 deletions docs/libs/v0/plugins/cookie/hooks/index.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

var parseRequestCookie = require('./parseRequestCookie.cjs');
var serializeResponseCookie = require('./serializeResponseCookie.cjs');
var cookieHooks = require('./cookieHooks.cjs');



exports.parseRequestCookieHook = parseRequestCookie.parseRequestCookieHook;
exports.serializeResponseCookieHook = serializeResponseCookie.serializeResponseCookieHook;
exports.cookieHooks = cookieHooks.cookieHooks;
3 changes: 3 additions & 0 deletions docs/libs/v0/plugins/cookie/hooks/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./parseRequestCookie";
export * from "./serializeResponseCookie";
export * from "./cookieHooks";
3 changes: 3 additions & 0 deletions docs/libs/v0/plugins/cookie/hooks/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { parseRequestCookieHook } from './parseRequestCookie.mjs';
export { serializeResponseCookieHook } from './serializeResponseCookie.mjs';
export { cookieHooks } from './cookieHooks.mjs';
Loading
Loading