@mokup/server 1.0.2 → 1.0.3

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
@@ -233,7 +233,7 @@ function createRouteHandler(route) {
233
233
  return normalizeHandlerValue(c, value);
234
234
  };
235
235
  }
236
- function createFinalizeMiddleware(route) {
236
+ function createFinalizeMiddleware(route, onResponse) {
237
237
  return async (c, next) => {
238
238
  const response = await next();
239
239
  const resolved = resolveResponse(response, c.res);
@@ -242,6 +242,15 @@ function createFinalizeMiddleware(route) {
242
242
  }
243
243
  const overridden = applyRouteOverrides(resolved, route);
244
244
  c.res = overridden;
245
+ if (onResponse) {
246
+ try {
247
+ const result = onResponse(route, overridden);
248
+ if (result instanceof Promise) {
249
+ result.catch(() => void 0);
250
+ }
251
+ } catch {
252
+ }
253
+ }
245
254
  return overridden;
246
255
  };
247
256
  }
@@ -251,14 +260,14 @@ function wrapMiddleware(handler) {
251
260
  return resolveResponse(response, c.res);
252
261
  };
253
262
  }
254
- function createHonoApp(routes) {
263
+ function createHonoApp(routes, options = {}) {
255
264
  const app = new hono.Hono({ router: new hono.PatternRouter(), strict: false });
256
265
  for (const route of routes) {
257
266
  const middlewares = route.middlewares?.map((entry) => wrapMiddleware(entry.handle)) ?? [];
258
267
  app.on(
259
268
  route.method,
260
269
  toHonoPath(route),
261
- createFinalizeMiddleware(route),
270
+ createFinalizeMiddleware(route, options.onResponse),
262
271
  ...middlewares,
263
272
  createRouteHandler(route)
264
273
  );
@@ -440,7 +449,16 @@ function registerPlaygroundRoutes(params) {
440
449
  return new Response("Playground is not available.", { status: 500 });
441
450
  }
442
451
  };
443
- params.app.get(playgroundPath, (c) => c.redirect(`${playgroundPath}/`));
452
+ params.app.get(playgroundPath, (c) => {
453
+ try {
454
+ const pathname = new URL(c.req.raw.url, "http://localhost").pathname;
455
+ if (pathname.endsWith("/")) {
456
+ return serveIndex();
457
+ }
458
+ } catch {
459
+ }
460
+ return c.redirect(`${playgroundPath}/`);
461
+ });
444
462
  params.app.get(`${playgroundPath}/`, () => serveIndex());
445
463
  params.app.get(`${playgroundPath}/index.html`, () => serveIndex());
446
464
  params.app.get(`${playgroundPath}/routes`, (c) => {
@@ -986,8 +1004,11 @@ function buildApp(params) {
986
1004
  config: params.playground,
987
1005
  root: params.root
988
1006
  });
1007
+ if (params.wsHandler && params.playground.enabled) {
1008
+ app.get(`${params.playground.path}/ws`, params.wsHandler);
1009
+ }
989
1010
  if (params.routes.length > 0) {
990
- const mockApp = createHonoApp(params.routes);
1011
+ const mockApp = createHonoApp(params.routes, { onResponse: params.onResponse });
991
1012
  app.route("/", mockApp);
992
1013
  }
993
1014
  return app;
@@ -1074,13 +1095,81 @@ async function createFetchServer(options = {}) {
1074
1095
  const logger = createLogger(logEnabled);
1075
1096
  const playgroundConfig = resolvePlaygroundOptions(resolvePlaygroundInput(optionList));
1076
1097
  const dirs = resolveAllDirs(optionList, root);
1098
+ const routeCounts = {};
1099
+ const wsClients = /* @__PURE__ */ new Set();
1100
+ let totalCount = 0;
1101
+ let wsHandler;
1102
+ let injectWebSocket;
1103
+ function getRouteKey(route) {
1104
+ return `${route.method} ${route.template}`;
1105
+ }
1106
+ function buildSnapshot() {
1107
+ return {
1108
+ type: "snapshot",
1109
+ total: totalCount,
1110
+ perRoute: { ...routeCounts }
1111
+ };
1112
+ }
1113
+ function broadcast(payload) {
1114
+ if (wsClients.size === 0) {
1115
+ return;
1116
+ }
1117
+ const message = JSON.stringify(payload);
1118
+ for (const client of wsClients) {
1119
+ try {
1120
+ client.send(message);
1121
+ } catch {
1122
+ wsClients.delete(client);
1123
+ }
1124
+ }
1125
+ }
1126
+ function registerWsClient(client) {
1127
+ wsClients.add(client);
1128
+ try {
1129
+ client.send(JSON.stringify(buildSnapshot()));
1130
+ } catch {
1131
+ wsClients.delete(client);
1132
+ }
1133
+ }
1134
+ function handleRouteResponse(route) {
1135
+ const routeKey = getRouteKey(route);
1136
+ routeCounts[routeKey] = (routeCounts[routeKey] ?? 0) + 1;
1137
+ totalCount += 1;
1138
+ broadcast({ type: "increment", routeKey, total: totalCount });
1139
+ }
1140
+ async function setupPlaygroundWebSocket(app2) {
1141
+ if (!playgroundConfig.enabled) {
1142
+ return;
1143
+ }
1144
+ try {
1145
+ const mod = await import('@hono/node-ws');
1146
+ const { createNodeWebSocket } = mod;
1147
+ const { upgradeWebSocket, injectWebSocket: inject } = createNodeWebSocket({ app: app2 });
1148
+ wsHandler = upgradeWebSocket(() => ({
1149
+ onOpen: (_event, ws) => {
1150
+ registerWsClient(ws);
1151
+ },
1152
+ onClose: (_event, ws) => {
1153
+ wsClients.delete(ws);
1154
+ },
1155
+ onMessage: () => {
1156
+ }
1157
+ }));
1158
+ injectWebSocket = (server2) => {
1159
+ inject(server2);
1160
+ };
1161
+ } catch {
1162
+ }
1163
+ }
1077
1164
  let routes = [];
1078
1165
  let app = buildApp({
1079
1166
  routes,
1080
1167
  dirs,
1081
1168
  playground: playgroundConfig,
1082
1169
  root,
1083
- logger
1170
+ logger,
1171
+ onResponse: handleRouteResponse,
1172
+ wsHandler
1084
1173
  });
1085
1174
  const refreshRoutes = async () => {
1086
1175
  try {
@@ -1107,7 +1196,9 @@ async function createFetchServer(options = {}) {
1107
1196
  dirs,
1108
1197
  playground: playgroundConfig,
1109
1198
  root,
1110
- logger
1199
+ logger,
1200
+ onResponse: handleRouteResponse,
1201
+ wsHandler
1111
1202
  });
1112
1203
  logger.info(`Loaded ${routes.length} mock routes.`);
1113
1204
  } catch (error) {
@@ -1115,6 +1206,10 @@ async function createFetchServer(options = {}) {
1115
1206
  }
1116
1207
  };
1117
1208
  await refreshRoutes();
1209
+ await setupPlaygroundWebSocket(app);
1210
+ if (wsHandler && playgroundConfig.enabled) {
1211
+ app.get(`${playgroundConfig.path}/ws`, wsHandler);
1212
+ }
1118
1213
  const scheduleRefresh = createDebouncer(80, () => {
1119
1214
  void refreshRoutes();
1120
1215
  });
@@ -1128,7 +1223,8 @@ async function createFetchServer(options = {}) {
1128
1223
  const server = {
1129
1224
  fetch,
1130
1225
  refresh: refreshRoutes,
1131
- getRoutes: () => routes
1226
+ getRoutes: () => routes,
1227
+ injectWebSocket
1132
1228
  };
1133
1229
  if (watcher) {
1134
1230
  server.close = async () => {
package/dist/index.d.cts CHANGED
@@ -86,8 +86,12 @@ interface FetchServer {
86
86
  fetch: (request: Request) => Promise<Response>;
87
87
  refresh: () => Promise<void>;
88
88
  getRoutes: () => RouteTable;
89
+ injectWebSocket?: (server: NodeWebSocketServer) => void;
89
90
  close?: () => Promise<void>;
90
91
  }
92
+ interface NodeWebSocketServer {
93
+ on: (event: string, listener: (...args: unknown[]) => void) => void;
94
+ }
91
95
  declare function createFetchServer(options?: FetchServerOptionsInput): Promise<FetchServer>;
92
96
 
93
97
  interface HonoContextLike {
package/dist/index.d.mts CHANGED
@@ -86,8 +86,12 @@ interface FetchServer {
86
86
  fetch: (request: Request) => Promise<Response>;
87
87
  refresh: () => Promise<void>;
88
88
  getRoutes: () => RouteTable;
89
+ injectWebSocket?: (server: NodeWebSocketServer) => void;
89
90
  close?: () => Promise<void>;
90
91
  }
92
+ interface NodeWebSocketServer {
93
+ on: (event: string, listener: (...args: unknown[]) => void) => void;
94
+ }
91
95
  declare function createFetchServer(options?: FetchServerOptionsInput): Promise<FetchServer>;
92
96
 
93
97
  interface HonoContextLike {
package/dist/index.d.ts CHANGED
@@ -86,8 +86,12 @@ interface FetchServer {
86
86
  fetch: (request: Request) => Promise<Response>;
87
87
  refresh: () => Promise<void>;
88
88
  getRoutes: () => RouteTable;
89
+ injectWebSocket?: (server: NodeWebSocketServer) => void;
89
90
  close?: () => Promise<void>;
90
91
  }
92
+ interface NodeWebSocketServer {
93
+ on: (event: string, listener: (...args: unknown[]) => void) => void;
94
+ }
91
95
  declare function createFetchServer(options?: FetchServerOptionsInput): Promise<FetchServer>;
92
96
 
93
97
  interface HonoContextLike {
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createRuntime, compareRouteScore, parseRouteTemplate } from '@mokup/runtime';
2
- import { t as toRuntimeOptions, a as toRuntimeRequestFromNode, b as applyRuntimeResultToNode, c as toBinaryBody, d as createFetchHandler } from './shared/server.HVB7OYyI.mjs';
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
5
  import { resolve, isAbsolute, join, normalize, extname, dirname, relative, basename } from '@mokup/shared/pathe';
@@ -230,7 +230,7 @@ function createRouteHandler(route) {
230
230
  return normalizeHandlerValue(c, value);
231
231
  };
232
232
  }
233
- function createFinalizeMiddleware(route) {
233
+ function createFinalizeMiddleware(route, onResponse) {
234
234
  return async (c, next) => {
235
235
  const response = await next();
236
236
  const resolved = resolveResponse(response, c.res);
@@ -239,6 +239,15 @@ function createFinalizeMiddleware(route) {
239
239
  }
240
240
  const overridden = applyRouteOverrides(resolved, route);
241
241
  c.res = overridden;
242
+ if (onResponse) {
243
+ try {
244
+ const result = onResponse(route, overridden);
245
+ if (result instanceof Promise) {
246
+ result.catch(() => void 0);
247
+ }
248
+ } catch {
249
+ }
250
+ }
242
251
  return overridden;
243
252
  };
244
253
  }
@@ -248,14 +257,14 @@ function wrapMiddleware(handler) {
248
257
  return resolveResponse(response, c.res);
249
258
  };
250
259
  }
251
- function createHonoApp(routes) {
260
+ function createHonoApp(routes, options = {}) {
252
261
  const app = new Hono({ router: new PatternRouter(), strict: false });
253
262
  for (const route of routes) {
254
263
  const middlewares = route.middlewares?.map((entry) => wrapMiddleware(entry.handle)) ?? [];
255
264
  app.on(
256
265
  route.method,
257
266
  toHonoPath(route),
258
- createFinalizeMiddleware(route),
267
+ createFinalizeMiddleware(route, options.onResponse),
259
268
  ...middlewares,
260
269
  createRouteHandler(route)
261
270
  );
@@ -437,7 +446,16 @@ function registerPlaygroundRoutes(params) {
437
446
  return new Response("Playground is not available.", { status: 500 });
438
447
  }
439
448
  };
440
- params.app.get(playgroundPath, (c) => c.redirect(`${playgroundPath}/`));
449
+ params.app.get(playgroundPath, (c) => {
450
+ try {
451
+ const pathname = new URL(c.req.raw.url, "http://localhost").pathname;
452
+ if (pathname.endsWith("/")) {
453
+ return serveIndex();
454
+ }
455
+ } catch {
456
+ }
457
+ return c.redirect(`${playgroundPath}/`);
458
+ });
441
459
  params.app.get(`${playgroundPath}/`, () => serveIndex());
442
460
  params.app.get(`${playgroundPath}/index.html`, () => serveIndex());
443
461
  params.app.get(`${playgroundPath}/routes`, (c) => {
@@ -983,8 +1001,11 @@ function buildApp(params) {
983
1001
  config: params.playground,
984
1002
  root: params.root
985
1003
  });
1004
+ if (params.wsHandler && params.playground.enabled) {
1005
+ app.get(`${params.playground.path}/ws`, params.wsHandler);
1006
+ }
986
1007
  if (params.routes.length > 0) {
987
- const mockApp = createHonoApp(params.routes);
1008
+ const mockApp = createHonoApp(params.routes, { onResponse: params.onResponse });
988
1009
  app.route("/", mockApp);
989
1010
  }
990
1011
  return app;
@@ -1071,13 +1092,81 @@ async function createFetchServer(options = {}) {
1071
1092
  const logger = createLogger(logEnabled);
1072
1093
  const playgroundConfig = resolvePlaygroundOptions(resolvePlaygroundInput(optionList));
1073
1094
  const dirs = resolveAllDirs(optionList, root);
1095
+ const routeCounts = {};
1096
+ const wsClients = /* @__PURE__ */ new Set();
1097
+ let totalCount = 0;
1098
+ let wsHandler;
1099
+ let injectWebSocket;
1100
+ function getRouteKey(route) {
1101
+ return `${route.method} ${route.template}`;
1102
+ }
1103
+ function buildSnapshot() {
1104
+ return {
1105
+ type: "snapshot",
1106
+ total: totalCount,
1107
+ perRoute: { ...routeCounts }
1108
+ };
1109
+ }
1110
+ function broadcast(payload) {
1111
+ if (wsClients.size === 0) {
1112
+ return;
1113
+ }
1114
+ const message = JSON.stringify(payload);
1115
+ for (const client of wsClients) {
1116
+ try {
1117
+ client.send(message);
1118
+ } catch {
1119
+ wsClients.delete(client);
1120
+ }
1121
+ }
1122
+ }
1123
+ function registerWsClient(client) {
1124
+ wsClients.add(client);
1125
+ try {
1126
+ client.send(JSON.stringify(buildSnapshot()));
1127
+ } catch {
1128
+ wsClients.delete(client);
1129
+ }
1130
+ }
1131
+ function handleRouteResponse(route) {
1132
+ const routeKey = getRouteKey(route);
1133
+ routeCounts[routeKey] = (routeCounts[routeKey] ?? 0) + 1;
1134
+ totalCount += 1;
1135
+ broadcast({ type: "increment", routeKey, total: totalCount });
1136
+ }
1137
+ async function setupPlaygroundWebSocket(app2) {
1138
+ if (!playgroundConfig.enabled) {
1139
+ return;
1140
+ }
1141
+ try {
1142
+ const mod = await import('@hono/node-ws');
1143
+ const { createNodeWebSocket } = mod;
1144
+ const { upgradeWebSocket, injectWebSocket: inject } = createNodeWebSocket({ app: app2 });
1145
+ wsHandler = upgradeWebSocket(() => ({
1146
+ onOpen: (_event, ws) => {
1147
+ registerWsClient(ws);
1148
+ },
1149
+ onClose: (_event, ws) => {
1150
+ wsClients.delete(ws);
1151
+ },
1152
+ onMessage: () => {
1153
+ }
1154
+ }));
1155
+ injectWebSocket = (server2) => {
1156
+ inject(server2);
1157
+ };
1158
+ } catch {
1159
+ }
1160
+ }
1074
1161
  let routes = [];
1075
1162
  let app = buildApp({
1076
1163
  routes,
1077
1164
  dirs,
1078
1165
  playground: playgroundConfig,
1079
1166
  root,
1080
- logger
1167
+ logger,
1168
+ onResponse: handleRouteResponse,
1169
+ wsHandler
1081
1170
  });
1082
1171
  const refreshRoutes = async () => {
1083
1172
  try {
@@ -1104,7 +1193,9 @@ async function createFetchServer(options = {}) {
1104
1193
  dirs,
1105
1194
  playground: playgroundConfig,
1106
1195
  root,
1107
- logger
1196
+ logger,
1197
+ onResponse: handleRouteResponse,
1198
+ wsHandler
1108
1199
  });
1109
1200
  logger.info(`Loaded ${routes.length} mock routes.`);
1110
1201
  } catch (error) {
@@ -1112,6 +1203,10 @@ async function createFetchServer(options = {}) {
1112
1203
  }
1113
1204
  };
1114
1205
  await refreshRoutes();
1206
+ await setupPlaygroundWebSocket(app);
1207
+ if (wsHandler && playgroundConfig.enabled) {
1208
+ app.get(`${playgroundConfig.path}/ws`, wsHandler);
1209
+ }
1115
1210
  const scheduleRefresh = createDebouncer(80, () => {
1116
1211
  void refreshRoutes();
1117
1212
  });
@@ -1125,7 +1220,8 @@ async function createFetchServer(options = {}) {
1125
1220
  const server = {
1126
1221
  fetch,
1127
1222
  refresh: refreshRoutes,
1128
- getRoutes: () => routes
1223
+ getRoutes: () => routes,
1224
+ injectWebSocket
1129
1225
  };
1130
1226
  if (watcher) {
1131
1227
  server.close = async () => {
package/dist/node.cjs ADDED
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const nodeServer = require('@hono/node-server');
4
+
5
+
6
+
7
+ exports.serve = nodeServer.serve;
@@ -0,0 +1 @@
1
+ export { serve } from '@hono/node-server';
@@ -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, toBinaryBody as c, createFetchHandler as d, toRuntimeOptions as t };
258
+ export { toRuntimeRequestFromNode as a, applyRuntimeResultToNode as b, createFetchHandler as c, toBinaryBody as d, toRuntimeOptions as t };
package/dist/worker.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { d as createFetchHandler } from './shared/server.HVB7OYyI.mjs';
1
+ import { c as createFetchHandler } from './shared/server.Dje1y79O.mjs';
2
2
  import '@mokup/runtime';
3
3
 
4
4
  function isManifest(value) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mokup/server",
3
3
  "type": "module",
4
- "version": "1.0.2",
4
+ "version": "1.0.3",
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,12 +37,13 @@
32
37
  "dist"
33
38
  ],
34
39
  "dependencies": {
35
- "@mokup/playground": "0.0.6",
40
+ "@hono/node-server": "^1.19.9",
41
+ "@hono/node-ws": "^1.1.1",
36
42
  "@mokup/runtime": "1.0.0",
43
+ "@mokup/playground": "0.0.7",
37
44
  "@mokup/shared": "1.0.0"
38
45
  },
39
46
  "devDependencies": {
40
- "@hono/node-server": "^1.19.9",
41
47
  "@types/node": "^25.0.9",
42
48
  "typescript": "^5.9.3",
43
49
  "unbuild": "^3.6.1"