mock-service-cli-ultra 4.5.0 → 4.6.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/README.md CHANGED
@@ -76,8 +76,9 @@ npx mock-service-cli [options] [path]
76
76
  | `-D` 或 `--web-dir` | 启用 Web 服务器,指定 web 目录(SPA 应用) | - |
77
77
  | `-P` 或 `--web-port` | Web 服务器端口 | 9090 |
78
78
  | `-b` 或 `--web-baseurl` | 指定 SPA Web 服务器的公共路径 | - |
79
- | `-R` 或 `--static-server` | 启用静态服务器,指定静态资源目录 | - |
80
- | `--static-config <file>` | 静态服务器的 JSON 配置:热更新、HTTPS、挂载、代理与 SPA 回退 | - |
79
+ | `-R` 或 `--static-server` | 启用单目录静态服务器,指定静态资源目录 | - |
80
+ | `--spa-fallback <path>` | 单目录模式显式启用 SPA 回退 | - |
81
+ | `--static-config <file>` | 使用自包含 JSON 配置启动静态服务器 | - |
81
82
  | `-w` 或 `--open` | 自动打开 API 概览页、文件浏览器页 | false |
82
83
  | `-e` 或 `--explorer` | 启用文件浏览器服务器,指定要浏览的目录 | ./ |
83
84
  | `--edit` | 启用文件浏览器的新建、重命名、删除和上传操作 | false |
@@ -108,16 +109,21 @@ mock-service-cli -f ./mock.js
108
109
  # 启动静态服务器
109
110
  mock-service-cli -R ./public
110
111
 
