raffel 0.1.2 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/README.md +314 -346
  2. package/dist/adapters/index.d.ts +3 -1
  3. package/dist/adapters/index.d.ts.map +1 -1
  4. package/dist/adapters/index.js +3 -1
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/adapters/s3db/adapter.d.ts.map +1 -1
  7. package/dist/adapters/s3db/adapter.js +0 -3
  8. package/dist/adapters/s3db/adapter.js.map +1 -1
  9. package/dist/adapters/udp.d.ts +83 -0
  10. package/dist/adapters/udp.d.ts.map +1 -0
  11. package/dist/adapters/udp.int.test.d.ts +5 -0
  12. package/dist/adapters/udp.int.test.d.ts.map +1 -0
  13. package/dist/adapters/udp.int.test.js +397 -0
  14. package/dist/adapters/udp.int.test.js.map +1 -0
  15. package/dist/adapters/udp.js +391 -0
  16. package/dist/adapters/udp.js.map +1 -0
  17. package/dist/cache/drivers/file.d.ts.map +1 -1
  18. package/dist/cache/drivers/file.js +13 -1
  19. package/dist/cache/drivers/file.js.map +1 -1
  20. package/dist/cache/drivers/memory.d.ts.map +1 -1
  21. package/dist/cache/drivers/memory.js +1 -0
  22. package/dist/cache/drivers/memory.js.map +1 -1
  23. package/dist/cache/types.d.ts +1 -0
  24. package/dist/cache/types.d.ts.map +1 -1
  25. package/dist/docs/generators/http-generator.d.ts.map +1 -1
  26. package/dist/docs/generators/http-generator.js +0 -1
  27. package/dist/docs/generators/http-generator.js.map +1 -1
  28. package/dist/graphql/graphql.int.test.d.ts +10 -0
  29. package/dist/graphql/graphql.int.test.d.ts.map +1 -0
  30. package/dist/graphql/graphql.int.test.js +698 -0
  31. package/dist/graphql/graphql.int.test.js.map +1 -0
  32. package/dist/graphql/schema-generator.d.ts.map +1 -1
  33. package/dist/graphql/schema-generator.js +20 -7
  34. package/dist/graphql/schema-generator.js.map +1 -1
  35. package/dist/http/auth.d.ts.map +1 -1
  36. package/dist/http/auth.js +15 -1
  37. package/dist/http/auth.js.map +1 -1
  38. package/dist/http/http.int.test.d.ts +7 -0
  39. package/dist/http/http.int.test.d.ts.map +1 -0
  40. package/dist/http/http.int.test.js +604 -0
  41. package/dist/http/http.int.test.js.map +1 -0
  42. package/dist/http/index.d.ts +2 -0
  43. package/dist/http/index.d.ts.map +1 -1
  44. package/dist/http/index.js +2 -0
  45. package/dist/http/index.js.map +1 -1
  46. package/dist/http/oauth2.d.ts.map +1 -1
  47. package/dist/http/oauth2.js +39 -0
  48. package/dist/http/oauth2.js.map +1 -1
  49. package/dist/http/oidc.d.ts.map +1 -1
  50. package/dist/http/oidc.js +9 -1
  51. package/dist/http/oidc.js.map +1 -1
  52. package/dist/http/session-redis.d.ts +187 -0
  53. package/dist/http/session-redis.d.ts.map +1 -0
  54. package/dist/http/session-redis.int.test.d.ts +8 -0
  55. package/dist/http/session-redis.int.test.d.ts.map +1 -0
  56. package/dist/http/session-redis.int.test.js +492 -0
  57. package/dist/http/session-redis.int.test.js.map +1 -0
  58. package/dist/http/session-redis.js +320 -0
  59. package/dist/http/session-redis.js.map +1 -0
  60. package/dist/index.d.ts +2 -1
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +25 -0
  63. package/dist/index.js.map +1 -1
  64. package/dist/mcp/cli.js +2 -1
  65. package/dist/mcp/cli.js.map +1 -1
  66. package/dist/mcp/docs/adapters.d.ts.map +1 -1
  67. package/dist/mcp/docs/adapters.js +175 -145
  68. package/dist/mcp/docs/adapters.js.map +1 -1
  69. package/dist/mcp/docs/interceptors.d.ts +1 -1
  70. package/dist/mcp/docs/interceptors.d.ts.map +1 -1
  71. package/dist/mcp/docs/interceptors.js +231 -305
  72. package/dist/mcp/docs/interceptors.js.map +1 -1
  73. package/dist/mcp/docs/patterns.d.ts.map +1 -1
  74. package/dist/mcp/docs/patterns.js +20 -18
  75. package/dist/mcp/docs/patterns.js.map +1 -1
  76. package/dist/mcp/docs/quickstart.d.ts +1 -1
  77. package/dist/mcp/docs/quickstart.d.ts.map +1 -1
  78. package/dist/mcp/docs/quickstart.js +48 -46
  79. package/dist/mcp/docs/quickstart.js.map +1 -1
  80. package/dist/mcp/server.d.ts +1 -1
  81. package/dist/mcp/server.d.ts.map +1 -1
  82. package/dist/mcp/server.js +6 -7
  83. package/dist/mcp/server.js.map +1 -1
  84. package/dist/mcp/version.d.ts +7 -0
  85. package/dist/mcp/version.d.ts.map +1 -0
  86. package/dist/mcp/version.js +20 -0
  87. package/dist/mcp/version.js.map +1 -0
  88. package/dist/middleware/auth/oauth2.d.ts +294 -0
  89. package/dist/middleware/auth/oauth2.d.ts.map +1 -0
  90. package/dist/middleware/auth/oauth2.int.test.d.ts +2 -0
  91. package/dist/middleware/auth/oauth2.int.test.d.ts.map +1 -0
  92. package/dist/middleware/auth/oauth2.int.test.js +714 -0
  93. package/dist/middleware/auth/oauth2.int.test.js.map +1 -0
  94. package/dist/middleware/auth/oauth2.js +671 -0
  95. package/dist/middleware/auth/oauth2.js.map +1 -0
  96. package/dist/middleware/auth.d.ts +2 -0
  97. package/dist/middleware/auth.d.ts.map +1 -1
  98. package/dist/middleware/auth.js +16 -0
  99. package/dist/middleware/auth.js.map +1 -1
  100. package/dist/middleware/index.d.ts +5 -2
  101. package/dist/middleware/index.d.ts.map +1 -1
  102. package/dist/middleware/index.js +4 -0
  103. package/dist/middleware/index.js.map +1 -1
  104. package/dist/middleware/interceptors/circuit-breaker.d.ts.map +1 -1
  105. package/dist/middleware/interceptors/circuit-breaker.js +0 -1
  106. package/dist/middleware/interceptors/circuit-breaker.js.map +1 -1
  107. package/dist/middleware/interceptors/envelope.d.ts +176 -0
  108. package/dist/middleware/interceptors/envelope.d.ts.map +1 -0
  109. package/dist/middleware/interceptors/envelope.int.test.d.ts +5 -0
  110. package/dist/middleware/interceptors/envelope.int.test.d.ts.map +1 -0
  111. package/dist/middleware/interceptors/envelope.int.test.js +409 -0
  112. package/dist/middleware/interceptors/envelope.int.test.js.map +1 -0
  113. package/dist/middleware/interceptors/envelope.js +294 -0
  114. package/dist/middleware/interceptors/envelope.js.map +1 -0
  115. package/dist/middleware/interceptors/index.d.ts +2 -0
  116. package/dist/middleware/interceptors/index.d.ts.map +1 -1
  117. package/dist/middleware/interceptors/index.js +2 -0
  118. package/dist/middleware/interceptors/index.js.map +1 -1
  119. package/dist/middleware/types.d.ts +25 -0
  120. package/dist/middleware/types.d.ts.map +1 -1
  121. package/dist/rate-limit/drivers/drivers.int.test.d.ts +7 -0
  122. package/dist/rate-limit/drivers/drivers.int.test.d.ts.map +1 -0
  123. package/dist/rate-limit/drivers/drivers.int.test.js +466 -0
  124. package/dist/rate-limit/drivers/drivers.int.test.js.map +1 -0
  125. package/dist/server/builder.d.ts.map +1 -1
  126. package/dist/server/builder.int.test.js +41 -0
  127. package/dist/server/builder.int.test.js.map +1 -1
  128. package/dist/server/builder.js +72 -15
  129. package/dist/server/builder.js.map +1 -1
  130. package/dist/server/channel-utils.d.ts +4 -1
  131. package/dist/server/channel-utils.d.ts.map +1 -1
  132. package/dist/server/channel-utils.js +12 -2
  133. package/dist/server/channel-utils.js.map +1 -1
  134. package/dist/server/errors.d.ts.map +1 -1
  135. package/dist/server/errors.js +0 -22
  136. package/dist/server/errors.js.map +1 -1
  137. package/dist/server/fs-routes/watcher.js +1 -1
  138. package/dist/server/fs-routes/watcher.js.map +1 -1
  139. package/dist/server/index.d.ts +1 -1
  140. package/dist/server/index.d.ts.map +1 -1
  141. package/dist/server/index.js.map +1 -1
  142. package/dist/server/types.d.ts +37 -33
  143. package/dist/server/types.d.ts.map +1 -1
  144. package/dist/tracing/interceptor.d.ts.map +1 -1
  145. package/dist/tracing/interceptor.js +4 -5
  146. package/dist/tracing/interceptor.js.map +1 -1
  147. package/dist/types/envelope.d.ts +1 -1
  148. package/dist/types/envelope.d.ts.map +1 -1
  149. package/dist/types/envelope.js.map +1 -1
  150. package/dist/types/handlers.d.ts +8 -0
  151. package/dist/types/handlers.d.ts.map +1 -1
  152. package/dist/ui/core/index.d.ts +7 -0
  153. package/dist/ui/core/index.d.ts.map +1 -0
  154. package/dist/ui/docs/generators/content-types.d.ts +10 -0
  155. package/dist/ui/docs/generators/content-types.d.ts.map +1 -0
  156. package/dist/ui/docs/generators/errors-types.d.ts +409 -0
  157. package/dist/ui/docs/generators/errors-types.d.ts.map +1 -0
  158. package/dist/ui/docs/generators/errors.d.ts +88 -0
  159. package/dist/ui/docs/generators/errors.d.ts.map +1 -0
  160. package/dist/ui/docs/generators/grpc-generator.d.ts +53 -0
  161. package/dist/ui/docs/generators/grpc-generator.d.ts.map +1 -0
  162. package/dist/ui/docs/generators/http-generator.d.ts +49 -0
  163. package/dist/ui/docs/generators/http-generator.d.ts.map +1 -0
  164. package/dist/ui/docs/generators/index.d.ts +17 -0
  165. package/dist/ui/docs/generators/index.d.ts.map +1 -0
  166. package/dist/ui/docs/generators/jsonrpc-generator.d.ts +53 -0
  167. package/dist/ui/docs/generators/jsonrpc-generator.d.ts.map +1 -0
  168. package/dist/ui/docs/generators/schema-converter.d.ts +117 -0
  169. package/dist/ui/docs/generators/schema-converter.d.ts.map +1 -0
  170. package/dist/ui/docs/generators/streams-generator.d.ts +85 -0
  171. package/dist/ui/docs/generators/streams-generator.d.ts.map +1 -0
  172. package/dist/ui/docs/generators/tcp-generator.d.ts +133 -0
  173. package/dist/ui/docs/generators/tcp-generator.d.ts.map +1 -0
  174. package/dist/ui/docs/generators/udp-generator.d.ts +119 -0
  175. package/dist/ui/docs/generators/udp-generator.d.ts.map +1 -0
  176. package/dist/ui/docs/generators/usd-generator.d.ts +182 -0
  177. package/dist/ui/docs/generators/usd-generator.d.ts.map +1 -0
  178. package/dist/ui/docs/generators/websocket-generator.d.ts +49 -0
  179. package/dist/ui/docs/generators/websocket-generator.d.ts.map +1 -0
  180. package/dist/ui/docs/index.d.ts +31 -0
  181. package/dist/ui/docs/index.d.ts.map +1 -0
  182. package/dist/ui/docs/usd-middleware.d.ts +157 -0
  183. package/dist/ui/docs/usd-middleware.d.ts.map +1 -0
  184. package/dist/ui/errors/factories.d.ts +142 -0
  185. package/dist/ui/errors/factories.d.ts.map +1 -0
  186. package/dist/ui/errors/index.d.ts +9 -0
  187. package/dist/ui/errors/index.d.ts.map +1 -0
  188. package/dist/ui/server/fs-routes/index.d.ts +66 -0
  189. package/dist/ui/server/fs-routes/index.d.ts.map +1 -0
  190. package/dist/ui/server/fs-routes/loader.d.ts +28 -0
  191. package/dist/ui/server/fs-routes/loader.d.ts.map +1 -0
  192. package/dist/ui/server/fs-routes/middleware-processor.d.ts +19 -0
  193. package/dist/ui/server/fs-routes/middleware-processor.d.ts.map +1 -0
  194. package/dist/ui/server/fs-routes/resources/index.d.ts +8 -0
  195. package/dist/ui/server/fs-routes/resources/index.d.ts.map +1 -0
  196. package/dist/ui/server/fs-routes/resources/loader.d.ts +16 -0
  197. package/dist/ui/server/fs-routes/resources/loader.d.ts.map +1 -0
  198. package/dist/ui/server/fs-routes/resources/types.d.ts +256 -0
  199. package/dist/ui/server/fs-routes/resources/types.d.ts.map +1 -0
  200. package/dist/ui/server/fs-routes/rest/index.d.ts +8 -0
  201. package/dist/ui/server/fs-routes/rest/index.d.ts.map +1 -0
  202. package/dist/ui/server/fs-routes/rest/loader.d.ts +11 -0
  203. package/dist/ui/server/fs-routes/rest/loader.d.ts.map +1 -0
  204. package/dist/ui/server/fs-routes/rest/types.d.ts +288 -0
  205. package/dist/ui/server/fs-routes/rest/types.d.ts.map +1 -0
  206. package/dist/ui/server/fs-routes/tcp/index.d.ts +8 -0
  207. package/dist/ui/server/fs-routes/tcp/index.d.ts.map +1 -0
  208. package/dist/ui/server/fs-routes/tcp/loader.d.ts +15 -0
  209. package/dist/ui/server/fs-routes/tcp/loader.d.ts.map +1 -0
  210. package/dist/ui/server/fs-routes/tcp/types.d.ts +215 -0
  211. package/dist/ui/server/fs-routes/tcp/types.d.ts.map +1 -0
  212. package/dist/ui/server/fs-routes/types.d.ts +437 -0
  213. package/dist/ui/server/fs-routes/types.d.ts.map +1 -0
  214. package/dist/ui/server/fs-routes/udp/index.d.ts +8 -0
  215. package/dist/ui/server/fs-routes/udp/index.d.ts.map +1 -0
  216. package/dist/ui/server/fs-routes/udp/loader.d.ts +15 -0
  217. package/dist/ui/server/fs-routes/udp/loader.d.ts.map +1 -0
  218. package/dist/ui/server/fs-routes/udp/types.d.ts +164 -0
  219. package/dist/ui/server/fs-routes/udp/types.d.ts.map +1 -0
  220. package/dist/ui/server/fs-routes/watcher.d.ts +34 -0
  221. package/dist/ui/server/fs-routes/watcher.d.ts.map +1 -0
  222. package/dist/ui/types/envelope.d.ts +1 -1
  223. package/dist/ui/types/envelope.d.ts.map +1 -1
  224. package/dist/ui/types/handlers.d.ts +8 -0
  225. package/dist/ui/types/handlers.d.ts.map +1 -1
  226. package/dist/ui/usd/builder/document.d.ts.map +1 -1
  227. package/dist/ui/usd/export/openapi.d.ts.map +1 -1
  228. package/dist/ui/usd/parser/normalize.d.ts.map +1 -1
  229. package/dist/ui/usd/spec/types.d.ts +14 -20
  230. package/dist/ui/usd/spec/types.d.ts.map +1 -1
  231. package/dist/ui/usd/utils/refs.d.ts.map +1 -1
  232. package/dist/ui/usd/validator/index.d.ts.map +1 -1
  233. package/dist/ui/usd/validator/schema.d.ts.map +1 -1
  234. package/dist/ui/usd/validator/semantic.d.ts.map +1 -1
  235. package/dist/ui/utils/logger.d.ts +15 -0
  236. package/dist/ui/utils/logger.d.ts.map +1 -0
  237. package/dist/usd/builder/document.d.ts.map +1 -1
  238. package/dist/usd/builder/document.js.map +1 -1
  239. package/dist/usd/export/openapi.d.ts.map +1 -1
  240. package/dist/usd/export/openapi.js +2 -4
  241. package/dist/usd/export/openapi.js.map +1 -1
  242. package/dist/usd/parser/normalize.d.ts.map +1 -1
  243. package/dist/usd/parser/normalize.js +0 -1
  244. package/dist/usd/parser/normalize.js.map +1 -1
  245. package/dist/usd/usd.int.test.d.ts +10 -0
  246. package/dist/usd/usd.int.test.d.ts.map +1 -0
  247. package/dist/usd/usd.int.test.js +719 -0
  248. package/dist/usd/usd.int.test.js.map +1 -0
  249. package/dist/usd/utils/refs.d.ts.map +1 -1
  250. package/dist/usd/validator/index.d.ts.map +1 -1
  251. package/dist/usd/validator/index.js.map +1 -1
  252. package/dist/usd/validator/schema.d.ts.map +1 -1
  253. package/dist/usd/validator/schema.js.map +1 -1
  254. package/dist/usd/validator/semantic.d.ts.map +1 -1
  255. package/dist/usd/validator/semantic.js.map +1 -1
  256. package/package.json +1 -1
  257. package/dist/middleware/rate-limit.d.ts +0 -105
  258. package/dist/middleware/rate-limit.d.ts.map +0 -1
  259. package/dist/middleware/rate-limit.int.test.d.ts +0 -5
  260. package/dist/middleware/rate-limit.int.test.d.ts.map +0 -1
  261. package/dist/middleware/rate-limit.int.test.js +0 -350
  262. package/dist/middleware/rate-limit.int.test.js.map +0 -1
  263. package/dist/middleware/rate-limit.js +0 -206
  264. package/dist/middleware/rate-limit.js.map +0 -1
  265. package/dist/openapi/index.d.ts +0 -9
  266. package/dist/openapi/index.d.ts.map +0 -1
  267. package/dist/openapi/index.js +0 -9
  268. package/dist/openapi/index.js.map +0 -1
