most-box 0.0.2 → 0.0.6

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 (181) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +222 -156
  3. package/electron/main.js +67 -0
  4. package/out/404/index.html +15 -0
  5. package/out/404.html +15 -0
  6. package/out/__next.__PAGE__.txt +11 -0
  7. package/out/__next._full.txt +21 -0
  8. package/out/__next._head.txt +5 -0
  9. package/out/__next._index.txt +7 -0
  10. package/out/__next._tree.txt +4 -0
  11. package/out/_next/static/alUUgRz4oMlw4EtULOYfV/_buildManifest.js +11 -0
  12. package/out/_next/static/alUUgRz4oMlw4EtULOYfV/_clientMiddlewareManifest.js +1 -0
  13. package/out/_next/static/alUUgRz4oMlw4EtULOYfV/_ssgManifest.js +1 -0
  14. package/out/_next/static/chunks/00s106sbq8t9v.js +1 -0
  15. package/out/_next/static/chunks/0174xh3wfsjm1.js +2 -0
  16. package/out/_next/static/chunks/01xlw8hd842-c.js +1 -0
  17. package/out/_next/static/chunks/02ou_44kkb5dz.js +1 -0
  18. package/out/_next/static/chunks/02pr2b_eos3~h.js +1 -0
  19. package/out/_next/static/chunks/03~yq9q893hmn.js +1 -0
  20. package/out/_next/static/chunks/07lsjkarm1p9f.css +1 -0
  21. package/out/_next/static/chunks/0_-ccbcyh_o30.css +1 -0
  22. package/out/_next/static/chunks/0_b839~4.q324.js +1 -0
  23. package/out/_next/static/chunks/0_sna3wdypbzr.js +1 -0
  24. package/out/_next/static/chunks/0_wia9ofmsi1c.css +2 -0
  25. package/out/_next/static/chunks/0byj66sc-9o0g.js +1 -0
  26. package/out/_next/static/chunks/0bzupvr5gt3k9.js +31 -0
  27. package/out/_next/static/chunks/0d3shmwh5_nmn.js +1 -0
  28. package/out/_next/static/chunks/0du450zbk4kq_.js +1 -0
  29. package/out/_next/static/chunks/0e_h0d3ekzks8.css +1 -0
  30. package/out/_next/static/chunks/0ho~log~~-jwp.css +1 -0
  31. package/out/_next/static/chunks/0ibjp~7qzxfjv.js +5 -0
  32. package/out/_next/static/chunks/0imvn_arv36xt.css +1 -0
  33. package/out/_next/static/chunks/0j9~17180dl8j.js +1 -0
  34. package/out/_next/static/chunks/0ji.28mehrvdp.js +1 -0
  35. package/out/_next/static/chunks/0jl~j62iz2uvr.js +1 -0
  36. package/out/_next/static/chunks/0nct0fubs64d-.js +1 -0
  37. package/out/_next/static/chunks/0n~dq4kpx9xxx.js +1 -0
  38. package/out/_next/static/chunks/0pqt~8bl3ukh4.js +4 -0
  39. package/out/_next/static/chunks/0q7ck9f.90_i9.js +1 -0
  40. package/out/_next/static/chunks/0qub_r0x_r-e9.css +1 -0
  41. package/out/_next/static/chunks/0rr4gwjp9z~9a.js +1 -0
  42. package/out/_next/static/chunks/0ry.po.a~iu4p.js +1 -0
  43. package/out/_next/static/chunks/0slwj0c46k5cu.js +1 -0
  44. package/out/_next/static/chunks/0sorqk.oc6b7j.css +1 -0
  45. package/out/_next/static/chunks/11dalasm30arx.js +1 -0
  46. package/out/_next/static/chunks/turbopack-0a_g3u0ud~jb8.js +1 -0
  47. package/out/_not-found/__next._full.txt +20 -0
  48. package/out/_not-found/__next._head.txt +5 -0
  49. package/out/_not-found/__next._index.txt +7 -0
  50. package/out/_not-found/__next._not-found.__PAGE__.txt +9 -0
  51. package/out/_not-found/__next._not-found.txt +5 -0
  52. package/out/_not-found/__next._tree.txt +2 -0
  53. package/out/_not-found/index.html +15 -0
  54. package/out/_not-found/index.txt +20 -0
  55. package/out/app/__next._full.txt +20 -0
  56. package/out/app/__next._head.txt +5 -0
  57. package/out/app/__next._index.txt +7 -0
  58. package/out/app/__next._tree.txt +2 -0
  59. package/out/app/__next.app.__PAGE__.txt +9 -0
  60. package/out/app/__next.app.txt +5 -0
  61. package/out/app/index.html +15 -0
  62. package/out/app/index.txt +20 -0
  63. package/out/changelog/__next._full.txt +22 -0
  64. package/out/changelog/__next._head.txt +5 -0
  65. package/out/changelog/__next._index.txt +7 -0
  66. package/out/changelog/__next._tree.txt +3 -0
  67. package/out/changelog/__next.changelog.__PAGE__.txt +10 -0
  68. package/out/changelog/__next.changelog.txt +5 -0
  69. package/out/changelog/index.html +15 -0
  70. package/out/changelog/index.txt +22 -0
  71. package/out/chat/__next._full.txt +21 -0
  72. package/out/chat/__next._head.txt +5 -0
  73. package/out/chat/__next._index.txt +7 -0
  74. package/out/chat/__next._tree.txt +3 -0
  75. package/out/chat/__next.chat.__PAGE__.txt +9 -0
  76. package/out/chat/__next.chat.txt +6 -0
  77. package/out/chat/index.html +15 -0
  78. package/out/chat/index.txt +21 -0
  79. package/out/docs/__next._full.txt +22 -0
  80. package/out/docs/__next._head.txt +5 -0
  81. package/out/docs/__next._index.txt +7 -0
  82. package/out/docs/__next._tree.txt +3 -0
  83. package/out/docs/__next.docs.__PAGE__.txt +10 -0
  84. package/out/docs/__next.docs.txt +5 -0
  85. package/out/docs/getting-started/__next._full.txt +22 -0
  86. package/out/docs/getting-started/__next._head.txt +5 -0
  87. package/out/docs/getting-started/__next._index.txt +7 -0
  88. package/out/docs/getting-started/__next._tree.txt +3 -0
  89. package/out/docs/getting-started/__next.docs.getting-started.__PAGE__.txt +10 -0
  90. package/out/docs/getting-started/__next.docs.getting-started.txt +5 -0
  91. package/out/docs/getting-started/__next.docs.txt +5 -0
  92. package/out/docs/getting-started/index.html +15 -0
  93. package/out/docs/getting-started/index.txt +22 -0
  94. package/out/docs/index.html +15 -0
  95. package/out/docs/index.txt +22 -0
  96. package/out/download/__next._full.txt +34 -0
  97. package/out/download/__next._head.txt +5 -0
  98. package/out/download/__next._index.txt +7 -0
  99. package/out/download/__next._tree.txt +4 -0
  100. package/out/download/__next.download.__PAGE__.txt +16 -0
  101. package/out/download/__next.download.txt +5 -0
  102. package/out/download/index.html +15 -0
  103. package/out/download/index.txt +34 -0
  104. package/out/favicon.ico +0 -0
  105. package/out/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  106. package/out/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
  107. package/out/fonts/jetbrains-mono-latin-600-normal.woff2 +0 -0
  108. package/out/fonts/jetbrains-mono-latin-700-normal.woff2 +0 -0
  109. package/out/index.html +15 -0
  110. package/out/index.txt +21 -0
  111. package/out/lottery/__next._full.txt +21 -0
  112. package/out/lottery/__next._head.txt +5 -0
  113. package/out/lottery/__next._index.txt +7 -0
  114. package/out/lottery/__next._tree.txt +3 -0
  115. package/out/lottery/__next.lottery.__PAGE__.txt +9 -0
  116. package/out/lottery/__next.lottery.txt +6 -0
  117. package/out/lottery/index.html +15 -0
  118. package/out/lottery/index.txt +21 -0
  119. package/out/ping/__next._full.txt +21 -0
  120. package/out/ping/__next._head.txt +5 -0
  121. package/out/ping/__next._index.txt +7 -0
  122. package/out/ping/__next._tree.txt +4 -0
  123. package/out/ping/__next.ping.__PAGE__.txt +10 -0
  124. package/out/ping/__next.ping.txt +5 -0
  125. package/out/ping/index.html +15 -0
  126. package/out/ping/index.txt +21 -0
  127. package/out/pwa-512x512.png +0 -0
  128. package/out/web3/__next._full.txt +21 -0
  129. package/out/web3/__next._head.txt +5 -0
  130. package/out/web3/__next._index.txt +7 -0
  131. package/out/web3/__next._tree.txt +3 -0
  132. package/out/web3/__next.web3.__PAGE__.txt +9 -0
  133. package/out/web3/__next.web3.txt +6 -0
  134. package/out/web3/ed25519/__next._full.txt +20 -0
  135. package/out/web3/ed25519/__next._head.txt +5 -0
  136. package/out/web3/ed25519/__next._index.txt +7 -0
  137. package/out/web3/ed25519/__next._tree.txt +3 -0
  138. package/out/web3/ed25519/__next.web3.ed25519.__PAGE__.txt +6 -0
  139. package/out/web3/ed25519/__next.web3.ed25519.txt +5 -0
  140. package/out/web3/ed25519/__next.web3.txt +6 -0
  141. package/out/web3/ed25519/index.html +1 -0
  142. package/out/web3/ed25519/index.txt +20 -0
  143. package/out/web3/index.html +15 -0
  144. package/out/web3/index.txt +21 -0
  145. package/out/web3/tools/__next._full.txt +20 -0
  146. package/out/web3/tools/__next._head.txt +5 -0
  147. package/out/web3/tools/__next._index.txt +7 -0
  148. package/out/web3/tools/__next._tree.txt +3 -0
  149. package/out/web3/tools/__next.web3.tools.__PAGE__.txt +6 -0
  150. package/out/web3/tools/__next.web3.tools.txt +5 -0
  151. package/out/web3/tools/__next.web3.txt +6 -0
  152. package/out/web3/tools/index.html +1 -0
  153. package/out/web3/tools/index.txt +20 -0
  154. package/package.json +162 -48
  155. package/public/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  156. package/public/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
  157. package/public/fonts/jetbrains-mono-latin-600-normal.woff2 +0 -0
  158. package/public/fonts/jetbrains-mono-latin-700-normal.woff2 +0 -0
  159. package/public/pwa-512x512.png +0 -0
  160. package/server/cli.js +3 -0
  161. package/server/index.js +963 -0
  162. package/{src → server/src}/config.js +51 -39
  163. package/{src → server/src}/core/cid.js +157 -146
  164. package/{src → server/src}/index.js +1950 -1201
  165. package/server/src/utils/api.js +68 -0
  166. package/server/src/utils/avatar.js +11 -0
  167. package/{src → server/src}/utils/errors.js +70 -66
  168. package/server/src/utils/mostWallet.js +42 -0
  169. package/server/src/utils/mp.js +117 -0
  170. package/{src → server/src}/utils/security.js +173 -169
  171. package/server/src/utils/userIdentity.js +93 -0
  172. package/build.mjs +0 -40
  173. package/cli.js +0 -2
  174. package/public/app.css +0 -1519
  175. package/public/app.jsx +0 -1543
  176. package/public/bundle.css +0 -1
  177. package/public/bundle.js +0 -107
  178. package/public/error-boundary.jsx +0 -50
  179. package/public/index.html +0 -16
  180. package/public/index.jsx +0 -20
  181. package/server.js +0 -698