111
- # 使用 Live Server 风格的热更新配置
112
- mock-service-cli -R ./public --static-config ./static-server.json
112
+ # 单目录模式启用 SPA 回退
113
+ mock-service-cli -R ./public --spa-fallback /index.html
114
+
115
+ # 使用自包含配置(不需要目录参数)
116
+ mock-service-cli -R --static-config ./static-server.json
117
+ mock-service-cli --static-config ./static-server.json
113
118
  ```
114
119
 
115
120
  静态服务器会监听资源变化并向 HTML 页面注入热更新客户端:CSS 文件更新时替换样式表,其余变更刷新页面。
116
121
  默认不打开浏览器;在配置中设置 `"open": true` 或提供页面路径即可打开。完整配置字段和示例见
117
122
  [`docs/static-server.config.example.json`](./docs/static-server.config.example.json):支持忽略规则、SPA 回退、挂载目录、代理、HTTPS、CORS、响应头和自定义浏览器命令。
118
- `-p`、`--host`、`-A` 和 `--proxy-options` 仍可使用,且同名 CLI 配置优先。
119
- 未设置 `spaFallback` 时,根目录和子目录会显示可点击的目录索引,不会自动加载 `index.html`;用户可直接访问该文件。
120
- 只有显式设置 `"spaFallback": "/index.html"`(或其他入口)时,未命中的路由才会返回 SPA 入口页。
123
+ 配置文件只在顶层保留服务器级字段。所有应用都放在 `mounts` 中,并可独立设置 `directory`、`spaFallback`、`proxy`、`cors`、`headers` 和 `secure`。mount 未设置 `path` 时默认挂载到 `/`;`directory` 可省略,但该应用必须配置至少一条代理。代理键使用完整公开路径,例如 `/api/app`。
124
+ 代理规则使用 `{ "target": "http://...", "rewrite": true }`;`rewrite` 为 true 时只移除匹配的完整代理前缀并保留剩余路径和 query string。应用级 `secure` 控制其代理 HTTPS 目标的证书校验,默认 `false`。配置模式不接受 `--proxy-options`、`--rewrite` 或 `--spa-fallback`。
125
+ 未设置 `spaFallback` 时,应用目录会显示可点击的目录索引;可直接访问其中的 HTML 文件。mount 未命中的路由不会继续进入其他应用的 SPA fallback。
126
+ 设置了 `spaFallback` 的应用可选配置 `accessLog.success` 与 `accessLog.failure` 文件路径。服务将把每次访问以 JSON Lines 写入对应文件:2xx/3xx 写入 success,4xx/5xx 写入 failure;路径相对配置文件解析。启动后控制台会突出显示每条代理所属的应用、公开前缀、目标和 rewrite 状态。
121
127
 
122
128
  ### Web 服务器 (SPA)
123
129
 
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>文件浏览器登录</title>
7
- <link rel="icon" type="image/svg+xml" href="/favicon-file-explorer-login.svg" />
7
+ <link rel="icon" type="image/svg+xml" href="__FILE_EXPLORER_FAVICON_URL__" />
8
8
  <style>
9
9
  * { box-sizing: border-box; }
10
10
  body { margin: 0; min-height: 100vh; display: grid; place-items: center; background: #f4f6f8; color: #202124; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>文件浏览器 - File Explorer</title>
7
- <link rel="icon" type="image/svg+xml" href="/favicon-file-explorer.svg" />
7
+ <link rel="icon" type="image/svg+xml" href="__FILE_EXPLORER_FAVICON_URL__" />
8
8
  <style>
9
9
  * {
10
10
  margin: 0;
@@ -1158,6 +1158,7 @@
1158
1158
  let explorerLifecycleVersion = 0;
1159
1159
  let currentArchiveJobId = null;
1160
1160
  let currentArchiveJobOperation = null;
1161
+ let explorerRootId = null;
1161
1162
  let archiveCapabilities = { createFormats: [], readExtensions: [] };
1162
1163
 
1163
1164
  const EXPLORER_AUTH_STORAGE_KEY = 'mock-service-cli.file-explorer.password';
@@ -1281,6 +1282,7 @@
1281
1282
  const config = await response.json();
1282
1283
  if (!response.ok || config.error) throw new Error(config.error || 'Failed to load configuration');
1283
1284
  editMode = Boolean(config.editMode);
1285
+ explorerRootId = typeof config.rootId === 'string' ? config.rootId : null;
1284
1286
  } catch (error) {
1285
1287
  console.warn('Failed to load file explorer configuration:', error);
1286
1288
  editMode = false;
@@ -1413,7 +1415,25 @@
1413
1415
  }
1414
1416
  }
1415
1417
 
1416
- async function initializeExplorer(isPageRestore = false) {
1418
+ function getStoredExplorerPath() {
1419
+ try {
1420
+ const stored = JSON.parse(sessionStorage.getItem(EXPLORER_PATH_STORAGE_KEY));
1421
+ if (stored && stored.rootId === explorerRootId && typeof stored.path === 'string' && stored.path.startsWith('/')) {
1422
+ return stored.path;
1423
+ }
1424
+ } catch (error) {
1425
+ // Earlier releases stored the path directly; discard that unscoped value.
1426
+ }
1427
+ sessionStorage.removeItem(EXPLORER_PATH_STORAGE_KEY);
1428
+ return '/';
1429
+ }
1430
+
1431
+ function storeExplorerPath(path) {
1432
+ if (!explorerRootId) return;
1433
+ sessionStorage.setItem(EXPLORER_PATH_STORAGE_KEY, JSON.stringify({ rootId: explorerRootId, path }));
1434
+ }
1435
+
1436
+ async function initializeExplorer() {
1417
1437
  if (explorerInitializationPromise) return explorerInitializationPromise;
1418
1438
 
1419
1439
  const lifecycleVersion = explorerLifecycleVersion;
@@ -1443,7 +1463,7 @@
1443
1463
  await loadArchiveCapabilities();
1444
1464
  if (lifecycleVersion !== explorerLifecycleVersion) return;
1445
1465
  startHealthCheck();
1446
- await loadDirectory(isPageRestore ? currentPath : sessionStorage.getItem(EXPLORER_PATH_STORAGE_KEY) || '/');
1466
+ await loadDirectory(getStoredExplorerPath(), { fallbackToRoot: true });
1447
1467
  })();
1448
1468
  explorerInitializationPromise = initializationPromise;
1449
1469
 
@@ -1460,7 +1480,7 @@
1460
1480
 
1461
1481
  window.addEventListener('pagehide', handlePageHide);
1462
1482
  window.addEventListener('pageshow', event => {
1463
- if (event.persisted) initializeExplorer(true);
1483
+ if (event.persisted) initializeExplorer();
1464
1484
  });
1465
1485
 
1466
1486
  function showContextMenu(x, y, filePath, isDirectory) {
@@ -1538,7 +1558,7 @@
1538
1558
  hideContextMenu();
1539
1559
  }
1540
1560
 
1541
- async function loadDirectory(path) {
1561
+ async function loadDirectory(path, { fallbackToRoot = false } = {}) {
1542
1562
  const fileGrid = document.getElementById('fileGrid');
1543
1563
  const oldCurrentPath = currentPath;
1544
1564
  const requestId = ++directoryRequestId;
@@ -1562,12 +1582,14 @@
1562
1582
  const data = await response.json();
1563
1583
 
1564
1584
  if (!response.ok || data.error) {
1565
- throw new Error(data.error);
1585
+ const error = new Error(data.error || 'Failed to load directory');
1586
+ error.status = response.status;
1587
+ throw error;
1566
1588
  }
1567
1589
  if (requestId !== directoryRequestId) return;
1568
1590
 
1569
1591
  currentPath = data.currentPath;
1570
- sessionStorage.setItem(EXPLORER_PATH_STORAGE_KEY, currentPath);
1592
+ storeExplorerPath(currentPath);
1571
1593
  allFiles = data.files || [];
1572
1594
 
1573
1595
  if (document.getElementById('searchInput')) {
@@ -1580,6 +1602,10 @@
1580
1602
  updateBackButton(data.parentPath);
1581
1603
  } catch (error) {
1582
1604
  if (error.name === 'AbortError' || requestId !== directoryRequestId) return;
1605
+ if (fallbackToRoot && path !== '/' && error.status === 404) {
1606
+ sessionStorage.removeItem(EXPLORER_PATH_STORAGE_KEY);
1607
+ return loadDirectory('/', { fallbackToRoot: false });
1608
+ }
1583
1609
  console.error('Error loading directory:', error);
1584
1610
  currentPath = oldCurrentPath;
1585
1611
  const parentPath = currentPath === '/' ? null : currentPath.substring(0, currentPath.lastIndexOf('/')) || '/';
package/dist/meta.json CHANGED
@@ -1088,7 +1088,7 @@
1088
1088
  "format": "cjs"
1089
1089
  },
1090
1090
  "src/bin/mock-service-cli": {
1091
- "bytes": 12219,
1091
+ "bytes": 13264,
1092
1092
  "imports": [
1093
1093
  {
1094
1094
  "path": "node_modules/colors/safe.js",
@@ -6704,7 +6704,7 @@
6704
6704
  "format": "cjs"
6705
6705
  },
6706
6706
  "src/lib/staticServer.js": {
6707
- "bytes": 23163,
6707
+ "bytes": 33699,
6708
6708
  "imports": [
6709
6709
  {
6710
6710
  "path": "node_modules/express/index.js",
@@ -7489,7 +7489,7 @@
7489
7489
  "format": "cjs"
7490
7490
  },
7491
7491
  "src/lib/fileExplorerServer.js": {
7492
- "bytes": 25074,
7492
+ "bytes": 25537,
7493
7493
  "imports": [
7494
7494
  {
7495
7495
  "path": "node_modules/express/index.js",
@@ -8882,7 +8882,7 @@
8882
8882
  "bytesInOutput": 429
8883
8883
  },
8884
8884
  "src/bin/mock-service-cli": {
8885
- "bytesInOutput": 9280
8885
+ "bytesInOutput": 10204
8886
8886
  },
8887
8887
  "node_modules/depd/index.js": {
8888
8888
  "bytesInOutput": 6779
@@ -9866,7 +9866,7 @@
9866
9866
  "bytesInOutput": 10974
9867
9867
  },
9868
9868
  "src/lib/staticServer.js": {
9869
- "bytesInOutput": 18312
9869
+ "bytesInOutput": 26622
9870
9870
  },
9871
9871
  "node_modules/busboy/lib/utils.js": {
9872
9872
  "bytesInOutput": 8707
@@ -9989,13 +9989,13 @@
9989
9989
  "bytesInOutput": 7774
9990
9990
  },
9991
9991
  "src/lib/fileExplorerServer.js": {
9992
- "bytesInOutput": 18585
9992
+ "bytesInOutput": 19066
9993
9993
  },
9994
9994
  "src/runtime.js": {
9995
9995
  "bytesInOutput": 507
9996
9996
  }
9997
9997
  },
9998
- "bytes": 1619475
9998
+ "bytes": 1629190
9999
9999
  }
10000
10000
  }
10001
10001
  }