@fluojs/platform-fastify 1.0.0-beta.7 → 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 +221 -71
- 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;AAuBzB,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,gBAAgB;QACxB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;QACvB,OAAO,CAAC,EAAE,UAAU,CAAC;KACtB;CACF;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,0EAA0E;AAC1E,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAC5D,wEAAwE;AACxE,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter.js
CHANGED
|
@@ -19,6 +19,7 @@ import { dispatchWithRequestResponseFactory } from '@fluojs/runtime/internal/req
|
|
|
19
19
|
const DEFAULT_MAX_BODY_SIZE = 1 * 1024 * 1024;
|
|
20
20
|
const DEFAULT_SHUTDOWN_TIMEOUT_MS = 10_000;
|
|
21
21
|
const FASTIFY_NATIVE_ROUTE_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'];
|
|
22
|
+
const EMPTY_NATIVE_ROUTE_PARAMS = Object.freeze({});
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Bootstrap options for creating a Fastify-backed application without
|
|
@@ -104,13 +105,11 @@ export class FastifyHttpApplicationAdapter {
|
|
|
104
105
|
for (const route of createFastifyNativeRoutes(descriptors)) {
|
|
105
106
|
this.app.route({
|
|
106
107
|
handler: async (request, reply) => {
|
|
107
|
-
const
|
|
108
|
+
const urlParts = splitRawRequestUrl(request.raw.url ?? '/');
|
|
108
109
|
const params = normalizeNativeRouteParams(request.params);
|
|
109
|
-
if (!isRoutePathNormalizationSensitive(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
params
|
|
113
|
-
});
|
|
110
|
+
if (!isRoutePathNormalizationSensitive(urlParts.path) && !hasNativeRouteParamSeparators(params)) {
|
|
111
|
+
await this.handleNativeRouteRequest(route.descriptor, params, urlParts, request, reply);
|
|
112
|
+
return;
|
|
114
113
|
}
|
|
115
114
|
await this.handleRequest(request, reply);
|
|
116
115
|
},
|
|
@@ -149,6 +148,128 @@ export class FastifyHttpApplicationAdapter {
|
|
|
149
148
|
rawResponse: reply
|
|
150
149
|
});
|
|
151
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;
|
|
152
273
|
}
|
|
153
274
|
function createFastifyRequestResponseFactory(multipartOptions, maxBodySize = DEFAULT_MAX_BODY_SIZE, preserveRawBody = false) {
|
|
154
275
|
return {
|
|
@@ -271,65 +392,71 @@ export async function runFastifyApplication(rootModule, options) {
|
|
|
271
392
|
shutdownRegistration: createNodeShutdownSignalRegistration(options.shutdownSignals ?? defaultNodeShutdownSignals())
|
|
272
393
|
}, adapter);
|
|
273
394
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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) {
|
|
287
419
|
this.committed = true;
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
this.committed = true;
|
|
301
|
-
await reply.send(serialized.payload);
|
|
302
|
-
},
|
|
303
|
-
async sendSimpleJson(body) {
|
|
304
|
-
if (reply.sent) {
|
|
305
|
-
this.committed = true;
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
if (!reply.hasHeader('content-type')) {
|
|
309
|
-
reply.header('content-type', 'application/json; charset=utf-8');
|
|
310
|
-
}
|
|
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) {
|
|
311
432
|
this.committed = true;
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
this.
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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);
|
|
333
460
|
}
|
|
334
461
|
function createFrameworkResponseStream(reply) {
|
|
335
462
|
let hijacked = false;
|
|
@@ -399,7 +526,8 @@ function createDeferredFrameworkRequest(request, signal, multipartOptions, maxBo
|
|
|
399
526
|
const querySnapshot = snapshotSimpleQueryRecord(request.query);
|
|
400
527
|
const isMultipart = isMultipartRequestContentType(headerSnapshot['content-type']);
|
|
401
528
|
let frameworkRequest;
|
|
402
|
-
const
|
|
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,7 +548,7 @@ function createDeferredFrameworkRequest(request, signal, multipartOptions, maxBo
|
|
|
420
548
|
frameworkRequest.rawBody = rawBodyValue;
|
|
421
549
|
}
|
|
422
550
|
}
|
|
423
|
-
});
|
|
551
|
+
}) : undefined;
|
|
424
552
|
frameworkRequest = createDeferredFrameworkRequestShell({
|
|
425
553
|
cookieHeader: cloneHeaderValue(headerSnapshot.cookie),
|
|
426
554
|
headersFactory: headers,
|
|
@@ -430,9 +558,13 @@ function createDeferredFrameworkRequest(request, signal, multipartOptions, maxBo
|
|
|
430
558
|
query: querySnapshot,
|
|
431
559
|
queryFactory: () => parseQueryParamsFromSearch(urlParts.search),
|
|
432
560
|
raw: request.raw,
|
|
561
|
+
requestId: resolvePrimaryRequestIdFromHeaders(headerSnapshot),
|
|
433
562
|
signal,
|
|
434
563
|
url: urlParts.path + urlParts.search
|
|
435
564
|
});
|
|
565
|
+
if (!needsDeferredBodyMaterialization) {
|
|
566
|
+
frameworkRequest.body = request.body;
|
|
567
|
+
}
|
|
436
568
|
const nativeRouteHandoff = consumeRawRequestNativeRouteHandoff(request.raw);
|
|
437
569
|
return nativeRouteHandoff ? attachFrameworkRequestNativeRouteHandoff(frameworkRequest, nativeRouteHandoff) : frameworkRequest;
|
|
438
570
|
}
|
|
@@ -442,12 +574,29 @@ async function materializeFrameworkRequestBody(request) {
|
|
|
442
574
|
}
|
|
443
575
|
function normalizeNativeRouteParams(params) {
|
|
444
576
|
if (typeof params !== 'object' || params === null) {
|
|
445
|
-
return
|
|
577
|
+
return EMPTY_NATIVE_ROUTE_PARAMS;
|
|
578
|
+
}
|
|
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);
|
|
446
590
|
}
|
|
447
|
-
return
|
|
591
|
+
return normalized ?? EMPTY_NATIVE_ROUTE_PARAMS;
|
|
448
592
|
}
|
|
449
593
|
function hasNativeRouteParamSeparators(params) {
|
|
450
|
-
|
|
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;
|
|
451
600
|
}
|
|
452
601
|
function collectVersionSensitiveRouteKeys(descriptors) {
|
|
453
602
|
const grouped = new Map();
|
|
@@ -466,9 +615,6 @@ function collectVersionSensitiveRouteKeys(descriptors) {
|
|
|
466
615
|
}
|
|
467
616
|
return new Set([...grouped.entries()].filter(([, current]) => current.count > 1 || current.hasVersioned).map(([routeKey]) => routeKey));
|
|
468
617
|
}
|
|
469
|
-
function readRequestPathFromRawUrl(rawUrl) {
|
|
470
|
-
return splitRawRequestUrl(rawUrl ?? '/').path;
|
|
471
|
-
}
|
|
472
618
|
async function parseMultipartRequest(request, options = {}) {
|
|
473
619
|
const fields = {};
|
|
474
620
|
const files = [];
|
|
@@ -595,6 +741,10 @@ function resolveRequestIdFromHeaders(headers) {
|
|
|
595
741
|
const requestId = headers['x-request-id'] ?? headers['x-correlation-id'];
|
|
596
742
|
return Array.isArray(requestId) ? requestId[0] : requestId;
|
|
597
743
|
}
|
|
744
|
+
function resolvePrimaryRequestIdFromHeaders(headers) {
|
|
745
|
+
const requestId = headers['x-request-id'];
|
|
746
|
+
return Array.isArray(requestId) ? requestId[0] : requestId;
|
|
747
|
+
}
|
|
598
748
|
function createFastifyApp(httpsOptions, maxBodySize) {
|
|
599
749
|
if (httpsOptions) {
|
|
600
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",
|