mokup 1.0.4 → 2.0.1

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.d.cts CHANGED
@@ -2,8 +2,10 @@ import { Context, MiddlewareHandler } from '@mokup/shared/hono';
2
2
  export { Context, MiddlewareHandler } from '@mokup/shared/hono';
3
3
 
4
4
  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
5
- type RequestHandler = (context: Context) => Response | Promise<Response> | unknown;
6
- type RouteResponse = unknown | RequestHandler;
5
+ type RouteStaticResponse = string | number | boolean | bigint | symbol | null | undefined | object;
6
+ type RouteHandlerResult = RouteStaticResponse | Response;
7
+ type RequestHandler = (context: Context) => RouteHandlerResult | Promise<RouteHandlerResult>;
8
+ type RouteResponse = RouteStaticResponse | RequestHandler;
7
9
  interface RouteRule {
8
10
  handler: RouteResponse;
9
11
  enabled?: boolean;
@@ -20,6 +22,10 @@ interface ServiceWorkerOptions {
20
22
  fallback?: boolean;
21
23
  basePath?: string | string[];
22
24
  }
25
+ type PlaygroundOptionsInput = boolean | {
26
+ path?: string;
27
+ enabled?: boolean;
28
+ } | undefined;
23
29
  interface RouteDirectoryConfig {
24
30
  headers?: Record<string, string>;
25
31
  status?: number;
@@ -40,11 +46,11 @@ interface VitePluginOptions {
40
46
  log?: boolean;
41
47
  mode?: RuntimeMode;
42
48
  sw?: ServiceWorkerOptions;
43
- playground?: boolean | {
44
- path?: string;
45
- enabled?: boolean;
46
- };
47
49
  }
48
- type VitePluginOptionsInput = VitePluginOptions | VitePluginOptions[];
50
+ interface MokupPluginOptions {
51
+ entries?: VitePluginOptions | VitePluginOptions[];
52
+ playground?: PlaygroundOptionsInput;
53
+ }
54
+ type VitePluginOptionsInput = MokupPluginOptions;
49
55
 
50
- export type { HttpMethod, RequestHandler, RouteDirectoryConfig, RouteResponse, RouteRule, RuntimeMode, ServiceWorkerOptions, VitePluginOptions, VitePluginOptionsInput };
56
+ export type { HttpMethod, MokupPluginOptions, PlaygroundOptionsInput, RequestHandler, RouteDirectoryConfig, RouteResponse, RouteRule, RuntimeMode, ServiceWorkerOptions, VitePluginOptions, VitePluginOptionsInput };
package/dist/index.d.mts CHANGED
@@ -2,8 +2,10 @@ import { Context, MiddlewareHandler } from '@mokup/shared/hono';
2
2
  export { Context, MiddlewareHandler } from '@mokup/shared/hono';
3
3
 
4
4
  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
5
- type RequestHandler = (context: Context) => Response | Promise<Response> | unknown;
6
- type RouteResponse = unknown | RequestHandler;
5
+ type RouteStaticResponse = string | number | boolean | bigint | symbol | null | undefined | object;
6
+ type RouteHandlerResult = RouteStaticResponse | Response;
7
+ type RequestHandler = (context: Context) => RouteHandlerResult | Promise<RouteHandlerResult>;
8
+ type RouteResponse = RouteStaticResponse | RequestHandler;
7
9
  interface RouteRule {
8
10
  handler: RouteResponse;
9
11
  enabled?: boolean;
@@ -20,6 +22,10 @@ interface ServiceWorkerOptions {
20
22
  fallback?: boolean;
21
23
  basePath?: string | string[];
22
24
  }
25
+ type PlaygroundOptionsInput = boolean | {
26
+ path?: string;
27
+ enabled?: boolean;
28
+ } | undefined;
23
29
  interface RouteDirectoryConfig {
24
30
  headers?: Record<string, string>;
25
31
  status?: number;
@@ -40,11 +46,11 @@ interface VitePluginOptions {
40
46
  log?: boolean;
41
47
  mode?: RuntimeMode;
42
48
  sw?: ServiceWorkerOptions;
43
- playground?: boolean | {
44
- path?: string;
45
- enabled?: boolean;
46
- };
47
49
  }
48
- type VitePluginOptionsInput = VitePluginOptions | VitePluginOptions[];
50
+ interface MokupPluginOptions {
51
+ entries?: VitePluginOptions | VitePluginOptions[];
52
+ playground?: PlaygroundOptionsInput;
53
+ }
54
+ type VitePluginOptionsInput = MokupPluginOptions;
49
55
 
50
- export type { HttpMethod, RequestHandler, RouteDirectoryConfig, RouteResponse, RouteRule, RuntimeMode, ServiceWorkerOptions, VitePluginOptions, VitePluginOptionsInput };
56
+ export type { HttpMethod, MokupPluginOptions, PlaygroundOptionsInput, RequestHandler, RouteDirectoryConfig, RouteResponse, RouteRule, RuntimeMode, ServiceWorkerOptions, VitePluginOptions, VitePluginOptionsInput };
package/dist/index.d.ts CHANGED
@@ -2,8 +2,10 @@ import { Context, MiddlewareHandler } from '@mokup/shared/hono';
2
2
  export { Context, MiddlewareHandler } from '@mokup/shared/hono';
3
3
 
4
4
  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
5
- type RequestHandler = (context: Context) => Response | Promise<Response> | unknown;
6
- type RouteResponse = unknown | RequestHandler;
5
+ type RouteStaticResponse = string | number | boolean | bigint | symbol | null | undefined | object;
6
+ type RouteHandlerResult = RouteStaticResponse | Response;
7
+ type RequestHandler = (context: Context) => RouteHandlerResult | Promise<RouteHandlerResult>;
8
+ type RouteResponse = RouteStaticResponse | RequestHandler;
7
9
  interface RouteRule {
8
10
  handler: RouteResponse;
9
11
  enabled?: boolean;
@@ -20,6 +22,10 @@ interface ServiceWorkerOptions {
20
22
  fallback?: boolean;
21
23
  basePath?: string | string[];
22
24
  }
25
+ type PlaygroundOptionsInput = boolean | {
26
+ path?: string;
27
+ enabled?: boolean;
28
+ } | undefined;
23
29
  interface RouteDirectoryConfig {
24
30
  headers?: Record<string, string>;
25
31
  status?: number;
@@ -40,11 +46,11 @@ interface VitePluginOptions {
40
46
  log?: boolean;
41
47
  mode?: RuntimeMode;
42
48
  sw?: ServiceWorkerOptions;
43
- playground?: boolean | {
44
- path?: string;
45
- enabled?: boolean;
46
- };
47
49
  }
48
- type VitePluginOptionsInput = VitePluginOptions | VitePluginOptions[];
50
+ interface MokupPluginOptions {
51
+ entries?: VitePluginOptions | VitePluginOptions[];
52
+ playground?: PlaygroundOptionsInput;
53
+ }
54
+ type VitePluginOptionsInput = MokupPluginOptions;
49
55
 
50
- export type { HttpMethod, RequestHandler, RouteDirectoryConfig, RouteResponse, RouteRule, RuntimeMode, ServiceWorkerOptions, VitePluginOptions, VitePluginOptionsInput };
56
+ export type { HttpMethod, MokupPluginOptions, PlaygroundOptionsInput, RequestHandler, RouteDirectoryConfig, RouteResponse, RouteRule, RuntimeMode, ServiceWorkerOptions, VitePluginOptions, VitePluginOptionsInput };
@@ -52,6 +52,7 @@ const supportedExtensions = /* @__PURE__ */ new Set([
52
52
  ".mjs",
53
53
  ".cjs"
54
54
  ]);
55
+ const configExtensions = [".ts", ".js", ".mjs", ".cjs"];
55
56
 
56
57
  function normalizeMethod(method) {
57
58
  if (!method) {
@@ -350,7 +351,7 @@ function createMiddleware(getApp, logger) {
350
351
  };
351
352
  }
352
353
 
353
- const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/mokup.C7VW7pSP.cjs', document.baseURI).href)));
354
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/mokup.B-yfMz5B.cjs', document.baseURI).href)));
354
355
  const mimeTypes = {
355
356
  ".html": "text/html; charset=utf-8",
356
357
  ".css": "text/css; charset=utf-8",
@@ -516,12 +517,23 @@ const disabledReasonSet = /* @__PURE__ */ new Set([
516
517
  "include",
517
518
  "unknown"
518
519
  ]);
520
+ const ignoredReasonSet = /* @__PURE__ */ new Set([
521
+ "unsupported",
522
+ "invalid-route",
523
+ "unknown"
524
+ ]);
519
525
  function normalizeDisabledReason(reason) {
520
526
  if (reason && disabledReasonSet.has(reason)) {
521
527
  return reason;
522
528
  }
523
529
  return "unknown";
524
530
  }
531
+ function normalizeIgnoredReason(reason) {
532
+ if (reason && ignoredReasonSet.has(reason)) {
533
+ return reason;
534
+ }
535
+ return "unknown";
536
+ }
525
537
  function formatRouteFile(file, root) {
526
538
  if (!root) {
527
539
  return toPosixPath(file);
@@ -584,14 +596,44 @@ function toPlaygroundRoute(route, root, groups) {
584
596
  }
585
597
  function toPlaygroundDisabledRoute(route, root, groups) {
586
598
  const matchedGroup = resolveRouteGroup(route.file, groups);
587
- return {
599
+ const disabled = {
588
600
  file: formatRouteFile(route.file, root),
589
- reason: normalizeDisabledReason(route.reason),
590
- method: route.method,
591
- url: route.url,
592
- groupKey: matchedGroup?.key,
593
- group: matchedGroup?.label
601
+ reason: normalizeDisabledReason(route.reason)
602
+ };
603
+ if (typeof route.method !== "undefined") {
604
+ disabled.method = route.method;
605
+ }
606
+ if (typeof route.url !== "undefined") {
607
+ disabled.url = route.url;
608
+ }
609
+ if (matchedGroup) {
610
+ disabled.groupKey = matchedGroup.key;
611
+ disabled.group = matchedGroup.label;
612
+ }
613
+ return disabled;
614
+ }
615
+ function toPlaygroundIgnoredRoute(route, root, groups) {
616
+ const matchedGroup = resolveRouteGroup(route.file, groups);
617
+ const ignored = {
618
+ file: formatRouteFile(route.file, root),
619
+ reason: normalizeIgnoredReason(route.reason)
594
620
  };
621
+ if (matchedGroup) {
622
+ ignored.groupKey = matchedGroup.key;
623
+ ignored.group = matchedGroup.label;
624
+ }
625
+ return ignored;
626
+ }
627
+ function toPlaygroundConfigFile(entry, root, groups) {
628
+ const matchedGroup = resolveRouteGroup(entry.file, groups);
629
+ const configFile = {
630
+ file: formatRouteFile(entry.file, root)
631
+ };
632
+ if (matchedGroup) {
633
+ configFile.groupKey = matchedGroup.key;
634
+ configFile.group = matchedGroup.label;
635
+ }
636
+ return configFile;
595
637
  }
596
638
  function createPlaygroundMiddleware(params) {
597
639
  const distDir = resolvePlaygroundDist();
@@ -641,13 +683,19 @@ function createPlaygroundMiddleware(params) {
641
683
  const groups = resolveGroups(dirs, baseRoot);
642
684
  const routes = params.getRoutes();
643
685
  const disabledRoutes = params.getDisabledRoutes?.() ?? [];
686
+ const ignoredRoutes = params.getIgnoredRoutes?.() ?? [];
687
+ const configFiles = params.getConfigFiles?.() ?? [];
688
+ const disabledConfigFiles = params.getDisabledConfigFiles?.() ?? [];
644
689
  sendJson(res, {
645
690
  basePath: matchedPath,
646
691
  root: baseRoot,
647
692
  count: routes.length,
648
693
  groups: groups.map((group) => ({ key: group.key, label: group.label })),
649
694
  routes: routes.map((route) => toPlaygroundRoute(route, baseRoot, groups)),
650
- disabled: disabledRoutes.map((route) => toPlaygroundDisabledRoute(route, baseRoot, groups))
695
+ disabled: disabledRoutes.map((route) => toPlaygroundDisabledRoute(route, baseRoot, groups)),
696
+ ignored: ignoredRoutes.map((route) => toPlaygroundIgnoredRoute(route, baseRoot, groups)),
697
+ configs: configFiles.map((entry) => toPlaygroundConfigFile(entry, baseRoot, groups)),
698
+ disabledConfigs: disabledConfigFiles.map((entry) => toPlaygroundConfigFile(entry, baseRoot, groups))
651
699
  });
652
700
  return;
653
701
  }
@@ -791,11 +839,10 @@ function sortRoutes(routes) {
791
839
  });
792
840
  }
793
841
 
794
- const configExtensions = [".ts", ".js", ".mjs", ".cjs"];
795
842
  async function loadModule$1(file) {
796
843
  const ext = configExtensions.find((extension) => file.endsWith(extension));
797
844
  if (ext === ".cjs") {
798
- const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/mokup.C7VW7pSP.cjs', document.baseURI).href)));
845
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/mokup.B-yfMz5B.cjs', document.baseURI).href)));
799
846
  delete require$1.cache[file];
800
847
  return require$1(file);
801
848
  }
@@ -978,17 +1025,28 @@ function isSupportedFile(file) {
978
1025
  if (file.endsWith(".d.ts")) {
979
1026
  return false;
980
1027
  }
981
- if (pathe.basename(file).startsWith("index.config.")) {
1028
+ if (isConfigFile(file)) {
982
1029
  return false;
983
1030
  }
984
1031
  const ext = pathe.extname(file).toLowerCase();
985
1032
  return supportedExtensions.has(ext);
986
1033
  }
1034
+ function isConfigFile(file) {
1035
+ if (file.endsWith(".d.ts")) {
1036
+ return false;
1037
+ }
1038
+ const base = pathe.basename(file);
1039
+ if (!base.startsWith("index.config.")) {
1040
+ return false;
1041
+ }
1042
+ const ext = pathe.extname(file).toLowerCase();
1043
+ return configExtensions.includes(ext);
1044
+ }
987
1045
 
988
1046
  async function loadModule(file) {
989
1047
  const ext = pathe.extname(file).toLowerCase();
990
1048
  if (ext === ".cjs") {
991
- const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/mokup.C7VW7pSP.cjs', document.baseURI).href)));
1049
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/mokup.B-yfMz5B.cjs', document.baseURI).href)));
992
1050
  delete require$1.cache[file];
993
1051
  return require$1(file);
994
1052
  }
@@ -1103,6 +1161,14 @@ function resolveSkipRoute(params) {
1103
1161
  url: resolved.template
1104
1162
  };
1105
1163
  }
1164
+ function buildSkipInfo(file, reason, resolved) {
1165
+ const info = { file, reason };
1166
+ if (resolved) {
1167
+ info.method = resolved.method;
1168
+ info.url = resolved.url;
1169
+ }
1170
+ return info;
1171
+ }
1106
1172
  async function scanRoutes(params) {
1107
1173
  const routes = [];
1108
1174
  const seen = /* @__PURE__ */ new Set();
@@ -1111,7 +1177,23 @@ async function scanRoutes(params) {
1111
1177
  const configCache = /* @__PURE__ */ new Map();
1112
1178
  const fileCache = /* @__PURE__ */ new Map();
1113
1179
  const shouldCollectSkip = typeof params.onSkip === "function";
1180
+ const shouldCollectIgnore = typeof params.onIgnore === "function";
1181
+ const shouldCollectConfig = typeof params.onConfig === "function";
1114
1182
  for (const fileInfo of files) {
1183
+ if (isConfigFile(fileInfo.file)) {
1184
+ if (shouldCollectConfig) {
1185
+ const config2 = await resolveDirectoryConfig({
1186
+ file: fileInfo.file,
1187
+ rootDir: fileInfo.rootDir,
1188
+ server: params.server,
1189
+ logger: params.logger,
1190
+ configCache,
1191
+ fileCache
1192
+ });
1193
+ params.onConfig?.({ file: fileInfo.file, enabled: config2.enabled !== false });
1194
+ }
1195
+ continue;
1196
+ }
1115
1197
  const configParams = {
1116
1198
  file: fileInfo.file,
1117
1199
  rootDir: fileInfo.rootDir,
@@ -1130,12 +1212,7 @@ async function scanRoutes(params) {
1130
1212
  rootDir: fileInfo.rootDir,
1131
1213
  prefix: params.prefix
1132
1214
  });
1133
- params.onSkip?.({
1134
- file: fileInfo.file,
1135
- reason: "disabled-dir",
1136
- method: resolved?.method,
1137
- url: resolved?.url
1138
- });
1215
+ params.onSkip?.(buildSkipInfo(fileInfo.file, "disabled-dir", resolved));
1139
1216
  }
1140
1217
  continue;
1141
1218
  }
@@ -1147,16 +1224,14 @@ async function scanRoutes(params) {
1147
1224
  rootDir: fileInfo.rootDir,
1148
1225
  prefix: params.prefix
1149
1226
  });
1150
- params.onSkip?.({
1151
- file: fileInfo.file,
1152
- reason: "ignore-prefix",
1153
- method: resolved?.method,
1154
- url: resolved?.url
1155
- });
1227
+ params.onSkip?.(buildSkipInfo(fileInfo.file, "ignore-prefix", resolved));
1156
1228
  }
1157
1229
  continue;
1158
1230
  }
1159
1231
  if (!isSupportedFile(fileInfo.file)) {
1232
+ if (shouldCollectIgnore) {
1233
+ params.onIgnore?.({ file: fileInfo.file, reason: "unsupported" });
1234
+ }
1160
1235
  continue;
1161
1236
  }
1162
1237
  const effectiveInclude = typeof config.include !== "undefined" ? config.include : params.include;
@@ -1169,17 +1244,15 @@ async function scanRoutes(params) {
1169
1244
  prefix: params.prefix
1170
1245
  });
1171
1246
  const reason = effectiveExclude && matchesFilter(fileInfo.file, void 0, effectiveExclude) ? "exclude" : "include";
1172
- params.onSkip?.({
1173
- file: fileInfo.file,
1174
- reason,
1175
- method: resolved?.method,
1176
- url: resolved?.url
1177
- });
1247
+ params.onSkip?.(buildSkipInfo(fileInfo.file, reason, resolved));
1178
1248
  }
1179
1249
  continue;
1180
1250
  }
1181
1251
  const derived = deriveRouteFromFile(fileInfo.file, fileInfo.rootDir, params.logger);
1182
1252
  if (!derived) {
1253
+ if (shouldCollectIgnore) {
1254
+ params.onIgnore?.({ file: fileInfo.file, reason: "invalid-route" });
1255
+ }
1183
1256
  continue;
1184
1257
  }
1185
1258
  const rules = await loadRules(fileInfo.file, params.server, params.logger);
@@ -1195,12 +1268,7 @@ async function scanRoutes(params) {
1195
1268
  prefix: params.prefix,
1196
1269
  derived
1197
1270
  });
1198
- params.onSkip?.({
1199
- file: fileInfo.file,
1200
- reason: "disabled",
1201
- method: resolved2?.method,
1202
- url: resolved2?.url
1203
- });
1271
+ params.onSkip?.(buildSkipInfo(fileInfo.file, "disabled", resolved2));
1204
1272
  }
1205
1273
  continue;
1206
1274
  }
@@ -49,6 +49,7 @@ const supportedExtensions = /* @__PURE__ */ new Set([
49
49
  ".mjs",
50
50
  ".cjs"
51
51
  ]);
52
+ const configExtensions = [".ts", ".js", ".mjs", ".cjs"];
52
53
 
53
54
  function normalizeMethod(method) {
54
55
  if (!method) {
@@ -513,12 +514,23 @@ const disabledReasonSet = /* @__PURE__ */ new Set([
513
514
  "include",
514
515
  "unknown"
515
516
  ]);
517
+ const ignoredReasonSet = /* @__PURE__ */ new Set([
518
+ "unsupported",
519
+ "invalid-route",
520
+ "unknown"
521
+ ]);
516
522
  function normalizeDisabledReason(reason) {
517
523
  if (reason && disabledReasonSet.has(reason)) {
518
524
  return reason;
519
525
  }
520
526
  return "unknown";
521
527
  }
528
+ function normalizeIgnoredReason(reason) {
529
+ if (reason && ignoredReasonSet.has(reason)) {
530
+ return reason;
531
+ }
532
+ return "unknown";
533
+ }
522
534
  function formatRouteFile(file, root) {
523
535
  if (!root) {
524
536
  return toPosixPath(file);
@@ -581,14 +593,44 @@ function toPlaygroundRoute(route, root, groups) {
581
593
  }
582
594
  function toPlaygroundDisabledRoute(route, root, groups) {
583
595
  const matchedGroup = resolveRouteGroup(route.file, groups);
584
- return {
596
+ const disabled = {
585
597
  file: formatRouteFile(route.file, root),
586
- reason: normalizeDisabledReason(route.reason),
587
- method: route.method,
588
- url: route.url,
589
- groupKey: matchedGroup?.key,
590
- group: matchedGroup?.label
598
+ reason: normalizeDisabledReason(route.reason)
599
+ };
600
+ if (typeof route.method !== "undefined") {
601
+ disabled.method = route.method;
602
+ }
603
+ if (typeof route.url !== "undefined") {
604
+ disabled.url = route.url;
605
+ }
606
+ if (matchedGroup) {
607
+ disabled.groupKey = matchedGroup.key;
608
+ disabled.group = matchedGroup.label;
609
+ }
610
+ return disabled;
611
+ }
612
+ function toPlaygroundIgnoredRoute(route, root, groups) {
613
+ const matchedGroup = resolveRouteGroup(route.file, groups);
614
+ const ignored = {
615
+ file: formatRouteFile(route.file, root),
616
+ reason: normalizeIgnoredReason(route.reason)
591
617
  };
618
+ if (matchedGroup) {
619
+ ignored.groupKey = matchedGroup.key;
620
+ ignored.group = matchedGroup.label;
621
+ }
622
+ return ignored;
623
+ }
624
+ function toPlaygroundConfigFile(entry, root, groups) {
625
+ const matchedGroup = resolveRouteGroup(entry.file, groups);
626
+ const configFile = {
627
+ file: formatRouteFile(entry.file, root)
628
+ };
629
+ if (matchedGroup) {
630
+ configFile.groupKey = matchedGroup.key;
631
+ configFile.group = matchedGroup.label;
632
+ }
633
+ return configFile;
592
634
  }
593
635
  function createPlaygroundMiddleware(params) {
594
636
  const distDir = resolvePlaygroundDist();
@@ -638,13 +680,19 @@ function createPlaygroundMiddleware(params) {
638
680
  const groups = resolveGroups(dirs, baseRoot);
639
681
  const routes = params.getRoutes();
640
682
  const disabledRoutes = params.getDisabledRoutes?.() ?? [];
683
+ const ignoredRoutes = params.getIgnoredRoutes?.() ?? [];
684
+ const configFiles = params.getConfigFiles?.() ?? [];
685
+ const disabledConfigFiles = params.getDisabledConfigFiles?.() ?? [];
641
686
  sendJson(res, {
642
687
  basePath: matchedPath,
643
688
  root: baseRoot,
644
689
  count: routes.length,
645
690
  groups: groups.map((group) => ({ key: group.key, label: group.label })),
646
691
  routes: routes.map((route) => toPlaygroundRoute(route, baseRoot, groups)),
647
- disabled: disabledRoutes.map((route) => toPlaygroundDisabledRoute(route, baseRoot, groups))
692
+ disabled: disabledRoutes.map((route) => toPlaygroundDisabledRoute(route, baseRoot, groups)),
693
+ ignored: ignoredRoutes.map((route) => toPlaygroundIgnoredRoute(route, baseRoot, groups)),
694
+ configs: configFiles.map((entry) => toPlaygroundConfigFile(entry, baseRoot, groups)),
695
+ disabledConfigs: disabledConfigFiles.map((entry) => toPlaygroundConfigFile(entry, baseRoot, groups))
648
696
  });
649
697
  return;
650
698
  }
@@ -788,7 +836,6 @@ function sortRoutes(routes) {
788
836
  });
789
837
  }
790
838
 
791
- const configExtensions = [".ts", ".js", ".mjs", ".cjs"];
792
839
  async function loadModule$1(file) {
793
840
  const ext = configExtensions.find((extension) => file.endsWith(extension));
794
841
  if (ext === ".cjs") {
@@ -975,12 +1022,23 @@ function isSupportedFile(file) {
975
1022
  if (file.endsWith(".d.ts")) {
976
1023
  return false;
977
1024
  }
978
- if (basename(file).startsWith("index.config.")) {
1025
+ if (isConfigFile(file)) {
979
1026
  return false;
980
1027
  }
981
1028
  const ext = extname(file).toLowerCase();
982
1029
  return supportedExtensions.has(ext);
983
1030
  }
1031
+ function isConfigFile(file) {
1032
+ if (file.endsWith(".d.ts")) {
1033
+ return false;
1034
+ }
1035
+ const base = basename(file);
1036
+ if (!base.startsWith("index.config.")) {
1037
+ return false;
1038
+ }
1039
+ const ext = extname(file).toLowerCase();
1040
+ return configExtensions.includes(ext);
1041
+ }
984
1042
 
985
1043
  async function loadModule(file) {
986
1044
  const ext = extname(file).toLowerCase();
@@ -1100,6 +1158,14 @@ function resolveSkipRoute(params) {
1100
1158
  url: resolved.template
1101
1159
  };
1102
1160
  }
1161
+ function buildSkipInfo(file, reason, resolved) {
1162
+ const info = { file, reason };
1163
+ if (resolved) {
1164
+ info.method = resolved.method;
1165
+ info.url = resolved.url;
1166
+ }
1167
+ return info;
1168
+ }
1103
1169
  async function scanRoutes(params) {
1104
1170
  const routes = [];
1105
1171
  const seen = /* @__PURE__ */ new Set();
@@ -1108,7 +1174,23 @@ async function scanRoutes(params) {
1108
1174
  const configCache = /* @__PURE__ */ new Map();
1109
1175
  const fileCache = /* @__PURE__ */ new Map();
1110
1176
  const shouldCollectSkip = typeof params.onSkip === "function";
1177
+ const shouldCollectIgnore = typeof params.onIgnore === "function";
1178
+ const shouldCollectConfig = typeof params.onConfig === "function";
1111
1179
  for (const fileInfo of files) {
1180
+ if (isConfigFile(fileInfo.file)) {
1181
+ if (shouldCollectConfig) {
1182
+ const config2 = await resolveDirectoryConfig({
1183
+ file: fileInfo.file,
1184
+ rootDir: fileInfo.rootDir,
1185
+ server: params.server,
1186
+ logger: params.logger,
1187
+ configCache,
1188
+ fileCache
1189
+ });
1190
+ params.onConfig?.({ file: fileInfo.file, enabled: config2.enabled !== false });
1191
+ }
1192
+ continue;
1193
+ }
1112
1194
  const configParams = {
1113
1195
  file: fileInfo.file,
1114
1196
  rootDir: fileInfo.rootDir,
@@ -1127,12 +1209,7 @@ async function scanRoutes(params) {
1127
1209
  rootDir: fileInfo.rootDir,
1128
1210
  prefix: params.prefix
1129
1211
  });
1130
- params.onSkip?.({
1131
- file: fileInfo.file,
1132
- reason: "disabled-dir",
1133
- method: resolved?.method,
1134
- url: resolved?.url
1135
- });
1212
+ params.onSkip?.(buildSkipInfo(fileInfo.file, "disabled-dir", resolved));
1136
1213
  }
1137
1214
  continue;
1138
1215
  }
@@ -1144,16 +1221,14 @@ async function scanRoutes(params) {
1144
1221
  rootDir: fileInfo.rootDir,
1145
1222
  prefix: params.prefix
1146
1223
  });
1147
- params.onSkip?.({
1148
- file: fileInfo.file,
1149
- reason: "ignore-prefix",
1150
- method: resolved?.method,
1151
- url: resolved?.url
1152
- });
1224
+ params.onSkip?.(buildSkipInfo(fileInfo.file, "ignore-prefix", resolved));
1153
1225
  }
1154
1226
  continue;
1155
1227
  }
1156
1228
  if (!isSupportedFile(fileInfo.file)) {
1229
+ if (shouldCollectIgnore) {
1230
+ params.onIgnore?.({ file: fileInfo.file, reason: "unsupported" });
1231
+ }
1157
1232
  continue;
1158
1233
  }
1159
1234
  const effectiveInclude = typeof config.include !== "undefined" ? config.include : params.include;
@@ -1166,17 +1241,15 @@ async function scanRoutes(params) {
1166
1241
  prefix: params.prefix
1167
1242
  });
1168
1243
  const reason = effectiveExclude && matchesFilter(fileInfo.file, void 0, effectiveExclude) ? "exclude" : "include";
1169
- params.onSkip?.({
1170
- file: fileInfo.file,
1171
- reason,
1172
- method: resolved?.method,
1173
- url: resolved?.url
1174
- });
1244
+ params.onSkip?.(buildSkipInfo(fileInfo.file, reason, resolved));
1175
1245
  }
1176
1246
  continue;
1177
1247
  }
1178
1248
  const derived = deriveRouteFromFile(fileInfo.file, fileInfo.rootDir, params.logger);
1179
1249
  if (!derived) {
1250
+ if (shouldCollectIgnore) {
1251
+ params.onIgnore?.({ file: fileInfo.file, reason: "invalid-route" });
1252
+ }
1180
1253
  continue;
1181
1254
  }
1182
1255
  const rules = await loadRules(fileInfo.file, params.server, params.logger);
@@ -1192,12 +1265,7 @@ async function scanRoutes(params) {
1192
1265
  prefix: params.prefix,
1193
1266
  derived
1194
1267
  });
1195
- params.onSkip?.({
1196
- file: fileInfo.file,
1197
- reason: "disabled",
1198
- method: resolved2?.method,
1199
- url: resolved2?.url
1200
- });
1268
+ params.onSkip?.(buildSkipInfo(fileInfo.file, "disabled", resolved2));
1201
1269
  }
1202
1270
  continue;
1203
1271
  }