@mokup/server 1.0.2 → 1.0.4
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 +265 -16
- package/dist/index.d.cts +5 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +267 -18
- package/dist/node.cjs +7 -0
- package/dist/node.d.cts +1 -0
- package/dist/node.d.mts +1 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.mjs +1 -0
- package/dist/shared/{server.HVB7OYyI.mjs → server.Dje1y79O.mjs} +1 -1
- package/dist/worker.mjs +1 -1
- package/package.json +10 -4
package/dist/index.cjs
CHANGED
|
@@ -141,6 +141,20 @@ function matchesFilter(file, include, exclude) {
|
|
|
141
141
|
}
|
|
142
142
|
return true;
|
|
143
143
|
}
|
|
144
|
+
function normalizeIgnorePrefix(value, fallback = ["."]) {
|
|
145
|
+
const list = typeof value === "undefined" ? fallback : Array.isArray(value) ? value : [value];
|
|
146
|
+
return list.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
147
|
+
}
|
|
148
|
+
function hasIgnoredPrefix(file, rootDir, prefixes) {
|
|
149
|
+
if (prefixes.length === 0) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const relativePath = toPosix(pathe.relative(rootDir, file));
|
|
153
|
+
const segments = relativePath.split("/");
|
|
154
|
+
return segments.some(
|
|
155
|
+
(segment) => prefixes.some((prefix) => segment.startsWith(prefix))
|
|
156
|
+
);
|
|
157
|
+
}
|
|
144
158
|
function delay(ms) {
|
|
145
159
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
146
160
|
}
|
|
@@ -233,7 +247,7 @@ function createRouteHandler(route) {
|
|
|
233
247
|
return normalizeHandlerValue(c, value);
|
|
234
248
|
};
|
|
235
249
|
}
|
|
236
|
-
function createFinalizeMiddleware(route) {
|
|
250
|
+
function createFinalizeMiddleware(route, onResponse) {
|
|
237
251
|
return async (c, next) => {
|
|
238
252
|
const response = await next();
|
|
239
253
|
const resolved = resolveResponse(response, c.res);
|
|
@@ -242,6 +256,15 @@ function createFinalizeMiddleware(route) {
|
|
|
242
256
|
}
|
|
243
257
|
const overridden = applyRouteOverrides(resolved, route);
|
|
244
258
|
c.res = overridden;
|
|
259
|
+
if (onResponse) {
|
|
260
|
+
try {
|
|
261
|
+
const result = onResponse(route, overridden);
|
|
262
|
+
if (result instanceof Promise) {
|
|
263
|
+
result.catch(() => void 0);
|
|
264
|
+
}
|
|
265
|
+
} catch {
|
|
266
|
+
}
|
|
267
|
+
}
|
|
245
268
|
return overridden;
|
|
246
269
|
};
|
|
247
270
|
}
|
|
@@ -251,14 +274,14 @@ function wrapMiddleware(handler) {
|
|
|
251
274
|
return resolveResponse(response, c.res);
|
|
252
275
|
};
|
|
253
276
|
}
|
|
254
|
-
function createHonoApp(routes) {
|
|
277
|
+
function createHonoApp(routes, options = {}) {
|
|
255
278
|
const app = new hono.Hono({ router: new hono.PatternRouter(), strict: false });
|
|
256
279
|
for (const route of routes) {
|
|
257
280
|
const middlewares = route.middlewares?.map((entry) => wrapMiddleware(entry.handle)) ?? [];
|
|
258
281
|
app.on(
|
|
259
282
|
route.method,
|
|
260
283
|
toHonoPath(route),
|
|
261
|
-
createFinalizeMiddleware(route),
|
|
284
|
+
createFinalizeMiddleware(route, options.onResponse),
|
|
262
285
|
...middlewares,
|
|
263
286
|
createRouteHandler(route)
|
|
264
287
|
);
|
|
@@ -363,6 +386,20 @@ function resolveGroupRoot(dirs, serverRoot) {
|
|
|
363
386
|
}
|
|
364
387
|
return common;
|
|
365
388
|
}
|
|
389
|
+
const disabledReasonSet = /* @__PURE__ */ new Set([
|
|
390
|
+
"disabled",
|
|
391
|
+
"disabled-dir",
|
|
392
|
+
"exclude",
|
|
393
|
+
"ignore-prefix",
|
|
394
|
+
"include",
|
|
395
|
+
"unknown"
|
|
396
|
+
]);
|
|
397
|
+
function normalizeDisabledReason(reason) {
|
|
398
|
+
if (reason && disabledReasonSet.has(reason)) {
|
|
399
|
+
return reason;
|
|
400
|
+
}
|
|
401
|
+
return "unknown";
|
|
402
|
+
}
|
|
366
403
|
function formatRouteFile(file, root) {
|
|
367
404
|
if (!root) {
|
|
368
405
|
return toPosixPath(file);
|
|
@@ -423,6 +460,17 @@ function toPlaygroundRoute(route, root, groups) {
|
|
|
423
460
|
group: matchedGroup?.label
|
|
424
461
|
};
|
|
425
462
|
}
|
|
463
|
+
function toPlaygroundDisabledRoute(route, root, groups) {
|
|
464
|
+
const matchedGroup = resolveRouteGroup(route.file, groups);
|
|
465
|
+
return {
|
|
466
|
+
file: formatRouteFile(route.file, root),
|
|
467
|
+
reason: normalizeDisabledReason(route.reason),
|
|
468
|
+
method: route.method,
|
|
469
|
+
url: route.url,
|
|
470
|
+
groupKey: matchedGroup?.key,
|
|
471
|
+
group: matchedGroup?.label
|
|
472
|
+
};
|
|
473
|
+
}
|
|
426
474
|
function registerPlaygroundRoutes(params) {
|
|
427
475
|
if (!params.config.enabled) {
|
|
428
476
|
return;
|
|
@@ -440,7 +488,16 @@ function registerPlaygroundRoutes(params) {
|
|
|
440
488
|
return new Response("Playground is not available.", { status: 500 });
|
|
441
489
|
}
|
|
442
490
|
};
|
|
443
|
-
params.app.get(playgroundPath, (c) =>
|
|
491
|
+
params.app.get(playgroundPath, (c) => {
|
|
492
|
+
try {
|
|
493
|
+
const pathname = new URL(c.req.raw.url, "http://localhost").pathname;
|
|
494
|
+
if (pathname.endsWith("/")) {
|
|
495
|
+
return serveIndex();
|
|
496
|
+
}
|
|
497
|
+
} catch {
|
|
498
|
+
}
|
|
499
|
+
return c.redirect(`${playgroundPath}/`);
|
|
500
|
+
});
|
|
444
501
|
params.app.get(`${playgroundPath}/`, () => serveIndex());
|
|
445
502
|
params.app.get(`${playgroundPath}/index.html`, () => serveIndex());
|
|
446
503
|
params.app.get(`${playgroundPath}/routes`, (c) => {
|
|
@@ -451,7 +508,8 @@ function registerPlaygroundRoutes(params) {
|
|
|
451
508
|
root: baseRoot,
|
|
452
509
|
count: params.routes.length,
|
|
453
510
|
groups: groups.map((group) => ({ key: group.key, label: group.label })),
|
|
454
|
-
routes: params.routes.map((route) => toPlaygroundRoute(route, baseRoot, groups))
|
|
511
|
+
routes: params.routes.map((route) => toPlaygroundRoute(route, baseRoot, groups)),
|
|
512
|
+
disabled: (params.disabledRoutes ?? []).map((route) => toPlaygroundDisabledRoute(route, baseRoot, groups))
|
|
455
513
|
});
|
|
456
514
|
});
|
|
457
515
|
params.app.get(`${playgroundPath}/*`, async (c) => {
|
|
@@ -718,6 +776,15 @@ async function resolveDirectoryConfig(params) {
|
|
|
718
776
|
if (typeof config.enabled === "boolean") {
|
|
719
777
|
merged.enabled = config.enabled;
|
|
720
778
|
}
|
|
779
|
+
if (typeof config.ignorePrefix !== "undefined") {
|
|
780
|
+
merged.ignorePrefix = config.ignorePrefix;
|
|
781
|
+
}
|
|
782
|
+
if (typeof config.include !== "undefined") {
|
|
783
|
+
merged.include = config.include;
|
|
784
|
+
}
|
|
785
|
+
if (typeof config.exclude !== "undefined") {
|
|
786
|
+
merged.exclude = config.exclude;
|
|
787
|
+
}
|
|
721
788
|
const normalized = normalizeMiddlewares(config.middleware, configPath, logger);
|
|
722
789
|
if (normalized.length > 0) {
|
|
723
790
|
merged.middlewares.push(...normalized);
|
|
@@ -849,19 +916,44 @@ async function loadRules(file, logger) {
|
|
|
849
916
|
return [value];
|
|
850
917
|
}
|
|
851
918
|
|
|
919
|
+
const silentLogger = {
|
|
920
|
+
info: () => {
|
|
921
|
+
},
|
|
922
|
+
warn: () => {
|
|
923
|
+
},
|
|
924
|
+
error: () => {
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
function resolveSkipRoute(params) {
|
|
928
|
+
const derived = params.derived ?? deriveRouteFromFile(params.file, params.rootDir, silentLogger);
|
|
929
|
+
if (!derived?.method) {
|
|
930
|
+
return null;
|
|
931
|
+
}
|
|
932
|
+
const resolved = resolveRule({
|
|
933
|
+
rule: { handler: null },
|
|
934
|
+
derivedTemplate: derived.template,
|
|
935
|
+
derivedMethod: derived.method,
|
|
936
|
+
prefix: params.prefix,
|
|
937
|
+
file: params.file,
|
|
938
|
+
logger: silentLogger
|
|
939
|
+
});
|
|
940
|
+
if (!resolved) {
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
return {
|
|
944
|
+
method: resolved.method,
|
|
945
|
+
url: resolved.template
|
|
946
|
+
};
|
|
947
|
+
}
|
|
852
948
|
async function scanRoutes(params) {
|
|
853
949
|
const routes = [];
|
|
854
950
|
const seen = /* @__PURE__ */ new Set();
|
|
855
951
|
const files = await collectFiles(params.dirs);
|
|
952
|
+
const globalIgnorePrefix = normalizeIgnorePrefix(params.ignorePrefix);
|
|
856
953
|
const configCache = /* @__PURE__ */ new Map();
|
|
857
954
|
const fileCache = /* @__PURE__ */ new Map();
|
|
955
|
+
const shouldCollectSkip = typeof params.onSkip === "function";
|
|
858
956
|
for (const fileInfo of files) {
|
|
859
|
-
if (!isSupportedFile(fileInfo.file)) {
|
|
860
|
-
continue;
|
|
861
|
-
}
|
|
862
|
-
if (!matchesFilter(fileInfo.file, params.include, params.exclude)) {
|
|
863
|
-
continue;
|
|
864
|
-
}
|
|
865
957
|
const config = await resolveDirectoryConfig({
|
|
866
958
|
file: fileInfo.file,
|
|
867
959
|
rootDir: fileInfo.rootDir,
|
|
@@ -870,6 +962,58 @@ async function scanRoutes(params) {
|
|
|
870
962
|
fileCache
|
|
871
963
|
});
|
|
872
964
|
if (config.enabled === false) {
|
|
965
|
+
if (shouldCollectSkip && isSupportedFile(fileInfo.file)) {
|
|
966
|
+
const resolved = resolveSkipRoute({
|
|
967
|
+
file: fileInfo.file,
|
|
968
|
+
rootDir: fileInfo.rootDir,
|
|
969
|
+
prefix: params.prefix
|
|
970
|
+
});
|
|
971
|
+
params.onSkip?.({
|
|
972
|
+
file: fileInfo.file,
|
|
973
|
+
reason: "disabled-dir",
|
|
974
|
+
method: resolved?.method,
|
|
975
|
+
url: resolved?.url
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
980
|
+
const effectiveIgnorePrefix = typeof config.ignorePrefix !== "undefined" ? normalizeIgnorePrefix(config.ignorePrefix, []) : globalIgnorePrefix;
|
|
981
|
+
if (hasIgnoredPrefix(fileInfo.file, fileInfo.rootDir, effectiveIgnorePrefix)) {
|
|
982
|
+
if (shouldCollectSkip && isSupportedFile(fileInfo.file)) {
|
|
983
|
+
const resolved = resolveSkipRoute({
|
|
984
|
+
file: fileInfo.file,
|
|
985
|
+
rootDir: fileInfo.rootDir,
|
|
986
|
+
prefix: params.prefix
|
|
987
|
+
});
|
|
988
|
+
params.onSkip?.({
|
|
989
|
+
file: fileInfo.file,
|
|
990
|
+
reason: "ignore-prefix",
|
|
991
|
+
method: resolved?.method,
|
|
992
|
+
url: resolved?.url
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
if (!isSupportedFile(fileInfo.file)) {
|
|
998
|
+
continue;
|
|
999
|
+
}
|
|
1000
|
+
const effectiveInclude = typeof config.include !== "undefined" ? config.include : params.include;
|
|
1001
|
+
const effectiveExclude = typeof config.exclude !== "undefined" ? config.exclude : params.exclude;
|
|
1002
|
+
if (!matchesFilter(fileInfo.file, effectiveInclude, effectiveExclude)) {
|
|
1003
|
+
if (shouldCollectSkip) {
|
|
1004
|
+
const resolved = resolveSkipRoute({
|
|
1005
|
+
file: fileInfo.file,
|
|
1006
|
+
rootDir: fileInfo.rootDir,
|
|
1007
|
+
prefix: params.prefix
|
|
1008
|
+
});
|
|
1009
|
+
const reason = effectiveExclude && matchesFilter(fileInfo.file, void 0, effectiveExclude) ? "exclude" : "include";
|
|
1010
|
+
params.onSkip?.({
|
|
1011
|
+
file: fileInfo.file,
|
|
1012
|
+
reason,
|
|
1013
|
+
method: resolved?.method,
|
|
1014
|
+
url: resolved?.url
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
873
1017
|
continue;
|
|
874
1018
|
}
|
|
875
1019
|
const derived = deriveRouteFromFile(fileInfo.file, fileInfo.rootDir, params.logger);
|
|
@@ -881,6 +1025,23 @@ async function scanRoutes(params) {
|
|
|
881
1025
|
if (!rule || typeof rule !== "object") {
|
|
882
1026
|
continue;
|
|
883
1027
|
}
|
|
1028
|
+
if (rule.enabled === false) {
|
|
1029
|
+
if (shouldCollectSkip) {
|
|
1030
|
+
const resolved2 = resolveSkipRoute({
|
|
1031
|
+
file: fileInfo.file,
|
|
1032
|
+
rootDir: fileInfo.rootDir,
|
|
1033
|
+
prefix: params.prefix,
|
|
1034
|
+
derived
|
|
1035
|
+
});
|
|
1036
|
+
params.onSkip?.({
|
|
1037
|
+
file: fileInfo.file,
|
|
1038
|
+
reason: "disabled",
|
|
1039
|
+
method: resolved2?.method,
|
|
1040
|
+
url: resolved2?.url
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
884
1045
|
const ruleValue = rule;
|
|
885
1046
|
const unsupportedKeys = ["response", "url", "method"].filter(
|
|
886
1047
|
(key2) => key2 in ruleValue
|
|
@@ -981,13 +1142,17 @@ function buildApp(params) {
|
|
|
981
1142
|
registerPlaygroundRoutes({
|
|
982
1143
|
app,
|
|
983
1144
|
routes: params.routes,
|
|
1145
|
+
disabledRoutes: params.disabledRoutes,
|
|
984
1146
|
dirs: params.dirs,
|
|
985
1147
|
logger: params.logger,
|
|
986
1148
|
config: params.playground,
|
|
987
1149
|
root: params.root
|
|
988
1150
|
});
|
|
1151
|
+
if (params.wsHandler && params.playground.enabled) {
|
|
1152
|
+
app.get(`${params.playground.path}/ws`, params.wsHandler);
|
|
1153
|
+
}
|
|
989
1154
|
if (params.routes.length > 0) {
|
|
990
|
-
const mockApp = createHonoApp(params.routes);
|
|
1155
|
+
const mockApp = createHonoApp(params.routes, { onResponse: params.onResponse });
|
|
991
1156
|
app.route("/", mockApp);
|
|
992
1157
|
}
|
|
993
1158
|
return app;
|
|
@@ -1074,22 +1239,94 @@ async function createFetchServer(options = {}) {
|
|
|
1074
1239
|
const logger = createLogger(logEnabled);
|
|
1075
1240
|
const playgroundConfig = resolvePlaygroundOptions(resolvePlaygroundInput(optionList));
|
|
1076
1241
|
const dirs = resolveAllDirs(optionList, root);
|
|
1242
|
+
const routeCounts = {};
|
|
1243
|
+
const wsClients = /* @__PURE__ */ new Set();
|
|
1244
|
+
let totalCount = 0;
|
|
1245
|
+
let wsHandler;
|
|
1246
|
+
let injectWebSocket;
|
|
1247
|
+
function getRouteKey(route) {
|
|
1248
|
+
return `${route.method} ${route.template}`;
|
|
1249
|
+
}
|
|
1250
|
+
function buildSnapshot() {
|
|
1251
|
+
return {
|
|
1252
|
+
type: "snapshot",
|
|
1253
|
+
total: totalCount,
|
|
1254
|
+
perRoute: { ...routeCounts }
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
function broadcast(payload) {
|
|
1258
|
+
if (wsClients.size === 0) {
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
const message = JSON.stringify(payload);
|
|
1262
|
+
for (const client of wsClients) {
|
|
1263
|
+
try {
|
|
1264
|
+
client.send(message);
|
|
1265
|
+
} catch {
|
|
1266
|
+
wsClients.delete(client);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
function registerWsClient(client) {
|
|
1271
|
+
wsClients.add(client);
|
|
1272
|
+
try {
|
|
1273
|
+
client.send(JSON.stringify(buildSnapshot()));
|
|
1274
|
+
} catch {
|
|
1275
|
+
wsClients.delete(client);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
function handleRouteResponse(route) {
|
|
1279
|
+
const routeKey = getRouteKey(route);
|
|
1280
|
+
routeCounts[routeKey] = (routeCounts[routeKey] ?? 0) + 1;
|
|
1281
|
+
totalCount += 1;
|
|
1282
|
+
broadcast({ type: "increment", routeKey, total: totalCount });
|
|
1283
|
+
}
|
|
1284
|
+
async function setupPlaygroundWebSocket(app2) {
|
|
1285
|
+
if (!playgroundConfig.enabled) {
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
try {
|
|
1289
|
+
const mod = await import('@hono/node-ws');
|
|
1290
|
+
const { createNodeWebSocket } = mod;
|
|
1291
|
+
const { upgradeWebSocket, injectWebSocket: inject } = createNodeWebSocket({ app: app2 });
|
|
1292
|
+
wsHandler = upgradeWebSocket(() => ({
|
|
1293
|
+
onOpen: (_event, ws) => {
|
|
1294
|
+
registerWsClient(ws);
|
|
1295
|
+
},
|
|
1296
|
+
onClose: (_event, ws) => {
|
|
1297
|
+
wsClients.delete(ws);
|
|
1298
|
+
},
|
|
1299
|
+
onMessage: () => {
|
|
1300
|
+
}
|
|
1301
|
+
}));
|
|
1302
|
+
injectWebSocket = (server2) => {
|
|
1303
|
+
inject(server2);
|
|
1304
|
+
};
|
|
1305
|
+
} catch {
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1077
1308
|
let routes = [];
|
|
1309
|
+
let disabledRoutes = [];
|
|
1078
1310
|
let app = buildApp({
|
|
1079
1311
|
routes,
|
|
1312
|
+
disabledRoutes,
|
|
1080
1313
|
dirs,
|
|
1081
1314
|
playground: playgroundConfig,
|
|
1082
1315
|
root,
|
|
1083
|
-
logger
|
|
1316
|
+
logger,
|
|
1317
|
+
onResponse: handleRouteResponse,
|
|
1318
|
+
wsHandler
|
|
1084
1319
|
});
|
|
1085
1320
|
const refreshRoutes = async () => {
|
|
1086
1321
|
try {
|
|
1087
1322
|
const collected = [];
|
|
1323
|
+
const collectedDisabled = [];
|
|
1088
1324
|
for (const entry of optionList) {
|
|
1089
1325
|
const scanParams = {
|
|
1090
1326
|
dirs: resolveDirs(entry.dir, root),
|
|
1091
1327
|
prefix: entry.prefix ?? "",
|
|
1092
|
-
logger
|
|
1328
|
+
logger,
|
|
1329
|
+
onSkip: (info) => collectedDisabled.push(info)
|
|
1093
1330
|
};
|
|
1094
1331
|
if (entry.include) {
|
|
1095
1332
|
scanParams.include = entry.include;
|
|
@@ -1097,17 +1334,24 @@ async function createFetchServer(options = {}) {
|
|
|
1097
1334
|
if (entry.exclude) {
|
|
1098
1335
|
scanParams.exclude = entry.exclude;
|
|
1099
1336
|
}
|
|
1337
|
+
if (typeof entry.ignorePrefix !== "undefined") {
|
|
1338
|
+
scanParams.ignorePrefix = entry.ignorePrefix;
|
|
1339
|
+
}
|
|
1100
1340
|
const scanned = await scanRoutes(scanParams);
|
|
1101
1341
|
collected.push(...scanned);
|
|
1102
1342
|
}
|
|
1103
1343
|
const resolvedRoutes = sortRoutes(collected);
|
|
1104
1344
|
routes = resolvedRoutes;
|
|
1345
|
+
disabledRoutes = collectedDisabled;
|
|
1105
1346
|
app = buildApp({
|
|
1106
1347
|
routes,
|
|
1348
|
+
disabledRoutes,
|
|
1107
1349
|
dirs,
|
|
1108
1350
|
playground: playgroundConfig,
|
|
1109
1351
|
root,
|
|
1110
|
-
logger
|
|
1352
|
+
logger,
|
|
1353
|
+
onResponse: handleRouteResponse,
|
|
1354
|
+
wsHandler
|
|
1111
1355
|
});
|
|
1112
1356
|
logger.info(`Loaded ${routes.length} mock routes.`);
|
|
1113
1357
|
} catch (error) {
|
|
@@ -1115,6 +1359,10 @@ async function createFetchServer(options = {}) {
|
|
|
1115
1359
|
}
|
|
1116
1360
|
};
|
|
1117
1361
|
await refreshRoutes();
|
|
1362
|
+
await setupPlaygroundWebSocket(app);
|
|
1363
|
+
if (wsHandler && playgroundConfig.enabled) {
|
|
1364
|
+
app.get(`${playgroundConfig.path}/ws`, wsHandler);
|
|
1365
|
+
}
|
|
1118
1366
|
const scheduleRefresh = createDebouncer(80, () => {
|
|
1119
1367
|
void refreshRoutes();
|
|
1120
1368
|
});
|
|
@@ -1128,7 +1376,8 @@ async function createFetchServer(options = {}) {
|
|
|
1128
1376
|
const server = {
|
|
1129
1377
|
fetch,
|
|
1130
1378
|
refresh: refreshRoutes,
|
|
1131
|
-
getRoutes: () => routes
|
|
1379
|
+
getRoutes: () => routes,
|
|
1380
|
+
injectWebSocket
|
|
1132
1381
|
};
|
|
1133
1382
|
if (watcher) {
|
|
1134
1383
|
server.close = async () => {
|
package/dist/index.d.cts
CHANGED
|
@@ -70,6 +70,7 @@ interface FetchServerOptions {
|
|
|
70
70
|
prefix?: string;
|
|
71
71
|
include?: RegExp | RegExp[];
|
|
72
72
|
exclude?: RegExp | RegExp[];
|
|
73
|
+
ignorePrefix?: string | string[];
|
|
73
74
|
watch?: boolean;
|
|
74
75
|
log?: boolean;
|
|
75
76
|
playground?: boolean | {
|
|
@@ -86,8 +87,12 @@ interface FetchServer {
|
|
|
86
87
|
fetch: (request: Request) => Promise<Response>;
|
|
87
88
|
refresh: () => Promise<void>;
|
|
88
89
|
getRoutes: () => RouteTable;
|
|
90
|
+
injectWebSocket?: (server: NodeWebSocketServer) => void;
|
|
89
91
|
close?: () => Promise<void>;
|
|
90
92
|
}
|
|
93
|
+
interface NodeWebSocketServer {
|
|
94
|
+
on: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
95
|
+
}
|
|
91
96
|
declare function createFetchServer(options?: FetchServerOptionsInput): Promise<FetchServer>;
|
|
92
97
|
|
|
93
98
|
interface HonoContextLike {
|
package/dist/index.d.mts
CHANGED
|
@@ -70,6 +70,7 @@ interface FetchServerOptions {
|
|
|
70
70
|
prefix?: string;
|
|
71
71
|
include?: RegExp | RegExp[];
|
|
72
72
|
exclude?: RegExp | RegExp[];
|
|
73
|
+
ignorePrefix?: string | string[];
|
|
73
74
|
watch?: boolean;
|
|
74
75
|
log?: boolean;
|
|
75
76
|
playground?: boolean | {
|
|
@@ -86,8 +87,12 @@ interface FetchServer {
|
|
|
86
87
|
fetch: (request: Request) => Promise<Response>;
|
|
87
88
|
refresh: () => Promise<void>;
|
|
88
89
|
getRoutes: () => RouteTable;
|
|
90
|
+
injectWebSocket?: (server: NodeWebSocketServer) => void;
|
|
89
91
|
close?: () => Promise<void>;
|
|
90
92
|
}
|
|
93
|
+
interface NodeWebSocketServer {
|
|
94
|
+
on: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
95
|
+
}
|
|
91
96
|
declare function createFetchServer(options?: FetchServerOptionsInput): Promise<FetchServer>;
|
|
92
97
|
|
|
93
98
|
interface HonoContextLike {
|
package/dist/index.d.ts
CHANGED
|
@@ -70,6 +70,7 @@ interface FetchServerOptions {
|
|
|
70
70
|
prefix?: string;
|
|
71
71
|
include?: RegExp | RegExp[];
|
|
72
72
|
exclude?: RegExp | RegExp[];
|
|
73
|
+
ignorePrefix?: string | string[];
|
|
73
74
|
watch?: boolean;
|
|
74
75
|
log?: boolean;
|
|
75
76
|
playground?: boolean | {
|
|
@@ -86,8 +87,12 @@ interface FetchServer {
|
|
|
86
87
|
fetch: (request: Request) => Promise<Response>;
|
|
87
88
|
refresh: () => Promise<void>;
|
|
88
89
|
getRoutes: () => RouteTable;
|
|
90
|
+
injectWebSocket?: (server: NodeWebSocketServer) => void;
|
|
89
91
|
close?: () => Promise<void>;
|
|
90
92
|
}
|
|
93
|
+
interface NodeWebSocketServer {
|
|
94
|
+
on: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
95
|
+
}
|
|
91
96
|
declare function createFetchServer(options?: FetchServerOptionsInput): Promise<FetchServer>;
|
|
92
97
|
|
|
93
98
|
interface HonoContextLike {
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createRuntime, compareRouteScore, parseRouteTemplate } from '@mokup/runtime';
|
|
2
|
-
import { t as toRuntimeOptions, a as toRuntimeRequestFromNode, b as applyRuntimeResultToNode,
|
|
2
|
+
import { t as toRuntimeOptions, a as toRuntimeRequestFromNode, b as applyRuntimeResultToNode, d as toBinaryBody, c as createFetchHandler } from './shared/server.Dje1y79O.mjs';
|
|
3
3
|
import { cwd } from 'node:process';
|
|
4
4
|
import { Hono, PatternRouter } from '@mokup/shared/hono';
|
|
5
|
-
import { resolve, isAbsolute, join, normalize, extname, dirname,
|
|
5
|
+
import { resolve, isAbsolute, relative, join, normalize, extname, dirname, basename } from '@mokup/shared/pathe';
|
|
6
6
|
import { promises } from 'node:fs';
|
|
7
7
|
import { createRequire } from 'node:module';
|
|
8
8
|
import { Buffer } from 'node:buffer';
|
|
@@ -138,6 +138,20 @@ function matchesFilter(file, include, exclude) {
|
|
|
138
138
|
}
|
|
139
139
|
return true;
|
|
140
140
|
}
|
|
141
|
+
function normalizeIgnorePrefix(value, fallback = ["."]) {
|
|
142
|
+
const list = typeof value === "undefined" ? fallback : Array.isArray(value) ? value : [value];
|
|
143
|
+
return list.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
144
|
+
}
|
|
145
|
+
function hasIgnoredPrefix(file, rootDir, prefixes) {
|
|
146
|
+
if (prefixes.length === 0) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
const relativePath = toPosix(relative(rootDir, file));
|
|
150
|
+
const segments = relativePath.split("/");
|
|
151
|
+
return segments.some(
|
|
152
|
+
(segment) => prefixes.some((prefix) => segment.startsWith(prefix))
|
|
153
|
+
);
|
|
154
|
+
}
|
|
141
155
|
function delay(ms) {
|
|
142
156
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
143
157
|
}
|
|
@@ -230,7 +244,7 @@ function createRouteHandler(route) {
|
|
|
230
244
|
return normalizeHandlerValue(c, value);
|
|
231
245
|
};
|
|
232
246
|
}
|
|
233
|
-
function createFinalizeMiddleware(route) {
|
|
247
|
+
function createFinalizeMiddleware(route, onResponse) {
|
|
234
248
|
return async (c, next) => {
|
|
235
249
|
const response = await next();
|
|
236
250
|
const resolved = resolveResponse(response, c.res);
|
|
@@ -239,6 +253,15 @@ function createFinalizeMiddleware(route) {
|
|
|
239
253
|
}
|
|
240
254
|
const overridden = applyRouteOverrides(resolved, route);
|
|
241
255
|
c.res = overridden;
|
|
256
|
+
if (onResponse) {
|
|
257
|
+
try {
|
|
258
|
+
const result = onResponse(route, overridden);
|
|
259
|
+
if (result instanceof Promise) {
|
|
260
|
+
result.catch(() => void 0);
|
|
261
|
+
}
|
|
262
|
+
} catch {
|
|
263
|
+
}
|
|
264
|
+
}
|
|
242
265
|
return overridden;
|
|
243
266
|
};
|
|
244
267
|
}
|
|
@@ -248,14 +271,14 @@ function wrapMiddleware(handler) {
|
|
|
248
271
|
return resolveResponse(response, c.res);
|
|
249
272
|
};
|
|
250
273
|
}
|
|
251
|
-
function createHonoApp(routes) {
|
|
274
|
+
function createHonoApp(routes, options = {}) {
|
|
252
275
|
const app = new Hono({ router: new PatternRouter(), strict: false });
|
|
253
276
|
for (const route of routes) {
|
|
254
277
|
const middlewares = route.middlewares?.map((entry) => wrapMiddleware(entry.handle)) ?? [];
|
|
255
278
|
app.on(
|
|
256
279
|
route.method,
|
|
257
280
|
toHonoPath(route),
|
|
258
|
-
createFinalizeMiddleware(route),
|
|
281
|
+
createFinalizeMiddleware(route, options.onResponse),
|
|
259
282
|
...middlewares,
|
|
260
283
|
createRouteHandler(route)
|
|
261
284
|
);
|
|
@@ -360,6 +383,20 @@ function resolveGroupRoot(dirs, serverRoot) {
|
|
|
360
383
|
}
|
|
361
384
|
return common;
|
|
362
385
|
}
|
|
386
|
+
const disabledReasonSet = /* @__PURE__ */ new Set([
|
|
387
|
+
"disabled",
|
|
388
|
+
"disabled-dir",
|
|
389
|
+
"exclude",
|
|
390
|
+
"ignore-prefix",
|
|
391
|
+
"include",
|
|
392
|
+
"unknown"
|
|
393
|
+
]);
|
|
394
|
+
function normalizeDisabledReason(reason) {
|
|
395
|
+
if (reason && disabledReasonSet.has(reason)) {
|
|
396
|
+
return reason;
|
|
397
|
+
}
|
|
398
|
+
return "unknown";
|
|
399
|
+
}
|
|
363
400
|
function formatRouteFile(file, root) {
|
|
364
401
|
if (!root) {
|
|
365
402
|
return toPosixPath(file);
|
|
@@ -420,6 +457,17 @@ function toPlaygroundRoute(route, root, groups) {
|
|
|
420
457
|
group: matchedGroup?.label
|
|
421
458
|
};
|
|
422
459
|
}
|
|
460
|
+
function toPlaygroundDisabledRoute(route, root, groups) {
|
|
461
|
+
const matchedGroup = resolveRouteGroup(route.file, groups);
|
|
462
|
+
return {
|
|
463
|
+
file: formatRouteFile(route.file, root),
|
|
464
|
+
reason: normalizeDisabledReason(route.reason),
|
|
465
|
+
method: route.method,
|
|
466
|
+
url: route.url,
|
|
467
|
+
groupKey: matchedGroup?.key,
|
|
468
|
+
group: matchedGroup?.label
|
|
469
|
+
};
|
|
470
|
+
}
|
|
423
471
|
function registerPlaygroundRoutes(params) {
|
|
424
472
|
if (!params.config.enabled) {
|
|
425
473
|
return;
|
|
@@ -437,7 +485,16 @@ function registerPlaygroundRoutes(params) {
|
|
|
437
485
|
return new Response("Playground is not available.", { status: 500 });
|
|
438
486
|
}
|
|
439
487
|
};
|
|
440
|
-
params.app.get(playgroundPath, (c) =>
|
|
488
|
+
params.app.get(playgroundPath, (c) => {
|
|
489
|
+
try {
|
|
490
|
+
const pathname = new URL(c.req.raw.url, "http://localhost").pathname;
|
|
491
|
+
if (pathname.endsWith("/")) {
|
|
492
|
+
return serveIndex();
|
|
493
|
+
}
|
|
494
|
+
} catch {
|
|
495
|
+
}
|
|
496
|
+
return c.redirect(`${playgroundPath}/`);
|
|
497
|
+
});
|
|
441
498
|
params.app.get(`${playgroundPath}/`, () => serveIndex());
|
|
442
499
|
params.app.get(`${playgroundPath}/index.html`, () => serveIndex());
|
|
443
500
|
params.app.get(`${playgroundPath}/routes`, (c) => {
|
|
@@ -448,7 +505,8 @@ function registerPlaygroundRoutes(params) {
|
|
|
448
505
|
root: baseRoot,
|
|
449
506
|
count: params.routes.length,
|
|
450
507
|
groups: groups.map((group) => ({ key: group.key, label: group.label })),
|
|
451
|
-
routes: params.routes.map((route) => toPlaygroundRoute(route, baseRoot, groups))
|
|
508
|
+
routes: params.routes.map((route) => toPlaygroundRoute(route, baseRoot, groups)),
|
|
509
|
+
disabled: (params.disabledRoutes ?? []).map((route) => toPlaygroundDisabledRoute(route, baseRoot, groups))
|
|
452
510
|
});
|
|
453
511
|
});
|
|
454
512
|
params.app.get(`${playgroundPath}/*`, async (c) => {
|
|
@@ -715,6 +773,15 @@ async function resolveDirectoryConfig(params) {
|
|
|
715
773
|
if (typeof config.enabled === "boolean") {
|
|
716
774
|
merged.enabled = config.enabled;
|
|
717
775
|
}
|
|
776
|
+
if (typeof config.ignorePrefix !== "undefined") {
|
|
777
|
+
merged.ignorePrefix = config.ignorePrefix;
|
|
778
|
+
}
|
|
779
|
+
if (typeof config.include !== "undefined") {
|
|
780
|
+
merged.include = config.include;
|
|
781
|
+
}
|
|
782
|
+
if (typeof config.exclude !== "undefined") {
|
|
783
|
+
merged.exclude = config.exclude;
|
|
784
|
+
}
|
|
718
785
|
const normalized = normalizeMiddlewares(config.middleware, configPath, logger);
|
|
719
786
|
if (normalized.length > 0) {
|
|
720
787
|
merged.middlewares.push(...normalized);
|
|
@@ -846,19 +913,44 @@ async function loadRules(file, logger) {
|
|
|
846
913
|
return [value];
|
|
847
914
|
}
|
|
848
915
|
|
|
916
|
+
const silentLogger = {
|
|
917
|
+
info: () => {
|
|
918
|
+
},
|
|
919
|
+
warn: () => {
|
|
920
|
+
},
|
|
921
|
+
error: () => {
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
function resolveSkipRoute(params) {
|
|
925
|
+
const derived = params.derived ?? deriveRouteFromFile(params.file, params.rootDir, silentLogger);
|
|
926
|
+
if (!derived?.method) {
|
|
927
|
+
return null;
|
|
928
|
+
}
|
|
929
|
+
const resolved = resolveRule({
|
|
930
|
+
rule: { handler: null },
|
|
931
|
+
derivedTemplate: derived.template,
|
|
932
|
+
derivedMethod: derived.method,
|
|
933
|
+
prefix: params.prefix,
|
|
934
|
+
file: params.file,
|
|
935
|
+
logger: silentLogger
|
|
936
|
+
});
|
|
937
|
+
if (!resolved) {
|
|
938
|
+
return null;
|
|
939
|
+
}
|
|
940
|
+
return {
|
|
941
|
+
method: resolved.method,
|
|
942
|
+
url: resolved.template
|
|
943
|
+
};
|
|
944
|
+
}
|
|
849
945
|
async function scanRoutes(params) {
|
|
850
946
|
const routes = [];
|
|
851
947
|
const seen = /* @__PURE__ */ new Set();
|
|
852
948
|
const files = await collectFiles(params.dirs);
|
|
949
|
+
const globalIgnorePrefix = normalizeIgnorePrefix(params.ignorePrefix);
|
|
853
950
|
const configCache = /* @__PURE__ */ new Map();
|
|
854
951
|
const fileCache = /* @__PURE__ */ new Map();
|
|
952
|
+
const shouldCollectSkip = typeof params.onSkip === "function";
|
|
855
953
|
for (const fileInfo of files) {
|
|
856
|
-
if (!isSupportedFile(fileInfo.file)) {
|
|
857
|
-
continue;
|
|
858
|
-
}
|
|
859
|
-
if (!matchesFilter(fileInfo.file, params.include, params.exclude)) {
|
|
860
|
-
continue;
|
|
861
|
-
}
|
|
862
954
|
const config = await resolveDirectoryConfig({
|
|
863
955
|
file: fileInfo.file,
|
|
864
956
|
rootDir: fileInfo.rootDir,
|
|
@@ -867,6 +959,58 @@ async function scanRoutes(params) {
|
|
|
867
959
|
fileCache
|
|
868
960
|
});
|
|
869
961
|
if (config.enabled === false) {
|
|
962
|
+
if (shouldCollectSkip && isSupportedFile(fileInfo.file)) {
|
|
963
|
+
const resolved = resolveSkipRoute({
|
|
964
|
+
file: fileInfo.file,
|
|
965
|
+
rootDir: fileInfo.rootDir,
|
|
966
|
+
prefix: params.prefix
|
|
967
|
+
});
|
|
968
|
+
params.onSkip?.({
|
|
969
|
+
file: fileInfo.file,
|
|
970
|
+
reason: "disabled-dir",
|
|
971
|
+
method: resolved?.method,
|
|
972
|
+
url: resolved?.url
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
const effectiveIgnorePrefix = typeof config.ignorePrefix !== "undefined" ? normalizeIgnorePrefix(config.ignorePrefix, []) : globalIgnorePrefix;
|
|
978
|
+
if (hasIgnoredPrefix(fileInfo.file, fileInfo.rootDir, effectiveIgnorePrefix)) {
|
|
979
|
+
if (shouldCollectSkip && isSupportedFile(fileInfo.file)) {
|
|
980
|
+
const resolved = resolveSkipRoute({
|
|
981
|
+
file: fileInfo.file,
|
|
982
|
+
rootDir: fileInfo.rootDir,
|
|
983
|
+
prefix: params.prefix
|
|
984
|
+
});
|
|
985
|
+
params.onSkip?.({
|
|
986
|
+
file: fileInfo.file,
|
|
987
|
+
reason: "ignore-prefix",
|
|
988
|
+
method: resolved?.method,
|
|
989
|
+
url: resolved?.url
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
if (!isSupportedFile(fileInfo.file)) {
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
const effectiveInclude = typeof config.include !== "undefined" ? config.include : params.include;
|
|
998
|
+
const effectiveExclude = typeof config.exclude !== "undefined" ? config.exclude : params.exclude;
|
|
999
|
+
if (!matchesFilter(fileInfo.file, effectiveInclude, effectiveExclude)) {
|
|
1000
|
+
if (shouldCollectSkip) {
|
|
1001
|
+
const resolved = resolveSkipRoute({
|
|
1002
|
+
file: fileInfo.file,
|
|
1003
|
+
rootDir: fileInfo.rootDir,
|
|
1004
|
+
prefix: params.prefix
|
|
1005
|
+
});
|
|
1006
|
+
const reason = effectiveExclude && matchesFilter(fileInfo.file, void 0, effectiveExclude) ? "exclude" : "include";
|
|
1007
|
+
params.onSkip?.({
|
|
1008
|
+
file: fileInfo.file,
|
|
1009
|
+
reason,
|
|
1010
|
+
method: resolved?.method,
|
|
1011
|
+
url: resolved?.url
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
870
1014
|
continue;
|
|
871
1015
|
}
|
|
872
1016
|
const derived = deriveRouteFromFile(fileInfo.file, fileInfo.rootDir, params.logger);
|
|
@@ -878,6 +1022,23 @@ async function scanRoutes(params) {
|
|
|
878
1022
|
if (!rule || typeof rule !== "object") {
|
|
879
1023
|
continue;
|
|
880
1024
|
}
|
|
1025
|
+
if (rule.enabled === false) {
|
|
1026
|
+
if (shouldCollectSkip) {
|
|
1027
|
+
const resolved2 = resolveSkipRoute({
|
|
1028
|
+
file: fileInfo.file,
|
|
1029
|
+
rootDir: fileInfo.rootDir,
|
|
1030
|
+
prefix: params.prefix,
|
|
1031
|
+
derived
|
|
1032
|
+
});
|
|
1033
|
+
params.onSkip?.({
|
|
1034
|
+
file: fileInfo.file,
|
|
1035
|
+
reason: "disabled",
|
|
1036
|
+
method: resolved2?.method,
|
|
1037
|
+
url: resolved2?.url
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
881
1042
|
const ruleValue = rule;
|
|
882
1043
|
const unsupportedKeys = ["response", "url", "method"].filter(
|
|
883
1044
|
(key2) => key2 in ruleValue
|
|
@@ -978,13 +1139,17 @@ function buildApp(params) {
|
|
|
978
1139
|
registerPlaygroundRoutes({
|
|
979
1140
|
app,
|
|
980
1141
|
routes: params.routes,
|
|
1142
|
+
disabledRoutes: params.disabledRoutes,
|
|
981
1143
|
dirs: params.dirs,
|
|
982
1144
|
logger: params.logger,
|
|
983
1145
|
config: params.playground,
|
|
984
1146
|
root: params.root
|
|
985
1147
|
});
|
|
1148
|
+
if (params.wsHandler && params.playground.enabled) {
|
|
1149
|
+
app.get(`${params.playground.path}/ws`, params.wsHandler);
|
|
1150
|
+
}
|
|
986
1151
|
if (params.routes.length > 0) {
|
|
987
|
-
const mockApp = createHonoApp(params.routes);
|
|
1152
|
+
const mockApp = createHonoApp(params.routes, { onResponse: params.onResponse });
|
|
988
1153
|
app.route("/", mockApp);
|
|
989
1154
|
}
|
|
990
1155
|
return app;
|
|
@@ -1071,22 +1236,94 @@ async function createFetchServer(options = {}) {
|
|
|
1071
1236
|
const logger = createLogger(logEnabled);
|
|
1072
1237
|
const playgroundConfig = resolvePlaygroundOptions(resolvePlaygroundInput(optionList));
|
|
1073
1238
|
const dirs = resolveAllDirs(optionList, root);
|
|
1239
|
+
const routeCounts = {};
|
|
1240
|
+
const wsClients = /* @__PURE__ */ new Set();
|
|
1241
|
+
let totalCount = 0;
|
|
1242
|
+
let wsHandler;
|
|
1243
|
+
let injectWebSocket;
|
|
1244
|
+
function getRouteKey(route) {
|
|
1245
|
+
return `${route.method} ${route.template}`;
|
|
1246
|
+
}
|
|
1247
|
+
function buildSnapshot() {
|
|
1248
|
+
return {
|
|
1249
|
+
type: "snapshot",
|
|
1250
|
+
total: totalCount,
|
|
1251
|
+
perRoute: { ...routeCounts }
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
function broadcast(payload) {
|
|
1255
|
+
if (wsClients.size === 0) {
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
const message = JSON.stringify(payload);
|
|
1259
|
+
for (const client of wsClients) {
|
|
1260
|
+
try {
|
|
1261
|
+
client.send(message);
|
|
1262
|
+
} catch {
|
|
1263
|
+
wsClients.delete(client);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
function registerWsClient(client) {
|
|
1268
|
+
wsClients.add(client);
|
|
1269
|
+
try {
|
|
1270
|
+
client.send(JSON.stringify(buildSnapshot()));
|
|
1271
|
+
} catch {
|
|
1272
|
+
wsClients.delete(client);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
function handleRouteResponse(route) {
|
|
1276
|
+
const routeKey = getRouteKey(route);
|
|
1277
|
+
routeCounts[routeKey] = (routeCounts[routeKey] ?? 0) + 1;
|
|
1278
|
+
totalCount += 1;
|
|
1279
|
+
broadcast({ type: "increment", routeKey, total: totalCount });
|
|
1280
|
+
}
|
|
1281
|
+
async function setupPlaygroundWebSocket(app2) {
|
|
1282
|
+
if (!playgroundConfig.enabled) {
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
try {
|
|
1286
|
+
const mod = await import('@hono/node-ws');
|
|
1287
|
+
const { createNodeWebSocket } = mod;
|
|
1288
|
+
const { upgradeWebSocket, injectWebSocket: inject } = createNodeWebSocket({ app: app2 });
|
|
1289
|
+
wsHandler = upgradeWebSocket(() => ({
|
|
1290
|
+
onOpen: (_event, ws) => {
|
|
1291
|
+
registerWsClient(ws);
|
|
1292
|
+
},
|
|
1293
|
+
onClose: (_event, ws) => {
|
|
1294
|
+
wsClients.delete(ws);
|
|
1295
|
+
},
|
|
1296
|
+
onMessage: () => {
|
|
1297
|
+
}
|
|
1298
|
+
}));
|
|
1299
|
+
injectWebSocket = (server2) => {
|
|
1300
|
+
inject(server2);
|
|
1301
|
+
};
|
|
1302
|
+
} catch {
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1074
1305
|
let routes = [];
|
|
1306
|
+
let disabledRoutes = [];
|
|
1075
1307
|
let app = buildApp({
|
|
1076
1308
|
routes,
|
|
1309
|
+
disabledRoutes,
|
|
1077
1310
|
dirs,
|
|
1078
1311
|
playground: playgroundConfig,
|
|
1079
1312
|
root,
|
|
1080
|
-
logger
|
|
1313
|
+
logger,
|
|
1314
|
+
onResponse: handleRouteResponse,
|
|
1315
|
+
wsHandler
|
|
1081
1316
|
});
|
|
1082
1317
|
const refreshRoutes = async () => {
|
|
1083
1318
|
try {
|
|
1084
1319
|
const collected = [];
|
|
1320
|
+
const collectedDisabled = [];
|
|
1085
1321
|
for (const entry of optionList) {
|
|
1086
1322
|
const scanParams = {
|
|
1087
1323
|
dirs: resolveDirs(entry.dir, root),
|
|
1088
1324
|
prefix: entry.prefix ?? "",
|
|
1089
|
-
logger
|
|
1325
|
+
logger,
|
|
1326
|
+
onSkip: (info) => collectedDisabled.push(info)
|
|
1090
1327
|
};
|
|
1091
1328
|
if (entry.include) {
|
|
1092
1329
|
scanParams.include = entry.include;
|
|
@@ -1094,17 +1331,24 @@ async function createFetchServer(options = {}) {
|
|
|
1094
1331
|
if (entry.exclude) {
|
|
1095
1332
|
scanParams.exclude = entry.exclude;
|
|
1096
1333
|
}
|
|
1334
|
+
if (typeof entry.ignorePrefix !== "undefined") {
|
|
1335
|
+
scanParams.ignorePrefix = entry.ignorePrefix;
|
|
1336
|
+
}
|
|
1097
1337
|
const scanned = await scanRoutes(scanParams);
|
|
1098
1338
|
collected.push(...scanned);
|
|
1099
1339
|
}
|
|
1100
1340
|
const resolvedRoutes = sortRoutes(collected);
|
|
1101
1341
|
routes = resolvedRoutes;
|
|
1342
|
+
disabledRoutes = collectedDisabled;
|
|
1102
1343
|
app = buildApp({
|
|
1103
1344
|
routes,
|
|
1345
|
+
disabledRoutes,
|
|
1104
1346
|
dirs,
|
|
1105
1347
|
playground: playgroundConfig,
|
|
1106
1348
|
root,
|
|
1107
|
-
logger
|
|
1349
|
+
logger,
|
|
1350
|
+
onResponse: handleRouteResponse,
|
|
1351
|
+
wsHandler
|
|
1108
1352
|
});
|
|
1109
1353
|
logger.info(`Loaded ${routes.length} mock routes.`);
|
|
1110
1354
|
} catch (error) {
|
|
@@ -1112,6 +1356,10 @@ async function createFetchServer(options = {}) {
|
|
|
1112
1356
|
}
|
|
1113
1357
|
};
|
|
1114
1358
|
await refreshRoutes();
|
|
1359
|
+
await setupPlaygroundWebSocket(app);
|
|
1360
|
+
if (wsHandler && playgroundConfig.enabled) {
|
|
1361
|
+
app.get(`${playgroundConfig.path}/ws`, wsHandler);
|
|
1362
|
+
}
|
|
1115
1363
|
const scheduleRefresh = createDebouncer(80, () => {
|
|
1116
1364
|
void refreshRoutes();
|
|
1117
1365
|
});
|
|
@@ -1125,7 +1373,8 @@ async function createFetchServer(options = {}) {
|
|
|
1125
1373
|
const server = {
|
|
1126
1374
|
fetch,
|
|
1127
1375
|
refresh: refreshRoutes,
|
|
1128
|
-
getRoutes: () => routes
|
|
1376
|
+
getRoutes: () => routes,
|
|
1377
|
+
injectWebSocket
|
|
1129
1378
|
};
|
|
1130
1379
|
if (watcher) {
|
|
1131
1380
|
server.close = async () => {
|
package/dist/node.cjs
ADDED
package/dist/node.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { serve } from '@hono/node-server';
|
package/dist/node.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { serve } from '@hono/node-server';
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { serve } from '@hono/node-server';
|
package/dist/node.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { serve } from '@hono/node-server';
|
|
@@ -255,4 +255,4 @@ function createFetchHandler(options) {
|
|
|
255
255
|
};
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
export { toRuntimeRequestFromNode as a, applyRuntimeResultToNode as b,
|
|
258
|
+
export { toRuntimeRequestFromNode as a, applyRuntimeResultToNode as b, createFetchHandler as c, toBinaryBody as d, toRuntimeOptions as t };
|
package/dist/worker.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mokup/server",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.4",
|
|
5
5
|
"description": "Server adapters for @mokup/runtime.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://mokup.icebreaker.top",
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
"import": "./dist/index.mjs",
|
|
17
17
|
"require": "./dist/index.cjs"
|
|
18
18
|
},
|
|
19
|
+
"./node": {
|
|
20
|
+
"types": "./dist/node.d.ts",
|
|
21
|
+
"import": "./dist/node.mjs",
|
|
22
|
+
"require": "./dist/node.cjs"
|
|
23
|
+
},
|
|
19
24
|
"./worker": {
|
|
20
25
|
"types": "./dist/worker.d.ts",
|
|
21
26
|
"import": "./dist/worker.mjs",
|
|
@@ -32,13 +37,14 @@
|
|
|
32
37
|
"dist"
|
|
33
38
|
],
|
|
34
39
|
"dependencies": {
|
|
35
|
-
"@
|
|
40
|
+
"@hono/node-server": "^1.19.9",
|
|
41
|
+
"@hono/node-ws": "^1.1.1",
|
|
42
|
+
"@mokup/playground": "0.0.8",
|
|
36
43
|
"@mokup/runtime": "1.0.0",
|
|
37
44
|
"@mokup/shared": "1.0.0"
|
|
38
45
|
},
|
|
39
46
|
"devDependencies": {
|
|
40
|
-
"@
|
|
41
|
-
"@types/node": "^25.0.9",
|
|
47
|
+
"@types/node": "^25.0.10",
|
|
42
48
|
"typescript": "^5.9.3",
|
|
43
49
|
"unbuild": "^3.6.1"
|
|
44
50
|
},
|