@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 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 createHook(getter, setter) {
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
- * Hook to the Response Status
206
+ * Ref to the Response Status
207
207
  * @decorator
208
- * @paramType TStatusHook
209
- */ const StatusHook = () => (0, moost.Resolve)((metas, level) => {
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 createHook(() => response.status, (v) => {
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
- * Hook to the Response Header
227
+ * Ref to the Response Header
228
228
  * @decorator
229
229
  * @param name - header name
230
- * @paramType THeaderHook
231
- */ const HeaderHook = (name) => (0, moost.Resolve)((metas, level) => {
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 createHook(() => response.getHeader(name), (v) => response.setHeader(name, v));
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
- * Hook to the Response Cookie
245
+ * Ref to the Response Cookie
246
246
  * @decorator
247
247
  * @param name - cookie name
248
- * @paramType TCookieHook
249
- */ const CookieHook = (name) => (0, moost.Resolve)((metas, level) => {
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 createHook(() => response.getCookie(name)?.value, (v) => response.setCookie(name, v ?? ""));
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
- * Hook to the Response Cookie Attributes
263
+ * Ref to the Response Cookie Attributes
264
264
  * @decorator
265
265
  * @param name - cookie name
266
266
  * @paramType TCookieAttributes
267
- */ const CookieAttrsHook = (name) => (0, moost.Resolve)((metas, level) => {
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 createHook(getAttrs, setAttrs);
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.0"
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.CookieAttrsHook = CookieAttrsHook;
705
- exports.CookieHook = CookieHook;
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.HeaderHook = HeaderHook;
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.StatusHook = StatusHook;
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
- * Hook to the Response Status
109
+ * Ref to the Response Status
110
110
  * @decorator
111
- * @paramType TStatusHook
111
+ * @paramType TStatusRef
112
112
  */
113
- declare const StatusHook: () => ParameterDecorator & PropertyDecorator;
113
+ declare const StatusRef: () => ParameterDecorator & PropertyDecorator;
114
114
  /**
115
- * Hook to the Response Header
115
+ * Ref to the Response Header
116
116
  * @decorator
117
117
  * @param name - header name
118
- * @paramType THeaderHook
118
+ * @paramType THeaderRef
119
119
  */
120
- declare const HeaderHook: (name: string) => ParameterDecorator & PropertyDecorator;
120
+ declare const HeaderRef: (name: string) => ParameterDecorator & PropertyDecorator;
121
121
  /**
122
- * Hook to the Response Cookie
122
+ * Ref to the Response Cookie
123
123
  * @decorator
124
124
  * @param name - cookie name
125
- * @paramType TCookieHook
125
+ * @paramType TCookieRef
126
126
  */
127
- declare const CookieHook: (name: string) => ParameterDecorator & PropertyDecorator;
127
+ declare const CookieRef: (name: string) => ParameterDecorator & PropertyDecorator;
128
128
  /**
129
- * Hook to the Response Cookie Attributes
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 CookieAttrsHook: (name: string) => ParameterDecorator & PropertyDecorator;
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 hook for reading/writing the response status code. */
223
- interface TStatusHook {
222
+ /** Reactive ref for reading/writing the response status code. */
223
+ interface TStatusRef {
224
224
  value: number;
225
225
  }
226
- /** Reactive hook for reading/writing a response header value. */
227
- interface THeaderHook {
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 hook for reading/writing a response cookie. */
233
- interface TCookieHook {
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
- export { All, AuthGuard, Authenticate, Authorization, Body, BodyReadTimeoutMs, BodySizeLimit, CompressedBodySizeLimit, Cookie, CookieAttrsHook, CookieHook, Delete, Get, Header, HeaderHook, HttpMethod, Ip, IpList, Method, MoostHttp, Patch, Post, Put, Query, RawBody, Req, ReqId, Res, SetCookie, SetHeader, SetStatus, StatusHook, Upgrade, Url, defineAuthGuard, extractTransports, globalBodyReadTimeoutMs, globalBodySizeLimit, globalCompressedBodySizeLimit };
429
- export type { TAuthGuardClass, TAuthGuardDef, TAuthGuardHandler, TAuthTransportApiKey, TAuthTransportBasic, TAuthTransportBearer, TAuthTransportCookie, TAuthTransportDeclaration, TAuthTransportValues, TCookieAttributes, TCookieHook, THeaderHook, THttpHandlerMeta, TStatusHook };
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 createHook(getter, setter) {
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
- * Hook to the Response Status
183
+ * Ref to the Response Status
184
184
  * @decorator
185
- * @paramType TStatusHook
186
- */ const StatusHook = () => Resolve((metas, level) => {
185
+ * @paramType TStatusRef
186
+ */ const StatusRef = () => Resolve((metas, level) => {
187
187
  const response = useResponse();
188
- if (level === "PARAM") return createHook(() => response.status, (v) => {
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
- * Hook to the Response Header
204
+ * Ref to the Response Header
205
205
  * @decorator
206
206
  * @param name - header name
207
- * @paramType THeaderHook
208
- */ const HeaderHook = (name) => Resolve((metas, level) => {
207
+ * @paramType THeaderRef
208
+ */ const HeaderRef = (name) => Resolve((metas, level) => {
209
209
  const response = useResponse();
210
- if (level === "PARAM") return createHook(() => response.getHeader(name), (v) => response.setHeader(name, v));
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
- * Hook to the Response Cookie
222
+ * Ref to the Response Cookie
223
223
  * @decorator
224
224
  * @param name - cookie name
225
- * @paramType TCookieHook
226
- */ const CookieHook = (name) => Resolve((metas, level) => {
225
+ * @paramType TCookieRef
226
+ */ const CookieRef = (name) => Resolve((metas, level) => {
227
227
  const response = useResponse();
228
- if (level === "PARAM") return createHook(() => response.getCookie(name)?.value, (v) => response.setCookie(name, v ?? ""));
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
- * Hook to the Response Cookie Attributes
240
+ * Ref to the Response Cookie Attributes
241
241
  * @decorator
242
242
  * @param name - cookie name
243
243
  * @paramType TCookieAttributes
244
- */ const CookieAttrsHook = (name) => Resolve((metas, level) => {
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 createHook(getAttrs, setAttrs);
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.0"
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
- export { All, AuthGuard, Authenticate, Authorization, Body, BodyReadTimeoutMs, BodySizeLimit, CompressedBodySizeLimit, Cookie, CookieAttrsHook, CookieHook, Delete, Get, Header, HeaderHook, HttpError, HttpMethod, Ip, IpList, Method, MoostHttp, Patch, Post, Put, Query, RawBody, Req, ReqId, Res, SetCookie, SetHeader, SetStatus, StatusHook, Upgrade, Url, defineAuthGuard, extractTransports, globalBodyReadTimeoutMs, globalBodySizeLimit, globalCompressedBodySizeLimit, httpKind, useHttpContext };
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.1",
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.3",
47
- "@wooksjs/http-body": "^0.7.3"
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
- "@prostojs/router": "^0.3.2",
55
- "@wooksjs/event-core": "^0.7.3",
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(), @StatusHook(), @HeaderHook(), @CookieHook(), @CookieAttrsHook(), 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().
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, StatusHook, HeaderHook, CookieHook, CookieAttrsHook } from '@moostjs/event-http'
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 hooks** (`@StatusHook`, `@HeaderHook`, `@CookieHook`, `@CookieAttrsHook`) — Proxy-based reactive bindings, set values programmatically at runtime
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. Hook decorators also work as parameter decorators and property decorators (on `FOR_EVENT`-scoped controllers).
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
- ### `@StatusHook()`
31
+ ### `@StatusRef()`
32
32
 
33
33
  Dynamic status code control via a `{ value: number }` proxy object.
34
34
 
35
35
  ```ts
36
- import { Get, StatusHook } from '@moostjs/event-http'
37
- import type { TStatusHook } from '@moostjs/event-http'
36
+ import { Get, StatusRef } from '@moostjs/event-http'
37
+ import type { TStatusRef } from '@moostjs/event-http'
38
38
 
39
39
  @Get('process')
40
- process(@StatusHook() status: TStatusHook) {
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
- @StatusHook()
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
- ### `@HeaderHook(name)`
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, HeaderHook } from '@moostjs/event-http'
99
- import type { THeaderHook } from '@moostjs/event-http'
98
+ import { Get, HeaderRef } from '@moostjs/event-http'
99
+ import type { THeaderRef } from '@moostjs/event-http'
100
100
 
101
101
  @Get('test')
102
- test(@HeaderHook('x-custom') header: THeaderHook) {
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
- ### `@CookieHook(name)`
122
+ ### `@CookieRef(name)`
123
123
 
124
124
  Dynamic cookie value control.
125
125
 
126
126
  ```ts
127
- import { Post, CookieHook } from '@moostjs/event-http'
128
- import type { TCookieHook } from '@moostjs/event-http'
127
+ import { Post, CookieRef } from '@moostjs/event-http'
128
+ import type { TCookieRef } from '@moostjs/event-http'
129
129
 
130
130
  @Post('login')
131
- login(@CookieHook('session') cookie: TCookieHook) {
131
+ login(@CookieRef('session') cookie: TCookieRef) {
132
132
  cookie.value = generateToken()
133
133
  return { ok: true }
134
134
  }
135
135
  ```
136
136
 
137
- ### `@CookieAttrsHook(name)`
137
+ ### `@CookieAttrsRef(name)`
138
138
 
139
139
  Dynamic cookie attributes control.
140
140
 
141
141
  ```ts
142
- import { Post, CookieAttrsHook } from '@moostjs/event-http'
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(@CookieAttrsHook('session') attrs: { value: TCookieAttributes }) {
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(@HeaderHook('x-cache') cache: THeaderHook) {
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 TStatusHook { value: number }
269
- export interface THeaderHook { value: string | string[] | undefined }
270
- export interface TCookieHook { value: string; attrs?: TCookieAttributes }
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 `@StatusHook` when the status depends on runtime logic
278
- - Use `@SetHeader` for static headers, `@HeaderHook` for dynamic ones
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
- - Hook decorators use Proxy objects — the `.value` property is reactive, not a plain field
287
+ - Ref decorators use Proxy objects — the `.value` property is reactive, not a plain field