@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 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
- response: value
219
+ handler: value
221
220
  }
222
221
  ];
223
222
  }
@@ -226,7 +225,7 @@ function normalizeRules(value) {
226
225
  }
227
226
  return [
228
227
  {
229
- response: value
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.response;
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(resolvedUrl ?? modulePath);
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
- return normalized.startsWith("text/") || normalized.includes("json") || normalized.includes("xml") || normalized.includes("javascript");
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 ?? response.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 ?? c.res;
545
+ const resolved = resolveResponse(response, c.res);
508
546
  if (route.delay && route.delay > 0) {
509
547
  await delay(route.delay);
510
548
  }
511
- return applyRouteOverrides(resolved, route);
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 patternRouter.PatternRouter(), strict: false });
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 match = app.router.match(matchMethod, req.path);
628
- if (!match || match[0].length === 0) {
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
- return await toRuntimeResult(response);
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, MiddlewareHandler } from 'hono';
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 MockContext = Context;
81
- type MockMiddleware = MiddlewareHandler;
82
- type MockResponseHandler = (context: Context) => Response | Promise<Response> | unknown;
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, MockContext, MockMiddleware, MockResponseHandler, ModuleMap, ParsedRouteTemplate, RouteToken, RuntimeOptions, RuntimeRequest, RuntimeResult };
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, MiddlewareHandler } from 'hono';
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 MockContext = Context;
81
- type MockMiddleware = MiddlewareHandler;
82
- type MockResponseHandler = (context: Context) => Response | Promise<Response> | unknown;
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, MockContext, MockMiddleware, MockResponseHandler, ModuleMap, ParsedRouteTemplate, RouteToken, RuntimeOptions, RuntimeRequest, RuntimeResult };
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, MiddlewareHandler } from 'hono';
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 MockContext = Context;
81
- type MockMiddleware = MiddlewareHandler;
82
- type MockResponseHandler = (context: Context) => Response | Promise<Response> | unknown;
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, MockContext, MockMiddleware, MockResponseHandler, ModuleMap, ParsedRouteTemplate, RouteToken, RuntimeOptions, RuntimeRequest, RuntimeResult };
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
- import { PatternRouter } from 'hono/router/pattern-router';
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
- response: value
218
+ handler: value
219
219
  }
220
220
  ];
221
221
  }
@@ -224,7 +224,7 @@ function normalizeRules(value) {
224
224
  }
225
225
  return [
226
226
  {
227
- response: value
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.response;
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(resolvedUrl ?? modulePath);
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
- return normalized.startsWith("text/") || normalized.includes("json") || normalized.includes("xml") || normalized.includes("javascript");
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 ?? response.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 ?? c.res;
544
+ const resolved = resolveResponse(response, c.res);
506
545
  if (route.delay && route.delay > 0) {
507
546
  await delay(route.delay);
508
547
  }
509
- return applyRouteOverrides(resolved, route);
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 match = app.router.match(matchMethod, req.path);
626
- if (!match || match[0].length === 0) {
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
- return await toRuntimeResult(response);
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": "0.1.0",
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
- "hono": "^4.11.4"
30
+ "@mokup/shared": "1.0.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "typescript": "^5.9.3",