@fluojs/platform-fastify 1.0.0-beta.6 → 1.0.0-beta.8
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/dist/adapter.d.ts +1 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +232 -171
- package/package.json +3 -3
package/dist/adapter.d.ts
CHANGED
|
@@ -88,6 +88,7 @@ export declare class FastifyHttpApplicationAdapter implements HttpApplicationAda
|
|
|
88
88
|
private registerWildcardFallbackRoute;
|
|
89
89
|
private listenWithRetry;
|
|
90
90
|
private handleRequest;
|
|
91
|
+
private handleNativeRouteRequest;
|
|
91
92
|
}
|
|
92
93
|
/**
|
|
93
94
|
* Create the recommended Fastify adapter for `FluoFactory.create(...)`.
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,IAAI,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAOtE,OAAO,EAOL,KAAK,WAAW,EAChB,KAAK,UAAU,EAIf,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC5B,MAAM,cAAc,CAAC;AAOtB,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter.js
CHANGED
|
@@ -4,6 +4,7 @@ import fastify from 'fastify';
|
|
|
4
4
|
import { createServerBackedHttpAdapterRealtimeCapability, createErrorResponse, HttpException, InternalServerErrorException, PayloadTooLargeException } from '@fluojs/http';
|
|
5
5
|
import { attachFrameworkRequestNativeRouteHandoff, bindRawRequestNativeRouteHandoff, consumeRawRequestNativeRouteHandoff, isRoutePathNormalizationSensitive } from '@fluojs/http/internal';
|
|
6
6
|
import { createNodeShutdownSignalRegistration, defaultNodeShutdownSignals } from '@fluojs/runtime/node';
|
|
7
|
+
import { cloneHeaderValue, createDeferredFrameworkRequestShell, createMemoizedAsyncValue, createMemoizedValue, parseQueryParamsFromSearch, snapshotSimpleQueryRecord, splitRawRequestUrl } from '@fluojs/runtime/internal-node';
|
|
7
8
|
import { bootstrapHttpAdapterApplication, runHttpAdapterApplication } from '@fluojs/runtime/internal/http-adapter';
|
|
8
9
|
import { dispatchWithRequestResponseFactory } from '@fluojs/runtime/internal/request-response-factory';
|
|
9
10
|
|
|
@@ -18,6 +19,7 @@ import { dispatchWithRequestResponseFactory } from '@fluojs/runtime/internal/req
|
|
|
18
19
|
const DEFAULT_MAX_BODY_SIZE = 1 * 1024 * 1024;
|
|
19
20
|
const DEFAULT_SHUTDOWN_TIMEOUT_MS = 10_000;
|
|
20
21
|
const FASTIFY_NATIVE_ROUTE_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'];
|
|
22
|
+
const EMPTY_NATIVE_ROUTE_PARAMS = Object.freeze({});
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* Bootstrap options for creating a Fastify-backed application without
|
|
@@ -103,13 +105,11 @@ export class FastifyHttpApplicationAdapter {
|
|
|
103
105
|
for (const route of createFastifyNativeRoutes(descriptors)) {
|
|
104
106
|
this.app.route({
|
|
105
107
|
handler: async (request, reply) => {
|
|
106
|
-
const
|
|
108
|
+
const urlParts = splitRawRequestUrl(request.raw.url ?? '/');
|
|
107
109
|
const params = normalizeNativeRouteParams(request.params);
|
|
108
|
-
if (!isRoutePathNormalizationSensitive(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
params
|
|
112
|
-
});
|
|
110
|
+
if (!isRoutePathNormalizationSensitive(urlParts.path) && !hasNativeRouteParamSeparators(params)) {
|
|
111
|
+
await this.handleNativeRouteRequest(route.descriptor, params, urlParts, request, reply);
|
|
112
|
+
return;
|
|
113
113
|
}
|
|
114
114
|
await this.handleRequest(request, reply);
|
|
115
115
|
},
|
|
@@ -148,6 +148,128 @@ export class FastifyHttpApplicationAdapter {
|
|
|
148
148
|
rawResponse: reply
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
|
+
async handleNativeRouteRequest(descriptor, params, urlParts, request, reply) {
|
|
152
|
+
if (isMultipartRequestContentType(request.raw.headers['content-type'])) {
|
|
153
|
+
bindRawRequestNativeRouteHandoff(request.raw, {
|
|
154
|
+
descriptor,
|
|
155
|
+
params
|
|
156
|
+
});
|
|
157
|
+
await this.handleRequest(request, reply);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const dispatcher = this.dispatcher;
|
|
161
|
+
if (!dispatcher?.dispatchNativeRoute) {
|
|
162
|
+
bindRawRequestNativeRouteHandoff(request.raw, {
|
|
163
|
+
descriptor,
|
|
164
|
+
params
|
|
165
|
+
});
|
|
166
|
+
await this.handleRequest(request, reply);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const factory = this.requestResponseFactory;
|
|
170
|
+
const frameworkResponse = factory.createResponse(reply, request);
|
|
171
|
+
const lazySignal = createLazyFastifyRequestSignal(reply, factory.createRequestSignal);
|
|
172
|
+
try {
|
|
173
|
+
const frameworkRequest = createNativeFastFrameworkRequest(request, lazySignal, urlParts, this.maxBodySize, this.preserveRawBody) ?? (await factory.createRequest(request, lazySignal.signal()));
|
|
174
|
+
if (!isNativeFastFrameworkRequest(frameworkRequest)) {
|
|
175
|
+
await factory.materializeRequest?.(frameworkRequest);
|
|
176
|
+
}
|
|
177
|
+
const handled = await dispatcher.dispatchNativeRoute({
|
|
178
|
+
descriptor,
|
|
179
|
+
params
|
|
180
|
+
}, frameworkRequest, frameworkResponse);
|
|
181
|
+
if (!handled) {
|
|
182
|
+
bindRawRequestNativeRouteHandoff(request.raw, {
|
|
183
|
+
descriptor,
|
|
184
|
+
params
|
|
185
|
+
});
|
|
186
|
+
await this.handleRequest(request, reply);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (!frameworkResponse.committed) {
|
|
190
|
+
await frameworkResponse.send(undefined);
|
|
191
|
+
}
|
|
192
|
+
} catch (error) {
|
|
193
|
+
if (lazySignal.isAborted() || frameworkResponse.committed) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
await factory.writeErrorResponse(error, frameworkResponse, factory.resolveRequestId(request));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function createNativeFastFrameworkRequest(request, lazySignal, urlParts, maxBodySize, preserveRawBody) {
|
|
201
|
+
const contentType = request.headers['content-type'];
|
|
202
|
+
if (preserveRawBody || isFastifyMultipartRequest(request) || isMultipartRequestContentType(contentType)) {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
const contentLength = Number(request.headers['content-length']);
|
|
206
|
+
if (Number.isFinite(contentLength) && contentLength > maxBodySize) {
|
|
207
|
+
throw new PayloadTooLargeException('Request body exceeds the size limit.');
|
|
208
|
+
}
|
|
209
|
+
const frameworkRequest = createDeferredFrameworkRequestShell({
|
|
210
|
+
cookieHeader: cloneHeaderValue(request.headers.cookie),
|
|
211
|
+
headersFactory: () => normalizeHeaders(cloneRequestHeaders(request.headers)),
|
|
212
|
+
method: request.method,
|
|
213
|
+
path: urlParts.path,
|
|
214
|
+
query: readSimpleQueryRecord(request.query),
|
|
215
|
+
queryFactory: () => parseQueryParamsFromSearch(urlParts.search),
|
|
216
|
+
raw: request.raw,
|
|
217
|
+
requestId: resolvePrimaryRequestIdFromHeaders(request.raw.headers),
|
|
218
|
+
signal: lazySignal.signal,
|
|
219
|
+
url: urlParts.path + urlParts.search
|
|
220
|
+
});
|
|
221
|
+
frameworkRequest.body = request.body;
|
|
222
|
+
frameworkRequest.isAborted = lazySignal.isAborted;
|
|
223
|
+
markNativeFastFrameworkRequest(frameworkRequest);
|
|
224
|
+
return frameworkRequest;
|
|
225
|
+
}
|
|
226
|
+
function createLazyFastifyRequestSignal(reply, signalFactory) {
|
|
227
|
+
let signal;
|
|
228
|
+
return {
|
|
229
|
+
isAborted() {
|
|
230
|
+
return signal?.aborted ?? isFastifyReplyAborted(reply);
|
|
231
|
+
},
|
|
232
|
+
signal() {
|
|
233
|
+
if (!signal) {
|
|
234
|
+
signal = isFastifyReplyAborted(reply) ? AbortSignal.abort(new Error('Response closed before response commit.')) : signalFactory(reply);
|
|
235
|
+
}
|
|
236
|
+
return signal;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function isFastifyReplyAborted(reply) {
|
|
241
|
+
return reply.raw.destroyed && !reply.raw.writableEnded;
|
|
242
|
+
}
|
|
243
|
+
const NATIVE_FAST_FRAMEWORK_REQUEST = Symbol('fluo.fastify.nativeFastFrameworkRequest');
|
|
244
|
+
function markNativeFastFrameworkRequest(request) {
|
|
245
|
+
request[NATIVE_FAST_FRAMEWORK_REQUEST] = true;
|
|
246
|
+
}
|
|
247
|
+
function isNativeFastFrameworkRequest(request) {
|
|
248
|
+
return request[NATIVE_FAST_FRAMEWORK_REQUEST] === true;
|
|
249
|
+
}
|
|
250
|
+
function isFastifyMultipartRequest(request) {
|
|
251
|
+
const probe = request.isMultipart;
|
|
252
|
+
return typeof probe === 'function' && probe.call(request) === true;
|
|
253
|
+
}
|
|
254
|
+
function readSimpleQueryRecord(query) {
|
|
255
|
+
if (typeof query !== 'object' || query === null) {
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
const record = query;
|
|
259
|
+
for (const key in record) {
|
|
260
|
+
if (!Object.prototype.hasOwnProperty.call(record, key)) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
const value = record[key];
|
|
264
|
+
if (typeof value === 'string') {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (Array.isArray(value) && value.every(item => typeof item === 'string')) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
return record;
|
|
151
273
|
}
|
|
152
274
|
function createFastifyRequestResponseFactory(multipartOptions, maxBodySize = DEFAULT_MAX_BODY_SIZE, preserveRawBody = false) {
|
|
153
275
|
return {
|
|
@@ -270,65 +392,71 @@ export async function runFastifyApplication(rootModule, options) {
|
|
|
270
392
|
shutdownRegistration: createNodeShutdownSignalRegistration(options.shutdownSignals ?? defaultNodeShutdownSignals())
|
|
271
393
|
}, adapter);
|
|
272
394
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
reply.header('content-type', serialized.defaultContentType);
|
|
298
|
-
}
|
|
395
|
+
class MutableFastifyFrameworkResponse {
|
|
396
|
+
committed;
|
|
397
|
+
headers = {};
|
|
398
|
+
raw;
|
|
399
|
+
statusCode;
|
|
400
|
+
statusSet = false;
|
|
401
|
+
activeStream;
|
|
402
|
+
constructor(reply) {
|
|
403
|
+
this.reply = reply;
|
|
404
|
+
this.committed = reply.sent;
|
|
405
|
+
this.raw = reply;
|
|
406
|
+
}
|
|
407
|
+
get stream() {
|
|
408
|
+
this.activeStream ??= createFrameworkResponseStream(this.reply);
|
|
409
|
+
return this.activeStream;
|
|
410
|
+
}
|
|
411
|
+
redirect(status, location) {
|
|
412
|
+
this.setStatus(status);
|
|
413
|
+
this.setHeader('Location', location);
|
|
414
|
+
this.committed = true;
|
|
415
|
+
this.reply.redirect(location, status);
|
|
416
|
+
}
|
|
417
|
+
send(body) {
|
|
418
|
+
if (this.reply.sent) {
|
|
299
419
|
this.committed = true;
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const existingContentType = this.reply.getHeader('content-type');
|
|
423
|
+
const serialized = serializeResponseBody(body, typeof existingContentType === 'string' ? existingContentType : undefined);
|
|
424
|
+
if (!this.reply.hasHeader('content-type') && serialized.defaultContentType) {
|
|
425
|
+
this.reply.header('content-type', serialized.defaultContentType);
|
|
426
|
+
}
|
|
427
|
+
this.committed = true;
|
|
428
|
+
void this.reply.send(serialized.payload);
|
|
429
|
+
}
|
|
430
|
+
sendSimpleJson(body) {
|
|
431
|
+
if (this.reply.sent) {
|
|
310
432
|
this.committed = true;
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
this.
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (!this.reply.hasHeader('content-type')) {
|
|
436
|
+
this.reply.header('content-type', 'application/json; charset=utf-8');
|
|
437
|
+
}
|
|
438
|
+
this.committed = true;
|
|
439
|
+
void this.reply.send(JSON.stringify(body));
|
|
440
|
+
}
|
|
441
|
+
setHeader(name, value) {
|
|
442
|
+
const lowerName = name.toLowerCase();
|
|
443
|
+
if (lowerName === 'set-cookie') {
|
|
444
|
+
const merged = mergeSetCookieHeader(this.reply.getHeader(name), value);
|
|
445
|
+
this.reply.header(name, merged);
|
|
446
|
+
this.headers[name] = merged;
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
this.reply.header(name, value);
|
|
450
|
+
this.headers[name] = value;
|
|
451
|
+
}
|
|
452
|
+
setStatus(code) {
|
|
453
|
+
this.reply.status(code);
|
|
454
|
+
this.statusCode = code;
|
|
455
|
+
this.statusSet = true;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function createFrameworkResponse(reply) {
|
|
459
|
+
return new MutableFastifyFrameworkResponse(reply);
|
|
332
460
|
}
|
|
333
461
|
function createFrameworkResponseStream(reply) {
|
|
334
462
|
let hijacked = false;
|
|
@@ -395,11 +523,11 @@ function createDeferredFrameworkRequest(request, signal, multipartOptions, maxBo
|
|
|
395
523
|
const urlParts = splitRawRequestUrl(rawUrl);
|
|
396
524
|
const headerSnapshot = cloneRequestHeaders(request.headers);
|
|
397
525
|
const headers = createMemoizedValue(() => normalizeHeaders(headerSnapshot));
|
|
398
|
-
const
|
|
399
|
-
const cookies = createMemoizedValue(() => parseCookieHeader(cookieHeader));
|
|
400
|
-
const query = createMemoizedValue(() => parseQueryParamsFromSearch(urlParts.search));
|
|
526
|
+
const querySnapshot = snapshotSimpleQueryRecord(request.query);
|
|
401
527
|
const isMultipart = isMultipartRequestContentType(headerSnapshot['content-type']);
|
|
402
|
-
|
|
528
|
+
let frameworkRequest;
|
|
529
|
+
const needsDeferredBodyMaterialization = isMultipart || preserveRawBody;
|
|
530
|
+
const materializeBody = needsDeferredBodyMaterialization ? createMemoizedAsyncValue(async () => {
|
|
403
531
|
let body = request.body;
|
|
404
532
|
let files;
|
|
405
533
|
if (isMultipart) {
|
|
@@ -420,25 +548,23 @@ function createDeferredFrameworkRequest(request, signal, multipartOptions, maxBo
|
|
|
420
548
|
frameworkRequest.rawBody = rawBodyValue;
|
|
421
549
|
}
|
|
422
550
|
}
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
get headers() {
|
|
429
|
-
return headers();
|
|
430
|
-
},
|
|
551
|
+
}) : undefined;
|
|
552
|
+
frameworkRequest = createDeferredFrameworkRequestShell({
|
|
553
|
+
cookieHeader: cloneHeaderValue(headerSnapshot.cookie),
|
|
554
|
+
headersFactory: headers,
|
|
555
|
+
materializeBody,
|
|
431
556
|
method: request.method,
|
|
432
|
-
params: {},
|
|
433
557
|
path: urlParts.path,
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
},
|
|
558
|
+
query: querySnapshot,
|
|
559
|
+
queryFactory: () => parseQueryParamsFromSearch(urlParts.search),
|
|
437
560
|
raw: request.raw,
|
|
561
|
+
requestId: resolvePrimaryRequestIdFromHeaders(headerSnapshot),
|
|
438
562
|
signal,
|
|
439
|
-
url: urlParts.path + urlParts.search
|
|
440
|
-
|
|
441
|
-
|
|
563
|
+
url: urlParts.path + urlParts.search
|
|
564
|
+
});
|
|
565
|
+
if (!needsDeferredBodyMaterialization) {
|
|
566
|
+
frameworkRequest.body = request.body;
|
|
567
|
+
}
|
|
442
568
|
const nativeRouteHandoff = consumeRawRequestNativeRouteHandoff(request.raw);
|
|
443
569
|
return nativeRouteHandoff ? attachFrameworkRequestNativeRouteHandoff(frameworkRequest, nativeRouteHandoff) : frameworkRequest;
|
|
444
570
|
}
|
|
@@ -448,12 +574,29 @@ async function materializeFrameworkRequestBody(request) {
|
|
|
448
574
|
}
|
|
449
575
|
function normalizeNativeRouteParams(params) {
|
|
450
576
|
if (typeof params !== 'object' || params === null) {
|
|
451
|
-
return
|
|
577
|
+
return EMPTY_NATIVE_ROUTE_PARAMS;
|
|
452
578
|
}
|
|
453
|
-
|
|
579
|
+
let normalized;
|
|
580
|
+
for (const key in params) {
|
|
581
|
+
if (!Object.prototype.hasOwnProperty.call(params, key)) {
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
const value = params[key];
|
|
585
|
+
if (value === undefined) {
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
normalized ??= {};
|
|
589
|
+
normalized[key] = typeof value === 'string' ? value : String(value);
|
|
590
|
+
}
|
|
591
|
+
return normalized ?? EMPTY_NATIVE_ROUTE_PARAMS;
|
|
454
592
|
}
|
|
455
593
|
function hasNativeRouteParamSeparators(params) {
|
|
456
|
-
|
|
594
|
+
for (const key in params) {
|
|
595
|
+
if (Object.prototype.hasOwnProperty.call(params, key) && params[key]?.includes('/')) {
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return false;
|
|
457
600
|
}
|
|
458
601
|
function collectVersionSensitiveRouteKeys(descriptors) {
|
|
459
602
|
const grouped = new Map();
|
|
@@ -472,9 +615,6 @@ function collectVersionSensitiveRouteKeys(descriptors) {
|
|
|
472
615
|
}
|
|
473
616
|
return new Set([...grouped.entries()].filter(([, current]) => current.count > 1 || current.hasVersioned).map(([routeKey]) => routeKey));
|
|
474
617
|
}
|
|
475
|
-
function readRequestPathFromRawUrl(rawUrl) {
|
|
476
|
-
return splitRawRequestUrl(rawUrl ?? '/').path;
|
|
477
|
-
}
|
|
478
618
|
async function parseMultipartRequest(request, options = {}) {
|
|
479
619
|
const fields = {};
|
|
480
620
|
const files = [];
|
|
@@ -568,92 +708,9 @@ function normalizeHeaders(headers) {
|
|
|
568
708
|
}
|
|
569
709
|
return normalized;
|
|
570
710
|
}
|
|
571
|
-
function parseQueryParamsFromSearch(search) {
|
|
572
|
-
return parseQueryParams(new URLSearchParams(search));
|
|
573
|
-
}
|
|
574
|
-
function parseQueryParams(searchParams) {
|
|
575
|
-
const query = {};
|
|
576
|
-
for (const [key, value] of searchParams.entries()) {
|
|
577
|
-
const current = query[key];
|
|
578
|
-
if (current === undefined) {
|
|
579
|
-
query[key] = value;
|
|
580
|
-
continue;
|
|
581
|
-
}
|
|
582
|
-
if (Array.isArray(current)) {
|
|
583
|
-
current.push(value);
|
|
584
|
-
continue;
|
|
585
|
-
}
|
|
586
|
-
query[key] = [current, value];
|
|
587
|
-
}
|
|
588
|
-
return query;
|
|
589
|
-
}
|
|
590
|
-
function parseCookieHeader(cookieHeader) {
|
|
591
|
-
const normalizedCookieHeader = Array.isArray(cookieHeader) ? cookieHeader.join('; ') : cookieHeader;
|
|
592
|
-
if (!normalizedCookieHeader) {
|
|
593
|
-
return {};
|
|
594
|
-
}
|
|
595
|
-
return Object.fromEntries(normalizedCookieHeader.split(';').map(pair => pair.trim()).filter(Boolean).map(pair => {
|
|
596
|
-
const index = pair.indexOf('=');
|
|
597
|
-
if (index === -1) {
|
|
598
|
-
return [pair.trim(), ''];
|
|
599
|
-
}
|
|
600
|
-
const rawValue = pair.slice(index + 1).trim();
|
|
601
|
-
try {
|
|
602
|
-
return [pair.slice(0, index).trim(), decodeURIComponent(rawValue)];
|
|
603
|
-
} catch {
|
|
604
|
-
return [pair.slice(0, index).trim(), rawValue];
|
|
605
|
-
}
|
|
606
|
-
}));
|
|
607
|
-
}
|
|
608
|
-
function createMemoizedValue(factory) {
|
|
609
|
-
let initialized = false;
|
|
610
|
-
let value;
|
|
611
|
-
return () => {
|
|
612
|
-
if (!initialized) {
|
|
613
|
-
value = factory();
|
|
614
|
-
initialized = true;
|
|
615
|
-
}
|
|
616
|
-
return value;
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
function createMemoizedAsyncValue(factory) {
|
|
620
|
-
let promise;
|
|
621
|
-
return () => {
|
|
622
|
-
promise ??= factory();
|
|
623
|
-
return promise;
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
function cloneHeaderValue(value) {
|
|
627
|
-
return Array.isArray(value) ? [...value] : value;
|
|
628
|
-
}
|
|
629
711
|
function cloneRequestHeaders(headers) {
|
|
630
712
|
return Object.fromEntries(Object.entries(headers).map(([name, value]) => [name, cloneHeaderValue(value)]));
|
|
631
713
|
}
|
|
632
|
-
function splitRawRequestUrl(rawUrl) {
|
|
633
|
-
if (rawUrl.startsWith('http://') || rawUrl.startsWith('https://')) {
|
|
634
|
-
const url = new URL(rawUrl);
|
|
635
|
-
return {
|
|
636
|
-
path: url.pathname,
|
|
637
|
-
search: url.search
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
const queryStart = rawUrl.indexOf('?');
|
|
641
|
-
const hashStart = rawUrl.indexOf('#');
|
|
642
|
-
const pathEndCandidates = [queryStart, hashStart].filter(index => index >= 0);
|
|
643
|
-
const pathEnd = pathEndCandidates.length > 0 ? Math.min(...pathEndCandidates) : rawUrl.length;
|
|
644
|
-
const path = rawUrl.slice(0, pathEnd) || '/';
|
|
645
|
-
if (queryStart === -1) {
|
|
646
|
-
return {
|
|
647
|
-
path,
|
|
648
|
-
search: ''
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
const searchEnd = hashStart >= 0 && hashStart > queryStart ? hashStart : rawUrl.length;
|
|
652
|
-
return {
|
|
653
|
-
path,
|
|
654
|
-
search: rawUrl.slice(queryStart, searchEnd)
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
714
|
function setMultiValue(target, key, value) {
|
|
658
715
|
const existing = target[key];
|
|
659
716
|
if (existing === undefined) {
|
|
@@ -684,6 +741,10 @@ function resolveRequestIdFromHeaders(headers) {
|
|
|
684
741
|
const requestId = headers['x-request-id'] ?? headers['x-correlation-id'];
|
|
685
742
|
return Array.isArray(requestId) ? requestId[0] : requestId;
|
|
686
743
|
}
|
|
744
|
+
function resolvePrimaryRequestIdFromHeaders(headers) {
|
|
745
|
+
const requestId = headers['x-request-id'];
|
|
746
|
+
return Array.isArray(requestId) ? requestId[0] : requestId;
|
|
747
|
+
}
|
|
687
748
|
function createFastifyApp(httpsOptions, maxBodySize) {
|
|
688
749
|
if (httpsOptions) {
|
|
689
750
|
return fastify({
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"platform",
|
|
9
9
|
"server"
|
|
10
10
|
],
|
|
11
|
-
"version": "1.0.0-beta.
|
|
11
|
+
"version": "1.0.0-beta.8",
|
|
12
12
|
"private": false,
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"repository": {
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"@fastify/multipart": "^9.2.1",
|
|
39
39
|
"fastify": "^5.8.5",
|
|
40
40
|
"fastify-raw-body": "^5.0.0",
|
|
41
|
-
"@fluojs/http": "^1.0.0-beta.
|
|
42
|
-
"@fluojs/runtime": "^1.0.0-beta.
|
|
41
|
+
"@fluojs/http": "^1.0.0-beta.6",
|
|
42
|
+
"@fluojs/runtime": "^1.0.0-beta.8"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"vitest": "^3.2.4",
|