package/README.md CHANGED
@@ -1,485 +1,453 @@
1
1
  <div align="center">
2
2
 
3
- # Raffel
3
+ # Raffel
4
4
 
5
- ### Unified Multi-Protocol Server Runtime
6
-
7
- One handler. Seven protocols. Zero duplication.
5
+ ### Build APIs Like Express. Scale Like Nothing Else.
8
6
 
9
7
  [![npm version](https://img.shields.io/npm/v/raffel.svg?style=flat-square&color=8b5cf6)](https://www.npmjs.com/package/raffel)
10
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178C6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
11
9
  [![Node.js](https://img.shields.io/badge/Node.js-18+-339933?style=flat-square&logo=node.js&logoColor=white)](https://nodejs.org/)
12
10
  [![License](https://img.shields.io/badge/License-MIT-green?style=flat-square)](LICENSE)
13
11
 
14
- [Documentation](https://forattini-dev.github.io/raffel) · [Quick Start](#quick-start) · [Examples](./examples) · [MCP Server](#mcp-server)
12
+ [Quick Start](#quick-start) · [Full Documentation](https://forattini-dev.github.io/raffel) · [Examples](./examples) · [Migration from Express](#migration-from-express)
15
13
 
16
14
  </div>
17
15
 
18
16
  ---
19
17
 
20
- ## Why Raffel?
18
+ ## If You Know Express, You Know Raffel
21
19
 
22
20
  ```typescript
23
- // Define once
24
- server.procedure('users.create')
25
- .input(z.object({ name: z.string(), email: z.string().email() }))
26
- .handler(async (input) => db.users.create({ data: input }))
27
-
28
- // Expose everywhere
29
- // ✓ HTTP POST /users.create
30
- // ✓ WebSocket { procedure: 'users.create', payload: {...} }
31
- // gRPC UsersService.Create()
32
- // ✓ JSON-RPC { method: 'users.create', params: {...} }
33
- // ✓ GraphQL mutation { usersCreate(...) }
34
- // ✓ TCP/UDP raw protocol support
21
+ import { createServer } from 'raffel'
22
+
23
+ const app = createServer({ port: 3000 })
24
+
25
+ app.get('/users', async () => {
26
+ return db.users.findMany()
27
+ })
28
+
29
+ app.get('/users/:id', async ({ id }) => {
30
+ return db.users.findById(id)
31
+ })
32
+
33
+ app.post('/users', async (body) => {
34
+ return db.users.create(body)
35
+ })
36
+
37
+ await app.start()
35
38
  ```
36
39
 
37
- **Same validation. Same errors. Same auth. Same metrics. All protocols.**
40
+ ```bash
41
+ curl http://localhost:3000/users
42
+ curl http://localhost:3000/users/123
43
+ curl -X POST http://localhost:3000/users -d '{"name":"John"}'
44
+ ```
45
+
46
+ **That's it. Familiar, right?**
38
47
 
39
48
  ---
40
49
 
41
50
  ## Quick Start
42
51
 
43
52
  ```bash
44
- pnpm add raffel zod
53
+ pnpm add raffel
45
54
  ```
46
55
 
47
- Prefer another validator? Swap Zod for Yup/Joi/Ajv and register its adapter.
56
+ ### Hello World
48
57
 
49
58
  ```typescript
50
- import { createServer, registerValidator, createZodAdapter } from 'raffel'
51
- import { z } from 'zod'
59
+ import { createServer } from 'raffel'
52
60
 
53
- registerValidator(createZodAdapter(z))
61
+ const app = createServer({ port: 3000 })
54
62
 
55
- const server = createServer({
56
- port: 3000,
57
- websocket: true,
58
- jsonrpc: '/rpc',
63
+ app.get('/hello/:name', async ({ name }) => {
64
+ return { message: `Hello, ${name}!` }
59
65
  })
60
66
 
61
- server
62
- .procedure('hello')
63
- .input(z.object({ name: z.string() }))
64
- .handler(async ({ name }) => ({ message: `Hello, ${name}!` }))
65
-
66
- await server.start()
67
+ await app.start()
67
68
  ```
68
69
 
69
70
  ```bash
70
- # Test all protocols
71
- curl -X POST localhost:3000/hello -d '{"name":"World"}'
72
- wscat -c ws://localhost:3000/ws -x '{"procedure":"hello","payload":{"name":"World"}}'
71
+ curl http://localhost:3000/hello/World
72
+ # {"message":"Hello, World!"}
73
73
  ```
74
74
 
75
- ---
76
-
77
- ## Documentation (Docsify Deep Dive)
78
-
79
- **Raffel's docs are the product** - dense, example-first, and protocol-accurate.
75
+ ### CRUD API in 30 Seconds
80
76
 
81
- **👉 Full documentation:** https://forattini-dev.github.io/raffel
82
-
83
- | Path | What you get |
84
- |------|--------------|
85
- | [Quickstart](https://forattini-dev.github.io/raffel/#/quickstart) | 5-minute multi-protocol server |
86
- | [Core Model](https://forattini-dev.github.io/raffel/#/core-model) | Envelope, Context, handler lifecycle |
87
- | [Handlers](https://forattini-dev.github.io/raffel/#/handlers/procedures) | Procedures, Streams, Events with real examples |
88
- | [Protocols](https://forattini-dev.github.io/raffel/#/protocols/http) | HTTP/WS/gRPC/JSON-RPC/GraphQL/TCP/UDP mappings |
89
- | [Interceptors](https://forattini-dev.github.io/raffel/#/interceptors) | Rate limit, retry, timeout, caching, fallback |
90
- | [Auth](https://forattini-dev.github.io/raffel/#/auth/overview) | Bearer/API key/OAuth2/OIDC/Sessions |
91
- | [Routing & Discovery](https://forattini-dev.github.io/raffel/#/file-system-discovery) | File-based routing, REST Auto-CRUD |
92
- | [Observability](https://forattini-dev.github.io/raffel/#/metrics) | Prometheus metrics + OpenTelemetry tracing |
93
- | [USD & OpenAPI](https://forattini-dev.github.io/raffel/#/usd) | Universal docs generated from schemas |
94
- | [MCP Server](https://forattini-dev.github.io/raffel/#/mcp) | AI tools, resources, prompts |
77
+ ```typescript
78
+ import { createServer } from 'raffel'
95
79
 
96
- ---
80
+ const app = createServer({ port: 3000 })
97
81
 
98
- ## What's Inside
82
+ const users = new Map()
99
83
 
100
- | Category | Features |
101
- |----------|----------|
102
- | **Protocols** | HTTP • WebSocket • gRPC • JSON-RPC • GraphQL • TCP • UDP |
103
- | **Handler Types** | Procedures (RPC) • Streams (Server/Client/Bidi) • Events (Pub/Sub) |
104
- | **Validation** | Zod • Yup • Joi • Ajv • fastest-validator |
105
- | **Auth** | JWT • API Key • OAuth2 • OIDC • Basic • Session |
106
- | **Resilience** | Rate Limit • Circuit Breaker • Retry • Timeout • Bulkhead • Fallback |
107
- | **Observability** | Prometheus Metrics • OpenTelemetry Tracing • Structured Logging |
108
- | **Caching** | Memory • Redis • S3DB • Read-through • Write-through |
109
- | **Real-time** | Channels (Pusher-like) • Presence • Broadcasting |
110
- | **Documentation** | USD (Universal Service Docs) • Auto-generated from schemas |
111
- | **DX** | Hot Reload • File-based Routing • REST Auto-CRUD |
84
+ app.get('/users', async () => [...users.values()])
112
85
 
113
- ---
86
+ app.get('/users/:id', async ({ id }) => {
87
+ const user = users.get(id)
88
+ if (!user) throw app.errors.notFound('User not found')
89
+ return user
90
+ })
114
91
 
115
- ## Highlights
92
+ app.post('/users', async (body) => {
93
+ const user = { id: crypto.randomUUID(), ...body }
94
+ users.set(user.id, user)
95
+ return user
96
+ })
116
97
 
117
- ### Unified Envelope Architecture
98
+ app.put('/users/:id', async ({ id, ...body }) => {
99
+ if (!users.has(id)) throw app.errors.notFound('User not found')
100
+ const user = { id, ...body }
101
+ users.set(id, user)
102
+ return user
103
+ })
118
104
 
119
- Every request becomes a normalized `Envelope` - same processing for all protocols:
105
+ app.delete('/users/:id', async ({ id }) => {
106
+ if (!users.delete(id)) throw app.errors.notFound('User not found')
107
+ return { success: true }
108
+ })
120
109
 
121
- ```typescript
122
- interface Envelope {
123
- id: string // Request correlation
124
- procedure: string // Handler name
125
- type: 'request' | 'response' | 'stream:data' | 'event'
126
- payload: unknown // Your data
127
- context: Context // Auth, tracing, deadline
128
- }
110
+ await app.start()
129
111
  ```
130
112
 
131
- ### Three Handler Types
113
+ ### Add Validation (with Zod)
132
114
 
133
115
  ```typescript
134
- // Procedures - Request Response
135
- server.procedure('math.add')
136
- .handler(async ({ a, b }) => ({ result: a + b }))
116
+ import { createServer } from 'raffel'
117
+ import { z } from 'zod'
137
118
 
138
- // Streams - Request Multiple Responses
139
- server.stream('logs.tail')
140
- .handler(async function* ({ file }) {
141
- for await (const line of readLines(file)) {
142
- yield { line }
143
- }
144
- })
119
+ const app = createServer({ port: 3000 })
145
120
 
146
- // Events - Fire and Forget with Guarantees
147
- server.event('emails.send')
148
- .delivery('at-least-once')
149
- .handler(async (payload, ctx, ack) => {
150
- await sendEmail(payload)
151
- ack()
152
- })
121
+ app.post('/users', {
122
+ body: z.object({
123
+ name: z.string().min(2),
124
+ email: z.string().email(),
125
+ }),
126
+ handler: async (body) => {
127
+ return db.users.create(body)
128
+ }
129
+ })
130
+
131
+ await app.start()
153
132
  ```
154
133
 
155
- ### Protocol-Agnostic Interceptors
134
+ Invalid request? Automatic error response:
135
+
136
+ ```json
137
+ {
138
+ "error": "VALIDATION_ERROR",
139
+ "message": "Validation failed",
140
+ "details": [
141
+ { "path": "email", "message": "Invalid email" }
142
+ ]
143
+ }
144
+ ```
156
145
 
157
- Write middleware once, apply everywhere:
146
+ ### Add Middleware
158
147
 
159
148
  ```typescript
160
- server.use(async (envelope, ctx, next) => {
149
+ import { createServer } from 'raffel'
150
+
151
+ const app = createServer({ port: 3000 })
152
+
153
+ // Global middleware
154
+ app.use(async (req, next) => {
161
155
  const start = Date.now()
162
156
  const result = await next()
163
- console.log(`${envelope.procedure}: ${Date.now() - start}ms`)
157
+ console.log(`${req.method} ${req.path} - ${Date.now() - start}ms`)
164
158
  return result
165
159
  })
166
- // Runs for HTTP, WebSocket, gRPC, JSON-RPC, TCP, UDP...
160
+
161
+ // Auth middleware
162
+ const requireAuth = async (req, next) => {
163
+ const token = req.headers.authorization?.replace('Bearer ', '')
164
+ if (!token) throw app.errors.unauthorized()
165
+ req.user = await verifyToken(token)
166
+ return next()
167
+ }
168
+
169
+ app.get('/profile', requireAuth, async (_, req) => {
170
+ return req.user
171
+ })
172
+
173
+ await app.start()
167
174
  ```
168
175
 
169
- <details>
170
- <summary><strong>Built-in Interceptors</strong></summary>
176
+ ---
177
+
178
+ ## Wait, There's More
179
+
180
+ Here's where Raffel gets interesting. **That same API you just wrote? It already works over WebSocket, JSON-RPC, and more.**
171
181
 
172
182
  ```typescript
173
- import {
174
- // Auth
175
- createAuthMiddleware,
176
- createBearerStrategy,
177
- createApiKeyStrategy,
178
-
179
- // Resilience
180
- createRateLimitInterceptor,
181
- createCircuitBreakerInterceptor,
182
- createRetryInterceptor,
183
- createTimeoutInterceptor,
184
- createBulkheadInterceptor,
185
- createFallbackInterceptor,
186
-
187
- // Observability
188
- createMetricsInterceptor,
189
- createTracingInterceptor,
190
- createLoggingInterceptor,
191
-
192
- // Caching
193
- createCacheInterceptor,
194
-
195
- // Response
196
- createEnvelopeInterceptor,
197
- } from 'raffel'
183
+ const app = createServer({
184
+ port: 3000,
185
+ websocket: { path: '/ws' },
186
+ jsonrpc: { path: '/rpc' },
187
+ })
188
+
189
+ app.get('/users/:id', async ({ id }) => {
190
+ return db.users.findById(id)
191
+ })
192
+
193
+ await app.start()
198
194
  ```
199
195
 
200
- </details>
196
+ **Same handler. Three protocols. Zero extra code.**
201
197
 
202
- ### File-System Discovery
198
+ ```bash
199
+ # HTTP (as usual)
200
+ curl http://localhost:3000/users/123
203
201
 
204
- Drop files in folders, get endpoints automatically:
202
+ # WebSocket
203
+ wscat -c ws://localhost:3000/ws
204
+ > {"method":"users.get","params":{"id":"123"}}
205
205
 
206
- ```
207
- src/
208
- ├── http/
209
- │ └── users/
210
- │ ├── get.ts → users.get
211
- │ └── create.ts → users.create
212
- ├── streams/
213
- │ └── logs/
214
- │ └── tail.ts → logs.tail
215
- ├── rest/
216
- │ └── users.ts → Auto-CRUD
217
- └── channels/
218
- └── chat-room.ts → WebSocket channel
206
+ # JSON-RPC
207
+ curl -X POST http://localhost:3000/rpc \
208
+ -d '{"jsonrpc":"2.0","method":"users.get","params":{"id":"123"},"id":1}'
219
209
  ```
220
210
 
221
- ```typescript
222
- const server = createServer({ port: 3000, discovery: true })
223
- ```
211
+ ### Why Does This Matter?
224
212
 
225
- ### REST Auto-CRUD
213
+ - **Write once** - Same validation, auth, and error handling everywhere
214
+ - **Client choice** - HTTP for REST, WebSocket for real-time, JSON-RPC for internal services
215
+ - **Zero friction** - No adapters, no mappings, no duplicate code
226
216
 
227
- Define a schema, get a full REST API:
217
+ ---
228
218
 
229
- ```typescript
230
- // src/rest/users.ts
231
- export const schema = z.object({
232
- id: z.string().uuid(),
233
- name: z.string(),
234
- email: z.string().email(),
235
- })
219
+ ## The Full Picture
236
220
 
237
- export const adapter = prisma.user
238
- ```
221
+ Under the hood, Raffel normalizes all requests into a unified format called an **Envelope**. But you don't need to think about that - it just works.
239
222
 
240
- ```
241
- GET /users → list
242
- GET /users/:id get
243
- POST /users create
244
- PUT /users/:id → update
245
- PATCH /users/:id → patch
246
- DELETE /users/:id → delete
247
- ```
223
+ | What You Write | What Raffel Exposes |
224
+ |----------------|---------------------|
225
+ | `app.get('/users/:id', handler)` | HTTP GET, WS, JSON-RPC, gRPC, GraphQL |
226
+ | `app.post('/users', handler)` | HTTP POST, WS, JSON-RPC, gRPC, GraphQL |
227
+ | Validation schema | Same validation, all protocols |
228
+ | Auth middleware | Same auth, all protocols |
229
+ | Error handling | Protocol-appropriate errors |
230
+
231
+ ### Supported Protocols
232
+
233
+ | Protocol | Status | Use Case |
234
+ |----------|--------|----------|
235
+ | HTTP | Production | REST APIs, webhooks |
236
+ | WebSocket | Production | Real-time, bi-directional |
237
+ | JSON-RPC | Production | Internal services, batch |
238
+ | gRPC | Production | Microservices, high-perf |
239
+ | GraphQL | Production | Flexible queries |
240
+ | TCP | Production | IoT, custom protocols |
241
+ | UDP | Production | Gaming, streaming |
242
+
243
+ ---
248
244
 
249
- ### WebSocket Channels
245
+ ## Going Deeper: The Procedure API
250
246
 
251
- Pusher-like real-time with authentication:
247
+ For power users who want full control, Raffel exposes its native API:
252
248
 
253
249
  ```typescript
250
+ import { createServer, registerValidator, createZodAdapter } from 'raffel'
251
+ import { z } from 'zod'
252
+
253
+ registerValidator(createZodAdapter(z))
254
+
254
255
  const server = createServer({
255
- websocket: {
256
- channels: {
257
- authorize: async (socketId, channel, ctx) => {
258
- if (channel.startsWith('private-')) {
259
- return ctx.auth?.authenticated ?? false
260
- }
261
- return true
262
- },
263
- },
264
- },
256
+ port: 3000,
257
+ websocket: { path: '/ws' },
265
258
  })
266
259
 
267
- // Public channels
268
- server.channels.broadcast('news', 'update', { headline: '...' })
260
+ // Full procedure definition
261
+ server.procedure('users.create')
262
+ .description('Create a new user')
263
+ .input(z.object({
264
+ name: z.string().min(2),
265
+ email: z.string().email(),
266
+ }))
267
+ .output(z.object({
268
+ id: z.string().uuid(),
269
+ name: z.string(),
270
+ email: z.string(),
271
+ }))
272
+ .handler(async (input, ctx) => {
273
+ // ctx has auth, tracing, request metadata
274
+ return db.users.create(input)
275
+ })
269
276
 
270
- // Private channels
271
- server.channels.broadcast('private-user-123', 'notification', {...})
277
+ // Streaming (server → client)
278
+ server.stream('logs.tail')
279
+ .handler(async function* ({ file }) {
280
+ for await (const line of readLines(file)) {
281
+ yield { line, timestamp: Date.now() }
282
+ }
283
+ })
272
284
 
273
- // Presence channels
274
- const members = server.channels.getMembers('presence-lobby')
285
+ // Events (fire-and-forget with guarantees)
286
+ server.event('emails.send')
287
+ .delivery('at-least-once')
288
+ .handler(async (payload, ctx, ack) => {
289
+ await sendEmail(payload)
290
+ ack()
291
+ })
292
+
293
+ await server.start()
275
294
  ```
276
295
 
277
- ### Unified Error Handling
296
+ ### The Envelope Model
278
297
 
279
- Throw once, convert automatically per protocol:
298
+ Every request becomes an Envelope:
280
299
 
281
300
  ```typescript
282
- import { RaffelError } from 'raffel'
283
-
284
- throw new RaffelError('NOT_FOUND', 'User not found', { userId: '123' })
285
-
286
- // HTTP → 404 Not Found + JSON body
287
- // JSON-RPC → { error: { code: -32601, message: '...' } }
288
- // gRPC → status: NOT_FOUND (5)
289
- // WebSocket → { type: 'error', code: 'NOT_FOUND' }
301
+ interface Envelope {
302
+ id: string // Correlation ID
303
+ procedure: string // Handler name (e.g., "users.create")
304
+ type: 'request' | 'response' | 'stream:data' | 'event'
305
+ payload: unknown // Your data
306
+ context: Context // Auth, tracing, deadline, metadata
307
+ }
290
308
  ```
291
309
 
292
- ---
293
-
294
- ## HTTP Module
310
+ This abstraction is what enables protocol-agnostic handlers. You write business logic once, Raffel handles the protocol translation.
295
311
 
296
- Raffel includes a complete HTTP toolkit - no extra dependencies needed:
297
-
298
- ```typescript
299
- import {
300
- // Server
301
- HttpApp, serve,
312
+ ---
302
313
 
303
- // Middleware
304
- cors, compress, secureHeaders, bodyLimit,
305
- basicAuth, bearerAuth, cookieSession, oauth2, oidc,
306
- rateLimitMiddleware, validate,
314
+ ## Features at a Glance
307
315
 
308
- // Static files
309
- serveStatic, serveStaticS3,
316
+ | Category | Features |
317
+ |----------|----------|
318
+ | **HTTP** | GET/POST/PUT/PATCH/DELETE, path params, query params, headers |
319
+ | **Validation** | Zod, Yup, Joi, Ajv, fastest-validator |
320
+ | **Auth** | JWT, API Key, OAuth2, OIDC, Basic, Session |
321
+ | **Resilience** | Rate Limit, Circuit Breaker, Retry, Timeout, Bulkhead |
322
+ | **Observability** | Prometheus Metrics, OpenTelemetry Tracing |
323
+ | **Caching** | Memory, Redis, S3, Read-through, Write-through |
324
+ | **Real-time** | WebSocket Channels, Presence, Broadcasting |
325
+ | **Documentation** | Auto-generated OpenAPI/Swagger from schemas |
326
+ | **DX** | Hot Reload, File-based Routing, TypeScript-first |
310
327
 
311
- // Responses
312
- success, error, list, created, notFound, validationError,
328
+ ---
313
329
 
314
- // Session
315
- createSessionTracker, createRedisSessionStore,
330
+ ## Migration from Express
316
331
 
317
- // Utils
318
- getCookie, setCookie, healthCheck,
319
- } from 'raffel/http'
320
- ```
332
+ Already have an Express app? Migration is straightforward:
321
333
 
322
- ---
334
+ <table>
335
+ <tr>
336
+ <th>Express</th>
337
+ <th>Raffel</th>
338
+ </tr>
339
+ <tr>
340
+ <td>
323
341
 
324
- ## MCP Server
342
+ ```javascript
343
+ const express = require('express')
344
+ const app = express()
325
345
 
326
- Raffel includes an MCP server for AI-powered development:
346
+ app.get('/users/:id', (req, res) => {
347
+ const user = getUser(req.params.id)
348
+ res.json(user)
349
+ })
327
350
 
328
- ```bash
329
- # Add to Claude Code
330
- claude mcp add raffel npx raffel-mcp
351
+ app.post('/users', (req, res) => {
352
+ const user = createUser(req.body)
353
+ res.status(201).json(user)
354
+ })
331
355
 
332
- # Or run directly
333
- npx raffel-mcp --category minimal
334
- npx raffel-mcp --category docs,codegen
335
- npx raffel-mcp --transport http --port 3200
356
+ app.listen(3000)
336
357
  ```
337
358
 
338
- <details>
339
- <summary><strong>Available Categories</strong></summary>
340
-
341
- | Category | Tokens | Tools |
342
- |----------|--------|-------|
343
- | `minimal` | ~2.5K | Essential docs & patterns |
344
- | `docs` | ~3K | Documentation search |
345
- | `codegen` | ~4K | Code generation |
346
- | `full` | ~8K | All 16 tools |
347
-
348
- </details>
349
-
350
- ### MCP Tools
351
-
352
- **Docs & Reference**
353
- - `raffel_getting_started` - Quick start guide
354
- - `raffel_search` - Search all documentation
355
- - `raffel_list_interceptors` - List interceptors by category
356
- - `raffel_get_interceptor` - Interceptor details + examples
357
- - `raffel_list_adapters` - List protocol adapters
358
- - `raffel_get_adapter` - Adapter details + protocol mapping
359
- - `raffel_api_patterns` - **Critical** - Correct code patterns
360
- - `raffel_explain_error` - Error code explanations
361
-
362
- **Codegen**
363
- - `raffel_create_server` - Generate server boilerplate
364
- - `raffel_create_procedure` - Generate RPC endpoints
365
- - `raffel_create_stream` - Generate streaming handlers
366
- - `raffel_create_event` - Generate event handlers
367
- - `raffel_add_middleware` - Add interceptors
368
- - `raffel_create_module` - Generate router modules
369
- - `raffel_boilerplate` - Multi-file project templates
370
-
371
- **Meta**
372
- - `raffel_version` - Version + compatibility info
373
-
374
- ### MCP Prompts
375
-
376
- - `create_rest_api` - Build complete REST API
377
- - `create_realtime_server` - WebSocket + channels
378
- - `create_grpc_service` - gRPC services (unary + streaming)
379
- - `create_microservice` - Production-ready service
380
- - `add_authentication` - Add JWT/API key auth
381
- - `add_caching` - Add caching drivers
382
- - `add_rate_limiting` - Add per-route or global limits
383
- - `add_observability` - Metrics + tracing
384
- - `migrate_from_express` - Convert from Express
385
- - `migrate_from_fastify` - Convert from Fastify
386
- - `migrate_from_trpc` - Convert from tRPC
387
- - `debug_middleware` - Diagnose interceptor order/issues
388
- - `optimize_performance` - Perf review + tuning ideas
389
-
390
- ### MCP Resources
391
-
392
- The MCP server also exposes documentation and boilerplates as resources:
393
-
394
- - `raffel://guide/quickstart`
395
- - `raffel://interceptor/{name}`
396
- - `raffel://adapter/{name}`
397
- - `raffel://pattern/{name}`
398
- - `raffel://error/{code}`
399
- - `raffel://boilerplate/{template}`
359
+ </td>
360
+ <td>
400
361
 
401
- ---
362
+ ```typescript
363
+ import { createServer } from 'raffel'
402
364
 
403
- ## Documentation
365
+ const app = createServer({ port: 3000 })
404
366
 
405
- The docs go deep on **every** adapter, interceptor, and design choice.
367
+ app.get('/users/:id', async ({ id }) => {
368
+ return getUser(id)
369
+ })
406
370
 
407
- | Topic | Highlights |
408
- |-------|------------|
409
- | [Quickstart](https://forattini-dev.github.io/raffel/#/quickstart) | Multi-protocol in 5 minutes |
410
- | [Core Model](https://forattini-dev.github.io/raffel/#/core-model) | Envelope + Context deep dive |
411
- | [Handlers](https://forattini-dev.github.io/raffel/#/handlers/procedures) | Procedures, Streams, Events |
412
- | [Interceptors](https://forattini-dev.github.io/raffel/#/interceptors) | Retry, timeout, bulkhead, cache |
413
- | [Auth](https://forattini-dev.github.io/raffel/#/auth/overview) | Bearer/API key/OAuth2/OIDC |
414
- | [Routing](https://forattini-dev.github.io/raffel/#/route-discovery) | Modules, discovery, REST Auto-CRUD |
415
- | [USD + OpenAPI](https://forattini-dev.github.io/raffel/#/usd) | Specs from schemas |
416
- | [MCP Server](https://forattini-dev.github.io/raffel/#/mcp) | Tools, resources, prompts |
371
+ app.post('/users', async (body) => {
372
+ return createUser(body)
373
+ })
417
374
 
418
- ---
375
+ await app.start()
376
+ ```
419
377
 
420
- ## By the Numbers
378
+ </td>
379
+ </tr>
380
+ </table>
421
381
 
422
- | Metric | Value |
423
- |--------|-------|
424
- | **Protocols** | 7 (HTTP, WS, gRPC, JSON-RPC, GraphQL, TCP, UDP) |
425
- | **Interceptors** | 20+ built-in |
426
- | **Validation Libraries** | 5 supported |
427
- | **Auth Strategies** | 8+ (JWT, API Key, OAuth2, OIDC, Basic, Session, etc.) |
428
- | **MCP Tools** | 16 |
429
- | **MCP Prompts** | 13 |
382
+ **Key differences:**
383
+ - Return values instead of `res.json()`
384
+ - Path params and body merged into handler argument
385
+ - `async/await` native (no callback hell)
386
+ - Errors thrown, not manually handled
387
+
388
+ See [full migration guide](./docs/migration.md) for middleware, error handling, and advanced patterns.
430
389
 
431
390
  ---
432
391
 
433
392
  ## Examples
434
393
 
435
394
  ```bash
436
- # Clone and run examples
395
+ # Clone and run
396
+ git clone https://github.com/tetis-io/raffel
397
+ cd raffel
398
+
399
+ # Basic examples
437
400
  pnpm tsx examples/00-hello-world.ts
438
401
  pnpm tsx examples/01-rest-api.ts
439
402
  pnpm tsx examples/02-websocket-server.ts
440
403
  pnpm tsx examples/03-rpc-server.ts
404
+
405
+ # Advanced
406
+ pnpm tsx examples/07-resource-builder.ts
407
+ pnpm tsx examples/08-declarative-api.ts
441
408
  ```
442
409
 
443
410
  ---
444
411
 
445
- ## Comparison
446
-
447
- ### vs Express, Koa, Fastify, Hono
448
-
449
- | Feature | Express/Koa | Raffel |
450
- |---------|-------------|--------|
451
- | HTTP routing | | |
452
- | WebSocket | separate | same handlers |
453
- | gRPC | separate | ✅ same handlers |
454
- | JSON-RPC | separate | same handlers |
455
- | GraphQL | ❌ separate | same handlers |
456
- | Unified validation | | ✅ one schema |
457
- | Unified errors | ❌ | ✅ auto-converted |
458
- | Unified auth | ❌ | ✅ all protocols |
459
-
460
- ### vs tRPC
461
-
462
- | Feature | tRPC | Raffel |
463
- |---------|------|--------|
464
- | Type-safe RPC | ✅ | ✅ |
465
- | HTTP | | ✅ |
466
- | WebSocket | | ✅ |
467
- | gRPC | ❌ | ✅ |
468
- | JSON-RPC | ❌ | ✅ |
469
- | TCP/UDP | ❌ | ✅ |
470
- | Channels/Presence | ❌ | ✅ |
471
- | File routing | ❌ | ✅ |
412
+ ## Documentation
413
+
414
+ | Topic | Description |
415
+ |-------|-------------|
416
+ | [Quickstart](https://forattini-dev.github.io/raffel/#/quickstart) | 5-minute guide |
417
+ | [HTTP Deep Dive](https://forattini-dev.github.io/raffel/#/protocols/http) | REST, middleware, routing |
418
+ | [Authentication](https://forattini-dev.github.io/raffel/#/auth/overview) | JWT, API Key, OAuth2, OIDC |
419
+ | [Validation](https://forattini-dev.github.io/raffel/#/validation) | Zod, Yup, Joi integration |
420
+ | [WebSocket](https://forattini-dev.github.io/raffel/#/protocols/websocket) | Real-time, channels, presence |
421
+ | [Interceptors](https://forattini-dev.github.io/raffel/#/interceptors) | Rate limit, retry, cache |
422
+ | [Core Model](https://forattini-dev.github.io/raffel/#/core-model) | Envelope, Context, architecture |
423
+ | [File-based Routing](https://forattini-dev.github.io/raffel/#/file-system-discovery) | Zero-config discovery |
424
+
425
+ ---
426
+
427
+ ## MCP Server (AI Integration)
428
+
429
+ Raffel includes an MCP server for AI-powered development:
430
+
431
+ ```bash
432
+ # Add to Claude Code
433
+ claude mcp add raffel npx raffel-mcp
434
+
435
+ # Or run directly
436
+ npx raffel-mcp --category minimal
437
+ ```
438
+
439
+ Tools: `raffel_create_server`, `raffel_create_procedure`, `raffel_add_middleware`, `raffel_api_patterns`
472
440
 
473
441
  ---
474
442
 
475
443
  ## License
476
444
 
477
- MIT
445
+ ISC
478
446
 
479
447
  ---
480
448
 
481
449
  <div align="center">
482
450
 
483
- **[Documentation](https://forattini-dev.github.io/raffel)** · **[GitHub](https://github.com/forattini-dev/raffel)** · **[npm](https://www.npmjs.com/package/raffel)**
451
+ **[Documentation](https://forattini-dev.github.io/raffel)** · **[GitHub](https://github.com/tetis-io/raffel)** · **[npm](https://www.npmjs.com/package/raffel)**
484
452
 
485
453
  </div>