@mokup/runtime 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/README.zh-CN.md +7 -0
- package/dist/index.cjs +233 -179
- package/dist/index.d.cts +6 -13
- package/dist/index.d.mts +6 -13
- package/dist/index.d.ts +6 -13
- package/dist/index.mjs +233 -179
- package/package.json +5 -2
package/README.md
ADDED
package/README.zh-CN.md
ADDED
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const hono = require('hono');
|
|
4
|
+
const patternRouter = require('hono/router/pattern-router');
|
|
5
|
+
|
|
3
6
|
const paramNamePattern = /^[\w-]+$/;
|
|
4
7
|
const paramPattern = /^\[([^\]/]+)\]$/;
|
|
5
8
|
const catchallPattern = /^\[\.\.\.([^\]/]+)\]$/;
|
|
@@ -227,14 +230,14 @@ function normalizeRules(value) {
|
|
|
227
230
|
}
|
|
228
231
|
];
|
|
229
232
|
}
|
|
230
|
-
async function executeRule(rule,
|
|
233
|
+
async function executeRule(rule, context) {
|
|
231
234
|
if (!rule) {
|
|
232
235
|
return void 0;
|
|
233
236
|
}
|
|
234
237
|
const value = rule.response;
|
|
235
238
|
if (typeof value === "function") {
|
|
236
239
|
const handler = value;
|
|
237
|
-
return handler(
|
|
240
|
+
return handler(context);
|
|
238
241
|
}
|
|
239
242
|
return value;
|
|
240
243
|
}
|
|
@@ -340,108 +343,153 @@ function normalizeMethod(method) {
|
|
|
340
343
|
}
|
|
341
344
|
return void 0;
|
|
342
345
|
}
|
|
343
|
-
function mergeHeaders(base, override) {
|
|
344
|
-
if (!override) {
|
|
345
|
-
return base;
|
|
346
|
-
}
|
|
347
|
-
return {
|
|
348
|
-
...base,
|
|
349
|
-
...override
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
346
|
function delay(ms) {
|
|
353
347
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
354
348
|
}
|
|
355
349
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
getHeader(key) {
|
|
363
|
-
return this.headers.get(key.toLowerCase());
|
|
364
|
-
}
|
|
365
|
-
removeHeader(key) {
|
|
366
|
-
this.headers.delete(key.toLowerCase());
|
|
367
|
-
}
|
|
368
|
-
toRecord() {
|
|
369
|
-
const record = {};
|
|
370
|
-
for (const [key, value] of this.headers.entries()) {
|
|
371
|
-
record[key] = value;
|
|
350
|
+
function decodeBase64(value) {
|
|
351
|
+
if (typeof atob === "function") {
|
|
352
|
+
const binary = atob(value);
|
|
353
|
+
const bytes = new Uint8Array(binary.length);
|
|
354
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
355
|
+
bytes[i] = binary.charCodeAt(i);
|
|
372
356
|
}
|
|
373
|
-
return
|
|
357
|
+
return bytes;
|
|
374
358
|
}
|
|
359
|
+
throw new Error("Base64 decoding is not supported in this runtime.");
|
|
375
360
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if (typeof body === "string") {
|
|
381
|
-
return {
|
|
382
|
-
body,
|
|
383
|
-
contentType: contentType ?? "text/plain; charset=utf-8"
|
|
384
|
-
};
|
|
361
|
+
|
|
362
|
+
function toHonoPath(tokens) {
|
|
363
|
+
if (!tokens || tokens.length === 0) {
|
|
364
|
+
return "/";
|
|
385
365
|
}
|
|
386
|
-
|
|
366
|
+
const segments = tokens.map((token) => {
|
|
367
|
+
if (token.type === "static") {
|
|
368
|
+
return token.value;
|
|
369
|
+
}
|
|
370
|
+
if (token.type === "param") {
|
|
371
|
+
return `:${token.name}`;
|
|
372
|
+
}
|
|
373
|
+
if (token.type === "catchall") {
|
|
374
|
+
return `:${token.name}{.+}`;
|
|
375
|
+
}
|
|
376
|
+
return `:${token.name}{.+}?`;
|
|
377
|
+
});
|
|
378
|
+
return `/${segments.join("/")}`;
|
|
379
|
+
}
|
|
380
|
+
function compileRoutes(manifest) {
|
|
381
|
+
const compiled = [];
|
|
382
|
+
for (const route of manifest.routes) {
|
|
383
|
+
const method = normalizeMethod(route.method) ?? "GET";
|
|
384
|
+
const parsed = route.tokens ? {
|
|
385
|
+
tokens: route.tokens,
|
|
386
|
+
score: route.score ?? scoreRouteTokens(route.tokens),
|
|
387
|
+
errors: []
|
|
388
|
+
} : parseRouteTemplate(route.url);
|
|
389
|
+
if (parsed.errors.length > 0) {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
compiled.push({
|
|
393
|
+
route,
|
|
394
|
+
method,
|
|
395
|
+
tokens: route.tokens ?? parsed.tokens,
|
|
396
|
+
score: route.score ?? parsed.score
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
return compiled.sort((a, b) => {
|
|
400
|
+
if (a.method !== b.method) {
|
|
401
|
+
return a.method.localeCompare(b.method);
|
|
402
|
+
}
|
|
403
|
+
return compareRouteScore(a.score, b.score);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
function shouldTreatAsText(contentType) {
|
|
407
|
+
const normalized = contentType.toLowerCase();
|
|
408
|
+
return normalized.startsWith("text/") || normalized.includes("json") || normalized.includes("xml") || normalized.includes("javascript");
|
|
409
|
+
}
|
|
410
|
+
async function toRuntimeResult(response) {
|
|
411
|
+
const headers = {};
|
|
412
|
+
response.headers.forEach((value, key) => {
|
|
413
|
+
headers[key.toLowerCase()] = value;
|
|
414
|
+
});
|
|
415
|
+
if (!response.body || [204, 205, 304].includes(response.status)) {
|
|
387
416
|
return {
|
|
388
|
-
|
|
389
|
-
|
|
417
|
+
status: response.status,
|
|
418
|
+
headers,
|
|
419
|
+
body: null
|
|
390
420
|
};
|
|
391
421
|
}
|
|
392
|
-
|
|
422
|
+
const contentType = headers["content-type"] ?? "";
|
|
423
|
+
if (shouldTreatAsText(contentType)) {
|
|
393
424
|
return {
|
|
394
|
-
|
|
395
|
-
|
|
425
|
+
status: response.status,
|
|
426
|
+
headers,
|
|
427
|
+
body: await response.text()
|
|
396
428
|
};
|
|
397
429
|
}
|
|
430
|
+
const buffer = new Uint8Array(await response.arrayBuffer());
|
|
398
431
|
return {
|
|
399
|
-
|
|
400
|
-
|
|
432
|
+
status: response.status,
|
|
433
|
+
headers,
|
|
434
|
+
body: buffer
|
|
401
435
|
};
|
|
402
436
|
}
|
|
403
|
-
function
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
for (
|
|
408
|
-
|
|
437
|
+
function applyRouteOverrides(response, route) {
|
|
438
|
+
const headers = new Headers(response.headers);
|
|
439
|
+
const hasHeaders = !!route.headers && Object.keys(route.headers).length > 0;
|
|
440
|
+
if (route.headers) {
|
|
441
|
+
for (const [key, value] of Object.entries(route.headers)) {
|
|
442
|
+
headers.set(key, value);
|
|
409
443
|
}
|
|
410
|
-
return bytes;
|
|
411
444
|
}
|
|
412
|
-
|
|
445
|
+
const status = route.status ?? response.status;
|
|
446
|
+
if (status === response.status && !hasHeaders) {
|
|
447
|
+
return response;
|
|
448
|
+
}
|
|
449
|
+
return new Response(response.body, { status, headers });
|
|
413
450
|
}
|
|
414
|
-
function
|
|
415
|
-
if (
|
|
416
|
-
return
|
|
451
|
+
function normalizeHandlerValue(c, value) {
|
|
452
|
+
if (value instanceof Response) {
|
|
453
|
+
return value;
|
|
454
|
+
}
|
|
455
|
+
if (typeof value === "undefined") {
|
|
456
|
+
const response = c.body(null);
|
|
457
|
+
if (response.status === 200) {
|
|
458
|
+
return new Response(response.body, {
|
|
459
|
+
status: 204,
|
|
460
|
+
headers: response.headers
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
return response;
|
|
464
|
+
}
|
|
465
|
+
if (typeof value === "string") {
|
|
466
|
+
return c.text(value);
|
|
417
467
|
}
|
|
418
|
-
|
|
468
|
+
if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
|
|
469
|
+
if (!c.res.headers.get("content-type")) {
|
|
470
|
+
c.header("content-type", "application/octet-stream");
|
|
471
|
+
}
|
|
472
|
+
const data = value instanceof ArrayBuffer ? value : new Uint8Array(value);
|
|
473
|
+
return c.body(data);
|
|
474
|
+
}
|
|
475
|
+
return c.json(value);
|
|
419
476
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const ctx = {
|
|
424
|
-
delay,
|
|
425
|
-
json: (data) => data
|
|
426
|
-
};
|
|
427
|
-
const runHandler = async () => {
|
|
477
|
+
function createRouteHandler(params) {
|
|
478
|
+
const { route, moduleCache, moduleBase, moduleMap } = params;
|
|
479
|
+
return async (c) => {
|
|
428
480
|
if (route.response.type === "json") {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
481
|
+
if (typeof route.response.body === "undefined") {
|
|
482
|
+
return normalizeHandlerValue(c, void 0);
|
|
483
|
+
}
|
|
484
|
+
return c.json(route.response.body);
|
|
433
485
|
}
|
|
434
486
|
if (route.response.type === "text") {
|
|
435
|
-
return
|
|
436
|
-
value: route.response.body,
|
|
437
|
-
contentType: "text/plain; charset=utf-8"
|
|
438
|
-
};
|
|
487
|
+
return c.text(route.response.body);
|
|
439
488
|
}
|
|
440
489
|
if (route.response.type === "binary") {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
};
|
|
490
|
+
const data = new Uint8Array(decodeBase64(route.response.body));
|
|
491
|
+
c.header("content-type", "application/octet-stream");
|
|
492
|
+
return c.body(data);
|
|
445
493
|
}
|
|
446
494
|
const rule = await loadModuleRule(
|
|
447
495
|
route.response,
|
|
@@ -449,75 +497,106 @@ async function executeRoute(route, req, moduleCache, middlewareCache, moduleBase
|
|
|
449
497
|
moduleBase,
|
|
450
498
|
moduleMap
|
|
451
499
|
);
|
|
452
|
-
const value = await executeRule(rule,
|
|
453
|
-
return
|
|
500
|
+
const value = await executeRule(rule, c);
|
|
501
|
+
return normalizeHandlerValue(c, value);
|
|
454
502
|
};
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return runHandler();
|
|
465
|
-
}
|
|
466
|
-
let nextResult;
|
|
467
|
-
const next = async () => {
|
|
468
|
-
nextResult = await dispatch(index + 1);
|
|
469
|
-
return nextResult.value;
|
|
470
|
-
};
|
|
471
|
-
const value = await handler(req, responder, ctx, next);
|
|
472
|
-
if (typeof value !== "undefined") {
|
|
473
|
-
return { value };
|
|
474
|
-
}
|
|
475
|
-
if (nextResult) {
|
|
476
|
-
return nextResult;
|
|
477
|
-
}
|
|
478
|
-
return { value: void 0 };
|
|
479
|
-
};
|
|
480
|
-
return dispatch(0);
|
|
503
|
+
}
|
|
504
|
+
function createFinalizeMiddleware(route) {
|
|
505
|
+
return async (c, next) => {
|
|
506
|
+
const response = await next();
|
|
507
|
+
const resolved = response ?? c.res;
|
|
508
|
+
if (route.delay && route.delay > 0) {
|
|
509
|
+
await delay(route.delay);
|
|
510
|
+
}
|
|
511
|
+
return applyRouteOverrides(resolved, route);
|
|
481
512
|
};
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
513
|
+
}
|
|
514
|
+
async function buildApp(params) {
|
|
515
|
+
const { manifest, moduleCache, middlewareCache, moduleBase, moduleMap } = params;
|
|
516
|
+
const app = new hono.Hono({ router: new patternRouter.PatternRouter(), strict: false });
|
|
517
|
+
const compiled = compileRoutes(manifest);
|
|
518
|
+
for (const entry of compiled) {
|
|
519
|
+
const middlewares = [];
|
|
520
|
+
for (const middleware of entry.route.middleware ?? []) {
|
|
521
|
+
const handler2 = await loadModuleMiddleware(
|
|
522
|
+
middleware,
|
|
523
|
+
middlewareCache,
|
|
524
|
+
moduleBase,
|
|
525
|
+
moduleMap
|
|
526
|
+
);
|
|
527
|
+
if (handler2) {
|
|
528
|
+
middlewares.push(handler2);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
const handler = createRouteHandler({
|
|
532
|
+
route: entry.route,
|
|
533
|
+
moduleCache,
|
|
534
|
+
...typeof moduleBase !== "undefined" ? { moduleBase } : {},
|
|
535
|
+
...typeof moduleMap !== "undefined" ? { moduleMap } : {}
|
|
536
|
+
});
|
|
537
|
+
app.on(
|
|
538
|
+
entry.method,
|
|
539
|
+
toHonoPath(entry.tokens),
|
|
540
|
+
createFinalizeMiddleware(entry.route),
|
|
541
|
+
...middlewares,
|
|
542
|
+
handler
|
|
489
543
|
);
|
|
490
|
-
|
|
491
|
-
|
|
544
|
+
}
|
|
545
|
+
return app;
|
|
546
|
+
}
|
|
547
|
+
function appendQueryParams(url, query) {
|
|
548
|
+
for (const [key, value] of Object.entries(query)) {
|
|
549
|
+
if (Array.isArray(value)) {
|
|
550
|
+
for (const entry of value) {
|
|
551
|
+
url.searchParams.append(key, entry);
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
url.searchParams.append(key, value);
|
|
492
555
|
}
|
|
493
556
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
await delay(route.delay);
|
|
557
|
+
}
|
|
558
|
+
function resolveRequestBody(req, contentType) {
|
|
559
|
+
if (typeof req.rawBody !== "undefined") {
|
|
560
|
+
return req.rawBody;
|
|
499
561
|
}
|
|
500
|
-
const
|
|
501
|
-
if (
|
|
502
|
-
|
|
562
|
+
const body = req.body;
|
|
563
|
+
if (typeof body === "undefined") {
|
|
564
|
+
return void 0;
|
|
503
565
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
route.status ?? responder.statusCode,
|
|
507
|
-
normalized.body
|
|
508
|
-
);
|
|
509
|
-
if (normalized.contentType && !headers["content-type"]) {
|
|
510
|
-
headers["content-type"] = normalized.contentType;
|
|
566
|
+
if (typeof body === "string") {
|
|
567
|
+
return body;
|
|
511
568
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
569
|
+
if (body instanceof Uint8Array || body instanceof ArrayBuffer) {
|
|
570
|
+
return body;
|
|
571
|
+
}
|
|
572
|
+
if (typeof body === "object") {
|
|
573
|
+
const normalized = contentType.toLowerCase();
|
|
574
|
+
if (normalized.includes("json")) {
|
|
575
|
+
return JSON.stringify(body);
|
|
576
|
+
}
|
|
577
|
+
return JSON.stringify(body);
|
|
578
|
+
}
|
|
579
|
+
return String(body);
|
|
580
|
+
}
|
|
581
|
+
function toFetchRequest(req) {
|
|
582
|
+
const url = new URL(req.path, "http://mokup.local");
|
|
583
|
+
appendQueryParams(url, req.query);
|
|
584
|
+
const headers = new Headers();
|
|
585
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
586
|
+
headers.set(key, value);
|
|
587
|
+
}
|
|
588
|
+
const method = normalizeMethod(req.method) ?? "GET";
|
|
589
|
+
const contentType = headers.get("content-type") ?? "";
|
|
590
|
+
const body = resolveRequestBody(req, contentType);
|
|
591
|
+
const init = { method, headers };
|
|
592
|
+
if (typeof body !== "undefined" && method !== "GET" && method !== "HEAD") {
|
|
593
|
+
init.body = body;
|
|
594
|
+
}
|
|
595
|
+
return new Request(url.toString(), init);
|
|
517
596
|
}
|
|
518
597
|
function createRuntime(options) {
|
|
519
598
|
let manifestCache = null;
|
|
520
|
-
let
|
|
599
|
+
let appPromise = null;
|
|
521
600
|
const moduleCache = /* @__PURE__ */ new Map();
|
|
522
601
|
const middlewareCache = /* @__PURE__ */ new Map();
|
|
523
602
|
const getManifest = async () => {
|
|
@@ -526,56 +605,31 @@ function createRuntime(options) {
|
|
|
526
605
|
}
|
|
527
606
|
return manifestCache;
|
|
528
607
|
};
|
|
529
|
-
const
|
|
530
|
-
if (
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
const tokens = route.tokens ?? parsed.tokens;
|
|
542
|
-
const score = route.score ?? parsed.score;
|
|
543
|
-
const list = map.get(method) ?? [];
|
|
544
|
-
list.push({ route, tokens, score });
|
|
545
|
-
map.set(method, list);
|
|
546
|
-
}
|
|
547
|
-
for (const list of map.values()) {
|
|
548
|
-
list.sort((a, b) => compareRouteScore(a.score, b.score));
|
|
608
|
+
const getApp = async () => {
|
|
609
|
+
if (!appPromise) {
|
|
610
|
+
appPromise = (async () => {
|
|
611
|
+
const manifest = await getManifest();
|
|
612
|
+
return buildApp({
|
|
613
|
+
manifest,
|
|
614
|
+
moduleCache,
|
|
615
|
+
middlewareCache,
|
|
616
|
+
...typeof options.moduleBase !== "undefined" ? { moduleBase: options.moduleBase } : {},
|
|
617
|
+
...typeof options.moduleMap !== "undefined" ? { moduleMap: options.moduleMap } : {}
|
|
618
|
+
});
|
|
619
|
+
})();
|
|
549
620
|
}
|
|
550
|
-
|
|
551
|
-
return compiledRoutes;
|
|
621
|
+
return appPromise;
|
|
552
622
|
};
|
|
553
623
|
const handle = async (req) => {
|
|
624
|
+
const app = await getApp();
|
|
554
625
|
const method = normalizeMethod(req.method) ?? "GET";
|
|
555
|
-
const
|
|
556
|
-
const
|
|
557
|
-
if (!
|
|
626
|
+
const matchMethod = method === "HEAD" ? "GET" : method;
|
|
627
|
+
const match = app.router.match(matchMethod, req.path);
|
|
628
|
+
if (!match || match[0].length === 0) {
|
|
558
629
|
return null;
|
|
559
630
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
if (!matched) {
|
|
563
|
-
continue;
|
|
564
|
-
}
|
|
565
|
-
const requestWithParams = {
|
|
566
|
-
...req,
|
|
567
|
-
params: matched.params
|
|
568
|
-
};
|
|
569
|
-
return executeRoute(
|
|
570
|
-
entry.route,
|
|
571
|
-
requestWithParams,
|
|
572
|
-
moduleCache,
|
|
573
|
-
middlewareCache,
|
|
574
|
-
options.moduleBase,
|
|
575
|
-
options.moduleMap
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
return null;
|
|
631
|
+
const response = await app.fetch(toFetchRequest(req));
|
|
632
|
+
return await toRuntimeResult(response);
|
|
579
633
|
};
|
|
580
634
|
return {
|
|
581
635
|
handle
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Context, MiddlewareHandler } from 'hono';
|
|
2
|
+
|
|
1
3
|
type RouteToken = {
|
|
2
4
|
type: 'static';
|
|
3
5
|
value: string;
|
|
@@ -75,18 +77,9 @@ interface RuntimeResult {
|
|
|
75
77
|
headers: Record<string, string>;
|
|
76
78
|
body: string | Uint8Array | null;
|
|
77
79
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
interface MockResponder {
|
|
83
|
-
statusCode: number;
|
|
84
|
-
setHeader: (key: string, value: string) => void;
|
|
85
|
-
getHeader: (key: string) => string | undefined;
|
|
86
|
-
removeHeader: (key: string) => void;
|
|
87
|
-
}
|
|
88
|
-
type MockMiddleware = (req: RuntimeRequest, res: MockResponder, ctx: MockContext, next: () => Promise<unknown>) => unknown | Promise<unknown>;
|
|
89
|
-
type MockResponseHandler = (req: RuntimeRequest, res: MockResponder, ctx: MockContext) => unknown | Promise<unknown>;
|
|
80
|
+
type MockContext = Context;
|
|
81
|
+
type MockMiddleware = MiddlewareHandler;
|
|
82
|
+
type MockResponseHandler = (context: Context) => Response | Promise<Response> | unknown;
|
|
90
83
|
interface RuntimeOptions {
|
|
91
84
|
manifest: Manifest | (() => Promise<Manifest>);
|
|
92
85
|
moduleBase?: string | URL;
|
|
@@ -99,4 +92,4 @@ declare function createRuntime(options: RuntimeOptions): {
|
|
|
99
92
|
};
|
|
100
93
|
|
|
101
94
|
export { compareRouteScore, createRuntime, matchRouteTokens, normalizePathname, parseRouteTemplate, scoreRouteTokens };
|
|
102
|
-
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute, MockContext, MockMiddleware,
|
|
95
|
+
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute, MockContext, MockMiddleware, MockResponseHandler, ModuleMap, ParsedRouteTemplate, RouteToken, RuntimeOptions, RuntimeRequest, RuntimeResult };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Context, MiddlewareHandler } from 'hono';
|
|
2
|
+
|
|
1
3
|
type RouteToken = {
|
|
2
4
|
type: 'static';
|
|
3
5
|
value: string;
|
|
@@ -75,18 +77,9 @@ interface RuntimeResult {
|
|
|
75
77
|
headers: Record<string, string>;
|
|
76
78
|
body: string | Uint8Array | null;
|
|
77
79
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
interface MockResponder {
|
|
83
|
-
statusCode: number;
|
|
84
|
-
setHeader: (key: string, value: string) => void;
|
|
85
|
-
getHeader: (key: string) => string | undefined;
|
|
86
|
-
removeHeader: (key: string) => void;
|
|
87
|
-
}
|
|
88
|
-
type MockMiddleware = (req: RuntimeRequest, res: MockResponder, ctx: MockContext, next: () => Promise<unknown>) => unknown | Promise<unknown>;
|
|
89
|
-
type MockResponseHandler = (req: RuntimeRequest, res: MockResponder, ctx: MockContext) => unknown | Promise<unknown>;
|
|
80
|
+
type MockContext = Context;
|
|
81
|
+
type MockMiddleware = MiddlewareHandler;
|
|
82
|
+
type MockResponseHandler = (context: Context) => Response | Promise<Response> | unknown;
|
|
90
83
|
interface RuntimeOptions {
|
|
91
84
|
manifest: Manifest | (() => Promise<Manifest>);
|
|
92
85
|
moduleBase?: string | URL;
|
|
@@ -99,4 +92,4 @@ declare function createRuntime(options: RuntimeOptions): {
|
|
|
99
92
|
};
|
|
100
93
|
|
|
101
94
|
export { compareRouteScore, createRuntime, matchRouteTokens, normalizePathname, parseRouteTemplate, scoreRouteTokens };
|
|
102
|
-
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute, MockContext, MockMiddleware,
|
|
95
|
+
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute, MockContext, MockMiddleware, MockResponseHandler, ModuleMap, ParsedRouteTemplate, RouteToken, RuntimeOptions, RuntimeRequest, RuntimeResult };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Context, MiddlewareHandler } from 'hono';
|
|
2
|
+
|
|
1
3
|
type RouteToken = {
|
|
2
4
|
type: 'static';
|
|
3
5
|
value: string;
|
|
@@ -75,18 +77,9 @@ interface RuntimeResult {
|
|
|
75
77
|
headers: Record<string, string>;
|
|
76
78
|
body: string | Uint8Array | null;
|
|
77
79
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
interface MockResponder {
|
|
83
|
-
statusCode: number;
|
|
84
|
-
setHeader: (key: string, value: string) => void;
|
|
85
|
-
getHeader: (key: string) => string | undefined;
|
|
86
|
-
removeHeader: (key: string) => void;
|
|
87
|
-
}
|
|
88
|
-
type MockMiddleware = (req: RuntimeRequest, res: MockResponder, ctx: MockContext, next: () => Promise<unknown>) => unknown | Promise<unknown>;
|
|
89
|
-
type MockResponseHandler = (req: RuntimeRequest, res: MockResponder, ctx: MockContext) => unknown | Promise<unknown>;
|
|
80
|
+
type MockContext = Context;
|
|
81
|
+
type MockMiddleware = MiddlewareHandler;
|
|
82
|
+
type MockResponseHandler = (context: Context) => Response | Promise<Response> | unknown;
|
|
90
83
|
interface RuntimeOptions {
|
|
91
84
|
manifest: Manifest | (() => Promise<Manifest>);
|
|
92
85
|
moduleBase?: string | URL;
|
|
@@ -99,4 +92,4 @@ declare function createRuntime(options: RuntimeOptions): {
|
|
|
99
92
|
};
|
|
100
93
|
|
|
101
94
|
export { compareRouteScore, createRuntime, matchRouteTokens, normalizePathname, parseRouteTemplate, scoreRouteTokens };
|
|
102
|
-
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute, MockContext, MockMiddleware,
|
|
95
|
+
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute, MockContext, MockMiddleware, MockResponseHandler, ModuleMap, ParsedRouteTemplate, RouteToken, RuntimeOptions, RuntimeRequest, RuntimeResult };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { PatternRouter } from 'hono/router/pattern-router';
|
|
3
|
+
|
|
1
4
|
const paramNamePattern = /^[\w-]+$/;
|
|
2
5
|
const paramPattern = /^\[([^\]/]+)\]$/;
|
|
3
6
|
const catchallPattern = /^\[\.\.\.([^\]/]+)\]$/;
|
|
@@ -225,14 +228,14 @@ function normalizeRules(value) {
|
|
|
225
228
|
}
|
|
226
229
|
];
|
|
227
230
|
}
|
|
228
|
-
async function executeRule(rule,
|
|
231
|
+
async function executeRule(rule, context) {
|
|
229
232
|
if (!rule) {
|
|
230
233
|
return void 0;
|
|
231
234
|
}
|
|
232
235
|
const value = rule.response;
|
|
233
236
|
if (typeof value === "function") {
|
|
234
237
|
const handler = value;
|
|
235
|
-
return handler(
|
|
238
|
+
return handler(context);
|
|
236
239
|
}
|
|
237
240
|
return value;
|
|
238
241
|
}
|
|
@@ -338,108 +341,153 @@ function normalizeMethod(method) {
|
|
|
338
341
|
}
|
|
339
342
|
return void 0;
|
|
340
343
|
}
|
|
341
|
-
function mergeHeaders(base, override) {
|
|
342
|
-
if (!override) {
|
|
343
|
-
return base;
|
|
344
|
-
}
|
|
345
|
-
return {
|
|
346
|
-
...base,
|
|
347
|
-
...override
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
344
|
function delay(ms) {
|
|
351
345
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
352
346
|
}
|
|
353
347
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
getHeader(key) {
|
|
361
|
-
return this.headers.get(key.toLowerCase());
|
|
362
|
-
}
|
|
363
|
-
removeHeader(key) {
|
|
364
|
-
this.headers.delete(key.toLowerCase());
|
|
365
|
-
}
|
|
366
|
-
toRecord() {
|
|
367
|
-
const record = {};
|
|
368
|
-
for (const [key, value] of this.headers.entries()) {
|
|
369
|
-
record[key] = value;
|
|
348
|
+
function decodeBase64(value) {
|
|
349
|
+
if (typeof atob === "function") {
|
|
350
|
+
const binary = atob(value);
|
|
351
|
+
const bytes = new Uint8Array(binary.length);
|
|
352
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
353
|
+
bytes[i] = binary.charCodeAt(i);
|
|
370
354
|
}
|
|
371
|
-
return
|
|
355
|
+
return bytes;
|
|
372
356
|
}
|
|
357
|
+
throw new Error("Base64 decoding is not supported in this runtime.");
|
|
373
358
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (typeof body === "string") {
|
|
379
|
-
return {
|
|
380
|
-
body,
|
|
381
|
-
contentType: contentType ?? "text/plain; charset=utf-8"
|
|
382
|
-
};
|
|
359
|
+
|
|
360
|
+
function toHonoPath(tokens) {
|
|
361
|
+
if (!tokens || tokens.length === 0) {
|
|
362
|
+
return "/";
|
|
383
363
|
}
|
|
384
|
-
|
|
364
|
+
const segments = tokens.map((token) => {
|
|
365
|
+
if (token.type === "static") {
|
|
366
|
+
return token.value;
|
|
367
|
+
}
|
|
368
|
+
if (token.type === "param") {
|
|
369
|
+
return `:${token.name}`;
|
|
370
|
+
}
|
|
371
|
+
if (token.type === "catchall") {
|
|
372
|
+
return `:${token.name}{.+}`;
|
|
373
|
+
}
|
|
374
|
+
return `:${token.name}{.+}?`;
|
|
375
|
+
});
|
|
376
|
+
return `/${segments.join("/")}`;
|
|
377
|
+
}
|
|
378
|
+
function compileRoutes(manifest) {
|
|
379
|
+
const compiled = [];
|
|
380
|
+
for (const route of manifest.routes) {
|
|
381
|
+
const method = normalizeMethod(route.method) ?? "GET";
|
|
382
|
+
const parsed = route.tokens ? {
|
|
383
|
+
tokens: route.tokens,
|
|
384
|
+
score: route.score ?? scoreRouteTokens(route.tokens),
|
|
385
|
+
errors: []
|
|
386
|
+
} : parseRouteTemplate(route.url);
|
|
387
|
+
if (parsed.errors.length > 0) {
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
compiled.push({
|
|
391
|
+
route,
|
|
392
|
+
method,
|
|
393
|
+
tokens: route.tokens ?? parsed.tokens,
|
|
394
|
+
score: route.score ?? parsed.score
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
return compiled.sort((a, b) => {
|
|
398
|
+
if (a.method !== b.method) {
|
|
399
|
+
return a.method.localeCompare(b.method);
|
|
400
|
+
}
|
|
401
|
+
return compareRouteScore(a.score, b.score);
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
function shouldTreatAsText(contentType) {
|
|
405
|
+
const normalized = contentType.toLowerCase();
|
|
406
|
+
return normalized.startsWith("text/") || normalized.includes("json") || normalized.includes("xml") || normalized.includes("javascript");
|
|
407
|
+
}
|
|
408
|
+
async function toRuntimeResult(response) {
|
|
409
|
+
const headers = {};
|
|
410
|
+
response.headers.forEach((value, key) => {
|
|
411
|
+
headers[key.toLowerCase()] = value;
|
|
412
|
+
});
|
|
413
|
+
if (!response.body || [204, 205, 304].includes(response.status)) {
|
|
385
414
|
return {
|
|
386
|
-
|
|
387
|
-
|
|
415
|
+
status: response.status,
|
|
416
|
+
headers,
|
|
417
|
+
body: null
|
|
388
418
|
};
|
|
389
419
|
}
|
|
390
|
-
|
|
420
|
+
const contentType = headers["content-type"] ?? "";
|
|
421
|
+
if (shouldTreatAsText(contentType)) {
|
|
391
422
|
return {
|
|
392
|
-
|
|
393
|
-
|
|
423
|
+
status: response.status,
|
|
424
|
+
headers,
|
|
425
|
+
body: await response.text()
|
|
394
426
|
};
|
|
395
427
|
}
|
|
428
|
+
const buffer = new Uint8Array(await response.arrayBuffer());
|
|
396
429
|
return {
|
|
397
|
-
|
|
398
|
-
|
|
430
|
+
status: response.status,
|
|
431
|
+
headers,
|
|
432
|
+
body: buffer
|
|
399
433
|
};
|
|
400
434
|
}
|
|
401
|
-
function
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
for (
|
|
406
|
-
|
|
435
|
+
function applyRouteOverrides(response, route) {
|
|
436
|
+
const headers = new Headers(response.headers);
|
|
437
|
+
const hasHeaders = !!route.headers && Object.keys(route.headers).length > 0;
|
|
438
|
+
if (route.headers) {
|
|
439
|
+
for (const [key, value] of Object.entries(route.headers)) {
|
|
440
|
+
headers.set(key, value);
|
|
407
441
|
}
|
|
408
|
-
return bytes;
|
|
409
442
|
}
|
|
410
|
-
|
|
443
|
+
const status = route.status ?? response.status;
|
|
444
|
+
if (status === response.status && !hasHeaders) {
|
|
445
|
+
return response;
|
|
446
|
+
}
|
|
447
|
+
return new Response(response.body, { status, headers });
|
|
411
448
|
}
|
|
412
|
-
function
|
|
413
|
-
if (
|
|
414
|
-
return
|
|
449
|
+
function normalizeHandlerValue(c, value) {
|
|
450
|
+
if (value instanceof Response) {
|
|
451
|
+
return value;
|
|
452
|
+
}
|
|
453
|
+
if (typeof value === "undefined") {
|
|
454
|
+
const response = c.body(null);
|
|
455
|
+
if (response.status === 200) {
|
|
456
|
+
return new Response(response.body, {
|
|
457
|
+
status: 204,
|
|
458
|
+
headers: response.headers
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
return response;
|
|
462
|
+
}
|
|
463
|
+
if (typeof value === "string") {
|
|
464
|
+
return c.text(value);
|
|
415
465
|
}
|
|
416
|
-
|
|
466
|
+
if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
|
|
467
|
+
if (!c.res.headers.get("content-type")) {
|
|
468
|
+
c.header("content-type", "application/octet-stream");
|
|
469
|
+
}
|
|
470
|
+
const data = value instanceof ArrayBuffer ? value : new Uint8Array(value);
|
|
471
|
+
return c.body(data);
|
|
472
|
+
}
|
|
473
|
+
return c.json(value);
|
|
417
474
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
const ctx = {
|
|
422
|
-
delay,
|
|
423
|
-
json: (data) => data
|
|
424
|
-
};
|
|
425
|
-
const runHandler = async () => {
|
|
475
|
+
function createRouteHandler(params) {
|
|
476
|
+
const { route, moduleCache, moduleBase, moduleMap } = params;
|
|
477
|
+
return async (c) => {
|
|
426
478
|
if (route.response.type === "json") {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
479
|
+
if (typeof route.response.body === "undefined") {
|
|
480
|
+
return normalizeHandlerValue(c, void 0);
|
|
481
|
+
}
|
|
482
|
+
return c.json(route.response.body);
|
|
431
483
|
}
|
|
432
484
|
if (route.response.type === "text") {
|
|
433
|
-
return
|
|
434
|
-
value: route.response.body,
|
|
435
|
-
contentType: "text/plain; charset=utf-8"
|
|
436
|
-
};
|
|
485
|
+
return c.text(route.response.body);
|
|
437
486
|
}
|
|
438
487
|
if (route.response.type === "binary") {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
};
|
|
488
|
+
const data = new Uint8Array(decodeBase64(route.response.body));
|
|
489
|
+
c.header("content-type", "application/octet-stream");
|
|
490
|
+
return c.body(data);
|
|
443
491
|
}
|
|
444
492
|
const rule = await loadModuleRule(
|
|
445
493
|
route.response,
|
|
@@ -447,75 +495,106 @@ async function executeRoute(route, req, moduleCache, middlewareCache, moduleBase
|
|
|
447
495
|
moduleBase,
|
|
448
496
|
moduleMap
|
|
449
497
|
);
|
|
450
|
-
const value = await executeRule(rule,
|
|
451
|
-
return
|
|
498
|
+
const value = await executeRule(rule, c);
|
|
499
|
+
return normalizeHandlerValue(c, value);
|
|
452
500
|
};
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
return runHandler();
|
|
463
|
-
}
|
|
464
|
-
let nextResult;
|
|
465
|
-
const next = async () => {
|
|
466
|
-
nextResult = await dispatch(index + 1);
|
|
467
|
-
return nextResult.value;
|
|
468
|
-
};
|
|
469
|
-
const value = await handler(req, responder, ctx, next);
|
|
470
|
-
if (typeof value !== "undefined") {
|
|
471
|
-
return { value };
|
|
472
|
-
}
|
|
473
|
-
if (nextResult) {
|
|
474
|
-
return nextResult;
|
|
475
|
-
}
|
|
476
|
-
return { value: void 0 };
|
|
477
|
-
};
|
|
478
|
-
return dispatch(0);
|
|
501
|
+
}
|
|
502
|
+
function createFinalizeMiddleware(route) {
|
|
503
|
+
return async (c, next) => {
|
|
504
|
+
const response = await next();
|
|
505
|
+
const resolved = response ?? c.res;
|
|
506
|
+
if (route.delay && route.delay > 0) {
|
|
507
|
+
await delay(route.delay);
|
|
508
|
+
}
|
|
509
|
+
return applyRouteOverrides(resolved, route);
|
|
479
510
|
};
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
511
|
+
}
|
|
512
|
+
async function buildApp(params) {
|
|
513
|
+
const { manifest, moduleCache, middlewareCache, moduleBase, moduleMap } = params;
|
|
514
|
+
const app = new Hono({ router: new PatternRouter(), strict: false });
|
|
515
|
+
const compiled = compileRoutes(manifest);
|
|
516
|
+
for (const entry of compiled) {
|
|
517
|
+
const middlewares = [];
|
|
518
|
+
for (const middleware of entry.route.middleware ?? []) {
|
|
519
|
+
const handler2 = await loadModuleMiddleware(
|
|
520
|
+
middleware,
|
|
521
|
+
middlewareCache,
|
|
522
|
+
moduleBase,
|
|
523
|
+
moduleMap
|
|
524
|
+
);
|
|
525
|
+
if (handler2) {
|
|
526
|
+
middlewares.push(handler2);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
const handler = createRouteHandler({
|
|
530
|
+
route: entry.route,
|
|
531
|
+
moduleCache,
|
|
532
|
+
...typeof moduleBase !== "undefined" ? { moduleBase } : {},
|
|
533
|
+
...typeof moduleMap !== "undefined" ? { moduleMap } : {}
|
|
534
|
+
});
|
|
535
|
+
app.on(
|
|
536
|
+
entry.method,
|
|
537
|
+
toHonoPath(entry.tokens),
|
|
538
|
+
createFinalizeMiddleware(entry.route),
|
|
539
|
+
...middlewares,
|
|
540
|
+
handler
|
|
487
541
|
);
|
|
488
|
-
|
|
489
|
-
|
|
542
|
+
}
|
|
543
|
+
return app;
|
|
544
|
+
}
|
|
545
|
+
function appendQueryParams(url, query) {
|
|
546
|
+
for (const [key, value] of Object.entries(query)) {
|
|
547
|
+
if (Array.isArray(value)) {
|
|
548
|
+
for (const entry of value) {
|
|
549
|
+
url.searchParams.append(key, entry);
|
|
550
|
+
}
|
|
551
|
+
} else {
|
|
552
|
+
url.searchParams.append(key, value);
|
|
490
553
|
}
|
|
491
554
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
await delay(route.delay);
|
|
555
|
+
}
|
|
556
|
+
function resolveRequestBody(req, contentType) {
|
|
557
|
+
if (typeof req.rawBody !== "undefined") {
|
|
558
|
+
return req.rawBody;
|
|
497
559
|
}
|
|
498
|
-
const
|
|
499
|
-
if (
|
|
500
|
-
|
|
560
|
+
const body = req.body;
|
|
561
|
+
if (typeof body === "undefined") {
|
|
562
|
+
return void 0;
|
|
501
563
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
route.status ?? responder.statusCode,
|
|
505
|
-
normalized.body
|
|
506
|
-
);
|
|
507
|
-
if (normalized.contentType && !headers["content-type"]) {
|
|
508
|
-
headers["content-type"] = normalized.contentType;
|
|
564
|
+
if (typeof body === "string") {
|
|
565
|
+
return body;
|
|
509
566
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
567
|
+
if (body instanceof Uint8Array || body instanceof ArrayBuffer) {
|
|
568
|
+
return body;
|
|
569
|
+
}
|
|
570
|
+
if (typeof body === "object") {
|
|
571
|
+
const normalized = contentType.toLowerCase();
|
|
572
|
+
if (normalized.includes("json")) {
|
|
573
|
+
return JSON.stringify(body);
|
|
574
|
+
}
|
|
575
|
+
return JSON.stringify(body);
|
|
576
|
+
}
|
|
577
|
+
return String(body);
|
|
578
|
+
}
|
|
579
|
+
function toFetchRequest(req) {
|
|
580
|
+
const url = new URL(req.path, "http://mokup.local");
|
|
581
|
+
appendQueryParams(url, req.query);
|
|
582
|
+
const headers = new Headers();
|
|
583
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
584
|
+
headers.set(key, value);
|
|
585
|
+
}
|
|
586
|
+
const method = normalizeMethod(req.method) ?? "GET";
|
|
587
|
+
const contentType = headers.get("content-type") ?? "";
|
|
588
|
+
const body = resolveRequestBody(req, contentType);
|
|
589
|
+
const init = { method, headers };
|
|
590
|
+
if (typeof body !== "undefined" && method !== "GET" && method !== "HEAD") {
|
|
591
|
+
init.body = body;
|
|
592
|
+
}
|
|
593
|
+
return new Request(url.toString(), init);
|
|
515
594
|
}
|
|
516
595
|
function createRuntime(options) {
|
|
517
596
|
let manifestCache = null;
|
|
518
|
-
let
|
|
597
|
+
let appPromise = null;
|
|
519
598
|
const moduleCache = /* @__PURE__ */ new Map();
|
|
520
599
|
const middlewareCache = /* @__PURE__ */ new Map();
|
|
521
600
|
const getManifest = async () => {
|
|
@@ -524,56 +603,31 @@ function createRuntime(options) {
|
|
|
524
603
|
}
|
|
525
604
|
return manifestCache;
|
|
526
605
|
};
|
|
527
|
-
const
|
|
528
|
-
if (
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
}
|
|
539
|
-
const tokens = route.tokens ?? parsed.tokens;
|
|
540
|
-
const score = route.score ?? parsed.score;
|
|
541
|
-
const list = map.get(method) ?? [];
|
|
542
|
-
list.push({ route, tokens, score });
|
|
543
|
-
map.set(method, list);
|
|
544
|
-
}
|
|
545
|
-
for (const list of map.values()) {
|
|
546
|
-
list.sort((a, b) => compareRouteScore(a.score, b.score));
|
|
606
|
+
const getApp = async () => {
|
|
607
|
+
if (!appPromise) {
|
|
608
|
+
appPromise = (async () => {
|
|
609
|
+
const manifest = await getManifest();
|
|
610
|
+
return buildApp({
|
|
611
|
+
manifest,
|
|
612
|
+
moduleCache,
|
|
613
|
+
middlewareCache,
|
|
614
|
+
...typeof options.moduleBase !== "undefined" ? { moduleBase: options.moduleBase } : {},
|
|
615
|
+
...typeof options.moduleMap !== "undefined" ? { moduleMap: options.moduleMap } : {}
|
|
616
|
+
});
|
|
617
|
+
})();
|
|
547
618
|
}
|
|
548
|
-
|
|
549
|
-
return compiledRoutes;
|
|
619
|
+
return appPromise;
|
|
550
620
|
};
|
|
551
621
|
const handle = async (req) => {
|
|
622
|
+
const app = await getApp();
|
|
552
623
|
const method = normalizeMethod(req.method) ?? "GET";
|
|
553
|
-
const
|
|
554
|
-
const
|
|
555
|
-
if (!
|
|
624
|
+
const matchMethod = method === "HEAD" ? "GET" : method;
|
|
625
|
+
const match = app.router.match(matchMethod, req.path);
|
|
626
|
+
if (!match || match[0].length === 0) {
|
|
556
627
|
return null;
|
|
557
628
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
if (!matched) {
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
const requestWithParams = {
|
|
564
|
-
...req,
|
|
565
|
-
params: matched.params
|
|
566
|
-
};
|
|
567
|
-
return executeRoute(
|
|
568
|
-
entry.route,
|
|
569
|
-
requestWithParams,
|
|
570
|
-
moduleCache,
|
|
571
|
-
middlewareCache,
|
|
572
|
-
options.moduleBase,
|
|
573
|
-
options.moduleMap
|
|
574
|
-
);
|
|
575
|
-
}
|
|
576
|
-
return null;
|
|
629
|
+
const response = await app.fetch(toFetchRequest(req));
|
|
630
|
+
return await toRuntimeResult(response);
|
|
577
631
|
};
|
|
578
632
|
return {
|
|
579
633
|
handle
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mokup/runtime",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"description": "Cross-runtime mock matching and response handling for mokup.",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"homepage": "https://mokup.icebreaker.top",
|
|
7
8
|
"repository": {
|
|
8
9
|
"type": "git",
|
|
9
10
|
"url": "git+https://github.com/sonofmagic/mokup.git",
|
|
@@ -25,7 +26,9 @@
|
|
|
25
26
|
"files": [
|
|
26
27
|
"dist"
|
|
27
28
|
],
|
|
28
|
-
"dependencies": {
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"hono": "^4.11.4"
|
|
31
|
+
},
|
|
29
32
|
"devDependencies": {
|
|
30
33
|
"typescript": "^5.9.3",
|
|
31
34
|
"unbuild": "^3.6.1"
|