@sansenjian/qq-music-api 2.2.9 → 2.3.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.
Files changed (173) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/README.md +10 -11
  3. package/dist/api/index.js +9 -0
  4. package/dist/app.js +8 -43
  5. package/dist/index.js +13 -1
  6. package/dist/koaApp.js +49 -0
  7. package/dist/module/apis/downloadQQMusic.js +2 -1
  8. package/dist/module/apis/music/getLyric.js +220 -16
  9. package/dist/module/apis/music/getMusicPlay.js +3 -2
  10. package/dist/package.json +19 -15
  11. package/dist/routers/context/getLyric.js +12 -2
  12. package/dist/routers/context/getNewDisks.js +4 -0
  13. package/dist/routers/context/getRecommend.js +4 -0
  14. package/dist/routers/context/getTicketInfo.js +4 -0
  15. package/dist/util/cookieResolver.js +10 -4
  16. package/docs-dist/404.html +6 -6
  17. package/docs-dist/CHANGELOG-ARCHITECTURE.html +14 -14
  18. package/docs-dist/COOKIE_CONFIG_GUIDE.html +13 -13
  19. package/docs-dist/DEGRADE_EXAMPLES.html +109 -0
  20. package/docs-dist/FALLBACK_MODE_GUIDE.html +21 -21
  21. package/docs-dist/QUICK_START.html +62 -0
  22. package/docs-dist/README.html +17 -17
  23. package/docs-dist/TEST_USER_PLAYLISTS.html +14 -14
  24. package/docs-dist/USER_AVATAR_GUIDE.html +16 -16
  25. package/docs-dist/api/comments.html +12 -12
  26. package/docs-dist/api/index.html +10 -10
  27. package/docs-dist/api/music.html +15 -13
  28. package/docs-dist/api/other.html +13 -13
  29. package/docs-dist/api/playground.html +27 -0
  30. package/docs-dist/api/playlist.html +15 -15
  31. package/docs-dist/api/rank.html +12 -12
  32. package/docs-dist/api/search.html +13 -13
  33. package/docs-dist/api/singer.html +12 -12
  34. package/docs-dist/api/user.html +16 -16
  35. package/docs-dist/assets/{CHANGELOG-ARCHITECTURE.md.r40JGJZK.js → CHANGELOG-ARCHITECTURE.md.CYHmaBdY.js} +5 -5
  36. package/docs-dist/assets/CHANGELOG-ARCHITECTURE.md.CYHmaBdY.lean.js +1 -0
  37. package/docs-dist/assets/COOKIE_CONFIG_GUIDE.md.Nvywo7CW.js +13 -0
  38. package/docs-dist/assets/COOKIE_CONFIG_GUIDE.md.Nvywo7CW.lean.js +1 -0
  39. package/docs-dist/assets/DEGRADE_EXAMPLES.md.Cz2J-qwE.js +83 -0
  40. package/docs-dist/assets/DEGRADE_EXAMPLES.md.Cz2J-qwE.lean.js +1 -0
  41. package/docs-dist/assets/{FALLBACK_MODE_GUIDE.md.BBdcIdh_.js → FALLBACK_MODE_GUIDE.md.wKA9yqoI.js} +12 -12
  42. package/docs-dist/assets/FALLBACK_MODE_GUIDE.md.wKA9yqoI.lean.js +1 -0
  43. package/docs-dist/assets/QUICK_START.md.D5KfDgbs.js +36 -0
  44. package/docs-dist/assets/QUICK_START.md.D5KfDgbs.lean.js +1 -0
  45. package/docs-dist/assets/README.md.CHbArqA7.js +421 -0
  46. package/docs-dist/assets/README.md.CHbArqA7.lean.js +1 -0
  47. package/docs-dist/assets/TEST_USER_PLAYLISTS.md.BfBxYbaM.js +16 -0
  48. package/docs-dist/assets/TEST_USER_PLAYLISTS.md.BfBxYbaM.lean.js +1 -0
  49. package/docs-dist/assets/{USER_AVATAR_GUIDE.md.CVHPs2Dn.js → USER_AVATAR_GUIDE.md.D5Rti1Gg.js} +7 -7
  50. package/docs-dist/assets/USER_AVATAR_GUIDE.md.D5Rti1Gg.lean.js +1 -0
  51. package/docs-dist/assets/{api_comments.md.79Q_C8Qp.js → api_comments.md.5nDhrWa8.js} +3 -3
  52. package/docs-dist/assets/api_comments.md.5nDhrWa8.lean.js +1 -0
  53. package/docs-dist/assets/api_index.md.DdG1WHkZ.js +1 -0
  54. package/docs-dist/assets/api_index.md.DdG1WHkZ.lean.js +1 -0
  55. package/docs-dist/assets/{api_music.md.B1AzLePX.js → api_music.md.D66hq-_4.js} +6 -4
  56. package/docs-dist/assets/api_music.md.D66hq-_4.lean.js +1 -0
  57. package/docs-dist/assets/api_other.md.9KhspVEM.js +7 -0
  58. package/docs-dist/assets/api_other.md.9KhspVEM.lean.js +1 -0
  59. package/docs-dist/assets/api_playground.md.BZBvYMm2.js +11 -0
  60. package/docs-dist/assets/api_playground.md.BZBvYMm2.lean.js +11 -0
  61. package/docs-dist/assets/{api_playlist.md.8ACJ3QqD.js → api_playlist.md.BQK32ZyE.js} +6 -6
  62. package/docs-dist/assets/api_playlist.md.BQK32ZyE.lean.js +1 -0
  63. package/docs-dist/assets/{api_rank.md.B8IP2ZRy.js → api_rank.md.z7YBwVgw.js} +3 -3
  64. package/docs-dist/assets/api_rank.md.z7YBwVgw.lean.js +1 -0
  65. package/docs-dist/assets/{api_search.md.DO9J6nvp.js → api_search.md.GfzIBRfc.js} +4 -4
  66. package/docs-dist/assets/api_search.md.GfzIBRfc.lean.js +1 -0
  67. package/docs-dist/assets/api_singer.md.BDR-_qDH.js +21 -0
  68. package/docs-dist/assets/api_singer.md.BDR-_qDH.lean.js +1 -0
  69. package/docs-dist/assets/{api_user.md.Cb7Ky3Sn.js → api_user.md.Dx8BWGrb.js} +7 -7
  70. package/docs-dist/assets/api_user.md.Dx8BWGrb.lean.js +1 -0
  71. package/docs-dist/assets/app.CxuIZ1W7.js +1 -0
  72. package/docs-dist/assets/chunks/@localSearchIndexroot.ygoKgu27.js +1 -0
  73. package/docs-dist/assets/chunks/VPLocalSearchBox.BqgkAhNM.js +3 -0
  74. package/docs-dist/assets/chunks/framework.BUY3a635.js +4 -0
  75. package/docs-dist/assets/chunks/theme.C-Z3DN0r.js +2 -0
  76. package/docs-dist/assets/{guide_architecture.md.CzgqynmB.js → guide_architecture.md.SHnKkzwb.js} +20 -20
  77. package/docs-dist/assets/guide_architecture.md.SHnKkzwb.lean.js +1 -0
  78. package/docs-dist/assets/guide_authentication.md.CZCKocgR.js +4 -0
  79. package/docs-dist/assets/guide_authentication.md.CZCKocgR.lean.js +1 -0
  80. package/docs-dist/assets/guide_index.md.CkJ-jjL0.js +1 -0
  81. package/docs-dist/assets/guide_index.md.CkJ-jjL0.lean.js +1 -0
  82. package/docs-dist/assets/guide_installation.md.D2TBzILh.js +7 -0
  83. package/docs-dist/assets/guide_installation.md.D2TBzILh.lean.js +1 -0
  84. package/docs-dist/assets/guide_quickstart.md.J7Sib8wg.js +13 -0
  85. package/docs-dist/assets/guide_quickstart.md.J7Sib8wg.lean.js +1 -0
  86. package/docs-dist/assets/index.md.ClwYf6Qc.js +1 -0
  87. package/docs-dist/assets/index.md.ClwYf6Qc.lean.js +1 -0
  88. package/docs-dist/assets/inter-italic-cyrillic-ext._dlW9xFb.woff2 +0 -0
  89. package/docs-dist/assets/inter-italic-cyrillic.D7dRslh9.woff2 +0 -0
  90. package/docs-dist/assets/inter-italic-greek-ext.Ct-Tf2bq.woff2 +0 -0
  91. package/docs-dist/assets/inter-italic-greek.DNcpQ8QC.woff2 +0 -0
  92. package/docs-dist/assets/inter-italic-latin-ext.DytegdRQ.woff2 +0 -0
  93. package/docs-dist/assets/inter-italic-latin.COaG5lWR.woff2 +0 -0
  94. package/docs-dist/assets/inter-italic-vietnamese.BI5UxJD-.woff2 +0 -0
  95. package/docs-dist/assets/inter-roman-cyrillic-ext.BeNbU08G.woff2 +0 -0
  96. package/docs-dist/assets/inter-roman-cyrillic.CD0kT8R4.woff2 +0 -0
  97. package/docs-dist/assets/inter-roman-greek-ext.CFAEQ5Ow.woff2 +0 -0
  98. package/docs-dist/assets/inter-roman-greek.Dsf7YjP7.woff2 +0 -0
  99. package/docs-dist/assets/inter-roman-latin-ext.Dl_ayf4-.woff2 +0 -0
  100. package/docs-dist/assets/inter-roman-latin.Cy4MYw_J.woff2 +0 -0
  101. package/docs-dist/assets/inter-roman-vietnamese.CpqCnS2H.woff2 +0 -0
  102. package/docs-dist/assets/reference_response-format.md.BrGoGoPV.js +12 -0
  103. package/docs-dist/assets/reference_response-format.md.BrGoGoPV.lean.js +1 -0
  104. package/docs-dist/assets/style.D_YoXH3a.css +1 -0
  105. package/docs-dist/guide/architecture.html +29 -29
  106. package/docs-dist/guide/authentication.html +12 -12
  107. package/docs-dist/guide/index.html +10 -10
  108. package/docs-dist/guide/installation.html +13 -13
  109. package/docs-dist/guide/quickstart.html +15 -15
  110. package/docs-dist/hashmap.json +1 -1
  111. package/docs-dist/index.html +10 -10
  112. package/docs-dist/reference/response-format.html +13 -13
  113. package/docs-dist/version.json +3 -3
  114. package/package.json +19 -15
  115. package/public/index.html +966 -0
  116. package/public/playground-utils.js +150 -0
  117. package/dist/jest.config.js +0 -52
  118. package/dist/scripts/run-tests-with-flags.js +0 -139
  119. package/dist/types/api.js +0 -55
  120. package/docs-dist/assets/CHANGELOG-ARCHITECTURE.md.r40JGJZK.lean.js +0 -1
  121. package/docs-dist/assets/COOKIE_CONFIG_GUIDE.md.BVXl7WHu.js +0 -13
  122. package/docs-dist/assets/COOKIE_CONFIG_GUIDE.md.BVXl7WHu.lean.js +0 -1
  123. package/docs-dist/assets/FALLBACK_MODE_GUIDE.md.BBdcIdh_.lean.js +0 -1
  124. package/docs-dist/assets/README.md.D6Tw0nRd.js +0 -421
  125. package/docs-dist/assets/README.md.D6Tw0nRd.lean.js +0 -1
  126. package/docs-dist/assets/TEST_USER_PLAYLISTS.md.DSt20Igj.js +0 -16
  127. package/docs-dist/assets/TEST_USER_PLAYLISTS.md.DSt20Igj.lean.js +0 -1
  128. package/docs-dist/assets/USER_AVATAR_GUIDE.md.CVHPs2Dn.lean.js +0 -1
  129. package/docs-dist/assets/api_comments.md.79Q_C8Qp.lean.js +0 -1
  130. package/docs-dist/assets/api_index.md.CU3By8tw.js +0 -1
  131. package/docs-dist/assets/api_index.md.CU3By8tw.lean.js +0 -1
  132. package/docs-dist/assets/api_music.md.B1AzLePX.lean.js +0 -1
  133. package/docs-dist/assets/api_other.md.DCg4bzA7.js +0 -7
  134. package/docs-dist/assets/api_other.md.DCg4bzA7.lean.js +0 -1
  135. package/docs-dist/assets/api_playlist.md.8ACJ3QqD.lean.js +0 -1
  136. package/docs-dist/assets/api_rank.md.B8IP2ZRy.lean.js +0 -1
  137. package/docs-dist/assets/api_search.md.DO9J6nvp.lean.js +0 -1
  138. package/docs-dist/assets/api_singer.md.CcL32xuN.js +0 -21
  139. package/docs-dist/assets/api_singer.md.CcL32xuN.lean.js +0 -1
  140. package/docs-dist/assets/api_user.md.Cb7Ky3Sn.lean.js +0 -1
  141. package/docs-dist/assets/app.CSainqD9.js +0 -1
  142. package/docs-dist/assets/chunks/@localSearchIndexroot.BKleDIv-.js +0 -1
  143. package/docs-dist/assets/chunks/VPLocalSearchBox.BUBaq7tw.js +0 -9
  144. package/docs-dist/assets/chunks/framework.aJbMEiY9.js +0 -19
  145. package/docs-dist/assets/chunks/theme.CzMhU0Ps.js +0 -2
  146. package/docs-dist/assets/guide_architecture.md.CzgqynmB.lean.js +0 -1
  147. package/docs-dist/assets/guide_authentication.md.a8yTA8Xe.js +0 -4
  148. package/docs-dist/assets/guide_authentication.md.a8yTA8Xe.lean.js +0 -1
  149. package/docs-dist/assets/guide_index.md.BgUUL6fI.js +0 -1
  150. package/docs-dist/assets/guide_index.md.BgUUL6fI.lean.js +0 -1
  151. package/docs-dist/assets/guide_installation.md.BCZ4jBl_.js +0 -7
  152. package/docs-dist/assets/guide_installation.md.BCZ4jBl_.lean.js +0 -1
  153. package/docs-dist/assets/guide_quickstart.md.9-4dA6wS.js +0 -13
  154. package/docs-dist/assets/guide_quickstart.md.9-4dA6wS.lean.js +0 -1
  155. package/docs-dist/assets/index.md.z0hAJioN.js +0 -1
  156. package/docs-dist/assets/index.md.z0hAJioN.lean.js +0 -1
  157. package/docs-dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  158. package/docs-dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  159. package/docs-dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  160. package/docs-dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  161. package/docs-dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  162. package/docs-dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  163. package/docs-dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  164. package/docs-dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  165. package/docs-dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  166. package/docs-dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  167. package/docs-dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  168. package/docs-dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  169. package/docs-dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  170. package/docs-dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  171. package/docs-dist/assets/reference_response-format.md.VvQTLDZr.js +0 -12
  172. package/docs-dist/assets/reference_response-format.md.VvQTLDZr.lean.js +0 -1
  173. package/docs-dist/assets/style.DM4qKDd4.css +0 -1
