@fluojs/http 1.0.0-beta.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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.ko.md +142 -0
  3. package/README.md +144 -0
  4. package/dist/adapter.d.ts +58 -0
  5. package/dist/adapter.d.ts.map +1 -0
  6. package/dist/adapter.js +42 -0
  7. package/dist/adapters/binding.d.ts +11 -0
  8. package/dist/adapters/binding.d.ts.map +1 -0
  9. package/dist/adapters/binding.js +185 -0
  10. package/dist/adapters/dto-validation-adapter.d.ts +10 -0
  11. package/dist/adapters/dto-validation-adapter.d.ts.map +1 -0
  12. package/dist/adapters/dto-validation-adapter.js +46 -0
  13. package/dist/client-identity.d.ts +21 -0
  14. package/dist/client-identity.d.ts.map +1 -0
  15. package/dist/client-identity.js +108 -0
  16. package/dist/context/request-context.d.ts +53 -0
  17. package/dist/context/request-context.d.ts.map +1 -0
  18. package/dist/context/request-context.js +89 -0
  19. package/dist/context/sse.d.ts +21 -0
  20. package/dist/context/sse.d.ts.map +1 -0
  21. package/dist/context/sse.js +106 -0
  22. package/dist/decorators.d.ts +188 -0
  23. package/dist/decorators.d.ts.map +1 -0
  24. package/dist/decorators.js +378 -0
  25. package/dist/dispatch/dispatch-content-negotiation.d.ts +9 -0
  26. package/dist/dispatch/dispatch-content-negotiation.d.ts.map +1 -0
  27. package/dist/dispatch/dispatch-content-negotiation.js +164 -0
  28. package/dist/dispatch/dispatch-error-policy.d.ts +3 -0
  29. package/dist/dispatch/dispatch-error-policy.d.ts.map +1 -0
  30. package/dist/dispatch/dispatch-error-policy.js +24 -0
  31. package/dist/dispatch/dispatch-handler-policy.d.ts +3 -0
  32. package/dist/dispatch/dispatch-handler-policy.d.ts.map +1 -0
  33. package/dist/dispatch/dispatch-handler-policy.js +21 -0
  34. package/dist/dispatch/dispatch-response-policy.d.ts +7 -0
  35. package/dist/dispatch/dispatch-response-policy.d.ts.map +1 -0
  36. package/dist/dispatch/dispatch-response-policy.js +45 -0
  37. package/dist/dispatch/dispatch-routing-policy.d.ts +4 -0
  38. package/dist/dispatch/dispatch-routing-policy.d.ts.map +1 -0
  39. package/dist/dispatch/dispatch-routing-policy.js +14 -0
  40. package/dist/dispatch/dispatcher.d.ts +36 -0
  41. package/dist/dispatch/dispatcher.d.ts.map +1 -0
  42. package/dist/dispatch/dispatcher.js +196 -0
  43. package/dist/errors.d.ts +23 -0
  44. package/dist/errors.d.ts.map +1 -0
  45. package/dist/errors.js +41 -0
  46. package/dist/exceptions.d.ts +174 -0
  47. package/dist/exceptions.d.ts.map +1 -0
  48. package/dist/exceptions.js +222 -0
  49. package/dist/guards.d.ts +3 -0
  50. package/dist/guards.d.ts.map +1 -0
  51. package/dist/guards.js +19 -0
  52. package/dist/index.d.ts +15 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +14 -0
  55. package/dist/input-error-detail.d.ts +10 -0
  56. package/dist/input-error-detail.d.ts.map +1 -0
  57. package/dist/input-error-detail.js +8 -0
  58. package/dist/interceptors.d.ts +3 -0
  59. package/dist/interceptors.d.ts.map +1 -0
  60. package/dist/interceptors.js +22 -0
  61. package/dist/internal.d.ts +3 -0
  62. package/dist/internal.d.ts.map +1 -0
  63. package/dist/internal.js +2 -0
  64. package/dist/mapping.d.ts +7 -0
  65. package/dist/mapping.d.ts.map +1 -0
  66. package/dist/mapping.js +244 -0
  67. package/dist/middleware/correlation.d.ts +3 -0
  68. package/dist/middleware/correlation.d.ts.map +1 -0
  69. package/dist/middleware/correlation.js +19 -0
  70. package/dist/middleware/cors.d.ts +11 -0
  71. package/dist/middleware/cors.d.ts.map +1 -0
  72. package/dist/middleware/cors.js +57 -0
  73. package/dist/middleware/middleware.d.ts +8 -0
  74. package/dist/middleware/middleware.d.ts.map +1 -0
  75. package/dist/middleware/middleware.js +64 -0
  76. package/dist/middleware/rate-limit.d.ts +39 -0
  77. package/dist/middleware/rate-limit.d.ts.map +1 -0
  78. package/dist/middleware/rate-limit.js +106 -0
  79. package/dist/middleware/security-headers.d.ts +12 -0
  80. package/dist/middleware/security-headers.d.ts.map +1 -0
  81. package/dist/middleware/security-headers.js +47 -0
  82. package/dist/route-path.d.ts +15 -0
  83. package/dist/route-path.d.ts.map +1 -0
  84. package/dist/route-path.js +69 -0
  85. package/dist/types.d.ts +274 -0
  86. package/dist/types.d.ts.map +1 -0
  87. package/dist/types.js +114 -0
  88. package/package.json +58 -0
