@fluojs/runtime 1.0.0-beta.1 → 1.0.0-beta.11
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.ko.md +53 -11
- package/README.md +53 -11
- package/dist/adapters/request-response-factory.d.ts +9 -0
- package/dist/adapters/request-response-factory.d.ts.map +1 -1
- package/dist/adapters/request-response-factory.js +14 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +327 -60
- package/dist/health/diagnostics.d.ts +38 -0
- package/dist/health/diagnostics.d.ts.map +1 -1
- package/dist/health/diagnostics.js +48 -0
- package/dist/health/health.d.ts +34 -0
- package/dist/health/health.d.ts.map +1 -1
- package/dist/health/health.js +54 -4
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/logging/json-logger.d.ts +5 -0
- package/dist/logging/json-logger.d.ts.map +1 -1
- package/dist/logging/json-logger.js +6 -0
- package/dist/logging/logger.d.ts +35 -1
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/logging/logger.js +69 -5
- package/dist/module-graph.d.ts +16 -0
- package/dist/module-graph.d.ts.map +1 -1
- package/dist/module-graph.js +304 -8
- package/dist/node/internal-node-compression.d.ts +15 -0
- package/dist/node/internal-node-compression.d.ts.map +1 -1
- package/dist/node/internal-node-compression.js +16 -0
- package/dist/node/internal-node-request.d.ts +128 -0
- package/dist/node/internal-node-request.d.ts.map +1 -1
- package/dist/node/internal-node-request.js +321 -40
- package/dist/node/internal-node-response.d.ts +21 -1
- package/dist/node/internal-node-response.d.ts.map +1 -1
- package/dist/node/internal-node-response.js +42 -3
- package/dist/node/internal-node.d.ts +43 -6
- package/dist/node/internal-node.d.ts.map +1 -1
- package/dist/node/internal-node.js +65 -9
- package/dist/node/node-request.d.ts +1 -1
- package/dist/node/node-request.d.ts.map +1 -1
- package/dist/node/node-request.js +1 -1
- package/dist/platform-shell.d.ts +4 -0
- package/dist/platform-shell.d.ts.map +1 -1
- package/dist/platform-shell.js +72 -20
- package/dist/request-transaction.d.ts +28 -0
- package/dist/request-transaction.d.ts.map +1 -1
- package/dist/request-transaction.js +33 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/web.d.ts +9 -1
- package/dist/web.d.ts.map +1 -1
- package/dist/web.js +207 -56
- package/package.json +6 -6
|
@@ -2,6 +2,14 @@ import { Readable } from 'node:stream';
|
|
|
2
2
|
import { URL } from 'node:url';
|
|
3
3
|
import { BadRequestException, PayloadTooLargeException } from '@fluojs/http';
|
|
4
4
|
import { parseMultipart } from '../multipart.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for creating a deferred framework request shell from a Node-backed adapter.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* HTTP payload-size error that closes the underlying Node request stream after the response commits.
|
|
12
|
+
*/
|
|
5
13
|
export class NodeRequestPayloadTooLargeException extends PayloadTooLargeException {
|
|
6
14
|
constructor(request) {
|
|
7
15
|
super('Request body exceeds the size limit.');
|
|
@@ -31,50 +39,190 @@ export class NodeRequestPayloadTooLargeException extends PayloadTooLargeExceptio
|
|
|
31
39
|
* @returns The normalized framework request used by the dispatcher.
|
|
32
40
|
*/
|
|
33
41
|
export async function createFrameworkRequest(request, signal, multipartOptions, maxBodySize = 1 * 1024 * 1024, preserveRawBody = false) {
|
|
34
|
-
const
|
|
42
|
+
const frameworkRequest = createDeferredFrameworkRequest(request, signal, multipartOptions, maxBodySize, preserveRawBody);
|
|
43
|
+
await materializeFrameworkRequestBody(frameworkRequest);
|
|
44
|
+
return frameworkRequest;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates the cheap Node framework request shell before consuming the body stream.
|
|
49
|
+
*
|
|
50
|
+
* @param request - Raw Node request carrying headers, URL, and body stream.
|
|
51
|
+
* @param signal - Abort signal tied to the response lifecycle.
|
|
52
|
+
* @param multipartOptions - Multipart parser options applied when materializing multipart requests.
|
|
53
|
+
* @param maxBodySize - Maximum allowed non-multipart body size in bytes.
|
|
54
|
+
* @param preserveRawBody - Whether materialization should retain raw request body bytes.
|
|
55
|
+
* @returns The framework request shell with metadata snapshotted and body materialization deferred.
|
|
56
|
+
*/
|
|
57
|
+
export function createDeferredFrameworkRequest(request, signal, multipartOptions, maxBodySize = 1 * 1024 * 1024, preserveRawBody = false) {
|
|
58
|
+
const rawUrl = request.url ?? '/';
|
|
59
|
+
const urlParts = splitRawRequestUrl(rawUrl);
|
|
35
60
|
const headers = cloneRequestHeaders(request.headers);
|
|
36
|
-
const contentType =
|
|
37
|
-
const isMultipart =
|
|
38
|
-
let
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
const contentType = normalizePrimaryContentType(headers['content-type']);
|
|
62
|
+
const isMultipart = contentType === 'multipart/form-data';
|
|
63
|
+
let frameworkRequest;
|
|
64
|
+
const materializeBody = createMemoizedAsyncValue(async () => {
|
|
65
|
+
if (isMultipart) {
|
|
66
|
+
const result = await parseMultipart({
|
|
67
|
+
body: Readable.toWeb(request),
|
|
68
|
+
headers,
|
|
69
|
+
method: request.method,
|
|
70
|
+
url: resolveAbsoluteRequestUrl(rawUrl)
|
|
71
|
+
}, {
|
|
72
|
+
...multipartOptions,
|
|
73
|
+
maxTotalSize: multipartOptions?.maxTotalSize ?? maxBodySize
|
|
74
|
+
});
|
|
75
|
+
frameworkRequest.body = result.fields;
|
|
76
|
+
frameworkRequest.files = result.files;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!hasNodeRequestBody(request)) {
|
|
80
|
+
frameworkRequest.body = undefined;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
54
83
|
const bodyResult = await readRequestBody(request, headers['content-type'], maxBodySize, preserveRawBody);
|
|
55
|
-
body = bodyResult.body;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
84
|
+
frameworkRequest.body = bodyResult.body;
|
|
85
|
+
if (bodyResult.rawBody) {
|
|
86
|
+
frameworkRequest.rawBody = bodyResult.rawBody;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
frameworkRequest = createDeferredFrameworkRequestShell({
|
|
90
|
+
cookieHeader: cloneHeaderValue(headers.cookie),
|
|
61
91
|
headers,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
path:
|
|
65
|
-
|
|
92
|
+
materializeBody,
|
|
93
|
+
method: request.method,
|
|
94
|
+
path: urlParts.path,
|
|
95
|
+
queryFactory: () => parseQueryParamsFromSearch(urlParts.search),
|
|
66
96
|
raw: request,
|
|
97
|
+
requestId: resolvePrimaryRequestIdFromHeaders(headers),
|
|
67
98
|
signal,
|
|
68
|
-
url:
|
|
99
|
+
url: urlParts.path + urlParts.search
|
|
100
|
+
});
|
|
101
|
+
return frameworkRequest;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Creates a framework request shell from already-snapshotted Node adapter metadata.
|
|
106
|
+
*
|
|
107
|
+
* @param options - Raw request, metadata factories, and deferred body materialization hooks.
|
|
108
|
+
* @returns A framework request with lazy headers, cookies, query values, and optional body materialization.
|
|
109
|
+
*/
|
|
110
|
+
export function createDeferredFrameworkRequestShell({
|
|
111
|
+
cookieHeader,
|
|
112
|
+
headers,
|
|
113
|
+
headersFactory,
|
|
114
|
+
materializeBody,
|
|
115
|
+
method,
|
|
116
|
+
path,
|
|
117
|
+
query,
|
|
118
|
+
queryFactory,
|
|
119
|
+
raw,
|
|
120
|
+
requestId,
|
|
121
|
+
signal,
|
|
122
|
+
url
|
|
123
|
+
}) {
|
|
124
|
+
const hasQuerySnapshot = query !== undefined;
|
|
125
|
+
const hasLazySignal = typeof signal === 'function';
|
|
126
|
+
const resolveHeaders = headersFactory ? createMemoizedValue(headersFactory) : () => headers ?? {};
|
|
127
|
+
const resolveCookies = createMemoizedValue(() => parseCookieHeader(cookieHeader ?? resolveHeaders().cookie));
|
|
128
|
+
const resolveQuery = hasQuerySnapshot ? undefined : createMemoizedValue(() => queryFactory?.() ?? {});
|
|
129
|
+
const frameworkRequest = {
|
|
130
|
+
get cookies() {
|
|
131
|
+
return resolveCookies();
|
|
132
|
+
},
|
|
133
|
+
get headers() {
|
|
134
|
+
return resolveHeaders();
|
|
135
|
+
},
|
|
136
|
+
method: method ?? 'GET',
|
|
137
|
+
params: {},
|
|
138
|
+
path,
|
|
139
|
+
requestId,
|
|
140
|
+
raw,
|
|
141
|
+
url
|
|
69
142
|
};
|
|
70
|
-
if (
|
|
71
|
-
frameworkRequest
|
|
143
|
+
if (hasLazySignal) {
|
|
144
|
+
Object.defineProperty(frameworkRequest, 'signal', {
|
|
145
|
+
configurable: true,
|
|
146
|
+
enumerable: true,
|
|
147
|
+
get() {
|
|
148
|
+
return signal();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
frameworkRequest.signal = signal;
|
|
72
153
|
}
|
|
73
|
-
if (
|
|
74
|
-
frameworkRequest.
|
|
154
|
+
if (hasQuerySnapshot) {
|
|
155
|
+
frameworkRequest.query = query;
|
|
156
|
+
} else {
|
|
157
|
+
Object.defineProperty(frameworkRequest, 'query', {
|
|
158
|
+
configurable: true,
|
|
159
|
+
enumerable: true,
|
|
160
|
+
get() {
|
|
161
|
+
return resolveQuery();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (materializeBody) {
|
|
166
|
+
frameworkRequest.materializeBody = materializeBody;
|
|
75
167
|
}
|
|
76
168
|
return frameworkRequest;
|
|
77
169
|
}
|
|
170
|
+
function hasNodeRequestBody(request) {
|
|
171
|
+
const contentLength = request.headers['content-length'];
|
|
172
|
+
const transferEncoding = request.headers['transfer-encoding'];
|
|
173
|
+
const primaryContentLength = Array.isArray(contentLength) ? contentLength[0] : contentLength;
|
|
174
|
+
if (transferEncoding !== undefined) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
if (primaryContentLength === undefined) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
const parsedContentLength = Number(primaryContentLength);
|
|
181
|
+
return !Number.isFinite(parsedContentLength) || parsedContentLength !== 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Materializes a deferred Node framework request body exactly once.
|
|
186
|
+
*
|
|
187
|
+
* @param request - Framework request returned by {@link createDeferredFrameworkRequest}.
|
|
188
|
+
* @returns A promise that settles after body, rawBody, and files fields are populated when applicable.
|
|
189
|
+
*/
|
|
190
|
+
export async function materializeFrameworkRequestBody(request) {
|
|
191
|
+
await request.materializeBody?.();
|
|
192
|
+
delete request.materializeBody;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Creates a synchronous memoized value resolver.
|
|
197
|
+
*
|
|
198
|
+
* @param factory - Function that computes the value on first access.
|
|
199
|
+
* @returns A stable resolver that returns the cached value after the first call.
|
|
200
|
+
*/
|
|
201
|
+
export function createMemoizedValue(factory) {
|
|
202
|
+
let initialized = false;
|
|
203
|
+
let value;
|
|
204
|
+
return () => {
|
|
205
|
+
if (!initialized) {
|
|
206
|
+
value = factory();
|
|
207
|
+
initialized = true;
|
|
208
|
+
}
|
|
209
|
+
return value;
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Creates an async memoized side-effect resolver.
|
|
215
|
+
*
|
|
216
|
+
* @param factory - Async function to run at most once.
|
|
217
|
+
* @returns A resolver that returns the same in-flight or completed promise for every call.
|
|
218
|
+
*/
|
|
219
|
+
export function createMemoizedAsyncValue(factory) {
|
|
220
|
+
let promise;
|
|
221
|
+
return () => {
|
|
222
|
+
promise ??= factory();
|
|
223
|
+
return promise;
|
|
224
|
+
};
|
|
225
|
+
}
|
|
78
226
|
|
|
79
227
|
/**
|
|
80
228
|
* Creates an abort signal that fires when the Node response closes unexpectedly.
|
|
@@ -107,6 +255,20 @@ export function resolveRequestIdFromHeaders(headers) {
|
|
|
107
255
|
const requestId = headers['x-request-id'] ?? headers['x-correlation-id'];
|
|
108
256
|
return Array.isArray(requestId) ? requestId[0] : requestId;
|
|
109
257
|
}
|
|
258
|
+
function resolvePrimaryRequestIdFromHeaders(headers) {
|
|
259
|
+
const requestId = headers['x-request-id'];
|
|
260
|
+
return Array.isArray(requestId) ? requestId[0] : requestId;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Parses a raw URL search string into the framework query shape.
|
|
265
|
+
*
|
|
266
|
+
* @param search - Raw search string, with or without a leading question mark.
|
|
267
|
+
* @returns Query values where repeated keys become string arrays.
|
|
268
|
+
*/
|
|
269
|
+
export function parseQueryParamsFromSearch(search) {
|
|
270
|
+
return parseQueryParams(new URLSearchParams(search));
|
|
271
|
+
}
|
|
110
272
|
function parseQueryParams(searchParams) {
|
|
111
273
|
const query = {};
|
|
112
274
|
for (const [key, value] of searchParams.entries()) {
|
|
@@ -123,16 +285,81 @@ function parseQueryParams(searchParams) {
|
|
|
123
285
|
}
|
|
124
286
|
return query;
|
|
125
287
|
}
|
|
126
|
-
|
|
127
|
-
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Snapshots host-parsed query values when they already match framework semantics.
|
|
291
|
+
*
|
|
292
|
+
* @param query - Host query object exposed by a Node-backed adapter.
|
|
293
|
+
* @returns A cloned query record when all values are strings or string arrays; otherwise `undefined` for raw URL fallback.
|
|
294
|
+
*/
|
|
295
|
+
export function snapshotSimpleQueryRecord(query) {
|
|
296
|
+
if (typeof query !== 'object' || query === null) {
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
const snapshot = {};
|
|
300
|
+
for (const [key, value] of Object.entries(query)) {
|
|
301
|
+
if (typeof value === 'string') {
|
|
302
|
+
snapshot[key] = value;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (Array.isArray(value) && value.every(item => typeof item === 'string')) {
|
|
306
|
+
snapshot[key] = [...value];
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
311
|
+
return snapshot;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Clones Node request headers into the framework header record shape.
|
|
316
|
+
*
|
|
317
|
+
* @param headers - Raw Node incoming headers.
|
|
318
|
+
* @returns A shallow header snapshot with array values cloned.
|
|
319
|
+
*/
|
|
320
|
+
export function cloneRequestHeaders(headers) {
|
|
321
|
+
const clonedEntries = Object.entries(headers).map(([name, value]) => [name, cloneHeaderValue(value)]);
|
|
128
322
|
return Object.fromEntries(clonedEntries);
|
|
129
323
|
}
|
|
130
|
-
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Clones a single Node header value when it is array-backed.
|
|
327
|
+
*
|
|
328
|
+
* @param value - Header value to snapshot.
|
|
329
|
+
* @returns The original scalar value or a cloned array value.
|
|
330
|
+
*/
|
|
331
|
+
export function cloneHeaderValue(value) {
|
|
332
|
+
return Array.isArray(value) ? [...value] : value;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Reads the primary value from a Node header value.
|
|
337
|
+
*
|
|
338
|
+
* @param headerValue - Header value that may contain multiple entries.
|
|
339
|
+
* @returns The first header value when present.
|
|
340
|
+
*/
|
|
341
|
+
export function readPrimaryHeaderValue(headerValue) {
|
|
131
342
|
if (Array.isArray(headerValue)) {
|
|
132
343
|
return headerValue[0];
|
|
133
344
|
}
|
|
134
345
|
return headerValue;
|
|
135
346
|
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Normalizes a Node content-type header to its primary media type.
|
|
350
|
+
*
|
|
351
|
+
* @param headerValue - Raw content-type header value.
|
|
352
|
+
* @returns Lowercase primary media type without parameters, or `undefined` when absent.
|
|
353
|
+
*/
|
|
354
|
+
export function normalizePrimaryContentType(headerValue) {
|
|
355
|
+
const primaryHeaderValue = readPrimaryHeaderValue(headerValue);
|
|
356
|
+
if (typeof primaryHeaderValue !== 'string') {
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
359
|
+
const [mediaType] = primaryHeaderValue.split(';', 1);
|
|
360
|
+
const normalizedMediaType = mediaType?.trim().toLowerCase();
|
|
361
|
+
return normalizedMediaType && normalizedMediaType.length > 0 ? normalizedMediaType : undefined;
|
|
362
|
+
}
|
|
136
363
|
function decodeCookieValue(raw) {
|
|
137
364
|
try {
|
|
138
365
|
return decodeURIComponent(raw);
|
|
@@ -140,7 +367,14 @@ function decodeCookieValue(raw) {
|
|
|
140
367
|
return raw;
|
|
141
368
|
}
|
|
142
369
|
}
|
|
143
|
-
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Parses a Node cookie header into framework cookie values.
|
|
373
|
+
*
|
|
374
|
+
* @param cookieHeader - Raw cookie header value or values.
|
|
375
|
+
* @returns Cookie name/value pairs with percent-decoded values when possible.
|
|
376
|
+
*/
|
|
377
|
+
export function parseCookieHeader(cookieHeader) {
|
|
144
378
|
const normalizedCookieHeader = Array.isArray(cookieHeader) ? cookieHeader.join('; ') : cookieHeader;
|
|
145
379
|
if (!normalizedCookieHeader) {
|
|
146
380
|
return {};
|
|
@@ -177,8 +411,8 @@ async function readRequestBody(request, contentType, maxBodySize = 1 * 1024 * 10
|
|
|
177
411
|
rawBody: preserveRawBody ? rawBody : undefined
|
|
178
412
|
};
|
|
179
413
|
}
|
|
180
|
-
const primaryContentType =
|
|
181
|
-
if (
|
|
414
|
+
const primaryContentType = normalizePrimaryContentType(contentType);
|
|
415
|
+
if (primaryContentType === 'application/json') {
|
|
182
416
|
try {
|
|
183
417
|
return {
|
|
184
418
|
body: JSON.parse(bodyText),
|
|
@@ -192,4 +426,51 @@ async function readRequestBody(request, contentType, maxBodySize = 1 * 1024 * 10
|
|
|
192
426
|
body: bodyText,
|
|
193
427
|
rawBody: preserveRawBody ? rawBody : undefined
|
|
194
428
|
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Splits a raw Node request URL into path and search components.
|
|
433
|
+
*
|
|
434
|
+
* @param rawUrl - Raw request URL, absolute URL, or undefined value from Node.
|
|
435
|
+
* @returns The pathname and search string used by framework request matching and query parsing.
|
|
436
|
+
*/
|
|
437
|
+
export function splitRawRequestUrl(rawUrl) {
|
|
438
|
+
const resolvedRawUrl = rawUrl ?? '/';
|
|
439
|
+
if (resolvedRawUrl.startsWith('http://') || resolvedRawUrl.startsWith('https://')) {
|
|
440
|
+
const url = new URL(resolvedRawUrl);
|
|
441
|
+
return {
|
|
442
|
+
path: url.pathname,
|
|
443
|
+
search: url.search
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
const queryStart = resolvedRawUrl.indexOf('?');
|
|
447
|
+
const hashStart = resolvedRawUrl.indexOf('#');
|
|
448
|
+
const pathEndCandidates = [queryStart, hashStart].filter(index => index >= 0);
|
|
449
|
+
const pathEnd = pathEndCandidates.length > 0 ? Math.min(...pathEndCandidates) : resolvedRawUrl.length;
|
|
450
|
+
const path = resolvedRawUrl.slice(0, pathEnd) || '/';
|
|
451
|
+
if (queryStart === -1) {
|
|
452
|
+
return {
|
|
453
|
+
path,
|
|
454
|
+
search: ''
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
const searchEnd = hashStart >= 0 && hashStart > queryStart ? hashStart : resolvedRawUrl.length;
|
|
458
|
+
return {
|
|
459
|
+
path,
|
|
460
|
+
search: resolvedRawUrl.slice(queryStart, searchEnd)
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Resolves a raw Node request URL into an absolute URL string.
|
|
466
|
+
*
|
|
467
|
+
* @param rawUrl - Raw request URL, absolute URL, or undefined value from Node.
|
|
468
|
+
* @returns An absolute URL suitable for Web-standard parsers.
|
|
469
|
+
*/
|
|
470
|
+
export function resolveAbsoluteRequestUrl(rawUrl) {
|
|
471
|
+
const resolvedRawUrl = rawUrl ?? '/';
|
|
472
|
+
if (resolvedRawUrl.startsWith('http://') || resolvedRawUrl.startsWith('https://')) {
|
|
473
|
+
return resolvedRawUrl;
|
|
474
|
+
}
|
|
475
|
+
return new URL(resolvedRawUrl, 'http://localhost').toString();
|
|
195
476
|
}
|
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
import type { ServerResponse } from 'node:http';
|
|
2
2
|
import { type FrameworkResponseCompression, type FrameworkResponse } from '@fluojs/http';
|
|
3
|
+
/**
|
|
4
|
+
* Defines the mutable framework response type.
|
|
5
|
+
*/
|
|
3
6
|
export type MutableFrameworkResponse = FrameworkResponse & {
|
|
4
7
|
statusSet?: boolean;
|
|
5
8
|
};
|
|
6
|
-
|
|
9
|
+
type FrameworkResponseCompressionFactory = () => FrameworkResponseCompression | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Create framework response.
|
|
12
|
+
*
|
|
13
|
+
* @param response The response.
|
|
14
|
+
* @param compression The compression.
|
|
15
|
+
* @returns The create framework response result.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createFrameworkResponse(response: ServerResponse, compression?: FrameworkResponseCompression | FrameworkResponseCompressionFactory): MutableFrameworkResponse;
|
|
18
|
+
/**
|
|
19
|
+
* Write node adapter error response.
|
|
20
|
+
*
|
|
21
|
+
* @param error The error.
|
|
22
|
+
* @param response The response.
|
|
23
|
+
* @param requestId The request id.
|
|
24
|
+
* @returns The write node adapter error response result.
|
|
25
|
+
*/
|
|
7
26
|
export declare function writeNodeAdapterErrorResponse(error: unknown, response: FrameworkResponse, requestId?: string): Promise<void>;
|
|
27
|
+
export {};
|
|
8
28
|
//# sourceMappingURL=internal-node-response.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal-node-response.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAIL,KAAK,4BAA4B,EACjC,KAAK,iBAAiB,EAEvB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,GAAG;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"internal-node-response.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAIL,KAAK,4BAA4B,EACjC,KAAK,iBAAiB,EAEvB,MAAM,cAAc,CAAC;AAEtB;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,GAAG;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEnF,KAAK,mCAAmC,GAAG,MAAM,4BAA4B,GAAG,SAAS,CAAC;AA6C1F;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,cAAc,EACxB,WAAW,CAAC,EAAE,4BAA4B,GAAG,mCAAmC,GAC/E,wBAAwB,CAqH1B;AAED;;;;;;;GAOG;AACH,wBAAsB,6BAA6B,CACjD,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { createErrorResponse, HttpException, InternalServerErrorException } from '@fluojs/http';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Defines the mutable framework response type.
|
|
5
|
+
*/
|
|
6
|
+
|
|
2
7
|
function createFrameworkResponseStream(response) {
|
|
3
8
|
return {
|
|
4
9
|
close() {
|
|
@@ -39,7 +44,28 @@ function createFrameworkResponseStream(response) {
|
|
|
39
44
|
}
|
|
40
45
|
};
|
|
41
46
|
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create framework response.
|
|
50
|
+
*
|
|
51
|
+
* @param response The response.
|
|
52
|
+
* @param compression The compression.
|
|
53
|
+
* @returns The create framework response result.
|
|
54
|
+
*/
|
|
42
55
|
export function createFrameworkResponse(response, compression) {
|
|
56
|
+
let activeStream;
|
|
57
|
+
const resolveCompression = (() => {
|
|
58
|
+
const factory = typeof compression === 'function' ? compression : () => compression;
|
|
59
|
+
let resolved = false;
|
|
60
|
+
let value;
|
|
61
|
+
return () => {
|
|
62
|
+
if (!resolved) {
|
|
63
|
+
value = factory();
|
|
64
|
+
resolved = true;
|
|
65
|
+
}
|
|
66
|
+
return value;
|
|
67
|
+
};
|
|
68
|
+
})();
|
|
43
69
|
const mergeSetCookieHeader = (current, incoming) => {
|
|
44
70
|
const nextValues = Array.isArray(incoming) ? incoming : [incoming];
|
|
45
71
|
if (current === undefined) {
|
|
@@ -56,7 +82,10 @@ export function createFrameworkResponse(response, compression) {
|
|
|
56
82
|
committed: response.headersSent || response.writableEnded,
|
|
57
83
|
headers: {},
|
|
58
84
|
raw: response,
|
|
59
|
-
stream
|
|
85
|
+
get stream() {
|
|
86
|
+
activeStream ??= createFrameworkResponseStream(response);
|
|
87
|
+
return activeStream;
|
|
88
|
+
},
|
|
60
89
|
redirect(status, location) {
|
|
61
90
|
this.setStatus(status);
|
|
62
91
|
this.setHeader('Location', location);
|
|
@@ -74,9 +103,10 @@ export function createFrameworkResponse(response, compression) {
|
|
|
74
103
|
}
|
|
75
104
|
const contentType = response.getHeader('Content-Type');
|
|
76
105
|
const payload = typeof serialized.payload === 'string' ? Buffer.from(serialized.payload, 'utf8') : serialized.payload;
|
|
77
|
-
|
|
106
|
+
const activeCompression = resolveCompression();
|
|
107
|
+
if (activeCompression) {
|
|
78
108
|
this.committed = true;
|
|
79
|
-
return Promise.resolve(
|
|
109
|
+
return Promise.resolve(activeCompression.write(payload, {
|
|
80
110
|
contentType
|
|
81
111
|
})).then(handled => {
|
|
82
112
|
if (!handled && !response.writableEnded) {
|
|
@@ -113,6 +143,15 @@ export function createFrameworkResponse(response, compression) {
|
|
|
113
143
|
};
|
|
114
144
|
return frameworkResponse;
|
|
115
145
|
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Write node adapter error response.
|
|
149
|
+
*
|
|
150
|
+
* @param error The error.
|
|
151
|
+
* @param response The response.
|
|
152
|
+
* @param requestId The request id.
|
|
153
|
+
* @returns The write node adapter error response result.
|
|
154
|
+
*/
|
|
116
155
|
export async function writeNodeAdapterErrorResponse(error, response, requestId) {
|
|
117
156
|
const httpError = toHttpException(error);
|
|
118
157
|
response.setStatus(httpError.status);
|
|
@@ -2,6 +2,7 @@ import { createServer as createHttpServer } from 'node:http';
|
|
|
2
2
|
import { createServer as createHttpsServer, type ServerOptions as HttpsServerOptions } from 'node:https';
|
|
3
3
|
import { type CorsOptions, type Dispatcher, type HttpApplicationAdapter, type MiddlewareLike, type SecurityHeadersOptions } from '@fluojs/http';
|
|
4
4
|
import { createNodeResponseCompression, compressNodeResponse } from './internal-node-compression.js';
|
|
5
|
+
import { cloneHeaderValue, cloneRequestHeaders, createDeferredFrameworkRequestShell, createMemoizedAsyncValue, createMemoizedValue, normalizePrimaryContentType, parseCookieHeader, parseQueryParamsFromSearch, createRequestSignal, readPrimaryHeaderValue, resolveAbsoluteRequestUrl, resolveRequestIdFromHeaders, snapshotSimpleQueryRecord, splitRawRequestUrl } from './internal-node-request.js';
|
|
5
6
|
import { createNodeShutdownSignalRegistration, defaultNodeShutdownSignals, registerShutdownSignals } from './internal-node-shutdown.js';
|
|
6
7
|
import type { MultipartOptions, UploadedFile } from '../multipart.js';
|
|
7
8
|
import type { Application, ApplicationLogger, CreateApplicationOptions, ModuleType } from '../types.js';
|
|
@@ -11,6 +12,9 @@ declare module '@fluojs/http' {
|
|
|
11
12
|
rawBody?: Uint8Array;
|
|
12
13
|
}
|
|
13
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Describes the node http adapter options contract.
|
|
17
|
+
*/
|
|
14
18
|
export interface NodeHttpAdapterOptions {
|
|
15
19
|
host?: string;
|
|
16
20
|
https?: HttpsServerOptions;
|
|
@@ -21,8 +25,17 @@ export interface NodeHttpAdapterOptions {
|
|
|
21
25
|
retryLimit?: number;
|
|
22
26
|
shutdownTimeoutMs?: number;
|
|
23
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Defines the node application signal type.
|
|
30
|
+
*/
|
|
24
31
|
export type NodeApplicationSignal = 'SIGINT' | 'SIGTERM';
|
|
32
|
+
/**
|
|
33
|
+
* Defines the cors input type.
|
|
34
|
+
*/
|
|
25
35
|
export type CorsInput = false | string | string[] | CorsOptions;
|
|
36
|
+
/**
|
|
37
|
+
* Describes the bootstrap node application options contract.
|
|
38
|
+
*/
|
|
26
39
|
export interface BootstrapNodeApplicationOptions extends Omit<CreateApplicationOptions, 'adapter' | 'logger' | 'middleware'> {
|
|
27
40
|
compression?: boolean;
|
|
28
41
|
cors?: CorsInput;
|
|
@@ -41,6 +54,9 @@ export interface BootstrapNodeApplicationOptions extends Omit<CreateApplicationO
|
|
|
41
54
|
securityHeaders?: false | SecurityHeadersOptions;
|
|
42
55
|
shutdownTimeoutMs?: number;
|
|
43
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Describes the run node application options contract.
|
|
59
|
+
*/
|
|
44
60
|
export interface RunNodeApplicationOptions extends BootstrapNodeApplicationOptions {
|
|
45
61
|
forceExitTimeoutMs?: number;
|
|
46
62
|
shutdownSignals?: false | readonly NodeApplicationSignal[];
|
|
@@ -50,22 +66,21 @@ interface NodeListenTarget {
|
|
|
50
66
|
url: string;
|
|
51
67
|
}
|
|
52
68
|
type NodeServer = ReturnType<typeof createHttpServer> | ReturnType<typeof createHttpsServer>;
|
|
69
|
+
/**
|
|
70
|
+
* Represents the node http application adapter.
|
|
71
|
+
*/
|
|
53
72
|
export declare class NodeHttpApplicationAdapter implements HttpApplicationAdapter {
|
|
54
73
|
private readonly port;
|
|
55
74
|
private readonly host;
|
|
56
75
|
private readonly retryDelayMs;
|
|
57
76
|
private readonly retryLimit;
|
|
58
|
-
private readonly compression;
|
|
59
77
|
private readonly httpsOptions;
|
|
60
|
-
private readonly multipartOptions?;
|
|
61
|
-
private readonly maxBodySize;
|
|
62
|
-
private readonly preserveRawBody;
|
|
63
78
|
private readonly shutdownTimeoutMs;
|
|
64
79
|
private readonly server;
|
|
65
80
|
private dispatcher?;
|
|
66
81
|
private readonly requestResponseFactory;
|
|
67
82
|
private readonly sockets;
|
|
68
|
-
constructor(port: number, host: string | undefined, retryDelayMs: number | undefined, retryLimit: number | undefined, compression: boolean | undefined, httpsOptions: HttpsServerOptions | undefined, multipartOptions?: MultipartOptions
|
|
83
|
+
constructor(port: number, host: string | undefined, retryDelayMs: number | undefined, retryLimit: number | undefined, compression: boolean | undefined, httpsOptions: HttpsServerOptions | undefined, multipartOptions?: MultipartOptions, maxBodySize?: number, preserveRawBody?: boolean, shutdownTimeoutMs?: number);
|
|
69
84
|
getServer(): NodeServer;
|
|
70
85
|
getRealtimeCapability(): import("@fluojs/http").ServerBackedHttpAdapterRealtimeCapability;
|
|
71
86
|
getListenTarget(): NodeListenTarget;
|
|
@@ -73,8 +88,30 @@ export declare class NodeHttpApplicationAdapter implements HttpApplicationAdapte
|
|
|
73
88
|
close(): Promise<void>;
|
|
74
89
|
private handleRequest;
|
|
75
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Create node http adapter.
|
|
93
|
+
*
|
|
94
|
+
* @param options The options.
|
|
95
|
+
* @param compression The compression.
|
|
96
|
+
* @param multipartOptions The multipart options.
|
|
97
|
+
* @returns The create node http adapter result.
|
|
98
|
+
*/
|
|
76
99
|
export declare function createNodeHttpAdapter(options?: NodeHttpAdapterOptions, compression?: boolean, multipartOptions?: MultipartOptions): HttpApplicationAdapter;
|
|
100
|
+
/**
|
|
101
|
+
* Bootstrap node application.
|
|
102
|
+
*
|
|
103
|
+
* @param rootModule The root module.
|
|
104
|
+
* @param options The options.
|
|
105
|
+
* @returns The bootstrap node application result.
|
|
106
|
+
*/
|
|
77
107
|
export declare function bootstrapNodeApplication(rootModule: ModuleType, options: BootstrapNodeApplicationOptions): Promise<Application>;
|
|
108
|
+
/**
|
|
109
|
+
* Run node application.
|
|
110
|
+
*
|
|
111
|
+
* @param rootModule The root module.
|
|
112
|
+
* @param options The options.
|
|
113
|
+
* @returns The run node application result.
|
|
114
|
+
*/
|
|
78
115
|
export declare function runNodeApplication(rootModule: ModuleType, options: RunNodeApplicationOptions): Promise<Application>;
|
|
79
|
-
export { compressNodeResponse, createNodeResponseCompression, createNodeShutdownSignalRegistration, defaultNodeShutdownSignals, registerShutdownSignals, };
|
|
116
|
+
export { cloneHeaderValue, cloneRequestHeaders, compressNodeResponse, createDeferredFrameworkRequestShell, createMemoizedAsyncValue, createMemoizedValue, createRequestSignal, createNodeResponseCompression, createNodeShutdownSignalRegistration, defaultNodeShutdownSignals, normalizePrimaryContentType, parseCookieHeader, parseQueryParamsFromSearch, readPrimaryHeaderValue, registerShutdownSignals, resolveAbsoluteRequestUrl, resolveRequestIdFromHeaders, snapshotSimpleQueryRecord, splitRawRequestUrl, };
|
|
80
117
|
//# sourceMappingURL=internal-node.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal-node.d.ts","sourceRoot":"","sources":["../../src/node/internal-node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAA6C,MAAM,WAAW,CAAC;AACxG,OAAO,EAAE,YAAY,IAAI,iBAAiB,EAAE,KAAK,aAAa,IAAI,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGzG,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC5B,MAAM,cAAc,CAAC;AAMtB,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACrB,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"internal-node.d.ts","sourceRoot":"","sources":["../../src/node/internal-node.ts"],"names":[],"mappings":"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"}
|