moost 0.5.33 → 0.6.1
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 +21 -0
- package/dist/index.cjs +743 -271
- package/dist/index.d.ts +298 -116
- package/dist/index.mjs +680 -254
- package/package.json +39 -33
- package/scripts/setup-skills.js +76 -0
- package/skills/moost/SKILL.md +34 -0
- package/skills/moost/core.md +174 -0
- package/skills/moost/custom-adapters.md +744 -0
- package/skills/moost/decorators.md +185 -0
- package/skills/moost/di.md +161 -0
- package/skills/moost/interceptors.md +199 -0
- package/skills/moost/pipes.md +213 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Pipes & Resolvers — moost
|
|
2
|
+
|
|
3
|
+
> Pipe pipeline for argument resolution, `@Pipe`, `@Resolve`, validation/transformation stages, and the argument resolution lifecycle.
|
|
4
|
+
|
|
5
|
+
## Concepts
|
|
6
|
+
|
|
7
|
+
Pipes process method arguments before the handler executes. They run in a defined priority pipeline:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
BEFORE_RESOLVE → RESOLVE → AFTER_RESOLVE → BEFORE_TRANSFORM → TRANSFORM → AFTER_TRANSFORM → BEFORE_VALIDATE → VALIDATE → AFTER_VALIDATE
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Each stage can modify the argument value. The most commonly used stages:
|
|
14
|
+
- **RESOLVE** — Fetch the initial value (from request, context, DI, etc.)
|
|
15
|
+
- **TRANSFORM** — Convert types, parse data
|
|
16
|
+
- **VALIDATE** — Check constraints, throw on invalid data
|
|
17
|
+
|
|
18
|
+
### Pipe Scope
|
|
19
|
+
|
|
20
|
+
Pipes can be registered at three levels:
|
|
21
|
+
1. **Global** — `app.pipe(myPipe)` — applies to all handlers
|
|
22
|
+
2. **Class** — `@Pipe(myPipe)` on controller class — applies to all methods
|
|
23
|
+
3. **Method/Parameter** — `@Pipe(myPipe)` on method or parameter — applies to that specific target
|
|
24
|
+
|
|
25
|
+
Pipes are merged and sorted by priority across all levels.
|
|
26
|
+
|
|
27
|
+
## API Reference
|
|
28
|
+
|
|
29
|
+
### `@Resolve(fn, name?)`
|
|
30
|
+
|
|
31
|
+
The most common pipe — resolves a parameter value from the event context. Used to create parameter decorators.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { Resolve } from 'moost'
|
|
35
|
+
|
|
36
|
+
// Simple resolver
|
|
37
|
+
function MyParam() {
|
|
38
|
+
return Resolve(() => useMyContext().value, 'my-param')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Usage
|
|
42
|
+
@Get('test')
|
|
43
|
+
handler(@MyParam() value: string) { }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`@Resolve()` registers a pipe at the `RESOLVE` stage. The function runs inside the event context and can use any composables.
|
|
47
|
+
|
|
48
|
+
### `@Param(name?: string)`
|
|
49
|
+
|
|
50
|
+
Built-in resolver for route parameters (from Wooks router).
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
@Get('users/:id')
|
|
54
|
+
getUser(@Param('id') id: string) { }
|
|
55
|
+
|
|
56
|
+
@Get('users/:id/posts/:postId')
|
|
57
|
+
getPost(@Params() params: { id: string; postId: string }) { }
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### `@Pipe(pipe)`
|
|
61
|
+
|
|
62
|
+
Registers a pipe at class, method, or parameter level.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { Pipe } from 'moost'
|
|
66
|
+
|
|
67
|
+
// Class-level pipe
|
|
68
|
+
@Pipe(validationPipe)
|
|
69
|
+
@Controller()
|
|
70
|
+
class MyController { }
|
|
71
|
+
|
|
72
|
+
// Method-level pipe
|
|
73
|
+
@Pipe(transformPipe)
|
|
74
|
+
@Get('data')
|
|
75
|
+
getData() { }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Pipe Definition (TPipeData)
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
interface TPipeData {
|
|
82
|
+
priority: TPipePriority // Stage in the pipeline
|
|
83
|
+
pipe: TPipeFunction // The pipe function
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
type TPipeFunction = (
|
|
87
|
+
value: unknown, // Current parameter value
|
|
88
|
+
metas: TPipeMetas, // Metadata about the parameter
|
|
89
|
+
level: 'PARAM' | 'METHOD' // Whether this is a parameter or return value pipe
|
|
90
|
+
) => unknown | Promise<unknown>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `TPipePriority` stages
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
enum TPipePriority {
|
|
97
|
+
BEFORE_RESOLVE = 0,
|
|
98
|
+
RESOLVE = 10,
|
|
99
|
+
AFTER_RESOLVE = 20,
|
|
100
|
+
BEFORE_TRANSFORM = 30,
|
|
101
|
+
TRANSFORM = 40,
|
|
102
|
+
AFTER_TRANSFORM = 50,
|
|
103
|
+
BEFORE_VALIDATE = 60,
|
|
104
|
+
VALIDATE = 70,
|
|
105
|
+
AFTER_VALIDATE = 80,
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### `TPipeMetas` (context passed to pipe functions)
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
interface TPipeMetas {
|
|
113
|
+
classMeta: TMoostMetadata // Controller class metadata
|
|
114
|
+
methodMeta: TMoostMetadata // Handler method metadata
|
|
115
|
+
paramMeta: TMoostParamsMetadata // Parameter metadata (type, inject, paramSource, etc.)
|
|
116
|
+
type: TClassConstructor // Controller class constructor
|
|
117
|
+
key: string | symbol // Method name
|
|
118
|
+
index: number // Parameter index
|
|
119
|
+
targetMeta: TMoostParamsMetadata
|
|
120
|
+
instantiate: <T>(cls: TClassConstructor<T>) => Promise<T> | T // DI helper
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Common Patterns
|
|
125
|
+
|
|
126
|
+
### Pattern: Custom Parameter Decorator with Resolve
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { Resolve } from 'moost'
|
|
130
|
+
import { useRequest } from '@wooksjs/event-http'
|
|
131
|
+
|
|
132
|
+
function ClientIp(): ParameterDecorator {
|
|
133
|
+
return Resolve(() => {
|
|
134
|
+
const { raw } = useRequest()
|
|
135
|
+
return raw.socket.remoteAddress
|
|
136
|
+
}, 'client-ip')
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Pattern: Validation Pipe
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { Pipe, TPipePriority } from 'moost'
|
|
144
|
+
|
|
145
|
+
const zodValidation: TPipeData = {
|
|
146
|
+
priority: TPipePriority.VALIDATE,
|
|
147
|
+
pipe(value, metas) {
|
|
148
|
+
const schema = metas.paramMeta.zodSchema
|
|
149
|
+
if (schema) {
|
|
150
|
+
return schema.parse(value) // Throws ZodError on invalid
|
|
151
|
+
}
|
|
152
|
+
return value
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@Pipe(zodValidation)
|
|
157
|
+
@Controller()
|
|
158
|
+
class ValidatedController { }
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Pattern: Type Transformation Pipe
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
const autoTransform: TPipeData = {
|
|
165
|
+
priority: TPipePriority.TRANSFORM,
|
|
166
|
+
pipe(value, metas) {
|
|
167
|
+
const targetType = metas.paramMeta.type
|
|
168
|
+
if (targetType === Number && typeof value === 'string') {
|
|
169
|
+
return Number(value)
|
|
170
|
+
}
|
|
171
|
+
if (targetType === Boolean && typeof value === 'string') {
|
|
172
|
+
return value === 'true'
|
|
173
|
+
}
|
|
174
|
+
return value
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Integration
|
|
180
|
+
|
|
181
|
+
### With Adapters
|
|
182
|
+
|
|
183
|
+
Argument resolution is provided to adapters via `opts.resolveArgs` in `bindHandler()`. Pass it through to `defineMoostEventHandler()` — pipes run automatically during the "Arguments:resolve" phase of the handler lifecycle.
|
|
184
|
+
|
|
185
|
+
### With the Handler Lifecycle
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
defineMoostEventHandler execution:
|
|
189
|
+
1. Scope + logger setup
|
|
190
|
+
2. Controller instance resolution
|
|
191
|
+
3. Interceptor before phase
|
|
192
|
+
4. ──► Argument resolution (pipes run here) ◄──
|
|
193
|
+
5. Handler method execution with resolved args
|
|
194
|
+
6. Interceptor after phase
|
|
195
|
+
7. Cleanup
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
If argument resolution throws (e.g., validation failure), the handler is skipped and the error flows to interceptor error handlers.
|
|
199
|
+
|
|
200
|
+
## Best Practices
|
|
201
|
+
|
|
202
|
+
- Use `@Resolve()` for parameter injection — it's the standard pattern across all adapters
|
|
203
|
+
- Register validation pipes at class level to apply consistently
|
|
204
|
+
- Keep pipe functions pure — avoid side effects during argument resolution
|
|
205
|
+
- Use `TPipePriority.VALIDATE` for validation and `TPipePriority.TRANSFORM` for type coercion
|
|
206
|
+
- The `instantiate` helper in `TPipeMetas` resolves classes via DI — use it for pipe dependencies
|
|
207
|
+
|
|
208
|
+
## Gotchas
|
|
209
|
+
|
|
210
|
+
- Pipes run in strict priority order — if two pipes have the same priority, class-level runs before method-level, which runs before parameter-level
|
|
211
|
+
- `@Resolve()` replaces the parameter value entirely — the initial value is `undefined` before the RESOLVE stage
|
|
212
|
+
- Async pipes are supported — if any pipe returns a Promise, the entire argument array is awaited via `Promise.all()`
|
|
213
|
+
- The `paramMeta.type` field comes from TypeScript's `emitDecoratorMetadata` — it's the design-time type, not a runtime validator
|