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.
- package/README.md +314 -346
- package/dist/adapters/index.d.ts +3 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +3 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/s3db/adapter.d.ts.map +1 -1
- package/dist/adapters/s3db/adapter.js +0 -3
- package/dist/adapters/s3db/adapter.js.map +1 -1
- package/dist/adapters/udp.d.ts +83 -0
- package/dist/adapters/udp.d.ts.map +1 -0
- package/dist/adapters/udp.int.test.d.ts +5 -0
- package/dist/adapters/udp.int.test.d.ts.map +1 -0
- package/dist/adapters/udp.int.test.js +397 -0
- package/dist/adapters/udp.int.test.js.map +1 -0
- package/dist/adapters/udp.js +391 -0
- package/dist/adapters/udp.js.map +1 -0
- package/dist/cache/drivers/file.d.ts.map +1 -1
- package/dist/cache/drivers/file.js +13 -1
- package/dist/cache/drivers/file.js.map +1 -1
- package/dist/cache/drivers/memory.d.ts.map +1 -1
- package/dist/cache/drivers/memory.js +1 -0
- package/dist/cache/drivers/memory.js.map +1 -1
- package/dist/cache/types.d.ts +1 -0
- package/dist/cache/types.d.ts.map +1 -1
- package/dist/docs/generators/http-generator.d.ts.map +1 -1
- package/dist/docs/generators/http-generator.js +0 -1
- package/dist/docs/generators/http-generator.js.map +1 -1
- package/dist/graphql/graphql.int.test.d.ts +10 -0
- package/dist/graphql/graphql.int.test.d.ts.map +1 -0
- package/dist/graphql/graphql.int.test.js +698 -0
- package/dist/graphql/graphql.int.test.js.map +1 -0
- package/dist/graphql/schema-generator.d.ts.map +1 -1
- package/dist/graphql/schema-generator.js +20 -7
- package/dist/graphql/schema-generator.js.map +1 -1
- package/dist/http/auth.d.ts.map +1 -1
- package/dist/http/auth.js +15 -1
- package/dist/http/auth.js.map +1 -1
- package/dist/http/http.int.test.d.ts +7 -0
- package/dist/http/http.int.test.d.ts.map +1 -0
- package/dist/http/http.int.test.js +604 -0
- package/dist/http/http.int.test.js.map +1 -0
- package/dist/http/index.d.ts +2 -0
- package/dist/http/index.d.ts.map +1 -1
- package/dist/http/index.js +2 -0
- package/dist/http/index.js.map +1 -1
- package/dist/http/oauth2.d.ts.map +1 -1
- package/dist/http/oauth2.js +39 -0
- package/dist/http/oauth2.js.map +1 -1
- package/dist/http/oidc.d.ts.map +1 -1
- package/dist/http/oidc.js +9 -1
- package/dist/http/oidc.js.map +1 -1
- package/dist/http/session-redis.d.ts +187 -0
- package/dist/http/session-redis.d.ts.map +1 -0
- package/dist/http/session-redis.int.test.d.ts +8 -0
- package/dist/http/session-redis.int.test.d.ts.map +1 -0
- package/dist/http/session-redis.int.test.js +492 -0
- package/dist/http/session-redis.int.test.js.map +1 -0
- package/dist/http/session-redis.js +320 -0
- package/dist/http/session-redis.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/cli.js +2 -1
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/docs/adapters.d.ts.map +1 -1
- package/dist/mcp/docs/adapters.js +175 -145
- package/dist/mcp/docs/adapters.js.map +1 -1
- package/dist/mcp/docs/interceptors.d.ts +1 -1
- package/dist/mcp/docs/interceptors.d.ts.map +1 -1
- package/dist/mcp/docs/interceptors.js +231 -305
- package/dist/mcp/docs/interceptors.js.map +1 -1
- package/dist/mcp/docs/patterns.d.ts.map +1 -1
- package/dist/mcp/docs/patterns.js +20 -18
- package/dist/mcp/docs/patterns.js.map +1 -1
- package/dist/mcp/docs/quickstart.d.ts +1 -1
- package/dist/mcp/docs/quickstart.d.ts.map +1 -1
- package/dist/mcp/docs/quickstart.js +48 -46
- package/dist/mcp/docs/quickstart.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +6 -7
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/version.d.ts +7 -0
- package/dist/mcp/version.d.ts.map +1 -0
- package/dist/mcp/version.js +20 -0
- package/dist/mcp/version.js.map +1 -0
- package/dist/middleware/auth/oauth2.d.ts +294 -0
- package/dist/middleware/auth/oauth2.d.ts.map +1 -0
- package/dist/middleware/auth/oauth2.int.test.d.ts +2 -0
- package/dist/middleware/auth/oauth2.int.test.d.ts.map +1 -0
- package/dist/middleware/auth/oauth2.int.test.js +714 -0
- package/dist/middleware/auth/oauth2.int.test.js.map +1 -0
- package/dist/middleware/auth/oauth2.js +671 -0
- package/dist/middleware/auth/oauth2.js.map +1 -0
- package/dist/middleware/auth.d.ts +2 -0
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +16 -0
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/index.d.ts +5 -2
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/interceptors/circuit-breaker.d.ts.map +1 -1
- package/dist/middleware/interceptors/circuit-breaker.js +0 -1
- package/dist/middleware/interceptors/circuit-breaker.js.map +1 -1
- package/dist/middleware/interceptors/envelope.d.ts +176 -0
- package/dist/middleware/interceptors/envelope.d.ts.map +1 -0
- package/dist/middleware/interceptors/envelope.int.test.d.ts +5 -0
- package/dist/middleware/interceptors/envelope.int.test.d.ts.map +1 -0
- package/dist/middleware/interceptors/envelope.int.test.js +409 -0
- package/dist/middleware/interceptors/envelope.int.test.js.map +1 -0
- package/dist/middleware/interceptors/envelope.js +294 -0
- package/dist/middleware/interceptors/envelope.js.map +1 -0
- package/dist/middleware/interceptors/index.d.ts +2 -0
- package/dist/middleware/interceptors/index.d.ts.map +1 -1
- package/dist/middleware/interceptors/index.js +2 -0
- package/dist/middleware/interceptors/index.js.map +1 -1
- package/dist/middleware/types.d.ts +25 -0
- package/dist/middleware/types.d.ts.map +1 -1
- package/dist/rate-limit/drivers/drivers.int.test.d.ts +7 -0
- package/dist/rate-limit/drivers/drivers.int.test.d.ts.map +1 -0
- package/dist/rate-limit/drivers/drivers.int.test.js +466 -0
- package/dist/rate-limit/drivers/drivers.int.test.js.map +1 -0
- package/dist/server/builder.d.ts.map +1 -1
- package/dist/server/builder.int.test.js +41 -0
- package/dist/server/builder.int.test.js.map +1 -1
- package/dist/server/builder.js +72 -15
- package/dist/server/builder.js.map +1 -1
- package/dist/server/channel-utils.d.ts +4 -1
- package/dist/server/channel-utils.d.ts.map +1 -1
- package/dist/server/channel-utils.js +12 -2
- package/dist/server/channel-utils.js.map +1 -1
- package/dist/server/errors.d.ts.map +1 -1
- package/dist/server/errors.js +0 -22
- package/dist/server/errors.js.map +1 -1
- package/dist/server/fs-routes/watcher.js +1 -1
- package/dist/server/fs-routes/watcher.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/types.d.ts +37 -33
- package/dist/server/types.d.ts.map +1 -1
- package/dist/tracing/interceptor.d.ts.map +1 -1
- package/dist/tracing/interceptor.js +4 -5
- package/dist/tracing/interceptor.js.map +1 -1
- package/dist/types/envelope.d.ts +1 -1
- package/dist/types/envelope.d.ts.map +1 -1
- package/dist/types/envelope.js.map +1 -1
- package/dist/types/handlers.d.ts +8 -0
- package/dist/types/handlers.d.ts.map +1 -1
- package/dist/ui/core/index.d.ts +7 -0
- package/dist/ui/core/index.d.ts.map +1 -0
- package/dist/ui/docs/generators/content-types.d.ts +10 -0
- package/dist/ui/docs/generators/content-types.d.ts.map +1 -0
- package/dist/ui/docs/generators/errors-types.d.ts +409 -0
- package/dist/ui/docs/generators/errors-types.d.ts.map +1 -0
- package/dist/ui/docs/generators/errors.d.ts +88 -0
- package/dist/ui/docs/generators/errors.d.ts.map +1 -0
- package/dist/ui/docs/generators/grpc-generator.d.ts +53 -0
- package/dist/ui/docs/generators/grpc-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/http-generator.d.ts +49 -0
- package/dist/ui/docs/generators/http-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/index.d.ts +17 -0
- package/dist/ui/docs/generators/index.d.ts.map +1 -0
- package/dist/ui/docs/generators/jsonrpc-generator.d.ts +53 -0
- package/dist/ui/docs/generators/jsonrpc-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/schema-converter.d.ts +117 -0
- package/dist/ui/docs/generators/schema-converter.d.ts.map +1 -0
- package/dist/ui/docs/generators/streams-generator.d.ts +85 -0
- package/dist/ui/docs/generators/streams-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/tcp-generator.d.ts +133 -0
- package/dist/ui/docs/generators/tcp-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/udp-generator.d.ts +119 -0
- package/dist/ui/docs/generators/udp-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/usd-generator.d.ts +182 -0
- package/dist/ui/docs/generators/usd-generator.d.ts.map +1 -0
- package/dist/ui/docs/generators/websocket-generator.d.ts +49 -0
- package/dist/ui/docs/generators/websocket-generator.d.ts.map +1 -0
- package/dist/ui/docs/index.d.ts +31 -0
- package/dist/ui/docs/index.d.ts.map +1 -0
- package/dist/ui/docs/usd-middleware.d.ts +157 -0
- package/dist/ui/docs/usd-middleware.d.ts.map +1 -0
- package/dist/ui/errors/factories.d.ts +142 -0
- package/dist/ui/errors/factories.d.ts.map +1 -0
- package/dist/ui/errors/index.d.ts +9 -0
- package/dist/ui/errors/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/index.d.ts +66 -0
- package/dist/ui/server/fs-routes/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/loader.d.ts +28 -0
- package/dist/ui/server/fs-routes/loader.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/middleware-processor.d.ts +19 -0
- package/dist/ui/server/fs-routes/middleware-processor.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/resources/index.d.ts +8 -0
- package/dist/ui/server/fs-routes/resources/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/resources/loader.d.ts +16 -0
- package/dist/ui/server/fs-routes/resources/loader.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/resources/types.d.ts +256 -0
- package/dist/ui/server/fs-routes/resources/types.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/rest/index.d.ts +8 -0
- package/dist/ui/server/fs-routes/rest/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/rest/loader.d.ts +11 -0
- package/dist/ui/server/fs-routes/rest/loader.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/rest/types.d.ts +288 -0
- package/dist/ui/server/fs-routes/rest/types.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/tcp/index.d.ts +8 -0
- package/dist/ui/server/fs-routes/tcp/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/tcp/loader.d.ts +15 -0
- package/dist/ui/server/fs-routes/tcp/loader.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/tcp/types.d.ts +215 -0
- package/dist/ui/server/fs-routes/tcp/types.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/types.d.ts +437 -0
- package/dist/ui/server/fs-routes/types.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/udp/index.d.ts +8 -0
- package/dist/ui/server/fs-routes/udp/index.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/udp/loader.d.ts +15 -0
- package/dist/ui/server/fs-routes/udp/loader.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/udp/types.d.ts +164 -0
- package/dist/ui/server/fs-routes/udp/types.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/watcher.d.ts +34 -0
- package/dist/ui/server/fs-routes/watcher.d.ts.map +1 -0
- package/dist/ui/types/envelope.d.ts +1 -1
- package/dist/ui/types/envelope.d.ts.map +1 -1
- package/dist/ui/types/handlers.d.ts +8 -0
- package/dist/ui/types/handlers.d.ts.map +1 -1
- package/dist/ui/usd/builder/document.d.ts.map +1 -1
- package/dist/ui/usd/export/openapi.d.ts.map +1 -1
- package/dist/ui/usd/parser/normalize.d.ts.map +1 -1
- package/dist/ui/usd/spec/types.d.ts +14 -20
- package/dist/ui/usd/spec/types.d.ts.map +1 -1
- package/dist/ui/usd/utils/refs.d.ts.map +1 -1
- package/dist/ui/usd/validator/index.d.ts.map +1 -1
- package/dist/ui/usd/validator/schema.d.ts.map +1 -1
- package/dist/ui/usd/validator/semantic.d.ts.map +1 -1
- package/dist/ui/utils/logger.d.ts +15 -0
- package/dist/ui/utils/logger.d.ts.map +1 -0
- package/dist/usd/builder/document.d.ts.map +1 -1
- package/dist/usd/builder/document.js.map +1 -1
- package/dist/usd/export/openapi.d.ts.map +1 -1
- package/dist/usd/export/openapi.js +2 -4
- package/dist/usd/export/openapi.js.map +1 -1
- package/dist/usd/parser/normalize.d.ts.map +1 -1
- package/dist/usd/parser/normalize.js +0 -1
- package/dist/usd/parser/normalize.js.map +1 -1
- package/dist/usd/usd.int.test.d.ts +10 -0
- package/dist/usd/usd.int.test.d.ts.map +1 -0
- package/dist/usd/usd.int.test.js +719 -0
- package/dist/usd/usd.int.test.js.map +1 -0
- package/dist/usd/utils/refs.d.ts.map +1 -1
- package/dist/usd/validator/index.d.ts.map +1 -1
- package/dist/usd/validator/index.js.map +1 -1
- package/dist/usd/validator/schema.d.ts.map +1 -1
- package/dist/usd/validator/schema.js.map +1 -1
- package/dist/usd/validator/semantic.d.ts.map +1 -1
- package/dist/usd/validator/semantic.js.map +1 -1
- package/package.json +1 -1
- package/dist/middleware/rate-limit.d.ts +0 -105
- package/dist/middleware/rate-limit.d.ts.map +0 -1
- package/dist/middleware/rate-limit.int.test.d.ts +0 -5
- package/dist/middleware/rate-limit.int.test.d.ts.map +0 -1
- package/dist/middleware/rate-limit.int.test.js +0 -350
- package/dist/middleware/rate-limit.int.test.js.map +0 -1
- package/dist/middleware/rate-limit.js +0 -206
- package/dist/middleware/rate-limit.js.map +0 -1
- package/dist/openapi/index.d.ts +0 -9
- package/dist/openapi/index.d.ts.map +0 -1
- package/dist/openapi/index.js +0 -9
- package/dist/openapi/index.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Raffel MCP - Interceptor Documentation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Built-in interceptors with options, examples, and use cases.
|
|
5
5
|
*/
|
|
6
6
|
export const interceptors = [
|
|
7
7
|
// === Auth ===
|
|
@@ -11,30 +11,22 @@ export const interceptors = [
|
|
|
11
11
|
category: 'auth',
|
|
12
12
|
options: [
|
|
13
13
|
{
|
|
14
|
-
name: '
|
|
15
|
-
type: 'AuthStrategy',
|
|
14
|
+
name: 'strategies',
|
|
15
|
+
type: 'AuthStrategy[]',
|
|
16
16
|
required: true,
|
|
17
|
-
description: 'Authentication
|
|
17
|
+
description: 'Authentication strategies to try in order',
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
|
-
name: '
|
|
21
|
-
type:
|
|
20
|
+
name: 'publicProcedures',
|
|
21
|
+
type: 'string[]',
|
|
22
22
|
required: false,
|
|
23
|
-
|
|
24
|
-
description: 'Where to extract the credential from',
|
|
23
|
+
description: 'Procedures that skip authentication',
|
|
25
24
|
},
|
|
26
25
|
{
|
|
27
|
-
name: '
|
|
28
|
-
type: '
|
|
29
|
-
required: false,
|
|
30
|
-
default: "'Authorization'",
|
|
31
|
-
description: 'Header name for credential extraction',
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: 'onUnauthenticated',
|
|
35
|
-
type: '(ctx) => void',
|
|
26
|
+
name: 'onError',
|
|
27
|
+
type: '(error, envelope) => void',
|
|
36
28
|
required: false,
|
|
37
|
-
description: '
|
|
29
|
+
description: 'Hook for strategy errors (non-fatal)',
|
|
38
30
|
},
|
|
39
31
|
],
|
|
40
32
|
examples: [
|
|
@@ -44,47 +36,18 @@ export const interceptors = [
|
|
|
44
36
|
|
|
45
37
|
const server = createServer()
|
|
46
38
|
.use(createAuthMiddleware({
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
return
|
|
39
|
+
strategies: [createBearerStrategy({
|
|
40
|
+
verify: async (token) => {
|
|
41
|
+
const payload = await verifyJwt(token)
|
|
42
|
+
return payload ? { authenticated: true, principal: payload.sub, claims: payload } : null
|
|
51
43
|
}
|
|
52
|
-
})
|
|
44
|
+
})]
|
|
53
45
|
}))
|
|
54
46
|
.procedure('users.me')
|
|
55
|
-
.handler(async (
|
|
56
|
-
|
|
57
|
-
return ctx.auth.principal
|
|
47
|
+
.handler(async (_input, ctx) => {
|
|
48
|
+
return { userId: ctx.auth?.principal }
|
|
58
49
|
})`,
|
|
59
50
|
},
|
|
60
|
-
{
|
|
61
|
-
title: 'API Key Authentication',
|
|
62
|
-
code: `import { createServer, createAuthMiddleware, createApiKeyStrategy } from 'raffel'
|
|
63
|
-
|
|
64
|
-
const server = createServer()
|
|
65
|
-
.use(createAuthMiddleware({
|
|
66
|
-
strategy: createApiKeyStrategy({
|
|
67
|
-
validate: async (key) => {
|
|
68
|
-
const app = await db.apiKeys.findByKey(key)
|
|
69
|
-
return app ? { authenticated: true, principal: app } : { authenticated: false }
|
|
70
|
-
},
|
|
71
|
-
extractFrom: 'header',
|
|
72
|
-
headerName: 'X-API-Key'
|
|
73
|
-
})
|
|
74
|
-
}))`,
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
title: 'Static API Key (Development)',
|
|
78
|
-
code: `import { createServer, createAuthMiddleware, createStaticApiKeyStrategy } from 'raffel'
|
|
79
|
-
|
|
80
|
-
const server = createServer()
|
|
81
|
-
.use(createAuthMiddleware({
|
|
82
|
-
strategy: createStaticApiKeyStrategy({
|
|
83
|
-
keys: ['dev-key-123', 'test-key-456'],
|
|
84
|
-
principal: { role: 'admin' }
|
|
85
|
-
})
|
|
86
|
-
}))`,
|
|
87
|
-
},
|
|
88
51
|
],
|
|
89
52
|
},
|
|
90
53
|
{
|
|
@@ -99,24 +62,24 @@ const server = createServer()
|
|
|
99
62
|
description: 'Array of authorization rules to evaluate',
|
|
100
63
|
},
|
|
101
64
|
{
|
|
102
|
-
name: '
|
|
65
|
+
name: 'defaultAllow',
|
|
103
66
|
type: 'boolean',
|
|
104
67
|
required: false,
|
|
105
|
-
default: '
|
|
106
|
-
description: '
|
|
68
|
+
default: 'false',
|
|
69
|
+
description: 'Allow access if no rule matches',
|
|
107
70
|
},
|
|
108
71
|
],
|
|
109
72
|
examples: [
|
|
110
73
|
{
|
|
111
74
|
title: 'Role-Based Access Control',
|
|
112
|
-
code: `import { createServer, createAuthzMiddleware, hasRole, hasAnyRole } from 'raffel'
|
|
75
|
+
code: `import { createServer, createAuthzMiddleware, hasRole, hasAnyRole, requireAuth } from 'raffel'
|
|
113
76
|
|
|
114
77
|
const server = createServer()
|
|
115
78
|
.use(createAuthzMiddleware({
|
|
116
79
|
rules: [
|
|
117
80
|
{ pattern: 'admin.*', check: hasRole('admin') },
|
|
118
81
|
{ pattern: 'users.delete', check: hasAnyRole(['admin', 'moderator']) },
|
|
119
|
-
{ pattern: 'users.*', check: requireAuth
|
|
82
|
+
{ pattern: 'users.*', check: requireAuth },
|
|
120
83
|
]
|
|
121
84
|
}))`,
|
|
122
85
|
},
|
|
@@ -142,11 +105,23 @@ const server = createServer()
|
|
|
142
105
|
default: '100',
|
|
143
106
|
description: 'Maximum requests per window',
|
|
144
107
|
},
|
|
108
|
+
{
|
|
109
|
+
name: 'maxUniqueKeys',
|
|
110
|
+
type: 'number',
|
|
111
|
+
required: false,
|
|
112
|
+
description: 'Maximum unique keys to track',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'skipSuccessfulRequests',
|
|
116
|
+
type: 'boolean',
|
|
117
|
+
required: false,
|
|
118
|
+
description: 'If true, successful requests are not counted',
|
|
119
|
+
},
|
|
145
120
|
{
|
|
146
121
|
name: 'keyGenerator',
|
|
147
122
|
type: '(envelope, ctx) => string',
|
|
148
123
|
required: false,
|
|
149
|
-
description: 'Function to generate rate limit key (default: user or
|
|
124
|
+
description: 'Function to generate rate limit key (default: user or metadata)',
|
|
150
125
|
},
|
|
151
126
|
{
|
|
152
127
|
name: 'rules',
|
|
@@ -156,9 +131,8 @@ const server = createServer()
|
|
|
156
131
|
},
|
|
157
132
|
{
|
|
158
133
|
name: 'driver',
|
|
159
|
-
type: '
|
|
134
|
+
type: 'RateLimitDriverConfig | RateLimitDriver',
|
|
160
135
|
required: false,
|
|
161
|
-
default: 'memory',
|
|
162
136
|
description: 'Storage driver (memory, filesystem, redis, or custom)',
|
|
163
137
|
},
|
|
164
138
|
],
|
|
@@ -170,27 +144,14 @@ const server = createServer()
|
|
|
170
144
|
const server = createServer()
|
|
171
145
|
.use(createRateLimitInterceptor({
|
|
172
146
|
windowMs: 60 * 1000, // 1 minute
|
|
173
|
-
maxRequests: 100, // 100 requests per minute
|
|
174
|
-
}))`,
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
title: 'Per-Procedure Rules',
|
|
178
|
-
code: `import { createServer, createRateLimitInterceptor } from 'raffel'
|
|
179
|
-
|
|
180
|
-
const server = createServer()
|
|
181
|
-
.use(createRateLimitInterceptor({
|
|
182
147
|
maxRequests: 100,
|
|
183
|
-
rules: [
|
|
184
|
-
{ id: 'auth', pattern: 'auth.*', maxRequests: 5, windowMs: 60000 },
|
|
185
|
-
{ id: 'reports', pattern: 'reports.*', maxRequests: 10, windowMs: 3600000 },
|
|
186
|
-
]
|
|
187
148
|
}))`,
|
|
188
149
|
},
|
|
189
150
|
],
|
|
190
151
|
},
|
|
191
152
|
{
|
|
192
|
-
name: '
|
|
193
|
-
description: 'Automatic retry with
|
|
153
|
+
name: 'createRetryInterceptor',
|
|
154
|
+
description: 'Automatic retry with backoff for transient failures. Use for downstream service calls.',
|
|
194
155
|
category: 'resilience',
|
|
195
156
|
options: [
|
|
196
157
|
{
|
|
@@ -201,50 +162,55 @@ const server = createServer()
|
|
|
201
162
|
description: 'Maximum retry attempts',
|
|
202
163
|
},
|
|
203
164
|
{
|
|
204
|
-
name: '
|
|
165
|
+
name: 'initialDelayMs',
|
|
205
166
|
type: 'number',
|
|
206
167
|
required: false,
|
|
207
|
-
default: '
|
|
168
|
+
default: '100',
|
|
208
169
|
description: 'Initial delay in ms before first retry',
|
|
209
170
|
},
|
|
210
171
|
{
|
|
211
|
-
name: '
|
|
172
|
+
name: 'maxDelayMs',
|
|
212
173
|
type: 'number',
|
|
213
174
|
required: false,
|
|
214
|
-
default: '
|
|
175
|
+
default: '10000',
|
|
215
176
|
description: 'Maximum delay between retries',
|
|
216
177
|
},
|
|
217
178
|
{
|
|
218
|
-
name: '
|
|
219
|
-
type: '
|
|
179
|
+
name: 'backoffStrategy',
|
|
180
|
+
type: "'linear' | 'exponential' | 'decorrelated'",
|
|
220
181
|
required: false,
|
|
221
|
-
default: '
|
|
222
|
-
description: '
|
|
182
|
+
default: "'exponential'",
|
|
183
|
+
description: 'Backoff strategy',
|
|
223
184
|
},
|
|
224
185
|
{
|
|
225
|
-
name: '
|
|
226
|
-
type: '
|
|
186
|
+
name: 'retryableCodes',
|
|
187
|
+
type: 'string[]',
|
|
227
188
|
required: false,
|
|
228
|
-
description: '
|
|
189
|
+
description: 'Error codes that should trigger retry',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: 'shouldRetry',
|
|
193
|
+
type: '(error, attempt) => boolean',
|
|
194
|
+
required: false,
|
|
195
|
+
description: 'Custom retry predicate',
|
|
229
196
|
},
|
|
230
197
|
],
|
|
231
198
|
examples: [
|
|
232
199
|
{
|
|
233
|
-
title: 'Retry
|
|
234
|
-
code: `import { createServer, forPattern,
|
|
200
|
+
title: 'Retry External Calls',
|
|
201
|
+
code: `import { createServer, forPattern, createRetryInterceptor } from 'raffel'
|
|
235
202
|
|
|
236
203
|
const server = createServer()
|
|
237
|
-
.use(forPattern('external.*',
|
|
204
|
+
.use(forPattern('external.*', createRetryInterceptor({
|
|
238
205
|
maxAttempts: 3,
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
retryOn: (err) => err.code === 'ECONNREFUSED' || err.status >= 500
|
|
206
|
+
initialDelayMs: 200,
|
|
207
|
+
retryableCodes: ['UNAVAILABLE', 'DEADLINE_EXCEEDED'],
|
|
242
208
|
})))`,
|
|
243
209
|
},
|
|
244
210
|
],
|
|
245
211
|
},
|
|
246
212
|
{
|
|
247
|
-
name: '
|
|
213
|
+
name: 'createCircuitBreakerInterceptor',
|
|
248
214
|
description: 'Circuit breaker pattern to prevent cascading failures. Opens circuit after threshold failures.',
|
|
249
215
|
category: 'resilience',
|
|
250
216
|
options: [
|
|
@@ -259,19 +225,32 @@ const server = createServer()
|
|
|
259
225
|
name: 'successThreshold',
|
|
260
226
|
type: 'number',
|
|
261
227
|
required: false,
|
|
262
|
-
default: '
|
|
228
|
+
default: '3',
|
|
263
229
|
description: 'Successes before circuit closes',
|
|
264
230
|
},
|
|
265
231
|
{
|
|
266
|
-
name: '
|
|
232
|
+
name: 'resetTimeoutMs',
|
|
267
233
|
type: 'number',
|
|
268
234
|
required: false,
|
|
269
235
|
default: '30000',
|
|
270
|
-
description: 'Time in ms before attempting
|
|
236
|
+
description: 'Time in ms before attempting recovery',
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: 'windowMs',
|
|
240
|
+
type: 'number',
|
|
241
|
+
required: false,
|
|
242
|
+
default: '60000',
|
|
243
|
+
description: 'Failure counting window in ms',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: 'failureCodes',
|
|
247
|
+
type: 'string[]',
|
|
248
|
+
required: false,
|
|
249
|
+
description: 'Error codes that count as failures',
|
|
271
250
|
},
|
|
272
251
|
{
|
|
273
252
|
name: 'onStateChange',
|
|
274
|
-
type: '(state) => void',
|
|
253
|
+
type: '(state, procedure) => void',
|
|
275
254
|
required: false,
|
|
276
255
|
description: 'Callback when circuit state changes',
|
|
277
256
|
},
|
|
@@ -279,70 +258,64 @@ const server = createServer()
|
|
|
279
258
|
examples: [
|
|
280
259
|
{
|
|
281
260
|
title: 'Circuit Breaker for External Service',
|
|
282
|
-
code: `import { createServer, forPattern,
|
|
261
|
+
code: `import { createServer, forPattern, createCircuitBreakerInterceptor } from 'raffel'
|
|
283
262
|
|
|
284
263
|
const server = createServer()
|
|
285
|
-
.use(forPattern('payments.*',
|
|
286
|
-
failureThreshold: 5,
|
|
287
|
-
|
|
288
|
-
timeout: 30000, // Try half-open after 30s
|
|
289
|
-
onStateChange: (state) => {
|
|
290
|
-
console.log(\`Circuit breaker state: \${state}\`)
|
|
291
|
-
if (state === 'open') alertOps('Payment service circuit open!')
|
|
292
|
-
}
|
|
264
|
+
.use(forPattern('payments.*', createCircuitBreakerInterceptor({
|
|
265
|
+
failureThreshold: 5,
|
|
266
|
+
resetTimeoutMs: 30000,
|
|
293
267
|
})))`,
|
|
294
268
|
},
|
|
295
269
|
],
|
|
296
270
|
},
|
|
297
271
|
{
|
|
298
|
-
name: '
|
|
299
|
-
description: 'Enforces deadline on handler execution
|
|
272
|
+
name: 'createTimeoutInterceptor',
|
|
273
|
+
description: 'Enforces deadline on handler execution (DEADLINE_EXCEEDED on timeout).',
|
|
300
274
|
category: 'resilience',
|
|
301
275
|
options: [
|
|
302
276
|
{
|
|
303
|
-
name: '
|
|
277
|
+
name: 'defaultMs',
|
|
304
278
|
type: 'number',
|
|
305
|
-
required:
|
|
306
|
-
|
|
279
|
+
required: false,
|
|
280
|
+
default: '30000',
|
|
281
|
+
description: 'Default timeout in ms',
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
name: 'procedures',
|
|
285
|
+
type: 'Record<string, number>',
|
|
286
|
+
required: false,
|
|
287
|
+
description: 'Per-procedure timeouts',
|
|
307
288
|
},
|
|
308
289
|
{
|
|
309
|
-
name: '
|
|
310
|
-
type: '
|
|
290
|
+
name: 'patterns',
|
|
291
|
+
type: 'Record<string, number>',
|
|
311
292
|
required: false,
|
|
312
|
-
description: '
|
|
293
|
+
description: 'Pattern-based timeouts',
|
|
313
294
|
},
|
|
314
295
|
],
|
|
315
296
|
examples: [
|
|
316
297
|
{
|
|
317
298
|
title: 'Global Timeout',
|
|
318
|
-
code: `import { createServer,
|
|
319
|
-
|
|
320
|
-
const server = createServer()
|
|
321
|
-
.use(timeout({ ms: 30000 })) // 30 second timeout for all procedures`,
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
title: 'Per-Procedure Timeout',
|
|
325
|
-
code: `import { createServer, forPattern, timeout } from 'raffel'
|
|
299
|
+
code: `import { createServer, createTimeoutInterceptor } from 'raffel'
|
|
326
300
|
|
|
327
301
|
const server = createServer()
|
|
328
|
-
.use(
|
|
329
|
-
.use(timeout({ ms: 10000 })) // 10s default`,
|
|
302
|
+
.use(createTimeoutInterceptor({ defaultMs: 30000 }))`,
|
|
330
303
|
},
|
|
331
304
|
],
|
|
332
305
|
},
|
|
333
306
|
{
|
|
334
|
-
name: '
|
|
307
|
+
name: 'createBulkheadInterceptor',
|
|
335
308
|
description: 'Limits concurrent executions to isolate failures. Prevents one slow procedure from consuming all resources.',
|
|
336
309
|
category: 'resilience',
|
|
337
310
|
options: [
|
|
338
311
|
{
|
|
339
|
-
name: '
|
|
312
|
+
name: 'concurrency',
|
|
340
313
|
type: 'number',
|
|
341
314
|
required: true,
|
|
342
315
|
description: 'Maximum concurrent executions',
|
|
343
316
|
},
|
|
344
317
|
{
|
|
345
|
-
name: '
|
|
318
|
+
name: 'maxQueueSize',
|
|
346
319
|
type: 'number',
|
|
347
320
|
required: false,
|
|
348
321
|
default: '0',
|
|
@@ -352,36 +325,49 @@ const server = createServer()
|
|
|
352
325
|
name: 'queueTimeout',
|
|
353
326
|
type: 'number',
|
|
354
327
|
required: false,
|
|
328
|
+
default: '0',
|
|
355
329
|
description: 'Max time to wait in queue (ms)',
|
|
356
330
|
},
|
|
331
|
+
{
|
|
332
|
+
name: 'onReject',
|
|
333
|
+
type: '(procedure) => void',
|
|
334
|
+
required: false,
|
|
335
|
+
description: 'Callback when a request is rejected',
|
|
336
|
+
},
|
|
357
337
|
],
|
|
358
338
|
examples: [
|
|
359
339
|
{
|
|
360
340
|
title: 'Limit Concurrent Database Queries',
|
|
361
|
-
code: `import { createServer, forPattern,
|
|
341
|
+
code: `import { createServer, forPattern, createBulkheadInterceptor } from 'raffel'
|
|
362
342
|
|
|
363
343
|
const server = createServer()
|
|
364
|
-
.use(forPattern('db.*',
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
queueTimeout: 5000
|
|
344
|
+
.use(forPattern('db.*', createBulkheadInterceptor({
|
|
345
|
+
concurrency: 10,
|
|
346
|
+
maxQueueSize: 50,
|
|
347
|
+
queueTimeout: 5000,
|
|
368
348
|
})))`,
|
|
369
349
|
},
|
|
370
350
|
],
|
|
371
351
|
},
|
|
372
352
|
{
|
|
373
|
-
name: '
|
|
353
|
+
name: 'createFallbackInterceptor',
|
|
374
354
|
description: 'Provides fallback response when handler fails. Useful for graceful degradation.',
|
|
375
355
|
category: 'resilience',
|
|
376
356
|
options: [
|
|
377
357
|
{
|
|
378
|
-
name: '
|
|
379
|
-
type: '
|
|
380
|
-
required:
|
|
381
|
-
description: '
|
|
358
|
+
name: 'response',
|
|
359
|
+
type: 'unknown',
|
|
360
|
+
required: false,
|
|
361
|
+
description: 'Static fallback response',
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: 'handler',
|
|
365
|
+
type: '(ctx, error) => unknown',
|
|
366
|
+
required: false,
|
|
367
|
+
description: 'Dynamic fallback handler',
|
|
382
368
|
},
|
|
383
369
|
{
|
|
384
|
-
name: '
|
|
370
|
+
name: 'when',
|
|
385
371
|
type: '(error) => boolean',
|
|
386
372
|
required: false,
|
|
387
373
|
description: 'Predicate to decide if fallback should be used',
|
|
@@ -390,15 +376,14 @@ const server = createServer()
|
|
|
390
376
|
examples: [
|
|
391
377
|
{
|
|
392
378
|
title: 'Fallback to Cached Data',
|
|
393
|
-
code: `import { createServer, forPattern,
|
|
379
|
+
code: `import { createServer, forPattern, createFallbackInterceptor } from 'raffel'
|
|
394
380
|
|
|
395
381
|
const server = createServer()
|
|
396
|
-
.use(forPattern('prices.*',
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
return await cache.get(\`prices:\${ctx.procedure}\`) || { prices: [], stale: true }
|
|
382
|
+
.use(forPattern('prices.*', createFallbackInterceptor({
|
|
383
|
+
handler: async (_ctx, error) => {
|
|
384
|
+
return await cache.get('prices') || { prices: [], stale: true, reason: error.message }
|
|
400
385
|
},
|
|
401
|
-
|
|
386
|
+
when: (err) => (err as any).code === 'UNAVAILABLE',
|
|
402
387
|
})))`,
|
|
403
388
|
},
|
|
404
389
|
],
|
|
@@ -406,7 +391,7 @@ const server = createServer()
|
|
|
406
391
|
// === Observability ===
|
|
407
392
|
{
|
|
408
393
|
name: 'createMetricsInterceptor',
|
|
409
|
-
description: 'Auto-instruments
|
|
394
|
+
description: 'Auto-instruments procedures with metrics (latency, count, errors).',
|
|
410
395
|
category: 'observability',
|
|
411
396
|
options: [
|
|
412
397
|
{
|
|
@@ -415,18 +400,6 @@ const server = createServer()
|
|
|
415
400
|
required: true,
|
|
416
401
|
description: 'Metric registry for storing metrics',
|
|
417
402
|
},
|
|
418
|
-
{
|
|
419
|
-
name: 'buckets',
|
|
420
|
-
type: 'number[]',
|
|
421
|
-
required: false,
|
|
422
|
-
description: 'Histogram buckets for latency',
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
name: 'labels',
|
|
426
|
-
type: '(ctx) => Record<string, string>',
|
|
427
|
-
required: false,
|
|
428
|
-
description: 'Additional labels to add to metrics',
|
|
429
|
-
},
|
|
430
403
|
],
|
|
431
404
|
examples: [
|
|
432
405
|
{
|
|
@@ -435,15 +408,7 @@ const server = createServer()
|
|
|
435
408
|
|
|
436
409
|
const metrics = createMetricRegistry()
|
|
437
410
|
const server = createServer()
|
|
438
|
-
.use(createMetricsInterceptor(
|
|
439
|
-
|
|
440
|
-
// Metrics automatically collected:
|
|
441
|
-
// - raffel_procedure_duration_seconds (histogram)
|
|
442
|
-
// - raffel_procedure_total (counter)
|
|
443
|
-
// - raffel_procedure_errors_total (counter)
|
|
444
|
-
|
|
445
|
-
server.procedure('metrics.export')
|
|
446
|
-
.handler(async () => exportPrometheus(metrics))`,
|
|
411
|
+
.use(createMetricsInterceptor(metrics))`,
|
|
447
412
|
},
|
|
448
413
|
],
|
|
449
414
|
},
|
|
@@ -458,18 +423,6 @@ server.procedure('metrics.export')
|
|
|
458
423
|
required: true,
|
|
459
424
|
description: 'Tracer instance for creating spans',
|
|
460
425
|
},
|
|
461
|
-
{
|
|
462
|
-
name: 'spanName',
|
|
463
|
-
type: '(ctx) => string',
|
|
464
|
-
required: false,
|
|
465
|
-
description: 'Custom span name generator',
|
|
466
|
-
},
|
|
467
|
-
{
|
|
468
|
-
name: 'attributes',
|
|
469
|
-
type: '(ctx) => SpanAttributes',
|
|
470
|
-
required: false,
|
|
471
|
-
description: 'Additional span attributes',
|
|
472
|
-
},
|
|
473
426
|
],
|
|
474
427
|
examples: [
|
|
475
428
|
{
|
|
@@ -483,22 +436,22 @@ server.procedure('metrics.export')
|
|
|
483
436
|
|
|
484
437
|
const tracer = createTracer({
|
|
485
438
|
serviceName: 'my-api',
|
|
486
|
-
|
|
439
|
+
exporters: [createJaegerExporter({ serviceName: 'my-api' })],
|
|
487
440
|
})
|
|
488
441
|
|
|
489
442
|
const server = createServer()
|
|
490
|
-
.use(createTracingInterceptor(
|
|
443
|
+
.use(createTracingInterceptor(tracer))`,
|
|
491
444
|
},
|
|
492
445
|
],
|
|
493
446
|
},
|
|
494
447
|
{
|
|
495
|
-
name: '
|
|
448
|
+
name: 'createLoggingInterceptor',
|
|
496
449
|
description: 'Structured logging for request/response with configurable levels and formats.',
|
|
497
450
|
category: 'observability',
|
|
498
451
|
options: [
|
|
499
452
|
{
|
|
500
453
|
name: 'level',
|
|
501
|
-
type: "'debug' | 'info' | 'warn' | 'error'",
|
|
454
|
+
type: "'trace' | 'debug' | 'info' | 'warn' | 'error'",
|
|
502
455
|
required: false,
|
|
503
456
|
default: "'info'",
|
|
504
457
|
description: 'Log level',
|
|
@@ -507,35 +460,47 @@ const server = createServer()
|
|
|
507
460
|
name: 'format',
|
|
508
461
|
type: "'json' | 'pretty'",
|
|
509
462
|
required: false,
|
|
510
|
-
default: "'
|
|
463
|
+
default: "'pretty'",
|
|
511
464
|
description: 'Log output format',
|
|
512
465
|
},
|
|
513
466
|
{
|
|
514
|
-
name: '
|
|
467
|
+
name: 'includePayload',
|
|
468
|
+
type: 'boolean',
|
|
469
|
+
required: false,
|
|
470
|
+
default: 'false',
|
|
471
|
+
description: 'Include request payload in logs',
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
name: 'includeResponse',
|
|
515
475
|
type: 'boolean',
|
|
516
476
|
required: false,
|
|
517
477
|
default: 'false',
|
|
518
|
-
description: 'Include
|
|
478
|
+
description: 'Include response payload in logs',
|
|
519
479
|
},
|
|
520
480
|
{
|
|
521
|
-
name: '
|
|
481
|
+
name: 'includeMetadata',
|
|
522
482
|
type: 'boolean',
|
|
523
483
|
required: false,
|
|
524
484
|
default: 'false',
|
|
525
|
-
description: 'Include
|
|
485
|
+
description: 'Include metadata (headers) in logs',
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
name: 'excludeProcedures',
|
|
489
|
+
type: 'string[]',
|
|
490
|
+
required: false,
|
|
491
|
+
description: 'Procedure patterns to exclude',
|
|
526
492
|
},
|
|
527
493
|
],
|
|
528
494
|
examples: [
|
|
529
495
|
{
|
|
530
496
|
title: 'Production Logging',
|
|
531
|
-
code: `import { createServer,
|
|
497
|
+
code: `import { createServer, createLoggingInterceptor, except } from 'raffel'
|
|
532
498
|
|
|
533
499
|
const server = createServer()
|
|
534
|
-
.use(except('health.*',
|
|
500
|
+
.use(except(['health.*'], createLoggingInterceptor({
|
|
535
501
|
level: 'info',
|
|
536
502
|
format: 'json',
|
|
537
|
-
|
|
538
|
-
includeOutput: false
|
|
503
|
+
includeMetadata: false,
|
|
539
504
|
})))`,
|
|
540
505
|
},
|
|
541
506
|
],
|
|
@@ -543,155 +508,118 @@ const server = createServer()
|
|
|
543
508
|
// === Validation ===
|
|
544
509
|
{
|
|
545
510
|
name: 'createValidationInterceptor',
|
|
546
|
-
description: 'Validates input/output against
|
|
511
|
+
description: 'Validates input/output against a schema for a specific handler.',
|
|
547
512
|
category: 'validation',
|
|
548
513
|
options: [
|
|
549
514
|
{
|
|
550
|
-
name: '
|
|
551
|
-
type: '
|
|
552
|
-
required:
|
|
553
|
-
|
|
554
|
-
description: 'Validate request input',
|
|
555
|
-
},
|
|
556
|
-
{
|
|
557
|
-
name: 'validateOutput',
|
|
558
|
-
type: 'boolean',
|
|
559
|
-
required: false,
|
|
560
|
-
default: 'false',
|
|
561
|
-
description: 'Validate response output',
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
name: 'onError',
|
|
565
|
-
type: '(errors) => void',
|
|
566
|
-
required: false,
|
|
567
|
-
description: 'Callback on validation error',
|
|
515
|
+
name: 'schema',
|
|
516
|
+
type: 'HandlerSchema',
|
|
517
|
+
required: true,
|
|
518
|
+
description: 'Schema with input/output validators',
|
|
568
519
|
},
|
|
569
520
|
],
|
|
570
521
|
examples: [
|
|
571
522
|
{
|
|
572
523
|
title: 'Zod Validation',
|
|
573
|
-
code: `import { createServer, createValidationInterceptor
|
|
524
|
+
code: `import { createServer, createValidationInterceptor } from 'raffel'
|
|
574
525
|
import { z } from 'zod'
|
|
575
526
|
|
|
576
|
-
|
|
527
|
+
const schema = {
|
|
528
|
+
input: z.object({ email: z.string().email() }),
|
|
529
|
+
output: z.object({ id: z.string(), email: z.string() }),
|
|
530
|
+
}
|
|
577
531
|
|
|
578
532
|
const server = createServer()
|
|
579
|
-
.use(createValidationInterceptor({ validateInput: true, validateOutput: true }))
|
|
580
533
|
.procedure('users.create')
|
|
581
|
-
.
|
|
582
|
-
|
|
583
|
-
name: z.string().min(2).max(100),
|
|
584
|
-
age: z.number().min(18).optional()
|
|
585
|
-
}))
|
|
586
|
-
.output(z.object({
|
|
587
|
-
id: z.string(),
|
|
588
|
-
email: z.string(),
|
|
589
|
-
name: z.string()
|
|
590
|
-
}))
|
|
591
|
-
.handler(async (input, ctx) => {
|
|
592
|
-
// Input is already validated and typed!
|
|
593
|
-
return await db.users.create(input)
|
|
594
|
-
})`,
|
|
534
|
+
.use(createValidationInterceptor(schema))
|
|
535
|
+
.handler(async (input) => createUser(input))`,
|
|
595
536
|
},
|
|
596
537
|
],
|
|
597
538
|
},
|
|
598
539
|
// === Caching ===
|
|
599
540
|
{
|
|
600
|
-
name: '
|
|
601
|
-
description: 'Response caching with pluggable drivers
|
|
541
|
+
name: 'createCacheInterceptor',
|
|
542
|
+
description: 'Response caching with pluggable drivers. Supports TTL, stale-while-revalidate, and invalidation.',
|
|
602
543
|
category: 'caching',
|
|
603
544
|
options: [
|
|
545
|
+
{
|
|
546
|
+
name: 'ttlMs',
|
|
547
|
+
type: 'number',
|
|
548
|
+
required: false,
|
|
549
|
+
default: '60000',
|
|
550
|
+
description: 'Time-to-live in milliseconds',
|
|
551
|
+
},
|
|
604
552
|
{
|
|
605
553
|
name: 'driver',
|
|
606
554
|
type: 'CacheDriver',
|
|
607
|
-
required:
|
|
608
|
-
description: 'Cache driver instance',
|
|
555
|
+
required: false,
|
|
556
|
+
description: 'Cache driver instance (memory, redis, file, s3db)',
|
|
609
557
|
},
|
|
610
558
|
{
|
|
611
|
-
name: '
|
|
612
|
-
type: '
|
|
559
|
+
name: 'procedures',
|
|
560
|
+
type: 'string[]',
|
|
613
561
|
required: false,
|
|
614
|
-
|
|
615
|
-
description: 'Time-to-live in milliseconds',
|
|
562
|
+
description: 'Procedures to include (glob patterns supported)',
|
|
616
563
|
},
|
|
617
564
|
{
|
|
618
|
-
name: '
|
|
619
|
-
type: '
|
|
565
|
+
name: 'excludeProcedures',
|
|
566
|
+
type: 'string[]',
|
|
620
567
|
required: false,
|
|
621
|
-
description: '
|
|
568
|
+
description: 'Procedures to exclude',
|
|
622
569
|
},
|
|
623
570
|
{
|
|
624
|
-
name: '
|
|
625
|
-
type: '(
|
|
571
|
+
name: 'keyGenerator',
|
|
572
|
+
type: '(envelope) => string',
|
|
626
573
|
required: false,
|
|
627
|
-
description: '
|
|
574
|
+
description: 'Custom cache key generator',
|
|
628
575
|
},
|
|
629
576
|
],
|
|
630
577
|
examples: [
|
|
631
578
|
{
|
|
632
|
-
title: '
|
|
633
|
-
code: `import { createServer
|
|
579
|
+
title: 'Redis Cache',
|
|
580
|
+
code: `import { createServer } from 'raffel'
|
|
581
|
+
import { createCacheInterceptor } from 'raffel/middleware'
|
|
582
|
+
import { createDriver } from 'raffel/cache'
|
|
634
583
|
|
|
635
|
-
const
|
|
636
|
-
|
|
637
|
-
evictionPolicy: 'lru'
|
|
638
|
-
})
|
|
584
|
+
const redisDriver = await createDriver('redis', { client: redis })
|
|
585
|
+
const cache = createCacheInterceptor({ ttlMs: 60000, driver: redisDriver })
|
|
639
586
|
|
|
640
|
-
const server = createServer()
|
|
641
|
-
.use(forPattern('products.list', cache({
|
|
642
|
-
driver: memoryCache,
|
|
643
|
-
ttl: 5 * 60 * 1000, // 5 minutes
|
|
644
|
-
keyGenerator: (ctx, input) => \`products:\${JSON.stringify(input)}\`
|
|
645
|
-
})))`,
|
|
646
|
-
},
|
|
647
|
-
{
|
|
648
|
-
title: 'Redis Distributed Cache',
|
|
649
|
-
code: `import { createServer, forPattern, cache, createCacheRedisDriver } from 'raffel'
|
|
650
|
-
import Redis from 'ioredis'
|
|
651
|
-
|
|
652
|
-
const redisCache = createCacheRedisDriver({
|
|
653
|
-
client: new Redis(process.env.REDIS_URL),
|
|
654
|
-
prefix: 'cache:'
|
|
655
|
-
})
|
|
656
|
-
|
|
657
|
-
const server = createServer()
|
|
658
|
-
.use(forPattern('*', cache({
|
|
659
|
-
driver: redisCache,
|
|
660
|
-
ttl: 60000,
|
|
661
|
-
shouldCache: (ctx, result) => !ctx.auth // Don't cache authenticated responses
|
|
662
|
-
})))`,
|
|
587
|
+
const server = createServer().use(cache)`,
|
|
663
588
|
},
|
|
664
589
|
],
|
|
665
590
|
},
|
|
666
591
|
{
|
|
667
|
-
name: '
|
|
592
|
+
name: 'createDedupInterceptor',
|
|
668
593
|
description: 'Request deduplication to prevent duplicate processing of identical concurrent requests.',
|
|
669
594
|
category: 'caching',
|
|
670
595
|
options: [
|
|
671
596
|
{
|
|
672
|
-
name: '
|
|
597
|
+
name: 'ttlMs',
|
|
673
598
|
type: 'number',
|
|
674
599
|
required: false,
|
|
675
|
-
default: '
|
|
676
|
-
description: '
|
|
600
|
+
default: '30000',
|
|
601
|
+
description: 'TTL for pending requests',
|
|
677
602
|
},
|
|
678
603
|
{
|
|
679
604
|
name: 'keyGenerator',
|
|
680
|
-
type: '(
|
|
605
|
+
type: '(envelope, ctx) => string',
|
|
681
606
|
required: false,
|
|
682
607
|
description: 'Custom dedup key generator',
|
|
683
608
|
},
|
|
609
|
+
{
|
|
610
|
+
name: 'procedures',
|
|
611
|
+
type: 'string[]',
|
|
612
|
+
required: false,
|
|
613
|
+
description: 'Procedures to deduplicate (glob patterns)',
|
|
614
|
+
},
|
|
684
615
|
],
|
|
685
616
|
examples: [
|
|
686
617
|
{
|
|
687
618
|
title: 'Prevent Double Submit',
|
|
688
|
-
code: `import { createServer, forPattern,
|
|
619
|
+
code: `import { createServer, forPattern, createDedupInterceptor } from 'raffel'
|
|
689
620
|
|
|
690
621
|
const server = createServer()
|
|
691
|
-
.use(forPattern('orders.create',
|
|
692
|
-
windowMs: 5000, // 5 second window
|
|
693
|
-
keyGenerator: (ctx, input) => \`order:\${ctx.auth.principal.id}:\${input.idempotencyKey}\`
|
|
694
|
-
})))`,
|
|
622
|
+
.use(forPattern('orders.create', createDedupInterceptor()))`,
|
|
695
623
|
},
|
|
696
624
|
],
|
|
697
625
|
},
|
|
@@ -711,16 +639,14 @@ const server = createServer()
|
|
|
711
639
|
examples: [
|
|
712
640
|
{
|
|
713
641
|
title: 'Compose Production Stack',
|
|
714
|
-
code: `import { createServer, compose,
|
|
642
|
+
code: `import { createServer, compose, createTimeoutInterceptor, createLoggingInterceptor } from 'raffel'
|
|
715
643
|
|
|
716
644
|
const productionStack = compose(
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
cache({ driver: memoryCache, ttl: 60000 })
|
|
645
|
+
createTimeoutInterceptor({ defaultMs: 30000 }),
|
|
646
|
+
createLoggingInterceptor({ level: 'info', format: 'json' })
|
|
720
647
|
)
|
|
721
648
|
|
|
722
|
-
const server = createServer()
|
|
723
|
-
.use(productionStack)`,
|
|
649
|
+
const server = createServer().use(productionStack)`,
|
|
724
650
|
},
|
|
725
651
|
],
|
|
726
652
|
},
|
|
@@ -731,7 +657,7 @@ const server = createServer()
|
|
|
731
657
|
options: [
|
|
732
658
|
{
|
|
733
659
|
name: 'predicate',
|
|
734
|
-
type: '(ctx) => boolean',
|
|
660
|
+
type: '(envelope, ctx) => boolean',
|
|
735
661
|
required: true,
|
|
736
662
|
description: 'Condition to evaluate',
|
|
737
663
|
},
|
|
@@ -744,13 +670,13 @@ const server = createServer()
|
|
|
744
670
|
],
|
|
745
671
|
examples: [
|
|
746
672
|
{
|
|
747
|
-
title: 'Environment-Based
|
|
748
|
-
code: `import { createServer, when,
|
|
673
|
+
title: 'Environment-Based Logging',
|
|
674
|
+
code: `import { createServer, when, createLoggingInterceptor } from 'raffel'
|
|
749
675
|
|
|
750
676
|
const server = createServer()
|
|
751
677
|
.use(when(
|
|
752
678
|
() => process.env.NODE_ENV === 'development',
|
|
753
|
-
|
|
679
|
+
createLoggingInterceptor({ level: 'debug', format: 'pretty' })
|
|
754
680
|
))`,
|
|
755
681
|
},
|
|
756
682
|
],
|
|
@@ -762,9 +688,9 @@ const server = createServer()
|
|
|
762
688
|
options: [
|
|
763
689
|
{
|
|
764
690
|
name: 'pattern',
|
|
765
|
-
type: 'string
|
|
691
|
+
type: 'string',
|
|
766
692
|
required: true,
|
|
767
|
-
description: 'Glob pattern
|
|
693
|
+
description: 'Glob pattern to match (e.g., "users.*", "admin.**")',
|
|
768
694
|
},
|
|
769
695
|
{
|
|
770
696
|
name: 'interceptor',
|
|
@@ -787,14 +713,14 @@ const server = createServer()
|
|
|
787
713
|
},
|
|
788
714
|
{
|
|
789
715
|
name: 'except',
|
|
790
|
-
description: 'Applies interceptor to all procedures except those matching
|
|
716
|
+
description: 'Applies interceptor to all procedures except those matching names.',
|
|
791
717
|
category: 'composition',
|
|
792
718
|
options: [
|
|
793
719
|
{
|
|
794
|
-
name: '
|
|
795
|
-
type: 'string
|
|
720
|
+
name: 'procedures',
|
|
721
|
+
type: 'string[]',
|
|
796
722
|
required: true,
|
|
797
|
-
description: '
|
|
723
|
+
description: 'Procedures to exclude',
|
|
798
724
|
},
|
|
799
725
|
{
|
|
800
726
|
name: 'interceptor',
|
|
@@ -806,10 +732,10 @@ const server = createServer()
|
|
|
806
732
|
examples: [
|
|
807
733
|
{
|
|
808
734
|
title: 'Exclude Health Checks from Logging',
|
|
809
|
-
code: `import { createServer, except,
|
|
735
|
+
code: `import { createServer, except, createLoggingInterceptor } from 'raffel'
|
|
810
736
|
|
|
811
737
|
const server = createServer()
|
|
812
|
-
.use(except('health
|
|
738
|
+
.use(except(['health.check'], createLoggingInterceptor({ level: 'info' })))`,
|
|
813
739
|
},
|
|
814
740
|
],
|
|
815
741
|
},
|
|
@@ -820,7 +746,7 @@ const server = createServer()
|
|
|
820
746
|
options: [
|
|
821
747
|
{
|
|
822
748
|
name: 'predicate',
|
|
823
|
-
type: '(ctx) => boolean',
|
|
749
|
+
type: '(envelope, ctx) => boolean',
|
|
824
750
|
required: true,
|
|
825
751
|
description: 'Condition to branch on',
|
|
826
752
|
},
|
|
@@ -840,13 +766,13 @@ const server = createServer()
|
|
|
840
766
|
examples: [
|
|
841
767
|
{
|
|
842
768
|
title: 'Different Caching by Auth Status',
|
|
843
|
-
code: `import { createServer, branch,
|
|
769
|
+
code: `import { createServer, branch, createCacheInterceptor } from 'raffel'
|
|
844
770
|
|
|
845
771
|
const server = createServer()
|
|
846
772
|
.use(branch(
|
|
847
|
-
(ctx) => !ctx.auth?.authenticated,
|
|
848
|
-
|
|
849
|
-
|
|
773
|
+
(_env, ctx) => !ctx.auth?.authenticated,
|
|
774
|
+
createCacheInterceptor({ ttlMs: 300000 }), // 5 min for public
|
|
775
|
+
createCacheInterceptor({ ttlMs: 60000 }) // 1 min for authenticated
|
|
850
776
|
))`,
|
|
851
777
|
},
|
|
852
778
|
],
|