@moostjs/event-http 0.6.1 → 0.6.3
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 +43 -0
- package/dist/index.cjs +85 -24
- package/dist/index.d.ts +49 -20
- package/dist/index.mjs +81 -21
- package/package.json +5 -6
- package/skills/moostjs-event-http/SKILL.md +2 -2
- package/skills/moostjs-event-http/response.md +25 -25
package/README.md
CHANGED
|
@@ -76,6 +76,49 @@ class UsersController { ... }
|
|
|
76
76
|
|
|
77
77
|
Supported transports: `bearer`, `basic`, `apiKey` (header/query/cookie), `cookie`.
|
|
78
78
|
|
|
79
|
+
## Programmatic Fetch (SSR)
|
|
80
|
+
|
|
81
|
+
`MoostHttp` exposes `fetch()` and `request()` methods for in-process route invocation with the full Moost pipeline (DI, interceptors, pipes, validation) — no TCP round-trip.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const http = new MoostHttp()
|
|
85
|
+
app.adapter(http)
|
|
86
|
+
await app.init()
|
|
87
|
+
|
|
88
|
+
// Web Standard fetch API
|
|
89
|
+
const response = await http.fetch(new Request('http://localhost/api/hello/world'))
|
|
90
|
+
|
|
91
|
+
// Convenience form
|
|
92
|
+
const response = await http.request('/api/hello/world')
|
|
93
|
+
const response = await http.request('/api/users', {
|
|
94
|
+
method: 'POST',
|
|
95
|
+
body: JSON.stringify({ name: 'Alice' }),
|
|
96
|
+
headers: { 'content-type': 'application/json' },
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Returns `Response | null` — `null` means no route matched.
|
|
101
|
+
|
|
102
|
+
When called from within an existing HTTP context (e.g. during SSR), identity headers (`authorization`, `cookie`) are automatically forwarded.
|
|
103
|
+
|
|
104
|
+
### `enableLocalFetch`
|
|
105
|
+
|
|
106
|
+
Patches `globalThis.fetch` so that local paths are routed in-process through Moost. External URLs pass through to the original `fetch`. Falls back to original `fetch` if no route matches.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { enableLocalFetch } from '@moostjs/event-http'
|
|
110
|
+
|
|
111
|
+
const teardown = enableLocalFetch(http)
|
|
112
|
+
|
|
113
|
+
// Now any fetch('/api/...') goes through Moost in-process
|
|
114
|
+
const res = await fetch('/api/hello/world')
|
|
115
|
+
|
|
116
|
+
// Restore original fetch
|
|
117
|
+
teardown()
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This is wired up automatically by `@moostjs/vite` (see `ssrFetch` option).
|
|
121
|
+
|
|
79
122
|
## [Official Documentation](https://moost.org/webapp/)
|
|
80
123
|
|
|
81
124
|
## AI Agent Skills
|
package/dist/index.cjs
CHANGED
|
@@ -193,7 +193,7 @@ AuthGuard = _ts_decorate([(0, moost.Interceptor)(moost.TInterceptorPriority.GUAR
|
|
|
193
193
|
|
|
194
194
|
//#endregion
|
|
195
195
|
//#region packages/event-http/src/decorators/resolve.decorator.ts
|
|
196
|
-
function
|
|
196
|
+
function createRef(getter, setter) {
|
|
197
197
|
return new Proxy({}, {
|
|
198
198
|
get: (_target, prop) => prop === "value" ? getter() : void 0,
|
|
199
199
|
set: (_target, prop, v) => {
|
|
@@ -203,12 +203,12 @@ function createHook(getter, setter) {
|
|
|
203
203
|
});
|
|
204
204
|
}
|
|
205
205
|
/**
|
|
206
|
-
*
|
|
206
|
+
* Ref to the Response Status
|
|
207
207
|
* @decorator
|
|
208
|
-
* @paramType
|
|
209
|
-
*/ const
|
|
208
|
+
* @paramType TStatusRef
|
|
209
|
+
*/ const StatusRef = () => (0, moost.Resolve)((metas, level) => {
|
|
210
210
|
const response = (0, __wooksjs_event_http.useResponse)();
|
|
211
|
-
if (level === "PARAM") return
|
|
211
|
+
if (level === "PARAM") return createRef(() => response.status, (v) => {
|
|
212
212
|
response.status = v;
|
|
213
213
|
});
|
|
214
214
|
if (level === "PROP" && metas.instance && metas.key) {
|
|
@@ -224,13 +224,13 @@ function createHook(getter, setter) {
|
|
|
224
224
|
}
|
|
225
225
|
}, "statusCode");
|
|
226
226
|
/**
|
|
227
|
-
*
|
|
227
|
+
* Ref to the Response Header
|
|
228
228
|
* @decorator
|
|
229
229
|
* @param name - header name
|
|
230
|
-
* @paramType
|
|
231
|
-
*/ const
|
|
230
|
+
* @paramType THeaderRef
|
|
231
|
+
*/ const HeaderRef = (name) => (0, moost.Resolve)((metas, level) => {
|
|
232
232
|
const response = (0, __wooksjs_event_http.useResponse)();
|
|
233
|
-
if (level === "PARAM") return
|
|
233
|
+
if (level === "PARAM") return createRef(() => response.getHeader(name), (v) => response.setHeader(name, v));
|
|
234
234
|
if (level === "PROP" && metas.instance && metas.key) {
|
|
235
235
|
const initialValue = metas.instance[metas.key];
|
|
236
236
|
Object.defineProperty(metas.instance, metas.key, {
|
|
@@ -242,13 +242,13 @@ function createHook(getter, setter) {
|
|
|
242
242
|
}
|
|
243
243
|
}, name);
|
|
244
244
|
/**
|
|
245
|
-
*
|
|
245
|
+
* Ref to the Response Cookie
|
|
246
246
|
* @decorator
|
|
247
247
|
* @param name - cookie name
|
|
248
|
-
* @paramType
|
|
249
|
-
*/ const
|
|
248
|
+
* @paramType TCookieRef
|
|
249
|
+
*/ const CookieRef = (name) => (0, moost.Resolve)((metas, level) => {
|
|
250
250
|
const response = (0, __wooksjs_event_http.useResponse)();
|
|
251
|
-
if (level === "PARAM") return
|
|
251
|
+
if (level === "PARAM") return createRef(() => response.getCookie(name)?.value, (v) => response.setCookie(name, v ?? ""));
|
|
252
252
|
if (level === "PROP" && metas.instance && metas.key) {
|
|
253
253
|
const initialValue = metas.instance[metas.key];
|
|
254
254
|
Object.defineProperty(metas.instance, metas.key, {
|
|
@@ -260,18 +260,18 @@ function createHook(getter, setter) {
|
|
|
260
260
|
}
|
|
261
261
|
}, name);
|
|
262
262
|
/**
|
|
263
|
-
*
|
|
263
|
+
* Ref to the Response Cookie Attributes
|
|
264
264
|
* @decorator
|
|
265
265
|
* @param name - cookie name
|
|
266
266
|
* @paramType TCookieAttributes
|
|
267
|
-
*/ const
|
|
267
|
+
*/ const CookieAttrsRef = (name) => (0, moost.Resolve)((metas, level) => {
|
|
268
268
|
const response = (0, __wooksjs_event_http.useResponse)();
|
|
269
|
-
const getAttrs = () => response.getCookie(name)?.attrs;
|
|
269
|
+
const getAttrs = () => response.getCookie(name)?.attrs ?? {};
|
|
270
270
|
const setAttrs = (v) => {
|
|
271
271
|
const existing = response.getCookie(name);
|
|
272
272
|
response.setCookie(name, existing?.value ?? "", v);
|
|
273
273
|
};
|
|
274
|
-
if (level === "PARAM") return
|
|
274
|
+
if (level === "PARAM") return createRef(getAttrs, setAttrs);
|
|
275
275
|
if (level === "PROP" && metas.instance && metas.key) {
|
|
276
276
|
const initialValue = metas.instance[metas.key];
|
|
277
277
|
Object.defineProperty(metas.instance, metas.key, {
|
|
@@ -597,8 +597,27 @@ const CONTEXT_TYPE = "HTTP";
|
|
|
597
597
|
getHttpApp() {
|
|
598
598
|
return this.httpApp;
|
|
599
599
|
}
|
|
600
|
-
getServerCb() {
|
|
601
|
-
return this.httpApp.getServerCb();
|
|
600
|
+
getServerCb(onNoMatch) {
|
|
601
|
+
return this.httpApp.getServerCb(onNoMatch);
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Programmatic route invocation using the Web Standard fetch API.
|
|
605
|
+
* Goes through the full Moost pipeline: DI scoping, interceptors,
|
|
606
|
+
* argument resolution, pipes, validation, and handler execution.
|
|
607
|
+
*
|
|
608
|
+
* When called from within an existing HTTP context (e.g. during SSR),
|
|
609
|
+
* identity headers (authorization, cookie) are automatically forwarded.
|
|
610
|
+
*/ fetch(request) {
|
|
611
|
+
return this.httpApp.fetch(request);
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Convenience wrapper for programmatic route invocation.
|
|
615
|
+
* Accepts a URL string (relative paths auto-prefixed with `http://localhost`),
|
|
616
|
+
* URL object, or Request, plus optional `RequestInit`.
|
|
617
|
+
*
|
|
618
|
+
* Returns `null` when no route matches the request.
|
|
619
|
+
*/ request(input, init) {
|
|
620
|
+
return this.httpApp.request(input, init);
|
|
602
621
|
}
|
|
603
622
|
listen(port, hostname, backlog, listeningListener) {
|
|
604
623
|
return this.httpApp.listen(port, hostname, backlog, listeningListener);
|
|
@@ -675,7 +694,7 @@ const CONTEXT_TYPE = "HTTP";
|
|
|
675
694
|
image: "https://moost.org/moost-full-logo.svg",
|
|
676
695
|
link: "https://moost.org/",
|
|
677
696
|
poweredBy: "moostjs",
|
|
678
|
-
version: "0.6.
|
|
697
|
+
version: "0.6.2"
|
|
679
698
|
});
|
|
680
699
|
if (httpApp && httpApp instanceof __wooksjs_event_http.WooksHttp) this.httpApp = httpApp;
|
|
681
700
|
else if (httpApp) this.httpApp = (0, __wooksjs_event_http.createHttpApp)({
|
|
@@ -686,6 +705,47 @@ const CONTEXT_TYPE = "HTTP";
|
|
|
686
705
|
}
|
|
687
706
|
};
|
|
688
707
|
|
|
708
|
+
//#endregion
|
|
709
|
+
//#region packages/event-http/src/local-fetch.ts
|
|
710
|
+
/**
|
|
711
|
+
* Patches `globalThis.fetch` so that requests to local paths (starting with `/`)
|
|
712
|
+
* are routed through `MoostHttp` in-process instead of making a network request.
|
|
713
|
+
* Falls back to the original `fetch` when no Moost route matches.
|
|
714
|
+
*
|
|
715
|
+
* Useful for SSR scenarios where server-side code needs to call its own API endpoints
|
|
716
|
+
* without the overhead of a TCP round-trip.
|
|
717
|
+
*
|
|
718
|
+
* @returns A teardown function that restores the original `globalThis.fetch`.
|
|
719
|
+
*/ function enableLocalFetch(http$1) {
|
|
720
|
+
const originalFetch = globalThis.fetch;
|
|
721
|
+
globalThis.fetch = async (input, init) => {
|
|
722
|
+
if (typeof input === "string") {
|
|
723
|
+
if (input.startsWith("/")) {
|
|
724
|
+
const response = await http$1.request(input, init);
|
|
725
|
+
if (response) return response;
|
|
726
|
+
}
|
|
727
|
+
} else if (input instanceof URL) {
|
|
728
|
+
if (isLocalOrigin(input)) {
|
|
729
|
+
const response = await http$1.request(input, init);
|
|
730
|
+
if (response) return response;
|
|
731
|
+
}
|
|
732
|
+
} else {
|
|
733
|
+
const url = new URL(input.url);
|
|
734
|
+
if (isLocalOrigin(url)) {
|
|
735
|
+
const response = await http$1.fetch(input);
|
|
736
|
+
if (response) return response;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return originalFetch(input, init);
|
|
740
|
+
};
|
|
741
|
+
return () => {
|
|
742
|
+
globalThis.fetch = originalFetch;
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
function isLocalOrigin(url) {
|
|
746
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1";
|
|
747
|
+
}
|
|
748
|
+
|
|
689
749
|
//#endregion
|
|
690
750
|
exports.All = All;
|
|
691
751
|
Object.defineProperty(exports, 'AuthGuard', {
|
|
@@ -701,12 +761,12 @@ exports.BodyReadTimeoutMs = BodyReadTimeoutMs;
|
|
|
701
761
|
exports.BodySizeLimit = BodySizeLimit;
|
|
702
762
|
exports.CompressedBodySizeLimit = CompressedBodySizeLimit;
|
|
703
763
|
exports.Cookie = Cookie;
|
|
704
|
-
exports.
|
|
705
|
-
exports.
|
|
764
|
+
exports.CookieAttrsRef = CookieAttrsRef;
|
|
765
|
+
exports.CookieRef = CookieRef;
|
|
706
766
|
exports.Delete = Delete;
|
|
707
767
|
exports.Get = Get;
|
|
708
768
|
exports.Header = Header;
|
|
709
|
-
exports.
|
|
769
|
+
exports.HeaderRef = HeaderRef;
|
|
710
770
|
Object.defineProperty(exports, 'HttpError', {
|
|
711
771
|
enumerable: true,
|
|
712
772
|
get: function () {
|
|
@@ -729,10 +789,11 @@ exports.Res = Res;
|
|
|
729
789
|
exports.SetCookie = SetCookie;
|
|
730
790
|
exports.SetHeader = SetHeader;
|
|
731
791
|
exports.SetStatus = SetStatus;
|
|
732
|
-
exports.
|
|
792
|
+
exports.StatusRef = StatusRef;
|
|
733
793
|
exports.Upgrade = Upgrade;
|
|
734
794
|
exports.Url = Url;
|
|
735
795
|
exports.defineAuthGuard = defineAuthGuard;
|
|
796
|
+
exports.enableLocalFetch = enableLocalFetch;
|
|
736
797
|
exports.extractTransports = extractTransports;
|
|
737
798
|
exports.globalBodyReadTimeoutMs = globalBodyReadTimeoutMs;
|
|
738
799
|
exports.globalBodySizeLimit = globalBodySizeLimit;
|
package/dist/index.d.ts
CHANGED
|
@@ -106,32 +106,32 @@ declare const Patch: (path?: string) => MethodDecorator;
|
|
|
106
106
|
declare const Upgrade: (path?: string) => MethodDecorator;
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
*
|
|
109
|
+
* Ref to the Response Status
|
|
110
110
|
* @decorator
|
|
111
|
-
* @paramType
|
|
111
|
+
* @paramType TStatusRef
|
|
112
112
|
*/
|
|
113
|
-
declare const
|
|
113
|
+
declare const StatusRef: () => ParameterDecorator & PropertyDecorator;
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
115
|
+
* Ref to the Response Header
|
|
116
116
|
* @decorator
|
|
117
117
|
* @param name - header name
|
|
118
|
-
* @paramType
|
|
118
|
+
* @paramType THeaderRef
|
|
119
119
|
*/
|
|
120
|
-
declare const
|
|
120
|
+
declare const HeaderRef: (name: string) => ParameterDecorator & PropertyDecorator;
|
|
121
121
|
/**
|
|
122
|
-
*
|
|
122
|
+
* Ref to the Response Cookie
|
|
123
123
|
* @decorator
|
|
124
124
|
* @param name - cookie name
|
|
125
|
-
* @paramType
|
|
125
|
+
* @paramType TCookieRef
|
|
126
126
|
*/
|
|
127
|
-
declare const
|
|
127
|
+
declare const CookieRef: (name: string) => ParameterDecorator & PropertyDecorator;
|
|
128
128
|
/**
|
|
129
|
-
*
|
|
129
|
+
* Ref to the Response Cookie Attributes
|
|
130
130
|
* @decorator
|
|
131
131
|
* @param name - cookie name
|
|
132
132
|
* @paramType TCookieAttributes
|
|
133
133
|
*/
|
|
134
|
-
declare const
|
|
134
|
+
declare const CookieAttrsRef: (name: string) => ParameterDecorator & PropertyDecorator;
|
|
135
135
|
/**
|
|
136
136
|
* Parse Authorisation Header
|
|
137
137
|
* @decorator
|
|
@@ -219,18 +219,18 @@ declare function Body(): MethodDecorator & ClassDecorator & ParameterDecorator &
|
|
|
219
219
|
* @paramType Promise<Buffer>
|
|
220
220
|
*/
|
|
221
221
|
declare function RawBody(): ParameterDecorator & PropertyDecorator;
|
|
222
|
-
/** Reactive
|
|
223
|
-
interface
|
|
222
|
+
/** Reactive ref for reading/writing the response status code. */
|
|
223
|
+
interface TStatusRef {
|
|
224
224
|
value: number;
|
|
225
225
|
}
|
|
226
|
-
/** Reactive
|
|
227
|
-
interface
|
|
226
|
+
/** Reactive ref for reading/writing a response header value. */
|
|
227
|
+
interface THeaderRef {
|
|
228
228
|
value: string | string[] | undefined;
|
|
229
229
|
}
|
|
230
230
|
/** Partial cookie attributes (all optional). */
|
|
231
231
|
type TCookieAttributes = Partial<TCookieAttributes$1>;
|
|
232
|
-
/** Reactive
|
|
233
|
-
interface
|
|
232
|
+
/** Reactive ref for reading/writing a response cookie. */
|
|
233
|
+
interface TCookieRef {
|
|
234
234
|
value: string;
|
|
235
235
|
attrs?: TCookieAttributes;
|
|
236
236
|
}
|
|
@@ -400,7 +400,24 @@ declare class MoostHttp implements TMoostAdapter<THttpHandlerMeta> {
|
|
|
400
400
|
protected httpApp: WooksHttp;
|
|
401
401
|
constructor(httpApp?: WooksHttp | TWooksHttpOptions);
|
|
402
402
|
getHttpApp(): WooksHttp;
|
|
403
|
-
getServerCb(): (req: http.IncomingMessage, res: http.ServerResponse) => void;
|
|
403
|
+
getServerCb(onNoMatch?: (req: http.IncomingMessage, res: http.ServerResponse) => void): (req: http.IncomingMessage, res: http.ServerResponse) => void;
|
|
404
|
+
/**
|
|
405
|
+
* Programmatic route invocation using the Web Standard fetch API.
|
|
406
|
+
* Goes through the full Moost pipeline: DI scoping, interceptors,
|
|
407
|
+
* argument resolution, pipes, validation, and handler execution.
|
|
408
|
+
*
|
|
409
|
+
* When called from within an existing HTTP context (e.g. during SSR),
|
|
410
|
+
* identity headers (authorization, cookie) are automatically forwarded.
|
|
411
|
+
*/
|
|
412
|
+
fetch(request: Request): Promise<Response | null>;
|
|
413
|
+
/**
|
|
414
|
+
* Convenience wrapper for programmatic route invocation.
|
|
415
|
+
* Accepts a URL string (relative paths auto-prefixed with `http://localhost`),
|
|
416
|
+
* URL object, or Request, plus optional `RequestInit`.
|
|
417
|
+
*
|
|
418
|
+
* Returns `null` when no route matches the request.
|
|
419
|
+
*/
|
|
420
|
+
request(input: string | URL | Request, init?: RequestInit): Promise<Response | null>;
|
|
404
421
|
listen(port?: number, hostname?: string, backlog?: number, listeningListener?: () => void): Promise<void>;
|
|
405
422
|
listen(port?: number, hostname?: string, listeningListener?: () => void): Promise<void>;
|
|
406
423
|
listen(port?: number, backlog?: number, listeningListener?: () => void): Promise<void>;
|
|
@@ -425,5 +442,17 @@ declare class MoostHttp implements TMoostAdapter<THttpHandlerMeta> {
|
|
|
425
442
|
bindHandler<T extends object = object>(opts: TMoostAdapterOptions<THttpHandlerMeta, T>): void;
|
|
426
443
|
}
|
|
427
444
|
|
|
428
|
-
|
|
429
|
-
|
|
445
|
+
/**
|
|
446
|
+
* Patches `globalThis.fetch` so that requests to local paths (starting with `/`)
|
|
447
|
+
* are routed through `MoostHttp` in-process instead of making a network request.
|
|
448
|
+
* Falls back to the original `fetch` when no Moost route matches.
|
|
449
|
+
*
|
|
450
|
+
* Useful for SSR scenarios where server-side code needs to call its own API endpoints
|
|
451
|
+
* without the overhead of a TCP round-trip.
|
|
452
|
+
*
|
|
453
|
+
* @returns A teardown function that restores the original `globalThis.fetch`.
|
|
454
|
+
*/
|
|
455
|
+
declare function enableLocalFetch(http: MoostHttp): () => void;
|
|
456
|
+
|
|
457
|
+
export { All, AuthGuard, Authenticate, Authorization, Body, BodyReadTimeoutMs, BodySizeLimit, CompressedBodySizeLimit, Cookie, CookieAttrsRef, CookieRef, Delete, Get, Header, HeaderRef, HttpMethod, Ip, IpList, Method, MoostHttp, Patch, Post, Put, Query, RawBody, Req, ReqId, Res, SetCookie, SetHeader, SetStatus, StatusRef, Upgrade, Url, defineAuthGuard, enableLocalFetch, extractTransports, globalBodyReadTimeoutMs, globalBodySizeLimit, globalCompressedBodySizeLimit };
|
|
458
|
+
export type { TAuthGuardClass, TAuthGuardDef, TAuthGuardHandler, TAuthTransportApiKey, TAuthTransportBasic, TAuthTransportBearer, TAuthTransportCookie, TAuthTransportDeclaration, TAuthTransportValues, TCookieAttributes, TCookieRef, THeaderRef, THttpHandlerMeta, TStatusRef };
|
package/dist/index.mjs
CHANGED
|
@@ -170,7 +170,7 @@ AuthGuard = _ts_decorate([Interceptor(TInterceptorPriority.GUARD)], AuthGuard);
|
|
|
170
170
|
|
|
171
171
|
//#endregion
|
|
172
172
|
//#region packages/event-http/src/decorators/resolve.decorator.ts
|
|
173
|
-
function
|
|
173
|
+
function createRef(getter, setter) {
|
|
174
174
|
return new Proxy({}, {
|
|
175
175
|
get: (_target, prop) => prop === "value" ? getter() : void 0,
|
|
176
176
|
set: (_target, prop, v) => {
|
|
@@ -180,12 +180,12 @@ function createHook(getter, setter) {
|
|
|
180
180
|
});
|
|
181
181
|
}
|
|
182
182
|
/**
|
|
183
|
-
*
|
|
183
|
+
* Ref to the Response Status
|
|
184
184
|
* @decorator
|
|
185
|
-
* @paramType
|
|
186
|
-
*/ const
|
|
185
|
+
* @paramType TStatusRef
|
|
186
|
+
*/ const StatusRef = () => Resolve((metas, level) => {
|
|
187
187
|
const response = useResponse();
|
|
188
|
-
if (level === "PARAM") return
|
|
188
|
+
if (level === "PARAM") return createRef(() => response.status, (v) => {
|
|
189
189
|
response.status = v;
|
|
190
190
|
});
|
|
191
191
|
if (level === "PROP" && metas.instance && metas.key) {
|
|
@@ -201,13 +201,13 @@ function createHook(getter, setter) {
|
|
|
201
201
|
}
|
|
202
202
|
}, "statusCode");
|
|
203
203
|
/**
|
|
204
|
-
*
|
|
204
|
+
* Ref to the Response Header
|
|
205
205
|
* @decorator
|
|
206
206
|
* @param name - header name
|
|
207
|
-
* @paramType
|
|
208
|
-
*/ const
|
|
207
|
+
* @paramType THeaderRef
|
|
208
|
+
*/ const HeaderRef = (name) => Resolve((metas, level) => {
|
|
209
209
|
const response = useResponse();
|
|
210
|
-
if (level === "PARAM") return
|
|
210
|
+
if (level === "PARAM") return createRef(() => response.getHeader(name), (v) => response.setHeader(name, v));
|
|
211
211
|
if (level === "PROP" && metas.instance && metas.key) {
|
|
212
212
|
const initialValue = metas.instance[metas.key];
|
|
213
213
|
Object.defineProperty(metas.instance, metas.key, {
|
|
@@ -219,13 +219,13 @@ function createHook(getter, setter) {
|
|
|
219
219
|
}
|
|
220
220
|
}, name);
|
|
221
221
|
/**
|
|
222
|
-
*
|
|
222
|
+
* Ref to the Response Cookie
|
|
223
223
|
* @decorator
|
|
224
224
|
* @param name - cookie name
|
|
225
|
-
* @paramType
|
|
226
|
-
*/ const
|
|
225
|
+
* @paramType TCookieRef
|
|
226
|
+
*/ const CookieRef = (name) => Resolve((metas, level) => {
|
|
227
227
|
const response = useResponse();
|
|
228
|
-
if (level === "PARAM") return
|
|
228
|
+
if (level === "PARAM") return createRef(() => response.getCookie(name)?.value, (v) => response.setCookie(name, v ?? ""));
|
|
229
229
|
if (level === "PROP" && metas.instance && metas.key) {
|
|
230
230
|
const initialValue = metas.instance[metas.key];
|
|
231
231
|
Object.defineProperty(metas.instance, metas.key, {
|
|
@@ -237,18 +237,18 @@ function createHook(getter, setter) {
|
|
|
237
237
|
}
|
|
238
238
|
}, name);
|
|
239
239
|
/**
|
|
240
|
-
*
|
|
240
|
+
* Ref to the Response Cookie Attributes
|
|
241
241
|
* @decorator
|
|
242
242
|
* @param name - cookie name
|
|
243
243
|
* @paramType TCookieAttributes
|
|
244
|
-
*/ const
|
|
244
|
+
*/ const CookieAttrsRef = (name) => Resolve((metas, level) => {
|
|
245
245
|
const response = useResponse();
|
|
246
|
-
const getAttrs = () => response.getCookie(name)?.attrs;
|
|
246
|
+
const getAttrs = () => response.getCookie(name)?.attrs ?? {};
|
|
247
247
|
const setAttrs = (v) => {
|
|
248
248
|
const existing = response.getCookie(name);
|
|
249
249
|
response.setCookie(name, existing?.value ?? "", v);
|
|
250
250
|
};
|
|
251
|
-
if (level === "PARAM") return
|
|
251
|
+
if (level === "PARAM") return createRef(getAttrs, setAttrs);
|
|
252
252
|
if (level === "PROP" && metas.instance && metas.key) {
|
|
253
253
|
const initialValue = metas.instance[metas.key];
|
|
254
254
|
Object.defineProperty(metas.instance, metas.key, {
|
|
@@ -574,8 +574,27 @@ const CONTEXT_TYPE = "HTTP";
|
|
|
574
574
|
getHttpApp() {
|
|
575
575
|
return this.httpApp;
|
|
576
576
|
}
|
|
577
|
-
getServerCb() {
|
|
578
|
-
return this.httpApp.getServerCb();
|
|
577
|
+
getServerCb(onNoMatch) {
|
|
578
|
+
return this.httpApp.getServerCb(onNoMatch);
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Programmatic route invocation using the Web Standard fetch API.
|
|
582
|
+
* Goes through the full Moost pipeline: DI scoping, interceptors,
|
|
583
|
+
* argument resolution, pipes, validation, and handler execution.
|
|
584
|
+
*
|
|
585
|
+
* When called from within an existing HTTP context (e.g. during SSR),
|
|
586
|
+
* identity headers (authorization, cookie) are automatically forwarded.
|
|
587
|
+
*/ fetch(request) {
|
|
588
|
+
return this.httpApp.fetch(request);
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Convenience wrapper for programmatic route invocation.
|
|
592
|
+
* Accepts a URL string (relative paths auto-prefixed with `http://localhost`),
|
|
593
|
+
* URL object, or Request, plus optional `RequestInit`.
|
|
594
|
+
*
|
|
595
|
+
* Returns `null` when no route matches the request.
|
|
596
|
+
*/ request(input, init) {
|
|
597
|
+
return this.httpApp.request(input, init);
|
|
579
598
|
}
|
|
580
599
|
listen(port, hostname, backlog, listeningListener) {
|
|
581
600
|
return this.httpApp.listen(port, hostname, backlog, listeningListener);
|
|
@@ -652,7 +671,7 @@ const CONTEXT_TYPE = "HTTP";
|
|
|
652
671
|
image: "https://moost.org/moost-full-logo.svg",
|
|
653
672
|
link: "https://moost.org/",
|
|
654
673
|
poweredBy: "moostjs",
|
|
655
|
-
version: "0.6.
|
|
674
|
+
version: "0.6.2"
|
|
656
675
|
});
|
|
657
676
|
if (httpApp && httpApp instanceof WooksHttp) this.httpApp = httpApp;
|
|
658
677
|
else if (httpApp) this.httpApp = createHttpApp({
|
|
@@ -664,4 +683,45 @@ const CONTEXT_TYPE = "HTTP";
|
|
|
664
683
|
};
|
|
665
684
|
|
|
666
685
|
//#endregion
|
|
667
|
-
|
|
686
|
+
//#region packages/event-http/src/local-fetch.ts
|
|
687
|
+
/**
|
|
688
|
+
* Patches `globalThis.fetch` so that requests to local paths (starting with `/`)
|
|
689
|
+
* are routed through `MoostHttp` in-process instead of making a network request.
|
|
690
|
+
* Falls back to the original `fetch` when no Moost route matches.
|
|
691
|
+
*
|
|
692
|
+
* Useful for SSR scenarios where server-side code needs to call its own API endpoints
|
|
693
|
+
* without the overhead of a TCP round-trip.
|
|
694
|
+
*
|
|
695
|
+
* @returns A teardown function that restores the original `globalThis.fetch`.
|
|
696
|
+
*/ function enableLocalFetch(http) {
|
|
697
|
+
const originalFetch = globalThis.fetch;
|
|
698
|
+
globalThis.fetch = async (input, init) => {
|
|
699
|
+
if (typeof input === "string") {
|
|
700
|
+
if (input.startsWith("/")) {
|
|
701
|
+
const response = await http.request(input, init);
|
|
702
|
+
if (response) return response;
|
|
703
|
+
}
|
|
704
|
+
} else if (input instanceof URL) {
|
|
705
|
+
if (isLocalOrigin(input)) {
|
|
706
|
+
const response = await http.request(input, init);
|
|
707
|
+
if (response) return response;
|
|
708
|
+
}
|
|
709
|
+
} else {
|
|
710
|
+
const url = new URL(input.url);
|
|
711
|
+
if (isLocalOrigin(url)) {
|
|
712
|
+
const response = await http.fetch(input);
|
|
713
|
+
if (response) return response;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return originalFetch(input, init);
|
|
717
|
+
};
|
|
718
|
+
return () => {
|
|
719
|
+
globalThis.fetch = originalFetch;
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function isLocalOrigin(url) {
|
|
723
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1";
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
//#endregion
|
|
727
|
+
export { All, AuthGuard, Authenticate, Authorization, Body, BodyReadTimeoutMs, BodySizeLimit, CompressedBodySizeLimit, Cookie, CookieAttrsRef, CookieRef, Delete, Get, Header, HeaderRef, HttpError, HttpMethod, Ip, IpList, Method, MoostHttp, Patch, Post, Put, Query, RawBody, Req, ReqId, Res, SetCookie, SetHeader, SetStatus, StatusRef, Upgrade, Url, defineAuthGuard, enableLocalFetch, extractTransports, globalBodyReadTimeoutMs, globalBodySizeLimit, globalCompressedBodySizeLimit, httpKind, useHttpContext };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moostjs/event-http",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "@moostjs/event-http",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"composables",
|
|
@@ -43,17 +43,16 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@wooksjs/event-http": "^0.7.
|
|
47
|
-
"@wooksjs/http-body": "^0.7.
|
|
46
|
+
"@wooksjs/event-http": "^0.7.7",
|
|
47
|
+
"@wooksjs/http-body": "^0.7.7"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"vitest": "3.2.4"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@prostojs/infact": "^0.4.1",
|
|
54
|
-
"@
|
|
55
|
-
"
|
|
56
|
-
"moost": "^0.6.1"
|
|
54
|
+
"@wooksjs/event-core": "^0.7.7",
|
|
55
|
+
"moost": "^0.6.3"
|
|
57
56
|
},
|
|
58
57
|
"scripts": {
|
|
59
58
|
"pub": "pnpm publish --access public",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: moostjs-event-http
|
|
3
|
-
description: Use this skill when working with @moostjs/event-http — to create an HTTP server with MoostHttp adapter, register route handlers with @Get()/@Post()/@Put()/@Delete()/@Patch()/@All(), extract request data with @Query(), @Header(), @Cookie(), @Body(), @RawBody(), @Authorization(), @Url(), @Method(), @Req(), @Res(), @ReqId(), @Ip(), @IpList(), control responses with @SetHeader(), @SetCookie(), @SetStatus(), @
|
|
3
|
+
description: Use this skill when working with @moostjs/event-http — to create an HTTP server with MoostHttp adapter, register route handlers with @Get()/@Post()/@Put()/@Delete()/@Patch()/@All(), extract request data with @Query(), @Header(), @Cookie(), @Body(), @RawBody(), @Authorization(), @Url(), @Method(), @Req(), @Res(), @ReqId(), @Ip(), @IpList(), control responses with @SetHeader(), @SetCookie(), @SetStatus(), @StatusRef(), @HeaderRef(), @CookieRef(), @CookieAttrsRef(), throw HTTP errors with HttpError, enforce body limits with @BodySizeLimit()/@CompressedBodySizeLimit()/@BodyReadTimeoutMs(), define auth guards with defineAuthGuard()/AuthGuard/Authenticate, or handle WebSocket upgrades with @Upgrade().
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# @moostjs/event-http
|
|
@@ -25,7 +25,7 @@ Read the domain file that matches the task. Do not load all files — only what
|
|
|
25
25
|
// Imports
|
|
26
26
|
import { MoostHttp, Get, Post, Put, Delete, Patch, All, HttpMethod, Upgrade } from '@moostjs/event-http'
|
|
27
27
|
import { Query, Header, Cookie, Body, RawBody, Authorization, Url, Method, Req, Res, ReqId, Ip, IpList } from '@moostjs/event-http'
|
|
28
|
-
import { SetHeader, SetCookie, SetStatus,
|
|
28
|
+
import { SetHeader, SetCookie, SetStatus, StatusRef, HeaderRef, CookieRef, CookieAttrsRef } from '@moostjs/event-http'
|
|
29
29
|
import { BodySizeLimit, CompressedBodySizeLimit, BodyReadTimeoutMs } from '@moostjs/event-http'
|
|
30
30
|
import { Authenticate, AuthGuard, defineAuthGuard, HttpError } from '@moostjs/event-http'
|
|
31
31
|
import { Controller, Param, Params } from 'moost'
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
Moost provides two styles for response control:
|
|
8
8
|
|
|
9
9
|
1. **Static decorators** (`@SetStatus`, `@SetHeader`, `@SetCookie`) — applied via interceptors at `AFTER_ALL` priority, declarative and fixed
|
|
10
|
-
2. **Dynamic
|
|
10
|
+
2. **Dynamic refs** (`@StatusRef`, `@HeaderRef`, `@CookieRef`, `@CookieAttrsRef`) — Proxy-based reactive bindings, set values programmatically at runtime
|
|
11
11
|
|
|
12
|
-
Both work as method decorators.
|
|
12
|
+
Both work as method decorators. Ref decorators also work as parameter decorators and property decorators (on `FOR_EVENT`-scoped controllers).
|
|
13
13
|
|
|
14
14
|
## API Reference
|
|
15
15
|
|
|
@@ -28,16 +28,16 @@ create() { return { created: true } }
|
|
|
28
28
|
@SetStatus(200, { force: true })
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
### `@
|
|
31
|
+
### `@StatusRef()`
|
|
32
32
|
|
|
33
33
|
Dynamic status code control via a `{ value: number }` proxy object.
|
|
34
34
|
|
|
35
35
|
```ts
|
|
36
|
-
import { Get,
|
|
37
|
-
import type {
|
|
36
|
+
import { Get, StatusRef } from '@moostjs/event-http'
|
|
37
|
+
import type { TStatusRef } from '@moostjs/event-http'
|
|
38
38
|
|
|
39
39
|
@Get('process')
|
|
40
|
-
process(@
|
|
40
|
+
process(@StatusRef() status: TStatusRef) {
|
|
41
41
|
if (someCondition) {
|
|
42
42
|
status.value = 202
|
|
43
43
|
return { status: 'processing' }
|
|
@@ -52,7 +52,7 @@ Works as a property decorator too:
|
|
|
52
52
|
@Injectable('FOR_EVENT')
|
|
53
53
|
@Controller()
|
|
54
54
|
class MyController {
|
|
55
|
-
@
|
|
55
|
+
@StatusRef()
|
|
56
56
|
status = 200 // initial value
|
|
57
57
|
|
|
58
58
|
@Get('test')
|
|
@@ -90,16 +90,16 @@ Options:
|
|
|
90
90
|
@SetHeader('x-error', 'true', { when: 'error' })
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
### `@
|
|
93
|
+
### `@HeaderRef(name)`
|
|
94
94
|
|
|
95
95
|
Dynamic header control via a `{ value: string | string[] | undefined }` proxy.
|
|
96
96
|
|
|
97
97
|
```ts
|
|
98
|
-
import { Get,
|
|
99
|
-
import type {
|
|
98
|
+
import { Get, HeaderRef } from '@moostjs/event-http'
|
|
99
|
+
import type { THeaderRef } from '@moostjs/event-http'
|
|
100
100
|
|
|
101
101
|
@Get('test')
|
|
102
|
-
test(@
|
|
102
|
+
test(@HeaderRef('x-custom') header: THeaderRef) {
|
|
103
103
|
header.value = `generated-${Date.now()}`
|
|
104
104
|
return 'ok'
|
|
105
105
|
}
|
|
@@ -119,31 +119,31 @@ login() { return { ok: true } }
|
|
|
119
119
|
|
|
120
120
|
Cookie attributes (`TCookieAttributesInput`): `maxAge`, `expires`, `domain`, `path`, `secure`, `httpOnly`, `sameSite`, etc.
|
|
121
121
|
|
|
122
|
-
### `@
|
|
122
|
+
### `@CookieRef(name)`
|
|
123
123
|
|
|
124
124
|
Dynamic cookie value control.
|
|
125
125
|
|
|
126
126
|
```ts
|
|
127
|
-
import { Post,
|
|
128
|
-
import type {
|
|
127
|
+
import { Post, CookieRef } from '@moostjs/event-http'
|
|
128
|
+
import type { TCookieRef } from '@moostjs/event-http'
|
|
129
129
|
|
|
130
130
|
@Post('login')
|
|
131
|
-
login(@
|
|
131
|
+
login(@CookieRef('session') cookie: TCookieRef) {
|
|
132
132
|
cookie.value = generateToken()
|
|
133
133
|
return { ok: true }
|
|
134
134
|
}
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
### `@
|
|
137
|
+
### `@CookieAttrsRef(name)`
|
|
138
138
|
|
|
139
139
|
Dynamic cookie attributes control.
|
|
140
140
|
|
|
141
141
|
```ts
|
|
142
|
-
import { Post,
|
|
142
|
+
import { Post, CookieAttrsRef } from '@moostjs/event-http'
|
|
143
143
|
import type { TCookieAttributes } from '@moostjs/event-http'
|
|
144
144
|
|
|
145
145
|
@Post('login')
|
|
146
|
-
login(@
|
|
146
|
+
login(@CookieAttrsRef('session') attrs: { value: TCookieAttributes }) {
|
|
147
147
|
attrs.value = { maxAge: '1h', httpOnly: true, secure: true }
|
|
148
148
|
return { ok: true }
|
|
149
149
|
}
|
|
@@ -252,7 +252,7 @@ class ItemController {
|
|
|
252
252
|
```ts
|
|
253
253
|
@Get('data')
|
|
254
254
|
@SetHeader('x-cache', 'miss', { when: 'always' })
|
|
255
|
-
getData(@
|
|
255
|
+
getData(@HeaderRef('x-cache') cache: THeaderRef) {
|
|
256
256
|
const cached = getFromCache()
|
|
257
257
|
if (cached) {
|
|
258
258
|
cache.value = 'hit'
|
|
@@ -265,17 +265,17 @@ getData(@HeaderHook('x-cache') cache: THeaderHook) {
|
|
|
265
265
|
## Types
|
|
266
266
|
|
|
267
267
|
```ts
|
|
268
|
-
export interface
|
|
269
|
-
export interface
|
|
270
|
-
export interface
|
|
268
|
+
export interface TStatusRef { value: number }
|
|
269
|
+
export interface THeaderRef { value: string | string[] | undefined }
|
|
270
|
+
export interface TCookieRef { value: string; attrs?: TCookieAttributes }
|
|
271
271
|
export type TCookieAttributes = Partial<TCookieAttributesRequired>
|
|
272
272
|
```
|
|
273
273
|
|
|
274
274
|
## Best Practices
|
|
275
275
|
|
|
276
276
|
- Use `@SetStatus` for fixed status codes (201 for creation, 204 for deletion)
|
|
277
|
-
- Use `@
|
|
278
|
-
- Use `@SetHeader` for static headers, `@
|
|
277
|
+
- Use `@StatusRef` when the status depends on runtime logic
|
|
278
|
+
- Use `@SetHeader` for static headers, `@HeaderRef` for dynamic ones
|
|
279
279
|
- Apply body limits on upload endpoints to prevent abuse
|
|
280
280
|
- Use global limit interceptors for application-wide defaults
|
|
281
281
|
|
|
@@ -284,4 +284,4 @@ export type TCookieAttributes = Partial<TCookieAttributesRequired>
|
|
|
284
284
|
- `@SetStatus` won't override a status already set (e.g., by an error) unless `{ force: true }` is passed
|
|
285
285
|
- `@SetCookie` won't overwrite a cookie already set in the response — this prevents accidental overwrites
|
|
286
286
|
- `@SetHeader` defaults to success-only (`after` interceptor); use `{ when: 'always' }` to include error responses
|
|
287
|
-
-
|
|
287
|
+
- Ref decorators use Proxy objects — the `.value` property is reactive, not a plain field
|