@@ -0,0 +1,21 @@
1
+ import type { FrameworkRequest } from './types.js';
2
+ interface ClientIdentityResolutionOptions {
3
+ trustProxyHeaders?: boolean;
4
+ }
5
+ /**
6
+ * Resolve one stable client identity from the normalized request contract.
7
+ *
8
+ * By default, resolution uses only the raw socket's `remoteAddress`. When
9
+ * `trustProxyHeaders` is enabled, resolution order becomes `Forwarded`,
10
+ * `X-Forwarded-For`, `X-Real-IP`, then the raw socket fallback. If none are
11
+ * available, callers must provide an explicit resolver because falling back to
12
+ * a shared `unknown` bucket is not safe in proxied or serverless environments.
13
+ *
14
+ * @param request Adapter-normalized request whose headers/raw transport state should be inspected.
15
+ * @param options Client-identity trust settings for proxy-header handling.
16
+ * @returns A stable client identity string suitable for rate limiting.
17
+ * @throws Error When the request exposes no trusted proxy header or socket identity.
18
+ */
19
+ export declare function resolveClientIdentity(request: FrameworkRequest, options?: ClientIdentityResolutionOptions): string;
20
+ export {};
21
+ //# sourceMappingURL=client-identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-identity.d.ts","sourceRoot":"","sources":["../src/client-identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMnD,UAAU,+BAA+B;IACvC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AA8HD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,+BAAoC,GAC5C,MAAM,CAeR"}
@@ -0,0 +1,108 @@
1
+ const FORWARDED_HEADER = 'forwarded';
2
+ const X_FORWARDED_FOR_HEADER = 'x-forwarded-for';
3
+ const X_REAL_IP_HEADER = 'x-real-ip';
4
+ function readHeader(headers, name) {
5
+ const direct = headers[name];
6
+ if (typeof direct === 'string') {
7
+ return direct;
8
+ }
9
+ if (Array.isArray(direct)) {
10
+ return direct.find(value => value.trim().length > 0);
11
+ }
12
+ const match = Object.entries(headers).find(([key]) => key.toLowerCase() === name);
13
+ const value = match?.[1];
14
+ if (typeof value === 'string') {
15
+ return value;
16
+ }
17
+ return value?.find(entry => entry.trim().length > 0);
18
+ }
19
+ function normalizeClientIdentity(value) {
20
+ if (!value) {
21
+ return undefined;
22
+ }
23
+ let normalized = value.trim();
24
+ if (!normalized || normalized.toLowerCase() === 'unknown') {
25
+ return undefined;
26
+ }
27
+ if (normalized.startsWith('"') && normalized.endsWith('"')) {
28
+ normalized = normalized.slice(1, -1).trim();
29
+ }
30
+ const bracketedHostPort = normalized.match(/^\[(.+)]:(\d+)$/);
31
+ if (bracketedHostPort) {
32
+ return bracketedHostPort[1]?.trim() || undefined;
33
+ }
34
+ if (normalized.startsWith('[') && normalized.endsWith(']')) {
35
+ normalized = normalized.slice(1, -1).trim();
36
+ }
37
+ const ipv4HostPort = normalized.match(/^((?:\d{1,3}\.){3}\d{1,3}):(\d+)$/);
38
+ if (ipv4HostPort) {
39
+ return ipv4HostPort[1]?.trim() || undefined;
40
+ }
41
+ return normalized || undefined;
42
+ }
43
+ function resolveForwardedClientIdentity(headers) {
44
+ const forwarded = readHeader(headers, FORWARDED_HEADER);
45
+ if (!forwarded) {
46
+ return undefined;
47
+ }
48
+ for (const field of forwarded.split(',')) {
49
+ for (const part of field.split(';')) {
50
+ const separator = part.indexOf('=');
51
+ if (separator === -1) {
52
+ continue;
53
+ }
54
+ const key = part.slice(0, separator).trim().toLowerCase();
55
+ if (key !== 'for') {
56
+ continue;
57
+ }
58
+ const normalized = normalizeClientIdentity(part.slice(separator + 1));
59
+ if (normalized) {
60
+ return normalized;
61
+ }
62
+ }
63
+ }
64
+ return undefined;
65
+ }
66
+ function resolveCommaSeparatedClientIdentity(headers, headerName) {
67
+ const headerValue = readHeader(headers, headerName);
68
+ if (!headerValue) {
69
+ return undefined;
70
+ }
71
+ for (const value of headerValue.split(',')) {
72
+ const normalized = normalizeClientIdentity(value);
73
+ if (normalized) {
74
+ return normalized;
75
+ }
76
+ }
77
+ return undefined;
78
+ }
79
+ function resolveSocketClientIdentity(raw) {
80
+ if (!raw || typeof raw !== 'object') {
81
+ return undefined;
82
+ }
83
+ const socket = raw.socket;
84
+ return typeof socket?.remoteAddress === 'string' ? normalizeClientIdentity(socket.remoteAddress) : undefined;
85
+ }
86
+
87
+ /**
88
+ * Resolve one stable client identity from the normalized request contract.
89
+ *
90
+ * By default, resolution uses only the raw socket's `remoteAddress`. When
91
+ * `trustProxyHeaders` is enabled, resolution order becomes `Forwarded`,
92
+ * `X-Forwarded-For`, `X-Real-IP`, then the raw socket fallback. If none are
93
+ * available, callers must provide an explicit resolver because falling back to
94
+ * a shared `unknown` bucket is not safe in proxied or serverless environments.
95
+ *
96
+ * @param request Adapter-normalized request whose headers/raw transport state should be inspected.
97
+ * @param options Client-identity trust settings for proxy-header handling.
98
+ * @returns A stable client identity string suitable for rate limiting.
99
+ * @throws Error When the request exposes no trusted proxy header or socket identity.
100
+ */
101
+ export function resolveClientIdentity(request, options = {}) {
102
+ const proxyClientIdentity = options.trustProxyHeaders ? resolveForwardedClientIdentity(request.headers) ?? resolveCommaSeparatedClientIdentity(request.headers, X_FORWARDED_FOR_HEADER) ?? normalizeClientIdentity(readHeader(request.headers, X_REAL_IP_HEADER)) : undefined;
103
+ const clientIdentity = proxyClientIdentity ?? resolveSocketClientIdentity(request.raw);
104
+ if (clientIdentity) {
105
+ return clientIdentity;
106
+ }
107
+ throw new Error('Unable to resolve client identity from the trusted request transport. Enable trustProxyHeaders only behind a trusted proxy, or provide an explicit keyResolver/keyGenerator for this environment.');
108
+ }
@@ -0,0 +1,53 @@
1
+ import type { ContextKey, RequestContext } from '../types.js';
2
+ /**
3
+ * Runs a callback inside the request-scoped AsyncLocalStorage context.
4
+ *
5
+ * @param context Request context snapshot to bind to the current async execution chain.
6
+ * @param callback Callback executed with `context` available through request-context helpers.
7
+ * @returns The return value from `callback`.
8
+ */
9
+ export declare function runWithRequestContext<T>(context: RequestContext, callback: () => T): T;
10
+ /**
11
+ * Returns the request context active in the current async scope, if available.
12
+ *
13
+ * @returns The active request context, or `undefined` when no request scope is bound.
14
+ */
15
+ export declare function getCurrentRequestContext(): RequestContext | undefined;
16
+ /**
17
+ * Returns the current request context or throws when no request scope is active.
18
+ *
19
+ * @returns The active request context bound to the current async execution scope.
20
+ * @throws {FluoError} When called outside a request scope managed by `runWithRequestContext(...)`.
21
+ */
22
+ export declare function assertRequestContext(): RequestContext;
23
+ /**
24
+ * Creates a defensive clone of a request context for AsyncLocalStorage storage.
25
+ *
26
+ * @param context Request context to clone before storing in AsyncLocalStorage.
27
+ * @returns A shallow clone with copied metadata map to avoid cross-request mutation.
28
+ */
29
+ export declare function createRequestContext(context: RequestContext): RequestContext;
30
+ /**
31
+ * Creates a typed key for `RequestContext.metadata`.
32
+ *
33
+ * @param description Human-readable key label used for debugging and symbol description.
34
+ * @returns A unique metadata key carrying the requested value type.
35
+ */
36
+ export declare function createContextKey<T>(description: string): ContextKey<T>;
37
+ /**
38
+ * Reads a typed value from request-context metadata.
39
+ *
40
+ * @param context Request context containing metadata values.
41
+ * @param key Typed metadata key created by `createContextKey(...)`.
42
+ * @returns The stored typed metadata value, or `undefined` when unset.
43
+ */
44
+ export declare function getContextValue<T>(context: RequestContext, key: ContextKey<T>): T | undefined;
45
+ /**
46
+ * Writes a typed value into request-context metadata.
47
+ *
48
+ * @param context Request context whose metadata map should be updated.
49
+ * @param key Typed metadata key created by `createContextKey(...)`.
50
+ * @param value Value to store for subsequent reads in the same request scope.
51
+ */
52
+ export declare function setContextValue<T>(context: RequestContext, key: ContextKey<T>, value: T): void;
53
+ //# sourceMappingURL=request-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/context/request-context.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAI9D;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAEtF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,cAAc,GAAG,SAAS,CAErE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CAUrD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CAK5E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAKtE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,CAE7F;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAE9F"}
@@ -0,0 +1,89 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { FluoError } from '@fluojs/core';
3
+ const requestContextStore = new AsyncLocalStorage();
4
+
5
+ /**
6
+ * Runs a callback inside the request-scoped AsyncLocalStorage context.
7
+ *
8
+ * @param context Request context snapshot to bind to the current async execution chain.
9
+ * @param callback Callback executed with `context` available through request-context helpers.
10
+ * @returns The return value from `callback`.
11
+ */
12
+ export function runWithRequestContext(context, callback) {
13
+ return requestContextStore.run(context, callback);
14
+ }
15
+
16
+ /**
17
+ * Returns the request context active in the current async scope, if available.
18
+ *
19
+ * @returns The active request context, or `undefined` when no request scope is bound.
20
+ */
21
+ export function getCurrentRequestContext() {
22
+ return requestContextStore.getStore();
23
+ }
24
+
25
+ /**
26
+ * Returns the current request context or throws when no request scope is active.
27
+ *
28
+ * @returns The active request context bound to the current async execution scope.
29
+ * @throws {FluoError} When called outside a request scope managed by `runWithRequestContext(...)`.
30
+ */
31
+ export function assertRequestContext() {
32
+ const context = getCurrentRequestContext();
33
+ if (!context) {
34
+ throw new FluoError('RequestContext is not available in the current async scope.', {
35
+ code: 'REQUEST_CONTEXT_MISSING'
36
+ });
37
+ }
38
+ return context;
39
+ }
40
+
41
+ /**
42
+ * Creates a defensive clone of a request context for AsyncLocalStorage storage.
43
+ *
44
+ * @param context Request context to clone before storing in AsyncLocalStorage.
45
+ * @returns A shallow clone with copied metadata map to avoid cross-request mutation.
46
+ */
47
+ export function createRequestContext(context) {
48
+ return {
49
+ ...context,
50
+ metadata: {
51
+ ...context.metadata
52
+ }
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Creates a typed key for `RequestContext.metadata`.
58
+ *
59
+ * @param description Human-readable key label used for debugging and symbol description.
60
+ * @returns A unique metadata key carrying the requested value type.
61
+ */
62
+ export function createContextKey(description) {
63
+ return {
64
+ description,
65
+ id: Symbol(description)
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Reads a typed value from request-context metadata.
71
+ *
72
+ * @param context Request context containing metadata values.
73
+ * @param key Typed metadata key created by `createContextKey(...)`.
74
+ * @returns The stored typed metadata value, or `undefined` when unset.
75
+ */
76
+ export function getContextValue(context, key) {
77
+ return context.metadata[key.id];
78
+ }
79
+
80
+ /**
81
+ * Writes a typed value into request-context metadata.
82
+ *
83
+ * @param context Request context whose metadata map should be updated.
84
+ * @param key Typed metadata key created by `createContextKey(...)`.
85
+ * @param value Value to store for subsequent reads in the same request scope.
86
+ */
87
+ export function setContextValue(context, key, value) {
88
+ context.metadata[key.id] = value;
89
+ }
@@ -0,0 +1,21 @@
1
+ import type { RequestContext } from '../types.js';
2
+ export interface SseSendOptions {
3
+ event?: string;
4
+ id?: string | number;
5
+ retry?: number;
6
+ }
7
+ export declare function encodeSseComment(comment: string): string;
8
+ export declare function encodeSseMessage(data: unknown, options?: SseSendOptions): string;
9
+ export declare class SseResponse {
10
+ private readonly context;
11
+ private closed;
12
+ private readonly stream;
13
+ private removeCloseListener?;
14
+ private readonly onAbort;
15
+ constructor(context: RequestContext);
16
+ send(data: unknown, options?: SseSendOptions): boolean;
17
+ comment(comment: string): boolean;
18
+ close(): void;
19
+ private writeFrame;
20
+ }
21
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/context/sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA8C,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9F,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoCD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,cAAmB,GAAG,MAAM,CAoBpF;AAED,qBAAa,WAAW;IASV,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,mBAAmB,CAAC,CAAa;IAEzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAEtB;gBAE2B,OAAO,EAAE,cAAc;IA0BpD,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO;IAI1D,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIjC,KAAK,IAAI,IAAI;IAiBb,OAAO,CAAC,UAAU;CAYnB"}
@@ -0,0 +1,106 @@
1
+ function sanitizeSseField(value) {
2
+ return value.replace(/\r/g, '').replace(/\n/g, '');
3
+ }
4
+ function splitSseLines(value) {
5
+ return value.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
6
+ }
7
+ function toSseDataString(data) {
8
+ if (data === undefined) {
9
+ return '';
10
+ }
11
+ if (typeof data === 'string') {
12
+ return data;
13
+ }
14
+ const serialized = JSON.stringify(data);
15
+ if (serialized === undefined) {
16
+ throw new TypeError(`SseResponse data must be JSON-serializable. Received ${typeof data}.`);
17
+ }
18
+ return serialized;
19
+ }
20
+ function resolveSseStream(response) {
21
+ if (!response.stream) {
22
+ throw new Error('SseResponse requires adapter-provided response.stream support.');
23
+ }
24
+ return response.stream;
25
+ }
26
+ export function encodeSseComment(comment) {
27
+ const lines = splitSseLines(comment);
28
+ const encoded = lines.map(line => line.length === 0 ? ':' : `: ${line}`);
29
+ return `${encoded.join('\n')}\n\n`;
30
+ }
31
+ export function encodeSseMessage(data, options = {}) {
32
+ const lines = [];
33
+ if (options.event !== undefined) {
34
+ lines.push(`event: ${sanitizeSseField(options.event)}`);
35
+ }
36
+ if (options.id !== undefined) {
37
+ lines.push(`id: ${sanitizeSseField(String(options.id))}`);
38
+ }
39
+ if (options.retry !== undefined && Number.isFinite(options.retry) && options.retry >= 0) {
40
+ lines.push(`retry: ${String(Math.floor(options.retry))}`);
41
+ }
42
+ for (const line of splitSseLines(toSseDataString(data))) {
43
+ lines.push(`data: ${line}`);
44
+ }
45
+ return `${lines.join('\n')}\n\n`;
46
+ }
47
+ export class SseResponse {
48
+ closed = false;
49
+ stream;
50
+ removeCloseListener;
51
+ onAbort = () => {
52
+ this.close();
53
+ };
54
+ constructor(context) {
55
+ this.context = context;
56
+ this.stream = resolveSseStream(context.response);
57
+ if (context.response.statusSet !== true) {
58
+ context.response.setStatus(200);
59
+ }
60
+ context.response.setHeader('Cache-Control', 'no-cache, no-transform');
61
+ context.response.setHeader('Connection', 'keep-alive');
62
+ context.response.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
63
+ context.response.setHeader('X-Accel-Buffering', 'no');
64
+ context.response.committed = true;
65
+ this.stream.flush?.();
66
+ if (context.request.signal?.aborted) {
67
+ this.close();
68
+ return;
69
+ }
70
+ context.request.signal?.addEventListener('abort', this.onAbort, {
71
+ once: true
72
+ });
73
+ if (context.request.signal === undefined) {
74
+ this.removeCloseListener = this.stream.onClose?.(this.onAbort) ?? undefined;
75
+ }
76
+ }
77
+ send(data, options = {}) {
78
+ return this.writeFrame(encodeSseMessage(data, options));
79
+ }
80
+ comment(comment) {
81
+ return this.writeFrame(encodeSseComment(comment));
82
+ }
83
+ close() {
84
+ if (this.closed) {
85
+ return;
86
+ }
87
+ this.closed = true;
88
+ this.context.request.signal?.removeEventListener('abort', this.onAbort);
89
+ this.removeCloseListener?.();
90
+ this.removeCloseListener = undefined;
91
+ if (!this.stream.closed) {
92
+ this.stream.close();
93
+ }
94
+ this.context.response.committed = true;
95
+ }
96
+ writeFrame(frame) {
97
+ if (this.closed) {
98
+ return false;
99
+ }
100
+ if (this.stream.closed) {
101
+ this.close();
102
+ return false;
103
+ }
104
+ return this.stream.write(frame);
105
+ }
106
+ }
@@ -0,0 +1,188 @@
1
+ import { type Constructor, type MetadataPropertyKey } from '@fluojs/core';
2
+ import type { ConverterLike, GuardLike, InterceptorLike } from './types.js';
3
+ type StandardClassDecoratorFn = (value: Function, context: ClassDecoratorContext) => void;
4
+ type StandardMethodDecoratorFn = (value: Function, context: ClassMethodDecoratorContext) => void;
5
+ type StandardFieldDecoratorFn = <This, Value>(value: undefined, context: ClassFieldDecoratorContext<This, Value>) => void;
6
+ type ClassDecoratorLike = StandardClassDecoratorFn;
7
+ type MethodDecoratorLike = StandardMethodDecoratorFn;
8
+ type ClassOrMethodDecoratorLike = StandardClassDecoratorFn & StandardMethodDecoratorFn;
9
+ type FieldDecoratorLike = StandardFieldDecoratorFn;
10
+ /**
11
+ * Marks a class as an HTTP controller and defines its base route path.
12
+ *
13
+ * @param basePath Controller base path prefixed to every route declared on the class.
14
+ * @returns A class decorator that writes controller metadata for route mapping.
15
+ */
16
+ export declare function Controller(basePath?: string): ClassDecoratorLike;
17
+ /**
18
+ * Sets API version metadata on a controller or route handler.
19
+ *
20
+ * @param version Version label interpreted by runtime versioning strategy (for example `"1"`).
21
+ * @returns A decorator that applies version metadata at class or method scope.
22
+ */
23
+ export declare function Version(version: string): ClassOrMethodDecoratorLike;
24
+ /**
25
+ * Registers a `GET` route handler.
26
+ *
27
+ * @param path Route path relative to the controller base path.
28
+ * @returns A method decorator that registers a `GET` handler mapping.
29
+ */
30
+ export declare const Get: (path: string) => MethodDecoratorLike;
31
+ /**
32
+ * Registers a `POST` route handler.
33
+ *
34
+ * @param path Route path relative to the controller base path.
35
+ * @returns A method decorator that registers a `POST` handler mapping.
36
+ */
37
+ export declare const Post: (path: string) => MethodDecoratorLike;
38
+ /**
39
+ * Registers a `PUT` route handler.
40
+ *
41
+ * @param path Route path relative to the controller base path.
42
+ * @returns A method decorator that registers a `PUT` handler mapping.
43
+ */
44
+ export declare const Put: (path: string) => MethodDecoratorLike;
45
+ /**
46
+ * Registers a `PATCH` route handler.
47
+ *
48
+ * @param path Route path relative to the controller base path.
49
+ * @returns A method decorator that registers a `PATCH` handler mapping.
50
+ */
51
+ export declare const Patch: (path: string) => MethodDecoratorLike;
52
+ /**
53
+ * Registers a `DELETE` route handler.
54
+ *
55
+ * @param path Route path relative to the controller base path.
56
+ * @returns A method decorator that registers a `DELETE` handler mapping.
57
+ */
58
+ export declare const Delete: (path: string) => MethodDecoratorLike;
59
+ /**
60
+ * Registers an `OPTIONS` route handler.
61
+ *
62
+ * @param path Route path relative to the controller base path.
63
+ * @returns A method decorator that registers an `OPTIONS` handler mapping.
64
+ */
65
+ export declare const Options: (path: string) => MethodDecoratorLike;
66
+ /**
67
+ * Registers a `HEAD` route handler.
68
+ *
69
+ * @param path Route path relative to the controller base path.
70
+ * @returns A method decorator that registers a `HEAD` handler mapping.
71
+ */
72
+ export declare const Head: (path: string) => MethodDecoratorLike;
73
+ /**
74
+ * Registers a route handler that matches all HTTP methods.
75
+ *
76
+ * @param path Route path relative to the controller base path.
77
+ * @returns A method decorator that registers an all-method handler mapping.
78
+ */
79
+ export declare const All: (path: string) => MethodDecoratorLike;
80
+ /**
81
+ * Associates a DTO class used for request binding and validation.
82
+ *
83
+ * @param dto DTO class consumed by request binding and validation.
84
+ * @returns A method decorator that stores request DTO metadata for the route.
85
+ */
86
+ export declare const RequestDto: (value: Constructor) => MethodDecoratorLike;
87
+ /**
88
+ * Declares response media types produced by a route handler.
89
+ *
90
+ * @param mediaTypes One or more media type strings written into route metadata.
91
+ * @returns A method decorator that stores normalized `produces` metadata.
92
+ */
93
+ export declare function Produces(...mediaTypes: string[]): MethodDecoratorLike;
94
+ /**
95
+ * Overrides the default success status code for a route handler.
96
+ *
97
+ * @param status HTTP status code used when the route completes successfully.
98
+ * @returns A method decorator that stores the route-level success status override.
99
+ */
100
+ export declare const HttpCode: (value: number) => MethodDecoratorLike;
101
+ /**
102
+ * Reads route-level `@Produces(...)` metadata from a controller method.
103
+ *
104
+ * @param controllerToken Controller class containing route metadata.
105
+ * @param propertyKey Controller method key to read.
106
+ * @returns A defensive copy of declared media types, or `undefined` when not configured.
107
+ */
108
+ export declare function getRouteProducesMetadata(controllerToken: Constructor, propertyKey: MetadataPropertyKey): string[] | undefined;
109
+ /**
110
+ * Binds a DTO field from a path parameter.
111
+ *
112
+ * @param key Optional source key override. Defaults to the DTO field name.
113
+ * @returns A field decorator that marks the binding source as `path`.
114
+ */
115
+ export declare const FromPath: (key?: string) => FieldDecoratorLike;
116
+ /**
117
+ * Binds a DTO field from query parameters.
118
+ *
119
+ * @param key Optional source key override. Defaults to the DTO field name.
120
+ * @returns A field decorator that marks the binding source as `query`.
121
+ */
122
+ export declare const FromQuery: (key?: string) => FieldDecoratorLike;
123
+ /**
124
+ * Binds a DTO field from a request header.
125
+ *
126
+ * @param key Optional source key override. Defaults to the DTO field name.
127
+ * @returns A field decorator that marks the binding source as `header`.
128
+ */
129
+ export declare const FromHeader: (key?: string) => FieldDecoratorLike;
130
+ /**
131
+ * Binds a DTO field from a cookie.
132
+ *
133
+ * @param key Optional source key override. Defaults to the DTO field name.
134
+ * @returns A field decorator that marks the binding source as `cookie`.
135
+ */
136
+ export declare const FromCookie: (key?: string) => FieldDecoratorLike;
137
+ /**
138
+ * Binds a DTO field from the request body.
139
+ *
140
+ * @param key Optional source key override. Defaults to the DTO field name.
141
+ * @returns A field decorator that marks the binding source as `body`.
142
+ */
143
+ export declare const FromBody: (key?: string) => FieldDecoratorLike;
144
+ /**
145
+ * Marks a DTO field binding as optional.
146
+ *
147
+ * @returns A field decorator that marks the DTO binding as optional.
148
+ */
149
+ export declare function Optional(): FieldDecoratorLike;
150
+ /**
151
+ * Applies a field-level converter to a DTO binding.
152
+ *
153
+ * @param converter Converter instance or token resolved during request binding.
154
+ * @returns A field decorator that stores converter metadata for the DTO field.
155
+ */
156
+ export declare function Convert(converter: ConverterLike): FieldDecoratorLike;
157
+ /**
158
+ * Adds a static response header to the route metadata.
159
+ *
160
+ * @param name Response header name.
161
+ * @param value Static response header value applied by the dispatcher.
162
+ * @returns A method decorator that appends route-level response-header metadata.
163
+ */
164
+ export declare function Header(name: string, value: string): MethodDecoratorLike;
165
+ /**
166
+ * Marks a route as a redirect with an optional status code.
167
+ *
168
+ * @param url Redirect target URL.
169
+ * @param statusCode Optional explicit redirect status code.
170
+ * @returns A method decorator that writes redirect metadata for the route.
171
+ */
172
+ export declare function Redirect(url: string, statusCode?: number): MethodDecoratorLike;
173
+ /**
174
+ * Attaches guards to a controller or route handler.
175
+ *
176
+ * @param guards One or more guards merged into existing class- or route-level guard metadata.
177
+ * @returns A decorator applicable to classes and methods.
178
+ */
179
+ export declare function UseGuards(...guards: GuardLike[]): ClassOrMethodDecoratorLike;
180
+ /**
181
+ * Attaches interceptors to a controller or route handler.
182
+ *
183
+ * @param interceptors One or more interceptors merged into existing class- or route-level metadata.
184
+ * @returns A decorator applicable to classes and methods.
185
+ */
186
+ export declare function UseInterceptors(...interceptors: InterceptorLike[]): ClassOrMethodDecoratorLike;
187
+ export {};
188
+ //# sourceMappingURL=decorators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}