@mokup/runtime 0.1.0 → 1.0.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/dist/index.cjs +117 -16
- package/dist/index.d.cts +8 -6
- package/dist/index.d.mts +8 -6
- package/dist/index.d.ts +8 -6
- package/dist/index.mjs +116 -16
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const hono = require('hono');
|
|
4
|
-
const patternRouter = require('hono/router/pattern-router');
|
|
3
|
+
const hono = require('@mokup/shared/hono');
|
|
5
4
|
|
|
6
5
|
const paramNamePattern = /^[\w-]+$/;
|
|
7
6
|
const paramPattern = /^\[([^\]/]+)\]$/;
|
|
@@ -217,7 +216,7 @@ function normalizeRules(value) {
|
|
|
217
216
|
if (typeof value === "function") {
|
|
218
217
|
return [
|
|
219
218
|
{
|
|
220
|
-
|
|
219
|
+
handler: value
|
|
221
220
|
}
|
|
222
221
|
];
|
|
223
222
|
}
|
|
@@ -226,7 +225,7 @@ function normalizeRules(value) {
|
|
|
226
225
|
}
|
|
227
226
|
return [
|
|
228
227
|
{
|
|
229
|
-
|
|
228
|
+
handler: value
|
|
230
229
|
}
|
|
231
230
|
];
|
|
232
231
|
}
|
|
@@ -234,7 +233,7 @@ async function executeRule(rule, context) {
|
|
|
234
233
|
if (!rule) {
|
|
235
234
|
return void 0;
|
|
236
235
|
}
|
|
237
|
-
const value = rule.
|
|
236
|
+
const value = rule.handler;
|
|
238
237
|
if (typeof value === "function") {
|
|
239
238
|
const handler = value;
|
|
240
239
|
return handler(context);
|
|
@@ -265,7 +264,10 @@ async function loadModuleExport(modulePath, exportName, moduleBase, moduleMap) {
|
|
|
265
264
|
const resolvedUrl = directMapValue ? void 0 : resolveModuleUrl(modulePath, moduleBase);
|
|
266
265
|
const resolvedMapValue = resolvedUrl ? moduleMap?.[resolvedUrl] : void 0;
|
|
267
266
|
const moduleValue = directMapValue ?? resolvedMapValue;
|
|
268
|
-
const module = moduleValue ?? await import(
|
|
267
|
+
const module = moduleValue ?? await import(
|
|
268
|
+
/* @vite-ignore */
|
|
269
|
+
resolvedUrl ?? modulePath
|
|
270
|
+
);
|
|
269
271
|
return module[exportName] ?? module.default ?? module;
|
|
270
272
|
}
|
|
271
273
|
function resolveModuleCacheKey(modulePath, exportName, moduleBase, moduleMap) {
|
|
@@ -405,7 +407,19 @@ function compileRoutes(manifest) {
|
|
|
405
407
|
}
|
|
406
408
|
function shouldTreatAsText(contentType) {
|
|
407
409
|
const normalized = contentType.toLowerCase();
|
|
408
|
-
|
|
410
|
+
if (!normalized) {
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
if (normalized.startsWith("text/") || normalized.includes("json") || normalized.includes("xml") || normalized.includes("javascript")) {
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
if (normalized.startsWith("image/") || normalized.startsWith("audio/") || normalized.startsWith("video/") || normalized.includes("octet-stream")) {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
if (normalized.startsWith("application/") && (normalized.includes("pdf") || normalized.includes("zip") || normalized.includes("gzip"))) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
return true;
|
|
409
423
|
}
|
|
410
424
|
async function toRuntimeResult(response) {
|
|
411
425
|
const headers = {};
|
|
@@ -434,6 +448,18 @@ async function toRuntimeResult(response) {
|
|
|
434
448
|
body: buffer
|
|
435
449
|
};
|
|
436
450
|
}
|
|
451
|
+
function isValidStatus(status) {
|
|
452
|
+
return typeof status === "number" && Number.isFinite(status) && status >= 200 && status <= 599;
|
|
453
|
+
}
|
|
454
|
+
function resolveStatus(routeStatus, responseStatus) {
|
|
455
|
+
if (isValidStatus(routeStatus)) {
|
|
456
|
+
return routeStatus;
|
|
457
|
+
}
|
|
458
|
+
if (isValidStatus(responseStatus)) {
|
|
459
|
+
return responseStatus;
|
|
460
|
+
}
|
|
461
|
+
return 200;
|
|
462
|
+
}
|
|
437
463
|
function applyRouteOverrides(response, route) {
|
|
438
464
|
const headers = new Headers(response.headers);
|
|
439
465
|
const hasHeaders = !!route.headers && Object.keys(route.headers).length > 0;
|
|
@@ -442,12 +468,24 @@ function applyRouteOverrides(response, route) {
|
|
|
442
468
|
headers.set(key, value);
|
|
443
469
|
}
|
|
444
470
|
}
|
|
445
|
-
const status = route.status
|
|
471
|
+
const status = resolveStatus(route.status, response.status);
|
|
446
472
|
if (status === response.status && !hasHeaders) {
|
|
447
473
|
return response;
|
|
448
474
|
}
|
|
449
475
|
return new Response(response.body, { status, headers });
|
|
450
476
|
}
|
|
477
|
+
function resolveResponse(value, fallback) {
|
|
478
|
+
if (value instanceof Response) {
|
|
479
|
+
return value;
|
|
480
|
+
}
|
|
481
|
+
if (value && typeof value === "object" && "res" in value) {
|
|
482
|
+
const resolved = value.res;
|
|
483
|
+
if (resolved instanceof Response) {
|
|
484
|
+
return resolved;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return fallback;
|
|
488
|
+
}
|
|
451
489
|
function normalizeHandlerValue(c, value) {
|
|
452
490
|
if (value instanceof Response) {
|
|
453
491
|
return value;
|
|
@@ -504,16 +542,18 @@ function createRouteHandler(params) {
|
|
|
504
542
|
function createFinalizeMiddleware(route) {
|
|
505
543
|
return async (c, next) => {
|
|
506
544
|
const response = await next();
|
|
507
|
-
const resolved = response
|
|
545
|
+
const resolved = resolveResponse(response, c.res);
|
|
508
546
|
if (route.delay && route.delay > 0) {
|
|
509
547
|
await delay(route.delay);
|
|
510
548
|
}
|
|
511
|
-
|
|
549
|
+
const overridden = applyRouteOverrides(resolved, route);
|
|
550
|
+
c.res = overridden;
|
|
551
|
+
return overridden;
|
|
512
552
|
};
|
|
513
553
|
}
|
|
514
554
|
async function buildApp(params) {
|
|
515
555
|
const { manifest, moduleCache, middlewareCache, moduleBase, moduleMap } = params;
|
|
516
|
-
const app = new hono.Hono({ router: new
|
|
556
|
+
const app = new hono.Hono({ router: new hono.PatternRouter(), strict: false });
|
|
517
557
|
const compiled = compileRoutes(manifest);
|
|
518
558
|
for (const entry of compiled) {
|
|
519
559
|
const middlewares = [];
|
|
@@ -544,6 +584,18 @@ async function buildApp(params) {
|
|
|
544
584
|
}
|
|
545
585
|
return app;
|
|
546
586
|
}
|
|
587
|
+
async function createRuntimeApp(options) {
|
|
588
|
+
const manifest = typeof options.manifest === "function" ? await options.manifest() : options.manifest;
|
|
589
|
+
const moduleCache = /* @__PURE__ */ new Map();
|
|
590
|
+
const middlewareCache = /* @__PURE__ */ new Map();
|
|
591
|
+
return await buildApp({
|
|
592
|
+
manifest,
|
|
593
|
+
moduleCache,
|
|
594
|
+
middlewareCache,
|
|
595
|
+
...typeof options.moduleBase !== "undefined" ? { moduleBase: options.moduleBase } : {},
|
|
596
|
+
...typeof options.moduleMap !== "undefined" ? { moduleMap: options.moduleMap } : {}
|
|
597
|
+
});
|
|
598
|
+
}
|
|
547
599
|
function appendQueryParams(url, query) {
|
|
548
600
|
for (const [key, value] of Object.entries(query)) {
|
|
549
601
|
if (Array.isArray(value)) {
|
|
@@ -579,7 +631,7 @@ function resolveRequestBody(req, contentType) {
|
|
|
579
631
|
return String(body);
|
|
580
632
|
}
|
|
581
633
|
function toFetchRequest(req) {
|
|
582
|
-
const url = new URL(req.path, "http://mokup.local");
|
|
634
|
+
const url = new URL(normalizePathname(req.path), "http://mokup.local");
|
|
583
635
|
appendQueryParams(url, req.query);
|
|
584
636
|
const headers = new Headers();
|
|
585
637
|
for (const [key, value] of Object.entries(req.headers)) {
|
|
@@ -594,9 +646,35 @@ function toFetchRequest(req) {
|
|
|
594
646
|
}
|
|
595
647
|
return new Request(url.toString(), init);
|
|
596
648
|
}
|
|
649
|
+
function isRelativeModulePath(modulePath) {
|
|
650
|
+
return !/^(?:data|http|https|file):/.test(modulePath);
|
|
651
|
+
}
|
|
652
|
+
function requiresModuleBase(modulePath, moduleMap) {
|
|
653
|
+
if (!isRelativeModulePath(modulePath)) {
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
if (moduleMap && Object.prototype.hasOwnProperty.call(moduleMap, modulePath)) {
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
return true;
|
|
660
|
+
}
|
|
661
|
+
function routeNeedsModuleBase(route, moduleMap) {
|
|
662
|
+
if (route.response.type === "module" && requiresModuleBase(route.response.module, moduleMap)) {
|
|
663
|
+
return true;
|
|
664
|
+
}
|
|
665
|
+
if (route.middleware) {
|
|
666
|
+
for (const middleware of route.middleware) {
|
|
667
|
+
if (requiresModuleBase(middleware.module, moduleMap)) {
|
|
668
|
+
return true;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
597
674
|
function createRuntime(options) {
|
|
598
675
|
let manifestCache = null;
|
|
599
676
|
let appPromise = null;
|
|
677
|
+
let compiledCache = null;
|
|
600
678
|
const moduleCache = /* @__PURE__ */ new Map();
|
|
601
679
|
const middlewareCache = /* @__PURE__ */ new Map();
|
|
602
680
|
const getManifest = async () => {
|
|
@@ -620,24 +698,47 @@ function createRuntime(options) {
|
|
|
620
698
|
}
|
|
621
699
|
return appPromise;
|
|
622
700
|
};
|
|
701
|
+
const getCompiled = async () => {
|
|
702
|
+
if (!compiledCache) {
|
|
703
|
+
compiledCache = compileRoutes(await getManifest());
|
|
704
|
+
}
|
|
705
|
+
return compiledCache;
|
|
706
|
+
};
|
|
623
707
|
const handle = async (req) => {
|
|
624
|
-
const app = await getApp();
|
|
625
708
|
const method = normalizeMethod(req.method) ?? "GET";
|
|
626
709
|
const matchMethod = method === "HEAD" ? "GET" : method;
|
|
627
|
-
const
|
|
628
|
-
|
|
710
|
+
const pathname = normalizePathname(req.path);
|
|
711
|
+
const compiled = await getCompiled();
|
|
712
|
+
let matchedRoute = null;
|
|
713
|
+
for (const entry of compiled) {
|
|
714
|
+
if (entry.method !== matchMethod) {
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
if (matchRouteTokens(entry.tokens, pathname)) {
|
|
718
|
+
matchedRoute = entry.route;
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (!matchedRoute) {
|
|
629
723
|
return null;
|
|
630
724
|
}
|
|
725
|
+
if (typeof options.moduleBase === "undefined" && routeNeedsModuleBase(matchedRoute, options.moduleMap)) {
|
|
726
|
+
throw new Error("moduleBase is required for relative module paths.");
|
|
727
|
+
}
|
|
728
|
+
const app = await getApp();
|
|
631
729
|
const response = await app.fetch(toFetchRequest(req));
|
|
632
|
-
|
|
730
|
+
const resolvedResponse = applyRouteOverrides(response, matchedRoute);
|
|
731
|
+
return await toRuntimeResult(resolvedResponse);
|
|
633
732
|
};
|
|
634
733
|
return {
|
|
635
734
|
handle
|
|
636
735
|
};
|
|
637
736
|
}
|
|
638
737
|
|
|
738
|
+
exports.handle = hono.handle;
|
|
639
739
|
exports.compareRouteScore = compareRouteScore;
|
|
640
740
|
exports.createRuntime = createRuntime;
|
|
741
|
+
exports.createRuntimeApp = createRuntimeApp;
|
|
641
742
|
exports.matchRouteTokens = matchRouteTokens;
|
|
642
743
|
exports.normalizePathname = normalizePathname;
|
|
643
744
|
exports.parseRouteTemplate = parseRouteTemplate;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, Hono } from '@mokup/shared/hono';
|
|
2
|
+
export { Context, MiddlewareHandler, handle } from '@mokup/shared/hono';
|
|
2
3
|
|
|
3
4
|
type RouteToken = {
|
|
4
5
|
type: 'static';
|
|
@@ -77,9 +78,9 @@ interface RuntimeResult {
|
|
|
77
78
|
headers: Record<string, string>;
|
|
78
79
|
body: string | Uint8Array | null;
|
|
79
80
|
}
|
|
80
|
-
type
|
|
81
|
-
type
|
|
82
|
-
|
|
81
|
+
type RequestHandler = (context: Context) => Response | Promise<Response> | unknown;
|
|
82
|
+
type RouteResponse = unknown | RequestHandler;
|
|
83
|
+
|
|
83
84
|
interface RuntimeOptions {
|
|
84
85
|
manifest: Manifest | (() => Promise<Manifest>);
|
|
85
86
|
moduleBase?: string | URL;
|
|
@@ -87,9 +88,10 @@ interface RuntimeOptions {
|
|
|
87
88
|
}
|
|
88
89
|
type ModuleMap = Record<string, Record<string, unknown>>;
|
|
89
90
|
|
|
91
|
+
declare function createRuntimeApp(options: RuntimeOptions): Promise<Hono>;
|
|
90
92
|
declare function createRuntime(options: RuntimeOptions): {
|
|
91
93
|
handle: (req: RuntimeRequest) => Promise<RuntimeResult | null>;
|
|
92
94
|
};
|
|
93
95
|
|
|
94
|
-
export { compareRouteScore, createRuntime, matchRouteTokens, normalizePathname, parseRouteTemplate, scoreRouteTokens };
|
|
95
|
-
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute,
|
|
96
|
+
export { compareRouteScore, createRuntime, createRuntimeApp, matchRouteTokens, normalizePathname, parseRouteTemplate, scoreRouteTokens };
|
|
97
|
+
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute, ModuleMap, ParsedRouteTemplate, RequestHandler, RouteResponse, RouteToken, RuntimeOptions, RuntimeRequest, RuntimeResult };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, Hono } from '@mokup/shared/hono';
|
|
2
|
+
export { Context, MiddlewareHandler, handle } from '@mokup/shared/hono';
|
|
2
3
|
|
|
3
4
|
type RouteToken = {
|
|
4
5
|
type: 'static';
|
|
@@ -77,9 +78,9 @@ interface RuntimeResult {
|
|
|
77
78
|
headers: Record<string, string>;
|
|
78
79
|
body: string | Uint8Array | null;
|
|
79
80
|
}
|
|
80
|
-
type
|
|
81
|
-
type
|
|
82
|
-
|
|
81
|
+
type RequestHandler = (context: Context) => Response | Promise<Response> | unknown;
|
|
82
|
+
type RouteResponse = unknown | RequestHandler;
|
|
83
|
+
|
|
83
84
|
interface RuntimeOptions {
|
|
84
85
|
manifest: Manifest | (() => Promise<Manifest>);
|
|
85
86
|
moduleBase?: string | URL;
|
|
@@ -87,9 +88,10 @@ interface RuntimeOptions {
|
|
|
87
88
|
}
|
|
88
89
|
type ModuleMap = Record<string, Record<string, unknown>>;
|
|
89
90
|
|
|
91
|
+
declare function createRuntimeApp(options: RuntimeOptions): Promise<Hono>;
|
|
90
92
|
declare function createRuntime(options: RuntimeOptions): {
|
|
91
93
|
handle: (req: RuntimeRequest) => Promise<RuntimeResult | null>;
|
|
92
94
|
};
|
|
93
95
|
|
|
94
|
-
export { compareRouteScore, createRuntime, matchRouteTokens, normalizePathname, parseRouteTemplate, scoreRouteTokens };
|
|
95
|
-
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute,
|
|
96
|
+
export { compareRouteScore, createRuntime, createRuntimeApp, matchRouteTokens, normalizePathname, parseRouteTemplate, scoreRouteTokens };
|
|
97
|
+
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute, ModuleMap, ParsedRouteTemplate, RequestHandler, RouteResponse, RouteToken, RuntimeOptions, RuntimeRequest, RuntimeResult };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Context,
|
|
1
|
+
import { Context, Hono } from '@mokup/shared/hono';
|
|
2
|
+
export { Context, MiddlewareHandler, handle } from '@mokup/shared/hono';
|
|
2
3
|
|
|
3
4
|
type RouteToken = {
|
|
4
5
|
type: 'static';
|
|
@@ -77,9 +78,9 @@ interface RuntimeResult {
|
|
|
77
78
|
headers: Record<string, string>;
|
|
78
79
|
body: string | Uint8Array | null;
|
|
79
80
|
}
|
|
80
|
-
type
|
|
81
|
-
type
|
|
82
|
-
|
|
81
|
+
type RequestHandler = (context: Context) => Response | Promise<Response> | unknown;
|
|
82
|
+
type RouteResponse = unknown | RequestHandler;
|
|
83
|
+
|
|
83
84
|
interface RuntimeOptions {
|
|
84
85
|
manifest: Manifest | (() => Promise<Manifest>);
|
|
85
86
|
moduleBase?: string | URL;
|
|
@@ -87,9 +88,10 @@ interface RuntimeOptions {
|
|
|
87
88
|
}
|
|
88
89
|
type ModuleMap = Record<string, Record<string, unknown>>;
|
|
89
90
|
|
|
91
|
+
declare function createRuntimeApp(options: RuntimeOptions): Promise<Hono>;
|
|
90
92
|
declare function createRuntime(options: RuntimeOptions): {
|
|
91
93
|
handle: (req: RuntimeRequest) => Promise<RuntimeResult | null>;
|
|
92
94
|
};
|
|
93
95
|
|
|
94
|
-
export { compareRouteScore, createRuntime, matchRouteTokens, normalizePathname, parseRouteTemplate, scoreRouteTokens };
|
|
95
|
-
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute,
|
|
96
|
+
export { compareRouteScore, createRuntime, createRuntimeApp, matchRouteTokens, normalizePathname, parseRouteTemplate, scoreRouteTokens };
|
|
97
|
+
export type { HttpMethod, Manifest, ManifestModuleRef, ManifestResponse, ManifestRoute, ModuleMap, ParsedRouteTemplate, RequestHandler, RouteResponse, RouteToken, RuntimeOptions, RuntimeRequest, RuntimeResult };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Hono } from 'hono';
|
|
2
|
-
|
|
1
|
+
import { Hono, PatternRouter } from '@mokup/shared/hono';
|
|
2
|
+
export { handle } from '@mokup/shared/hono';
|
|
3
3
|
|
|
4
4
|
const paramNamePattern = /^[\w-]+$/;
|
|
5
5
|
const paramPattern = /^\[([^\]/]+)\]$/;
|
|
@@ -215,7 +215,7 @@ function normalizeRules(value) {
|
|
|
215
215
|
if (typeof value === "function") {
|
|
216
216
|
return [
|
|
217
217
|
{
|
|
218
|
-
|
|
218
|
+
handler: value
|
|
219
219
|
}
|
|
220
220
|
];
|
|
221
221
|
}
|
|
@@ -224,7 +224,7 @@ function normalizeRules(value) {
|
|
|
224
224
|
}
|
|
225
225
|
return [
|
|
226
226
|
{
|
|
227
|
-
|
|
227
|
+
handler: value
|
|
228
228
|
}
|
|
229
229
|
];
|
|
230
230
|
}
|
|
@@ -232,7 +232,7 @@ async function executeRule(rule, context) {
|
|
|
232
232
|
if (!rule) {
|
|
233
233
|
return void 0;
|
|
234
234
|
}
|
|
235
|
-
const value = rule.
|
|
235
|
+
const value = rule.handler;
|
|
236
236
|
if (typeof value === "function") {
|
|
237
237
|
const handler = value;
|
|
238
238
|
return handler(context);
|
|
@@ -263,7 +263,10 @@ async function loadModuleExport(modulePath, exportName, moduleBase, moduleMap) {
|
|
|
263
263
|
const resolvedUrl = directMapValue ? void 0 : resolveModuleUrl(modulePath, moduleBase);
|
|
264
264
|
const resolvedMapValue = resolvedUrl ? moduleMap?.[resolvedUrl] : void 0;
|
|
265
265
|
const moduleValue = directMapValue ?? resolvedMapValue;
|
|
266
|
-
const module = moduleValue ?? await import(
|
|
266
|
+
const module = moduleValue ?? await import(
|
|
267
|
+
/* @vite-ignore */
|
|
268
|
+
resolvedUrl ?? modulePath
|
|
269
|
+
);
|
|
267
270
|
return module[exportName] ?? module.default ?? module;
|
|
268
271
|
}
|
|
269
272
|
function resolveModuleCacheKey(modulePath, exportName, moduleBase, moduleMap) {
|
|
@@ -403,7 +406,19 @@ function compileRoutes(manifest) {
|
|
|
403
406
|
}
|
|
404
407
|
function shouldTreatAsText(contentType) {
|
|
405
408
|
const normalized = contentType.toLowerCase();
|
|
406
|
-
|
|
409
|
+
if (!normalized) {
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
if (normalized.startsWith("text/") || normalized.includes("json") || normalized.includes("xml") || normalized.includes("javascript")) {
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
if (normalized.startsWith("image/") || normalized.startsWith("audio/") || normalized.startsWith("video/") || normalized.includes("octet-stream")) {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
if (normalized.startsWith("application/") && (normalized.includes("pdf") || normalized.includes("zip") || normalized.includes("gzip"))) {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
return true;
|
|
407
422
|
}
|
|
408
423
|
async function toRuntimeResult(response) {
|
|
409
424
|
const headers = {};
|
|
@@ -432,6 +447,18 @@ async function toRuntimeResult(response) {
|
|
|
432
447
|
body: buffer
|
|
433
448
|
};
|
|
434
449
|
}
|
|
450
|
+
function isValidStatus(status) {
|
|
451
|
+
return typeof status === "number" && Number.isFinite(status) && status >= 200 && status <= 599;
|
|
452
|
+
}
|
|
453
|
+
function resolveStatus(routeStatus, responseStatus) {
|
|
454
|
+
if (isValidStatus(routeStatus)) {
|
|
455
|
+
return routeStatus;
|
|
456
|
+
}
|
|
457
|
+
if (isValidStatus(responseStatus)) {
|
|
458
|
+
return responseStatus;
|
|
459
|
+
}
|
|
460
|
+
return 200;
|
|
461
|
+
}
|
|
435
462
|
function applyRouteOverrides(response, route) {
|
|
436
463
|
const headers = new Headers(response.headers);
|
|
437
464
|
const hasHeaders = !!route.headers && Object.keys(route.headers).length > 0;
|
|
@@ -440,12 +467,24 @@ function applyRouteOverrides(response, route) {
|
|
|
440
467
|
headers.set(key, value);
|
|
441
468
|
}
|
|
442
469
|
}
|
|
443
|
-
const status = route.status
|
|
470
|
+
const status = resolveStatus(route.status, response.status);
|
|
444
471
|
if (status === response.status && !hasHeaders) {
|
|
445
472
|
return response;
|
|
446
473
|
}
|
|
447
474
|
return new Response(response.body, { status, headers });
|
|
448
475
|
}
|
|
476
|
+
function resolveResponse(value, fallback) {
|
|
477
|
+
if (value instanceof Response) {
|
|
478
|
+
return value;
|
|
479
|
+
}
|
|
480
|
+
if (value && typeof value === "object" && "res" in value) {
|
|
481
|
+
const resolved = value.res;
|
|
482
|
+
if (resolved instanceof Response) {
|
|
483
|
+
return resolved;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return fallback;
|
|
487
|
+
}
|
|
449
488
|
function normalizeHandlerValue(c, value) {
|
|
450
489
|
if (value instanceof Response) {
|
|
451
490
|
return value;
|
|
@@ -502,11 +541,13 @@ function createRouteHandler(params) {
|
|
|
502
541
|
function createFinalizeMiddleware(route) {
|
|
503
542
|
return async (c, next) => {
|
|
504
543
|
const response = await next();
|
|
505
|
-
const resolved = response
|
|
544
|
+
const resolved = resolveResponse(response, c.res);
|
|
506
545
|
if (route.delay && route.delay > 0) {
|
|
507
546
|
await delay(route.delay);
|
|
508
547
|
}
|
|
509
|
-
|
|
548
|
+
const overridden = applyRouteOverrides(resolved, route);
|
|
549
|
+
c.res = overridden;
|
|
550
|
+
return overridden;
|
|
510
551
|
};
|
|
511
552
|
}
|
|
512
553
|
async function buildApp(params) {
|
|
@@ -542,6 +583,18 @@ async function buildApp(params) {
|
|
|
542
583
|
}
|
|
543
584
|
return app;
|
|
544
585
|
}
|
|
586
|
+
async function createRuntimeApp(options) {
|
|
587
|
+
const manifest = typeof options.manifest === "function" ? await options.manifest() : options.manifest;
|
|
588
|
+
const moduleCache = /* @__PURE__ */ new Map();
|
|
589
|
+
const middlewareCache = /* @__PURE__ */ new Map();
|
|
590
|
+
return await buildApp({
|
|
591
|
+
manifest,
|
|
592
|
+
moduleCache,
|
|
593
|
+
middlewareCache,
|
|
594
|
+
...typeof options.moduleBase !== "undefined" ? { moduleBase: options.moduleBase } : {},
|
|
595
|
+
...typeof options.moduleMap !== "undefined" ? { moduleMap: options.moduleMap } : {}
|
|
596
|
+
});
|
|
597
|
+
}
|
|
545
598
|
function appendQueryParams(url, query) {
|
|
546
599
|
for (const [key, value] of Object.entries(query)) {
|
|
547
600
|
if (Array.isArray(value)) {
|
|
@@ -577,7 +630,7 @@ function resolveRequestBody(req, contentType) {
|
|
|
577
630
|
return String(body);
|
|
578
631
|
}
|
|
579
632
|
function toFetchRequest(req) {
|
|
580
|
-
const url = new URL(req.path, "http://mokup.local");
|
|
633
|
+
const url = new URL(normalizePathname(req.path), "http://mokup.local");
|
|
581
634
|
appendQueryParams(url, req.query);
|
|
582
635
|
const headers = new Headers();
|
|
583
636
|
for (const [key, value] of Object.entries(req.headers)) {
|
|
@@ -592,9 +645,35 @@ function toFetchRequest(req) {
|
|
|
592
645
|
}
|
|
593
646
|
return new Request(url.toString(), init);
|
|
594
647
|
}
|
|
648
|
+
function isRelativeModulePath(modulePath) {
|
|
649
|
+
return !/^(?:data|http|https|file):/.test(modulePath);
|
|
650
|
+
}
|
|
651
|
+
function requiresModuleBase(modulePath, moduleMap) {
|
|
652
|
+
if (!isRelativeModulePath(modulePath)) {
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
if (moduleMap && Object.prototype.hasOwnProperty.call(moduleMap, modulePath)) {
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
660
|
+
function routeNeedsModuleBase(route, moduleMap) {
|
|
661
|
+
if (route.response.type === "module" && requiresModuleBase(route.response.module, moduleMap)) {
|
|
662
|
+
return true;
|
|
663
|
+
}
|
|
664
|
+
if (route.middleware) {
|
|
665
|
+
for (const middleware of route.middleware) {
|
|
666
|
+
if (requiresModuleBase(middleware.module, moduleMap)) {
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
595
673
|
function createRuntime(options) {
|
|
596
674
|
let manifestCache = null;
|
|
597
675
|
let appPromise = null;
|
|
676
|
+
let compiledCache = null;
|
|
598
677
|
const moduleCache = /* @__PURE__ */ new Map();
|
|
599
678
|
const middlewareCache = /* @__PURE__ */ new Map();
|
|
600
679
|
const getManifest = async () => {
|
|
@@ -618,20 +697,41 @@ function createRuntime(options) {
|
|
|
618
697
|
}
|
|
619
698
|
return appPromise;
|
|
620
699
|
};
|
|
700
|
+
const getCompiled = async () => {
|
|
701
|
+
if (!compiledCache) {
|
|
702
|
+
compiledCache = compileRoutes(await getManifest());
|
|
703
|
+
}
|
|
704
|
+
return compiledCache;
|
|
705
|
+
};
|
|
621
706
|
const handle = async (req) => {
|
|
622
|
-
const app = await getApp();
|
|
623
707
|
const method = normalizeMethod(req.method) ?? "GET";
|
|
624
708
|
const matchMethod = method === "HEAD" ? "GET" : method;
|
|
625
|
-
const
|
|
626
|
-
|
|
709
|
+
const pathname = normalizePathname(req.path);
|
|
710
|
+
const compiled = await getCompiled();
|
|
711
|
+
let matchedRoute = null;
|
|
712
|
+
for (const entry of compiled) {
|
|
713
|
+
if (entry.method !== matchMethod) {
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
if (matchRouteTokens(entry.tokens, pathname)) {
|
|
717
|
+
matchedRoute = entry.route;
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
if (!matchedRoute) {
|
|
627
722
|
return null;
|
|
628
723
|
}
|
|
724
|
+
if (typeof options.moduleBase === "undefined" && routeNeedsModuleBase(matchedRoute, options.moduleMap)) {
|
|
725
|
+
throw new Error("moduleBase is required for relative module paths.");
|
|
726
|
+
}
|
|
727
|
+
const app = await getApp();
|
|
629
728
|
const response = await app.fetch(toFetchRequest(req));
|
|
630
|
-
|
|
729
|
+
const resolvedResponse = applyRouteOverrides(response, matchedRoute);
|
|
730
|
+
return await toRuntimeResult(resolvedResponse);
|
|
631
731
|
};
|
|
632
732
|
return {
|
|
633
733
|
handle
|
|
634
734
|
};
|
|
635
735
|
}
|
|
636
736
|
|
|
637
|
-
export { compareRouteScore, createRuntime, matchRouteTokens, normalizePathname, parseRouteTemplate, scoreRouteTokens };
|
|
737
|
+
export { compareRouteScore, createRuntime, createRuntimeApp, matchRouteTokens, normalizePathname, parseRouteTemplate, scoreRouteTokens };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mokup/runtime",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"description": "Cross-runtime mock matching and response handling for mokup.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://mokup.icebreaker.top",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"dist"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"
|
|
30
|
+
"@mokup/shared": "1.0.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"typescript": "^5.9.3",
|