@@ -1,39 +1,51 @@
1
- /**
2
- * 应用配置
3
- */
4
-
5
- // 文件大小限制
6
- export const MAX_FILE_SIZE = 100 * 1024 * 1024 * 1024 // 100 GB
7
-
8
- // 网络超时(毫秒)
9
- export const CONNECTION_TIMEOUT = 120000
10
- export const DOWNLOAD_TIMEOUT = 900000
11
-
12
- // P2P 设置
13
- export const GLOBAL_SHARED_SEED_STRING = 'most-box-global-shared-seed-v1'
14
- export const MAX_PEERS = 64
15
- export const SWARM_KEEP_ALIVE_INTERVAL = 5000
16
- export const SWARM_RANDOM_PUNCH_INTERVAL = 20000
17
-
18
- // 驱动器超时(毫秒)
19
- export const DRIVE_ENTRY_TIMEOUT = 10000
20
- export const DRIVE_SYNC_TIMEOUT = 10000
21
- export const STREAM_READ_TIMEOUT = 10000
22
-
23
- // 下载轮询间隔(毫秒)
24
- export const DOWNLOAD_POLL_INTERVAL = 1000
25
-
26
- // 进度更新节流间隔(毫秒)
27
- export const PROGRESS_THROTTLE = 500
28
-
29
- // 默认读取限制
30
- export const DEFAULT_READ_LIMIT = 10000
31
-
32
- // DHT 引导节点,用于 Hyperswarm/HyperDHT
33
- // 使用与 Keet.io/HyperDHT 相同的引导节点以保证兼容性
34
- // 格式:[suggested-IP@]<host>:<port> 以避免 DNS 调用
35
- export const SWARM_BOOTSTRAP = [
36
- '88.99.3.86@node1.hyperdht.org:49737',
37
- '142.93.90.113@node2.hyperdht.org:49737',
38
- '138.68.147.8@node3.hyperdht.org:49737'
39
- ]
1
+ /**
2
+ * 应用配置
3
+ */
4
+
5
+ // 文件大小限制
6
+ export const MAX_FILE_SIZE = 100 * 1024 * 1024 * 1024 // 100 GB
7
+
8
+ // 网络超时(毫秒)
9
+ export const CONNECTION_TIMEOUT = 120000
10
+ export const DOWNLOAD_TIMEOUT = 900000
11
+
12
+ // P2P 设置
13
+ export const GLOBAL_SHARED_SEED_STRING = 'most-box-global-shared-seed-v1'
14
+ export const MAX_PEERS = 64
15
+ export const SWARM_KEEP_ALIVE_INTERVAL = 5000
16
+ export const SWARM_RANDOM_PUNCH_INTERVAL = 20000
17
+
18
+ // 驱动器超时(毫秒)
19
+ export const DRIVE_ENTRY_TIMEOUT = 10000
20
+ export const DRIVE_SYNC_TIMEOUT = 10000
21
+ export const STREAM_READ_TIMEOUT = 10000
22
+
23
+ // 文件写入块大小(字节)
24
+ export const FILE_WRITE_CHUNK_SIZE = 64 * 1024
25
+
26
+ // 下载轮询间隔(毫秒)
27
+ export const DOWNLOAD_POLL_INTERVAL_MIN = 500
28
+ export const DOWNLOAD_POLL_INTERVAL_MAX = 2000
29
+ export const DRIVE_UPDATE_INTERVAL = 2000
30
+
31
+ // 进度更新节流间隔(毫秒)
32
+ export const PROGRESS_THROTTLE = 500
33
+
34
+ // 默认读取限制
35
+ export const DEFAULT_READ_LIMIT = 10000
36
+
37
+ // DHT 引导节点,用于 Hyperswarm/HyperDHT
38
+ // 使用与 Keet.io/HyperDHT 相同的引导节点以保证兼容性
39
+ // 格式:[suggested-IP@]<host>:<port> 以避免 DNS 调用
40
+ export const SWARM_BOOTSTRAP = [
41
+ '88.99.3.86@node1.hyperdht.org:49737',
42
+ '142.93.90.113@node2.hyperdht.org:49737',
43
+ '138.68.147.8@node3.hyperdht.org:49737',
44
+ ]
45
+
46
+ export const CHANNEL_NAME_MIN_LENGTH = 3
47
+ export const CHANNEL_NAME_MAX_LENGTH = 20
48
+ export const CHANNEL_NAME_REGEX = /^[a-zA-Z0-9_-]+$/
49
+ export const CHANNEL_NAME_PREFIX = 'most-box-room-'
50
+ export const CHANNEL_MESSAGE_LIMIT = 100
51
+ export const MAX_MESSAGE_LENGTH = 10000
@@ -1,146 +1,157 @@
1
- /**
2
- * CID(内容标识符)计算模块
3
- * 处理文件的 IPFS UnixFS CID 计算
4
- */
5
-
6
- import fs from 'node:fs'
7
- import { Readable } from 'node:stream'
8
- import { importer } from 'ipfs-unixfs-importer'
9
-
10
- /**
11
- * 用于 CID 计算的虚拟 Blockstore
12
- * 不存储任何数据,仅用于流式 CID 计算
13
- */
14
- function createDummyBlockstore() {
15
- return {
16
- put: async (key, val) => key,
17
- get: async () => { throw new Error('Not implemented') },
18
- has: async () => false
19
- }
20
- }
21
-
22
- /**
23
- * 计算内容的 IPFS UnixFS CID v1
24
- * 使用流式方法高效处理大文件
25
- *
26
- * @param {string|Buffer|AsyncIterable} content - 文件路径(字符串)、Buffer 或异步迭代器
27
- * @param {object} options - 计算选项
28
- * @param {boolean} [options.rawLeaves=true] - 使用原始叶子节点以支持现代 CID
29
- * @param {number} [options.cidVersion=1] - CID 版本(0 或 1)
30
- * @param {number} [options.size] - 总字节数(可选,自动检测)
31
- * @returns {Promise<{cid: import('multiformats/cid').CID, size: number}>}
32
- */
33
- export async function calculateCid(content, options = {}) {
34
- const {
35
- rawLeaves = true,
36
- cidVersion = 1,
37
- size: providedSize
38
- } = options
39
-
40
- const blockstore = createDummyBlockstore()
41
-
42
- let rootCid = null
43
- let totalSize = providedSize || 0
44
-
45
- let source
46
-
47
- if (typeof content === 'string') {
48
- const filePath = content
49
- try {
50
- const stat = await fs.promises.stat(filePath)
51
- totalSize = stat.size
52
- } catch {
53
- // 忽略 stat 错误,大小将为 0
54
- }
55
- source = [{
56
- path: 'file',
57
- content: fs.createReadStream(filePath)
58
- }]
59
- } else if (Buffer.isBuffer(content)) {
60
- totalSize = content.length
61
- source = [{
62
- path: 'file',
63
- content: Readable.from(content)
64
- }]
65
- } else {
66
- source = [{
67
- path: 'file',
68
- content
69
- }]
70
- }
71
-
72
- try {
73
- for await (const entry of importer(source, blockstore, {
74
- cidVersion,
75
- rawLeaves,
76
- wrapWithDirectory: false
77
- })) {
78
- rootCid = entry.cid
79
- }
80
- } catch (err) {
81
- throw new Error(`Failed to calculate CID: ${err.message}`)
82
- }
83
-
84
- if (!rootCid) {
85
- throw new Error('Failed to calculate CID: no root CID generated')
86
- }
87
-
88
- return {
89
- cid: rootCid,
90
- size: totalSize
91
- }
92
- }
93
-
94
- /**
95
- * 验证 CID 字符串
96
- * @param {string} cidString - 要验证的 CID 字符串
97
- * @returns {{ valid: boolean, error?: string }}
98
- */
99
- export function validateCidString(cidString) {
100
- if (!cidString || typeof cidString !== 'string') {
101
- return { valid: false, error: 'CID must be a non-empty string' }
102
- }
103
-
104
- if (!cidString.startsWith('b')) {
105
- return { valid: false, error: 'Invalid CID format: CID v1 must start with "b"' }
106
- }
107
-
108
- return { valid: true }
109
- }
110
-
111
- /**
112
- * 解析 most:// 链接并提取 CID
113
- * @param {string} link - most://<cid> 格式的链接
114
- * @returns {{ cid: string, error?: string }}
115
- */
116
- export function parseMostLink(link) {
117
- if (!link || typeof link !== 'string') {
118
- return { cid: '', error: 'Link must be a non-empty string' }
119
- }
120
-
121
- let cidString = link
122
-
123
- if (link.startsWith('most://')) {
124
- cidString = link.replace('most://', '')
125
- }
126
-
127
- // 移除尾部斜杠和空白
128
- cidString = cidString.trim().replace(/\/+$/, '')
129
-
130
- // 移除 query string
131
- if (cidString.includes('?')) {
132
- cidString = cidString.split('?')[0]
133
- }
134
-
135
- // 处理可能的额外路径的 URL 解析
136
- if (cidString.includes('/')) {
137
- cidString = cidString.split('/')[0]
138
- }
139
-
140
- const validation = validateCidString(cidString)
141
- if (!validation.valid) {
142
- return { cid: '', error: validation.error }
143
- }
144
-
145
- return { cid: cidString }
146
- }
1
+ /**
2
+ * CID(内容标识符)计算模块
3
+ * 处理文件的 IPFS UnixFS CID 计算
4
+ */
5
+
6
+ import fs from 'node:fs'
7
+ import { Readable } from 'node:stream'
8
+ import { importer } from 'ipfs-unixfs-importer'
9
+
10
+ /**
11
+ * 用于 CID 计算的虚拟 Blockstore
12
+ * 不存储任何数据,仅用于流式 CID 计算
13
+ */
14
+ function createDummyBlockstore() {
15
+ return {
16
+ put: async (key, _val) => key,
17
+ get: async () => {
18
+ throw new Error('Not implemented')
19
+ },
20
+ has: async () => false,
21
+ }
22
+ }
23
+
24
+ /**
25
+ * 计算内容的 IPFS UnixFS CID v1
26
+ * 使用流式方法高效处理大文件
27
+ *
28
+ * @param {string|Buffer|AsyncIterable} content - 文件路径(字符串)、Buffer 或异步迭代器
29
+ * @param {object} options - 计算选项
30
+ * @param {boolean} [options.rawLeaves=true] - 使用原始叶子节点以支持现代 CID
31
+ * @param {number} [options.cidVersion=1] - CID 版本(0 或 1)
32
+ * @param {number} [options.size] - 总字节数(可选,自动检测)
33
+ * @returns {Promise<{cid: import('multiformats/cid').CID, size: number}>}
34
+ */
35
+ export async function calculateCid(content, options = {}) {
36
+ const { rawLeaves = true, cidVersion = 1, size: providedSize } = options
37
+
38
+ const blockstore = createDummyBlockstore()
39
+
40
+ let rootCid = null
41
+ let totalSize = providedSize || 0
42
+
43
+ let source
44
+
45
+ if (typeof content === 'string') {
46
+ const filePath = content
47
+ try {
48
+ const stat = await fs.promises.stat(filePath)
49
+ totalSize = stat.size
50
+ } catch {
51
+ // 忽略 stat 错误,大小将为 0
52
+ }
53
+ source = [
54
+ {
55
+ path: 'file',
56
+ content: fs.createReadStream(filePath),
57
+ },
58
+ ]
59
+ } else if (Buffer.isBuffer(content)) {
60
+ totalSize = content.length
61
+ source = [
62
+ {
63
+ path: 'file',
64
+ content: Readable.from(content),
65
+ },
66
+ ]
67
+ } else {
68
+ source = [
69
+ {
70
+ path: 'file',
71
+ content,
72
+ },
73
+ ]
74
+ }
75
+
76
+ try {
77
+ for await (const entry of importer(source, blockstore, {
78
+ cidVersion,
79
+ rawLeaves,
80
+ wrapWithDirectory: false,
81
+ })) {
82
+ rootCid = entry.cid
83
+ }
84
+ } catch (err) {
85
+ throw new Error(`Failed to calculate CID: ${err.message}`)
86
+ }
87
+
88
+ if (!rootCid) {
89
+ throw new Error('Failed to calculate CID: no root CID generated')
90
+ }
91
+
92
+ return {
93
+ cid: rootCid,
94
+ size: totalSize,
95
+ }
96
+ }
97
+
98
+ /**
99
+ * 验证 CID 字符串
100
+ * @param {string} cidString - 要验证的 CID 字符串
101
+ * @returns {{ valid: boolean, error?: string }}
102
+ */
103
+ export function validateCidString(cidString) {
104
+ if (!cidString || typeof cidString !== 'string') {
105
+ return { valid: false, error: 'CID must be a non-empty string' }
106
+ }
107
+
108
+ if (!cidString.startsWith('b')) {
109
+ return {
110
+ valid: false,
111
+ error: 'Invalid CID format: CID v1 must start with "b"',
112
+ }
113
+ }
114
+
115
+ return { valid: true }
116
+ }
117
+
118
+ /**
119
+ * 解析 most:// 链接并提取 CID
120
+ * @param {string} link - most://<cid> 格式的链接
121
+ * @returns {{ cid: string, error?: string }}
122
+ */
123
+ export function parseMostLink(link) {
124
+ if (!link || typeof link !== 'string') {
125
+ return { cid: '', error: 'Link must be a non-empty string' }
126
+ }
127
+
128
+ let cidString = link
129
+
130
+ if (link.startsWith('most://')) {
131
+ cidString = link.replace('most://', '')
132
+ }
133
+
134
+ // 移除尾部斜杠和空白
135
+ cidString = cidString.trim().replace(/\/+$/, '')
136
+
137
+ // 移除 query string,提取 filename
138
+ let fileName = null
139
+ if (cidString.includes('?')) {
140
+ const [cidPart, queryPart] = cidString.split('?')
141
+ cidString = cidPart
142
+ const params = new URLSearchParams(queryPart)
143
+ fileName = params.get('filename')
144
+ }
145
+
146
+ // 处理可能的额外路径的 URL 解析
147
+ if (cidString.includes('/')) {
148
+ cidString = cidString.split('/')[0]
149
+ }
150
+
151
+ const validation = validateCidString(cidString)
152
+ if (!validation.valid) {
153
+ return { cid: '', error: validation.error }
154
+ }
155
+
156
+ return { cid: cidString, fileName }
157
+ }