@@ -0,0 +1,150 @@
1
+ export const defaultBaseUrl = 'http://localhost:3200';
2
+
3
+ const HTTP_PROTOCOLS = new Set(['http:', 'https:']);
4
+
5
+ const getLocationOrigin = () => {
6
+ const location = globalThis.location;
7
+
8
+ if (location && HTTP_PROTOCOLS.has(location.protocol)) {
9
+ return location.origin;
10
+ }
11
+
12
+ return defaultBaseUrl;
13
+ };
14
+
15
+ export const getInitialBaseUrl = () => {
16
+ const location = globalThis.location;
17
+
18
+ if (location && HTTP_PROTOCOLS.has(location.protocol)) {
19
+ return location.origin;
20
+ }
21
+
22
+ return defaultBaseUrl;
23
+ };
24
+
25
+ export const normalizeBaseUrl = value => {
26
+ const trimmed = String(value || '').trim();
27
+
28
+ if (!trimmed) {
29
+ return defaultBaseUrl;
30
+ }
31
+
32
+ try {
33
+ const candidate = /^https?:\/\//i.test(trimmed)
34
+ ? trimmed
35
+ : `http://${trimmed.replace(/^\/+/, '')}`;
36
+ const parsed = new URL(candidate, getLocationOrigin());
37
+
38
+ if (!HTTP_PROTOCOLS.has(parsed.protocol)) {
39
+ return defaultBaseUrl;
40
+ }
41
+
42
+ return parsed.origin.replace(/\/+$/, '');
43
+ } catch {
44
+ return defaultBaseUrl;
45
+ }
46
+ };
47
+
48
+ export const normalizePath = value => {
49
+ const trimmed = String(value || '').trim();
50
+
51
+ if (!trimmed) {
52
+ return '/';
53
+ }
54
+
55
+ if (/^https?:\/\//i.test(trimmed)) {
56
+ try {
57
+ const url = new URL(trimmed);
58
+ return `${url.pathname}${url.search}${url.hash}` || '/';
59
+ } catch {
60
+ return '/';
61
+ }
62
+ }
63
+
64
+ return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
65
+ };
66
+
67
+ export const normalizeQuery = value => String(value || '').trim().replace(/^\?/, '');
68
+
69
+ export const buildRequestUrl = ({ baseUrl, path = '/', query = '' }) => {
70
+ const normalizedBase = normalizeBaseUrl(baseUrl);
71
+ const normalizedPath = normalizePath(path);
72
+ const normalizedQuery = normalizeQuery(query);
73
+
74
+ if (!normalizedQuery) {
75
+ return `${normalizedBase}${normalizedPath}`;
76
+ }
77
+
78
+ const separator = normalizedPath.includes('?') ? '&' : '?';
79
+ return `${normalizedBase}${normalizedPath}${separator}${normalizedQuery}`;
80
+ };
81
+
82
+ export const buildFetchOptions = ({ method = 'GET', cookie = '', body = '', signal } = {}) => {
83
+ const normalizedMethod = String(method || 'GET').toUpperCase();
84
+ const headers = {};
85
+ const trimmedCookie = String(cookie || '').trim();
86
+ const options = {
87
+ method: normalizedMethod,
88
+ headers
89
+ };
90
+
91
+ if (signal) {
92
+ options.signal = signal;
93
+ }
94
+
95
+ if (trimmedCookie) {
96
+ headers['X-Custom-Cookie'] = trimmedCookie;
97
+ }
98
+
99
+ if (normalizedMethod !== 'GET' && String(body || '').trim()) {
100
+ headers['Content-Type'] = 'application/json';
101
+ options.body = body;
102
+ }
103
+
104
+ return options;
105
+ };
106
+
107
+ export const formatResponseText = (text, fallbackText = '') => {
108
+ if (!text) {
109
+ return fallbackText;
110
+ }
111
+
112
+ try {
113
+ return JSON.stringify(JSON.parse(text), null, 2);
114
+ } catch {
115
+ return text || fallbackText;
116
+ }
117
+ };
118
+
119
+ export const formatBytes = bytes => {
120
+ if (!bytes) {
121
+ return '0 B';
122
+ }
123
+
124
+ if (bytes < 1024) {
125
+ return `${bytes} B`;
126
+ }
127
+
128
+ return `${(bytes / 1024).toFixed(1)} KB`;
129
+ };
130
+
131
+ export const isAbortError = error => error instanceof DOMException && error.name === 'AbortError';
132
+
133
+ export const getErrorMessage = error => error instanceof Error ? error.message : String(error);
134
+
135
+ export const fetchPlaygroundRequest = async ({ url, options }) => {
136
+ const startedAt = performance.now();
137
+ const response = await fetch(url, options);
138
+ const rawText = await response.text();
139
+ const elapsedMs = Math.round(performance.now() - startedAt);
140
+ const statusText = `${response.status} ${response.statusText || 'OK'}`;
141
+
142
+ return {
143
+ elapsedMs,
144
+ formattedText: formatResponseText(rawText, statusText),
145
+ ok: response.ok,
146
+ rawText,
147
+ sizeBytes: new Blob([rawText]).size,
148
+ statusText
149
+ };
150
+ };
@@ -1,52 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const config = {
4
- preset: 'ts-jest',
5
- testEnvironment: 'node',
6
- testMatch: ['**/tests/**/*.test.ts'],
7
- collectCoverageFrom: [
8
- 'module/**/*.ts',
9
- 'routers/**/*.ts',
10
- 'util/**/*.ts',
11
- 'middlewares/**/*.ts',
12
- '!**/node_modules/**',
13
- '!**/tests/**',
14
- '!**/dist/**',
15
- '!**/*.d.ts',
16
- '!**/types/**/*.ts'
17
- ],
18
- coverageDirectory: 'coverage',
19
- coverageReporters: ['text', 'lcov', 'html', 'json-summary', 'json'],
20
- coveragePathIgnorePatterns: [
21
- '/node_modules/',
22
- '/tests/',
23
- '/dist/',
24
- '\\.d\\.ts$',
25
- '/types/'
26
- ],
27
- coverageThreshold: {
28
- global: {
29
- branches: 35,
30
- functions: 40,
31
- lines: 50,
32
- statements: 50
33
- }
34
- },
35
- setupFilesAfterEnv: ['<rootDir>/tests/setup/jest.setup.ts'],
36
- modulePathIgnorePatterns: ['<rootDir>/dist/'],
37
- testTimeout: 10000,
38
- verbose: true,
39
- collectCoverage: true, // 默认开启覆盖率
40
- coverageProvider: 'v8', // 使用 v8 引擎,更准确
41
- moduleNameMapper: {
42
- '^@module/(.*)$': '<rootDir>/module/$1',
43
- '^@routers/(.*)$': '<rootDir>/routers/$1',
44
- '^@util/(.*)$': '<rootDir>/util/$1'
45
- },
46
- transform: {
47
- '^.+[.]ts$': ['ts-jest', {
48
- tsconfig: 'tsconfig.test.json'
49
- }]
50
- }
51
- };
52
- exports.default = config;
@@ -1,139 +0,0 @@
1
- "use strict";
2
- /**
3
- * 运行测试并生成带 flags 的覆盖率报告
4
- *
5
- * 用法:
6
- * - 运行所有测试:npm run test:flags
7
- * - 只运行单元测试:npm run test:flags:unit
8
- *
9
- * 注意:此脚本通过 tsx 运行,不使用 ts-node
10
- */
11
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
- if (k2 === undefined) k2 = k;
13
- var desc = Object.getOwnPropertyDescriptor(m, k);
14
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
- desc = { enumerable: true, get: function() { return m[k]; } };
16
- }
17
- Object.defineProperty(o, k2, desc);
18
- }) : (function(o, m, k, k2) {
19
- if (k2 === undefined) k2 = k;
20
- o[k2] = m[k];
21
- }));
22
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
- Object.defineProperty(o, "default", { enumerable: true, value: v });
24
- }) : function(o, v) {
25
- o["default"] = v;
26
- });
27
- var __importStar = (this && this.__importStar) || (function () {
28
- var ownKeys = function(o) {
29
- ownKeys = Object.getOwnPropertyNames || function (o) {
30
- var ar = [];
31
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
- return ar;
33
- };
34
- return ownKeys(o);
35
- };
36
- return function (mod) {
37
- if (mod && mod.__esModule) return mod;
38
- var result = {};
39
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
- __setModuleDefault(result, mod);
41
- return result;
42
- };
43
- })();
44
- Object.defineProperty(exports, "__esModule", { value: true });
45
- const child_process_1 = require("child_process");
46
- const fs = __importStar(require("fs"));
47
- const path = __importStar(require("path"));
48
- const ROOT_DIR = path.resolve(__dirname, '..');
49
- const COVERAGE_DIR = path.join(ROOT_DIR, 'coverage');
50
- // 确保覆盖率目录存在
51
- if (!fs.existsSync(COVERAGE_DIR)) {
52
- fs.mkdirSync(COVERAGE_DIR, { recursive: true });
53
- }
54
- /**
55
- * 运行命令
56
- * 注意:command 参数必须是硬编码的命令字符串,不能包含用户输入
57
- * @param command - 要执行的命令(必须是硬编码字符串)
58
- * @param env - 环境变量
59
- */
60
- function runCommand(command, env = {}) {
61
- try {
62
- // 使用 execSync 执行命令,命令必须是固定的字符串
63
- // 安全说明:此函数只能用于执行硬编码的命令,不接受外部输入
64
- // 所有调用此函数的地方都必须使用字符串字面量
65
- (0, child_process_1.execSync)(command, {
66
- stdio: 'inherit',
67
- env: { ...process.env, ...env },
68
- cwd: ROOT_DIR
69
- });
70
- }
71
- catch (error) {
72
- console.error(`命令执行失败:${command}`);
73
- throw error;
74
- }
75
- }
76
- /**
77
- * 运行 Jest 测试(安全版本)
78
- * 使用参数化方式执行 Jest,避免命令注入风险
79
- * @param args - Jest 命令行参数数组
80
- * @param env - 环境变量
81
- */
82
- function runJest(args, env = {}) {
83
- const command = `npx jest ${args.join(' ')}`;
84
- return runCommand(command, env);
85
- }
86
- function runUnitTests() {
87
- console.log('\n🧪 运行单元测试...\n');
88
- // 运行单元测试 - 使用参数化方式
89
- runJest(['--coverage', '--testPathPattern=tests/unit'], {
90
- JEST_JUNIT_OUTPUT_NAME: 'unit-test-results.xml',
91
- COVERAGE_FILE: 'coverage/unit-coverage.json'
92
- });
93
- // 移动覆盖率文件
94
- const coverageFile = path.join(COVERAGE_DIR, 'coverage-final.json');
95
- if (fs.existsSync(coverageFile)) {
96
- const unitCoverageFile = path.join(COVERAGE_DIR, 'unit-coverage-final.json');
97
- fs.copyFileSync(coverageFile, unitCoverageFile);
98
- console.log(`✅ 单元测试覆盖率已保存到:${unitCoverageFile}`);
99
- }
100
- }
101
- function runAllTests() {
102
- console.log('\n🧪 运行所有测试...\n');
103
- // 运行所有测试 - 使用参数化方式
104
- runJest(['--coverage'], {
105
- JEST_JUNIT_OUTPUT_NAME: 'test-results.xml',
106
- COVERAGE_FILE: 'coverage/all-coverage.json'
107
- });
108
- // 移动覆盖率文件
109
- const coverageFile = path.join(COVERAGE_DIR, 'coverage-final.json');
110
- if (fs.existsSync(coverageFile)) {
111
- const allCoverageFile = path.join(COVERAGE_DIR, 'all-coverage-final.json');
112
- fs.copyFileSync(coverageFile, allCoverageFile);
113
- console.log(`✅ 所有测试覆盖率已保存到:${allCoverageFile}`);
114
- }
115
- }
116
- function main() {
117
- const args = process.argv.slice(2);
118
- const testType = args[0] || 'all';
119
- console.log('🚀 开始运行测试...\n');
120
- try {
121
- if (testType === 'unit') {
122
- runUnitTests();
123
- }
124
- else if (testType === 'all') {
125
- runAllTests();
126
- }
127
- else {
128
- console.error(`❌ 未知的测试类型:${testType}`);
129
- console.error('可用选项:unit, all');
130
- process.exit(1);
131
- }
132
- console.log('\n✅ 测试完成!\n');
133
- }
134
- catch (error) {
135
- console.error('\n❌ 测试失败!\n');
136
- process.exit(1);
137
- }
138
- }
139
- main();
package/dist/types/api.js DELETED
@@ -1,55 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createSuccessResponse = createSuccessResponse;
4
- exports.createErrorResponse = createErrorResponse;
5
- exports.handleApi = handleApi;
6
- /**
7
- * 成功响应创建器
8
- */
9
- function createSuccessResponse(data, status = 200) {
10
- return {
11
- status,
12
- body: {
13
- response: data,
14
- },
15
- };
16
- }
17
- /**
18
- * 错误响应创建器
19
- */
20
- function createErrorResponse(error, status = 500) {
21
- return {
22
- status,
23
- body: {
24
- error,
25
- },
26
- };
27
- }
28
- /**
29
- * API Promise 处理器 - 自动处理成功和错误情况
30
- */
31
- async function handleApi(promise, options) {
32
- try {
33
- const result = await promise;
34
- const responseData = options?.transformData
35
- ? options.transformData(result.data || result)
36
- : result.data || result;
37
- return {
38
- status: options?.customStatus || 200,
39
- body: {
40
- response: responseData,
41
- },
42
- };
43
- }
44
- catch (error) {
45
- if (options?.logError !== false) {
46
- console.log('API error:', error);
47
- }
48
- return {
49
- status: options?.customStatus || 500,
50
- body: {
51
- error,
52
- },
53
- };
54
- }
55
- }
@@ -1 +0,0 @@
1
- import{_ as i,o as a,c as n,ag as h}from"./chunks/framework.aJbMEiY9.js";const y=JSON.parse('{"title":"代码架构优化说明","description":"","frontmatter":{},"headers":[],"relativePath":"CHANGELOG-ARCHITECTURE.md","filePath":"CHANGELOG-ARCHITECTURE.md","lastUpdated":1773499772000}'),l={name:"CHANGELOG-ARCHITECTURE.md"};function k(t,s,p,e,r,d){return a(),n("div",null,[...s[0]||(s[0]=[h("",38)])])}const o=i(l,[["render",k]]);export{y as __pageData,o as default};
@@ -1,13 +0,0 @@
1
- import{_ as s,o as a,c as e,ag as l}from"./chunks/framework.aJbMEiY9.js";const c=JSON.parse('{"title":"Cookie 配置指南","description":"","frontmatter":{},"headers":[],"relativePath":"COOKIE_CONFIG_GUIDE.md","filePath":"COOKIE_CONFIG_GUIDE.md","lastUpdated":1773499772000}'),t={name:"COOKIE_CONFIG_GUIDE.md"};function o(n,i,h,r,p,k){return a(),e("div",null,[...i[0]||(i[0]=[l(`<h1 id="cookie-配置指南" tabindex="-1">Cookie 配置指南 <a class="header-anchor" href="#cookie-配置指南" aria-label="Permalink to &quot;Cookie 配置指南&quot;">​</a></h1><h2 id="为什么需要配置-cookie" tabindex="-1">为什么需要配置 Cookie? <a class="header-anchor" href="#为什么需要配置-cookie" aria-label="Permalink to &quot;为什么需要配置 Cookie?&quot;">​</a></h2><p>QQ 音乐的许多接口(如获取用户信息、用户歌单、播放历史等)需要用户登录状态才能访问。Cookie 包含了你的登录凭证,配置后 API 才能代表你请求这些数据。</p><h2 id="配置步骤" tabindex="-1">配置步骤 <a class="header-anchor" href="#配置步骤" aria-label="Permalink to &quot;配置步骤&quot;">​</a></h2><h3 id="_1-登录-qq-音乐" tabindex="-1">1. 登录 QQ 音乐 <a class="header-anchor" href="#_1-登录-qq-音乐" aria-label="Permalink to &quot;1. 登录 QQ 音乐&quot;">​</a></h3><p>访问 <a href="https://y.qq.com" target="_blank" rel="noreferrer">https://y.qq.com</a> 并登录你的 QQ 账号。</p><h3 id="_2-获取-cookie" tabindex="-1">2. 获取 Cookie <a class="header-anchor" href="#_2-获取-cookie" aria-label="Permalink to &quot;2. 获取 Cookie&quot;">​</a></h3><h4 id="chrome-edge-浏览器" tabindex="-1">Chrome/Edge 浏览器: <a class="header-anchor" href="#chrome-edge-浏览器" aria-label="Permalink to &quot;Chrome/Edge 浏览器:&quot;">​</a></h4><ol><li>按 <code>F12</code> 打开开发者工具</li><li>点击 <strong>Network(网络)</strong> 标签</li><li>刷新页面(<code>F5</code> 或 <code>Ctrl+R</code>)</li><li>在左侧请求列表中选择第一个请求(通常是 <code>y.qq.com</code>)</li><li>在右侧找到 <strong>Request Headers(请求标头)</strong></li><li>滚动找到 <strong>Cookie</strong> 字段</li><li>复制整个 Cookie 值(从 <code>uin=</code> 开始到最后一个字段)</li></ol><h4 id="firefox-浏览器" tabindex="-1">Firefox 浏览器: <a class="header-anchor" href="#firefox-浏览器" aria-label="Permalink to &quot;Firefox 浏览器:&quot;">​</a></h4><ol><li>按 <code>F12</code> 打开开发者工具</li><li>点击 <strong>Network(网络)</strong> 标签</li><li>刷新页面</li><li>选择第一个请求</li><li>在 <strong>Request Headers</strong> 中找到 <strong>Cookie</strong></li><li>复制整个值</li></ol><h3 id="_3-获取你的-qq-号" tabindex="-1">3. 获取你的 QQ 号 <a class="header-anchor" href="#_3-获取你的-qq-号" aria-label="Permalink to &quot;3. 获取你的 QQ 号&quot;">​</a></h3><p>有几种方法可以获取:</p><p><strong>方法 1</strong>: 从 Cookie 中提取</p><ul><li>Cookie 中的 <code>uin=</code> 后面的数字就是你的 QQ 号(去掉开头的 0)</li><li>例如:<code>uin=o0123456789</code> → QQ 号是 <code>123456789</code></li></ul><p><strong>方法 2</strong>: 从个人主页 URL 获取</p><ul><li>点击头像进入个人主页</li><li>URL 格式:<code>https://y.qq.com/portal/profile.html?uin=123456789</code></li><li><code>uin=</code> 后面的数字就是</li></ul><p><strong>方法 3</strong>: 在控制台查看</p><ul><li>按 <code>F12</code> 打开开发者工具</li><li>进入 <strong>Console(控制台)</strong> 标签</li><li>输入 <code>window.user</code> 并回车</li><li>查看返回的对象中的 <code>uin</code> 字段</li></ul><h3 id="_4-编辑配置文件" tabindex="-1">4. 编辑配置文件 <a class="header-anchor" href="#_4-编辑配置文件" aria-label="Permalink to &quot;4. 编辑配置文件&quot;">​</a></h3><p>打开项目中的 <code>config/user-info.json</code> 文件,填入你的信息:</p><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">{</span></span>
2
- <span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;"> &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">loginUin</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">123456789</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
3
- <span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;"> &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">cookie</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">uin=o0123456789; qqmusic_key=abcdef123456; qqmusic_uin=123456789; ...</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
4
- <span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;"> &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">uin</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">123456789</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span></span>
5
- <span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">}</span></span></code></pre></div><p><strong>字段说明</strong>:</p><ul><li><code>loginUin</code>: 你的 QQ 号</li><li><code>cookie</code>: 完整的 Cookie 字符串(从浏览器复制的)</li><li><code>uin</code>: 你的 QQ 号(可以和 loginUin 相同)</li></ul><h3 id="_5-重启服务" tabindex="-1">5. 重启服务 <a class="header-anchor" href="#_5-重启服务" aria-label="Permalink to &quot;5. 重启服务&quot;">​</a></h3><p>配置完成后,需要重启 Node.js 服务使配置生效:</p><ol><li>停止当前运行的服务(<code>Ctrl+C</code>)</li><li>重新启动:<code>npm run dev</code></li></ol><h2 id="验证配置" tabindex="-1">验证配置 <a class="header-anchor" href="#验证配置" aria-label="Permalink to &quot;验证配置&quot;">​</a></h2><p>配置完成后,可以通过以下方式验证:</p><h3 id="方法-1-访问首页" tabindex="-1">方法 1: 访问首页 <a class="header-anchor" href="#方法-1-访问首页" aria-label="Permalink to &quot;方法 1: 访问首页&quot;">​</a></h3><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code" tabindex="0"><code><span class="line"><span>http://localhost:3200</span></span></code></pre></div><p>如果配置成功,页面应该正常显示,不会提示 Cookie 未配置。</p><h3 id="方法-2-测试用户接口" tabindex="-1">方法 2: 测试用户接口 <a class="header-anchor" href="#方法-2-测试用户接口" aria-label="Permalink to &quot;方法 2: 测试用户接口&quot;">​</a></h3><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code" tabindex="0"><code><span class="line"><span>http://localhost:3200/user/getCookie</span></span></code></pre></div><p>如果配置成功,会返回你配置的 Cookie 信息。</p><h3 id="方法-3-测试用户歌单" tabindex="-1">方法 3: 测试用户歌单 <a class="header-anchor" href="#方法-3-测试用户歌单" aria-label="Permalink to &quot;方法 3: 测试用户歌单&quot;">​</a></h3><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code" tabindex="0"><code><span class="line"><span>http://localhost:3200/user/getUserPlaylists?uin=你的 QQ 号</span></span></code></pre></div><p>如果配置成功,会返回你的歌单列表。</p><h2 id="常见问题" tabindex="-1">常见问题 <a class="header-anchor" href="#常见问题" aria-label="Permalink to &quot;常见问题&quot;">​</a></h2><h3 id="q-cookie-有效期多久" tabindex="-1">Q: Cookie 有效期多久? <a class="header-anchor" href="#q-cookie-有效期多久" aria-label="Permalink to &quot;Q: Cookie 有效期多久?&quot;">​</a></h3><p><strong>A</strong>: Cookie 通常有较长的有效期(几天到几周不等),但如果发现接口返回 404 或权限错误,可能是 Cookie 过期了,需要重新获取。</p><h3 id="q-配置后还是返回-404" tabindex="-1">Q: 配置后还是返回 404? <a class="header-anchor" href="#q-配置后还是返回-404" aria-label="Permalink to &quot;Q: 配置后还是返回 404?&quot;">​</a></h3><p><strong>A</strong>: 可能的原因:</p><ol><li>Cookie 已过期,需要重新获取</li><li>Cookie 格式不正确,确保完整复制</li><li>QQ 音乐 API 接口已更新</li><li>服务未重启,配置未生效</li></ol><h3 id="q-安全吗-会泄露我的账号吗" tabindex="-1">Q: 安全吗?会泄露我的账号吗? <a class="header-anchor" href="#q-安全吗-会泄露我的账号吗" aria-label="Permalink to &quot;Q: 安全吗?会泄露我的账号吗?&quot;">​</a></h3><p><strong>A</strong>:</p><ul><li>Cookie 只存储在本地 <code>config/user-info.json</code> 文件中</li><li>这个文件已经在 <code>.gitignore</code> 中,不会被提交到 Git</li><li>不要将 <code>config/user-info.json</code> 分享给他人</li><li>如果担心,可以定期更换 Cookie</li></ul><h3 id="q-可以配置多个账号吗" tabindex="-1">Q: 可以配置多个账号吗? <a class="header-anchor" href="#q-可以配置多个账号吗" aria-label="Permalink to &quot;Q: 可以配置多个账号吗?&quot;">​</a></h3><p><strong>A</strong>: 当前版本只支持单个账号配置。如需切换账号,修改 <code>config/user-info.json</code> 中的值并重启服务即可。</p><h2 id="示例配置" tabindex="-1">示例配置 <a class="header-anchor" href="#示例配置" aria-label="Permalink to &quot;示例配置&quot;">​</a></h2><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">{</span></span>
6
- <span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;"> &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">loginUin</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">123456789</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
7
- <span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;"> &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">cookie</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">uin=o0123456789; qqmusic_key=xxxxxxxxxx; qqmusic_uin=123456789; qqmusic_fromstatus=1; _qpsvr_localtk=xxxxxx</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">,</span></span>
8
- <span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;"> &quot;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965;">uin</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577;">&quot;</span><span style="--shiki-light:#999999;--shiki-dark:#666666;">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">123456789</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span></span>
9
- <span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666;">}</span></span></code></pre></div><h2 id="注意事项" tabindex="-1">注意事项 <a class="header-anchor" href="#注意事项" aria-label="Permalink to &quot;注意事项&quot;">​</a></h2><ol><li><strong>不要提交到 Git</strong>: <code>config/user-info.json</code> 已添加到 <code>.gitignore</code>,请勿手动移除</li><li><strong>定期更新</strong>: 如果发现接口异常,尝试更新 Cookie</li><li><strong>备份</strong>: 建议备份你的 Cookie,方便下次使用</li><li><strong>隐私</strong>: 不要公开分享你的 Cookie</li></ol><h2 id="快速测试" tabindex="-1">快速测试 <a class="header-anchor" href="#快速测试" aria-label="Permalink to &quot;快速测试&quot;">​</a></h2><p>配置完成后,运行以下命令测试:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># 测试 Cookie 是否配置成功</span></span>
10
- <span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">curl</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">http://localhost:3200/user/getCookie</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span></span>
11
- <span class="line"></span>
12
- <span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD;"># 测试获取用户歌单</span></span>
13
- <span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665;">curl</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;"> &quot;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D;">http://localhost:3200/user/getUserPlaylists?uin=你的 QQ 号</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77;">&quot;</span></span></code></pre></div><p>如果返回 JSON 数据而不是错误信息,说明配置成功!</p>`,57)])])}const g=s(t,[["render",o]]);export{c as __pageData,g as default};
@@ -1 +0,0 @@
1
- import{_ as s,o as a,c as e,ag as l}from"./chunks/framework.aJbMEiY9.js";const c=JSON.parse('{"title":"Cookie 配置指南","description":"","frontmatter":{},"headers":[],"relativePath":"COOKIE_CONFIG_GUIDE.md","filePath":"COOKIE_CONFIG_GUIDE.md","lastUpdated":1773499772000}'),t={name:"COOKIE_CONFIG_GUIDE.md"};function o(n,i,h,r,p,k){return a(),e("div",null,[...i[0]||(i[0]=[l("",57)])])}const g=s(t,[["render",o]]);export{c as __pageData,g as default};
@@ -1 +0,0 @@
1
- import{_ as i,o as a,c as n,ag as e}from"./chunks/framework.aJbMEiY9.js";const c=JSON.parse('{"title":"降级服务使用指南","description":"","frontmatter":{},"headers":[],"relativePath":"FALLBACK_MODE_GUIDE.md","filePath":"FALLBACK_MODE_GUIDE.md","lastUpdated":1773499772000}'),l={name:"FALLBACK_MODE_GUIDE.md"};function t(h,s,p,k,o,r){return a(),n("div",null,[...s[0]||(s[0]=[e("",62)])])}const g=i(l,[["render",t]]);export{c as __pageData,g as default};