@yunzai-ng/core 0.1.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.
Files changed (357) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +51 -0
  3. package/dist/adapter/accounts.d.ts +174 -0
  4. package/dist/adapter/accounts.d.ts.map +1 -0
  5. package/dist/adapter/accounts.js +653 -0
  6. package/dist/adapter/accounts.js.map +1 -0
  7. package/dist/adapter/bots.d.ts +142 -0
  8. package/dist/adapter/bots.d.ts.map +1 -0
  9. package/dist/adapter/bots.js +454 -0
  10. package/dist/adapter/bots.js.map +1 -0
  11. package/dist/adapter/host.d.ts +114 -0
  12. package/dist/adapter/host.d.ts.map +1 -0
  13. package/dist/adapter/host.js +90 -0
  14. package/dist/adapter/host.js.map +1 -0
  15. package/dist/adapter/login.d.ts +174 -0
  16. package/dist/adapter/login.d.ts.map +1 -0
  17. package/dist/adapter/login.js +345 -0
  18. package/dist/adapter/login.js.map +1 -0
  19. package/dist/adapter/registry.d.ts +102 -0
  20. package/dist/adapter/registry.d.ts.map +1 -0
  21. package/dist/adapter/registry.js +150 -0
  22. package/dist/adapter/registry.js.map +1 -0
  23. package/dist/config/core-config.d.ts +151 -0
  24. package/dist/config/core-config.d.ts.map +1 -0
  25. package/dist/config/core-config.js +338 -0
  26. package/dist/config/core-config.js.map +1 -0
  27. package/dist/config/schema.d.ts +449 -0
  28. package/dist/config/schema.d.ts.map +1 -0
  29. package/dist/config/schema.js +871 -0
  30. package/dist/config/schema.js.map +1 -0
  31. package/dist/config/store.d.ts +184 -0
  32. package/dist/config/store.d.ts.map +1 -0
  33. package/dist/config/store.js +428 -0
  34. package/dist/config/store.js.map +1 -0
  35. package/dist/config/yaml.d.ts +23 -0
  36. package/dist/config/yaml.d.ts.map +1 -0
  37. package/dist/config/yaml.js +108 -0
  38. package/dist/config/yaml.js.map +1 -0
  39. package/dist/http/client.d.ts +82 -0
  40. package/dist/http/client.d.ts.map +1 -0
  41. package/dist/http/client.js +658 -0
  42. package/dist/http/client.js.map +1 -0
  43. package/dist/index.d.ts +77 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +94 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/kernel/app.d.ts +244 -0
  48. package/dist/kernel/app.d.ts.map +1 -0
  49. package/dist/kernel/app.js +654 -0
  50. package/dist/kernel/app.js.map +1 -0
  51. package/dist/kernel/kv-sink.d.ts +34 -0
  52. package/dist/kernel/kv-sink.d.ts.map +1 -0
  53. package/dist/kernel/kv-sink.js +25 -0
  54. package/dist/kernel/kv-sink.js.map +1 -0
  55. package/dist/kernel/policy.d.ts +77 -0
  56. package/dist/kernel/policy.d.ts.map +1 -0
  57. package/dist/kernel/policy.js +99 -0
  58. package/dist/kernel/policy.js.map +1 -0
  59. package/dist/kernel/runtime.d.ts +117 -0
  60. package/dist/kernel/runtime.d.ts.map +1 -0
  61. package/dist/kernel/runtime.js +197 -0
  62. package/dist/kernel/runtime.js.map +1 -0
  63. package/dist/kernel/sql-sink.d.ts +36 -0
  64. package/dist/kernel/sql-sink.d.ts.map +1 -0
  65. package/dist/kernel/sql-sink.js +114 -0
  66. package/dist/kernel/sql-sink.js.map +1 -0
  67. package/dist/logger/format.d.ts +67 -0
  68. package/dist/logger/format.d.ts.map +1 -0
  69. package/dist/logger/format.js +218 -0
  70. package/dist/logger/format.js.map +1 -0
  71. package/dist/logger/index.d.ts +97 -0
  72. package/dist/logger/index.d.ts.map +1 -0
  73. package/dist/logger/index.js +363 -0
  74. package/dist/logger/index.js.map +1 -0
  75. package/dist/logger/rotate.d.ts +47 -0
  76. package/dist/logger/rotate.d.ts.map +1 -0
  77. package/dist/logger/rotate.js +242 -0
  78. package/dist/logger/rotate.js.map +1 -0
  79. package/dist/message/segment.d.ts +255 -0
  80. package/dist/message/segment.d.ts.map +1 -0
  81. package/dist/message/segment.js +510 -0
  82. package/dist/message/segment.js.map +1 -0
  83. package/dist/message/split.d.ts +34 -0
  84. package/dist/message/split.d.ts.map +1 -0
  85. package/dist/message/split.js +79 -0
  86. package/dist/message/split.js.map +1 -0
  87. package/dist/message/target.d.ts +29 -0
  88. package/dist/message/target.d.ts.map +1 -0
  89. package/dist/message/target.js +34 -0
  90. package/dist/message/target.js.map +1 -0
  91. package/dist/pipeline/cooldown.d.ts +84 -0
  92. package/dist/pipeline/cooldown.d.ts.map +1 -0
  93. package/dist/pipeline/cooldown.js +94 -0
  94. package/dist/pipeline/cooldown.js.map +1 -0
  95. package/dist/pipeline/dispatch.d.ts +98 -0
  96. package/dist/pipeline/dispatch.d.ts.map +1 -0
  97. package/dist/pipeline/dispatch.js +323 -0
  98. package/dist/pipeline/dispatch.js.map +1 -0
  99. package/dist/pipeline/event.d.ts +130 -0
  100. package/dist/pipeline/event.d.ts.map +1 -0
  101. package/dist/pipeline/event.js +538 -0
  102. package/dist/pipeline/event.js.map +1 -0
  103. package/dist/pipeline/middleware.d.ts +63 -0
  104. package/dist/pipeline/middleware.d.ts.map +1 -0
  105. package/dist/pipeline/middleware.js +174 -0
  106. package/dist/pipeline/middleware.js.map +1 -0
  107. package/dist/pipeline/prompt.d.ts +80 -0
  108. package/dist/pipeline/prompt.d.ts.map +1 -0
  109. package/dist/pipeline/prompt.js +155 -0
  110. package/dist/pipeline/prompt.js.map +1 -0
  111. package/dist/pipeline/router.d.ts +98 -0
  112. package/dist/pipeline/router.d.ts.map +1 -0
  113. package/dist/pipeline/router.js +489 -0
  114. package/dist/pipeline/router.js.map +1 -0
  115. package/dist/platform/detect.d.ts +24 -0
  116. package/dist/platform/detect.d.ts.map +1 -0
  117. package/dist/platform/detect.js +178 -0
  118. package/dist/platform/detect.js.map +1 -0
  119. package/dist/platform/paths.d.ts +38 -0
  120. package/dist/platform/paths.d.ts.map +1 -0
  121. package/dist/platform/paths.js +129 -0
  122. package/dist/platform/paths.js.map +1 -0
  123. package/dist/platform/system.d.ts +71 -0
  124. package/dist/platform/system.d.ts.map +1 -0
  125. package/dist/platform/system.js +242 -0
  126. package/dist/platform/system.js.map +1 -0
  127. package/dist/plugin/context.d.ts +92 -0
  128. package/dist/plugin/context.d.ts.map +1 -0
  129. package/dist/plugin/context.js +573 -0
  130. package/dist/plugin/context.js.map +1 -0
  131. package/dist/plugin/define.d.ts +92 -0
  132. package/dist/plugin/define.d.ts.map +1 -0
  133. package/dist/plugin/define.js +82 -0
  134. package/dist/plugin/define.js.map +1 -0
  135. package/dist/plugin/discover.d.ts +106 -0
  136. package/dist/plugin/discover.d.ts.map +1 -0
  137. package/dist/plugin/discover.js +254 -0
  138. package/dist/plugin/discover.js.map +1 -0
  139. package/dist/plugin/events.d.ts +109 -0
  140. package/dist/plugin/events.d.ts.map +1 -0
  141. package/dist/plugin/events.js +209 -0
  142. package/dist/plugin/events.js.map +1 -0
  143. package/dist/plugin/hooks.d.ts +266 -0
  144. package/dist/plugin/hooks.d.ts.map +1 -0
  145. package/dist/plugin/hooks.js +84 -0
  146. package/dist/plugin/hooks.js.map +1 -0
  147. package/dist/plugin/host.d.ts +160 -0
  148. package/dist/plugin/host.d.ts.map +1 -0
  149. package/dist/plugin/host.js +546 -0
  150. package/dist/plugin/host.js.map +1 -0
  151. package/dist/plugin/market.d.ts +264 -0
  152. package/dist/plugin/market.d.ts.map +1 -0
  153. package/dist/plugin/market.js +700 -0
  154. package/dist/plugin/market.js.map +1 -0
  155. package/dist/plugin/services.d.ts +116 -0
  156. package/dist/plugin/services.d.ts.map +1 -0
  157. package/dist/plugin/services.js +213 -0
  158. package/dist/plugin/services.js.map +1 -0
  159. package/dist/plugin/tar.d.ts +65 -0
  160. package/dist/plugin/tar.d.ts.map +1 -0
  161. package/dist/plugin/tar.js +277 -0
  162. package/dist/plugin/tar.js.map +1 -0
  163. package/dist/render/registry.d.ts +127 -0
  164. package/dist/render/registry.d.ts.map +1 -0
  165. package/dist/render/registry.js +306 -0
  166. package/dist/render/registry.js.map +1 -0
  167. package/dist/scheduler/index.d.ts +67 -0
  168. package/dist/scheduler/index.d.ts.map +1 -0
  169. package/dist/scheduler/index.js +279 -0
  170. package/dist/scheduler/index.js.map +1 -0
  171. package/dist/server/api.d.ts +179 -0
  172. package/dist/server/api.d.ts.map +1 -0
  173. package/dist/server/api.js +602 -0
  174. package/dist/server/api.js.map +1 -0
  175. package/dist/server/auth.d.ts +83 -0
  176. package/dist/server/auth.d.ts.map +1 -0
  177. package/dist/server/auth.js +178 -0
  178. package/dist/server/auth.js.map +1 -0
  179. package/dist/server/browse.d.ts +60 -0
  180. package/dist/server/browse.d.ts.map +1 -0
  181. package/dist/server/browse.js +187 -0
  182. package/dist/server/browse.js.map +1 -0
  183. package/dist/server/files.d.ts +27 -0
  184. package/dist/server/files.d.ts.map +1 -0
  185. package/dist/server/files.js +76 -0
  186. package/dist/server/files.js.map +1 -0
  187. package/dist/server/index.d.ts +189 -0
  188. package/dist/server/index.d.ts.map +1 -0
  189. package/dist/server/index.js +1123 -0
  190. package/dist/server/index.js.map +1 -0
  191. package/dist/server/table.d.ts +105 -0
  192. package/dist/server/table.d.ts.map +1 -0
  193. package/dist/server/table.js +289 -0
  194. package/dist/server/table.js.map +1 -0
  195. package/dist/store/index.d.ts +50 -0
  196. package/dist/store/index.d.ts.map +1 -0
  197. package/dist/store/index.js +120 -0
  198. package/dist/store/index.js.map +1 -0
  199. package/dist/store/json.d.ts +73 -0
  200. package/dist/store/json.d.ts.map +1 -0
  201. package/dist/store/json.js +174 -0
  202. package/dist/store/json.js.map +1 -0
  203. package/dist/store/kv.d.ts +126 -0
  204. package/dist/store/kv.d.ts.map +1 -0
  205. package/dist/store/kv.js +232 -0
  206. package/dist/store/kv.js.map +1 -0
  207. package/dist/store/level.d.ts +103 -0
  208. package/dist/store/level.d.ts.map +1 -0
  209. package/dist/store/level.js +119 -0
  210. package/dist/store/level.js.map +1 -0
  211. package/dist/store/memory.d.ts +61 -0
  212. package/dist/store/memory.d.ts.map +1 -0
  213. package/dist/store/memory.js +94 -0
  214. package/dist/store/memory.js.map +1 -0
  215. package/dist/store/sql.d.ts +142 -0
  216. package/dist/store/sql.d.ts.map +1 -0
  217. package/dist/store/sql.js +246 -0
  218. package/dist/store/sql.js.map +1 -0
  219. package/dist/testing/fake.d.ts +109 -0
  220. package/dist/testing/fake.d.ts.map +1 -0
  221. package/dist/testing/fake.js +196 -0
  222. package/dist/testing/fake.js.map +1 -0
  223. package/dist/testing/index.d.ts +16 -0
  224. package/dist/testing/index.d.ts.map +1 -0
  225. package/dist/testing/index.js +16 -0
  226. package/dist/testing/index.js.map +1 -0
  227. package/dist/testing/mock-adapter.d.ts +205 -0
  228. package/dist/testing/mock-adapter.d.ts.map +1 -0
  229. package/dist/testing/mock-adapter.js +309 -0
  230. package/dist/testing/mock-adapter.js.map +1 -0
  231. package/dist/util/deep.d.ts +108 -0
  232. package/dist/util/deep.d.ts.map +1 -0
  233. package/dist/util/deep.js +275 -0
  234. package/dist/util/deep.js.map +1 -0
  235. package/dist/util/defer.d.ts +107 -0
  236. package/dist/util/defer.d.ts.map +1 -0
  237. package/dist/util/defer.js +163 -0
  238. package/dist/util/defer.js.map +1 -0
  239. package/dist/util/dispose.d.ts +88 -0
  240. package/dist/util/dispose.d.ts.map +1 -0
  241. package/dist/util/dispose.js +143 -0
  242. package/dist/util/dispose.js.map +1 -0
  243. package/dist/util/duration.d.ts +28 -0
  244. package/dist/util/duration.d.ts.map +1 -0
  245. package/dist/util/duration.js +77 -0
  246. package/dist/util/duration.js.map +1 -0
  247. package/dist/util/fs.d.ts +110 -0
  248. package/dist/util/fs.d.ts.map +1 -0
  249. package/dist/util/fs.js +269 -0
  250. package/dist/util/fs.js.map +1 -0
  251. package/dist/util/id.d.ts +49 -0
  252. package/dist/util/id.d.ts.map +1 -0
  253. package/dist/util/id.js +87 -0
  254. package/dist/util/id.js.map +1 -0
  255. package/dist/util/lru.d.ts +74 -0
  256. package/dist/util/lru.d.ts.map +1 -0
  257. package/dist/util/lru.js +156 -0
  258. package/dist/util/lru.js.map +1 -0
  259. package/dist/util/queue.d.ts +64 -0
  260. package/dist/util/queue.d.ts.map +1 -0
  261. package/dist/util/queue.js +147 -0
  262. package/dist/util/queue.js.map +1 -0
  263. package/dist/util/text.d.ts +86 -0
  264. package/dist/util/text.d.ts.map +1 -0
  265. package/dist/util/text.js +182 -0
  266. package/dist/util/text.js.map +1 -0
  267. package/package.json +44 -0
  268. package/src/adapter/accounts.ts +758 -0
  269. package/src/adapter/bots.ts +582 -0
  270. package/src/adapter/host.ts +224 -0
  271. package/src/adapter/login.ts +481 -0
  272. package/src/adapter/registry.ts +202 -0
  273. package/src/config/core-config.test.ts +60 -0
  274. package/src/config/core-config.ts +401 -0
  275. package/src/config/schema.test.ts +179 -0
  276. package/src/config/schema.ts +1036 -0
  277. package/src/config/store.test.ts +245 -0
  278. package/src/config/store.ts +506 -0
  279. package/src/config/yaml.ts +119 -0
  280. package/src/http/client.test.ts +568 -0
  281. package/src/http/client.ts +777 -0
  282. package/src/index.ts +106 -0
  283. package/src/kernel/app.test.ts +267 -0
  284. package/src/kernel/app.ts +843 -0
  285. package/src/kernel/kv-sink.ts +58 -0
  286. package/src/kernel/policy.ts +120 -0
  287. package/src/kernel/runtime.test.ts +542 -0
  288. package/src/kernel/runtime.ts +307 -0
  289. package/src/kernel/sql-sink.ts +154 -0
  290. package/src/logger/format.ts +259 -0
  291. package/src/logger/index.ts +427 -0
  292. package/src/logger/rotate.ts +265 -0
  293. package/src/message/segment.test.ts +335 -0
  294. package/src/message/segment.ts +569 -0
  295. package/src/message/split.ts +97 -0
  296. package/src/message/target.ts +48 -0
  297. package/src/pipeline/cooldown.ts +136 -0
  298. package/src/pipeline/dispatch.ts +419 -0
  299. package/src/pipeline/event.ts +735 -0
  300. package/src/pipeline/middleware.test.ts +426 -0
  301. package/src/pipeline/middleware.ts +199 -0
  302. package/src/pipeline/prompt.ts +212 -0
  303. package/src/pipeline/router.test.ts +432 -0
  304. package/src/pipeline/router.ts +575 -0
  305. package/src/platform/detect.ts +178 -0
  306. package/src/platform/paths.ts +147 -0
  307. package/src/platform/system.test.ts +102 -0
  308. package/src/platform/system.ts +289 -0
  309. package/src/plugin/context.test.ts +459 -0
  310. package/src/plugin/context.ts +757 -0
  311. package/src/plugin/define.test.ts +120 -0
  312. package/src/plugin/define.ts +142 -0
  313. package/src/plugin/discover.test.ts +253 -0
  314. package/src/plugin/discover.ts +353 -0
  315. package/src/plugin/events.test.ts +162 -0
  316. package/src/plugin/events.ts +266 -0
  317. package/src/plugin/hooks.ts +373 -0
  318. package/src/plugin/host.test.ts +696 -0
  319. package/src/plugin/host.ts +738 -0
  320. package/src/plugin/market.test.ts +781 -0
  321. package/src/plugin/market.ts +870 -0
  322. package/src/plugin/services.test.ts +135 -0
  323. package/src/plugin/services.ts +261 -0
  324. package/src/plugin/tar.test.ts +158 -0
  325. package/src/plugin/tar.ts +309 -0
  326. package/src/render/registry.test.ts +127 -0
  327. package/src/render/registry.ts +439 -0
  328. package/src/scheduler/index.ts +357 -0
  329. package/src/server/api.test.ts +802 -0
  330. package/src/server/api.ts +869 -0
  331. package/src/server/auth.ts +201 -0
  332. package/src/server/browse.test.ts +193 -0
  333. package/src/server/browse.ts +240 -0
  334. package/src/server/files.ts +97 -0
  335. package/src/server/index.test.ts +566 -0
  336. package/src/server/index.ts +1386 -0
  337. package/src/server/table.ts +351 -0
  338. package/src/store/index.ts +156 -0
  339. package/src/store/json.ts +198 -0
  340. package/src/store/kv.test.ts +307 -0
  341. package/src/store/kv.ts +256 -0
  342. package/src/store/level.ts +167 -0
  343. package/src/store/memory.ts +109 -0
  344. package/src/store/sql.test.ts +204 -0
  345. package/src/store/sql.ts +324 -0
  346. package/src/testing/fake.ts +297 -0
  347. package/src/testing/index.ts +15 -0
  348. package/src/testing/mock-adapter.ts +588 -0
  349. package/src/util/deep.ts +272 -0
  350. package/src/util/defer.ts +211 -0
  351. package/src/util/dispose.ts +169 -0
  352. package/src/util/duration.ts +82 -0
  353. package/src/util/fs.ts +279 -0
  354. package/src/util/id.ts +94 -0
  355. package/src/util/lru.ts +185 -0
  356. package/src/util/queue.ts +167 -0
  357. package/src/util/text.ts +189 -0
@@ -0,0 +1,143 @@
1
+ /**
2
+ * 资源回收登记簿
3
+ *
4
+ * 线程模型:同进程单线程,无需加锁;但 `dispose()` 可能重入
5
+ * (插件在 disposer 里又触发卸载),故有 `#disposed` 幂等保护。
6
+ */
7
+ export class DisposalRegistry {
8
+ /** 登记的回收器,按登记顺序存放 */
9
+ #entries = [];
10
+ /** 是否已回收 */
11
+ #disposed = false;
12
+ /** 归属标识,仅用于日志 */
13
+ #owner;
14
+ /**
15
+ * @param owner 归属标识(插件名等),出错时用于定位
16
+ */
17
+ constructor(owner) {
18
+ this.#owner = owner;
19
+ }
20
+ /** 是否已回收 */
21
+ get disposed() {
22
+ return this.#disposed;
23
+ }
24
+ /** 尚未回收的登记项数量 */
25
+ get size() {
26
+ return this.#entries.length;
27
+ }
28
+ /**
29
+ * 登记一个回收器
30
+ *
31
+ * 若 registry 已回收,传入的 disposer 会**立即执行** —— 这防止"卸载竞态":
32
+ * 异步初始化的插件在卸载后才完成注册,资源没人回收。
33
+ * @param dispose 回收函数
34
+ * @param label 标签,用于报错定位
35
+ * @returns 取消登记并执行回收的函数;重复调用无副作用
36
+ */
37
+ add(dispose, label = "anonymous") {
38
+ if (this.#disposed) {
39
+ this.#runOne({ label, dispose });
40
+ return () => undefined;
41
+ }
42
+ const entry = { label, dispose };
43
+ this.#entries.push(entry);
44
+ let done = false;
45
+ return () => {
46
+ if (done)
47
+ return;
48
+ done = true;
49
+ const index = this.#entries.indexOf(entry);
50
+ if (index >= 0)
51
+ this.#entries.splice(index, 1);
52
+ this.#runOne(entry);
53
+ };
54
+ }
55
+ /**
56
+ * 登记一个 `Map`/`Set` 条目的清理
57
+ * @param collection 容器
58
+ * @param key 键
59
+ * @param label 标签
60
+ * @returns 回收函数
61
+ */
62
+ addEntry(collection, key, label = "entry") {
63
+ return this.add(() => void collection.delete(key), label);
64
+ }
65
+ /**
66
+ * 登记一个定时器
67
+ *
68
+ * 顺带 `unref`,让"只剩定时器"时进程能正常退出。
69
+ * @param timer 定时器句柄
70
+ * @param label 标签
71
+ * @returns 回收函数
72
+ */
73
+ addTimer(timer, label = "timer") {
74
+ if (typeof timer.unref === "function")
75
+ timer.unref();
76
+ return this.add(() => clearTimeout(timer), label);
77
+ }
78
+ /**
79
+ * 创建从属子登记簿
80
+ *
81
+ * 父簿回收时自动带走子簿。用于"插件里的账号连接"这类嵌套生命周期。
82
+ * @param owner 子簿归属标识
83
+ * @returns 子登记簿
84
+ */
85
+ child(owner) {
86
+ const sub = new DisposalRegistry(`${this.#owner}/${owner}`);
87
+ this.add(() => void sub.dispose(), `child:${owner}`);
88
+ return sub;
89
+ }
90
+ /**
91
+ * 逆序回收全部登记项
92
+ *
93
+ * 单个 disposer 抛错**不会**中断后续回收 —— 一个插件写错清理逻辑不应该
94
+ * 导致其余资源全部泄漏。所有错误汇总返回,由调用方(插件宿主)记日志。
95
+ * @returns 回收过程中发生的错误列表;无错时为空数组
96
+ */
97
+ dispose() {
98
+ if (this.#disposed)
99
+ return [];
100
+ this.#disposed = true;
101
+ const failures = [];
102
+ // 逆序:后注册的可能依赖先注册的
103
+ for (let i = this.#entries.length - 1; i >= 0; i--) {
104
+ const failure = this.#runOne(this.#entries[i]);
105
+ if (failure)
106
+ failures.push(failure);
107
+ }
108
+ this.#entries.length = 0;
109
+ return failures;
110
+ }
111
+ /**
112
+ * 执行单个回收器并捕获错误
113
+ * @param entry 登记项
114
+ * @returns 出错时返回错误信息,否则 undefined
115
+ */
116
+ #runOne(entry) {
117
+ try {
118
+ entry.dispose();
119
+ return undefined;
120
+ }
121
+ catch (error) {
122
+ return { label: `${this.#owner}:${entry.label}`, error };
123
+ }
124
+ }
125
+ }
126
+ /**
127
+ * 把多个 Disposer 合成一个
128
+ * @param disposers 回收函数列表(可含 undefined,便于条件注册)
129
+ * @returns 合成后的回收函数;逆序执行,单个抛错不影响其余
130
+ */
131
+ export function combineDisposers(...disposers) {
132
+ return () => {
133
+ for (let i = disposers.length - 1; i >= 0; i--) {
134
+ try {
135
+ disposers[i]?.();
136
+ }
137
+ catch {
138
+ // 合成 disposer 拿不到 logger,只能忽略;需要记录的场景请用 DisposalRegistry
139
+ }
140
+ }
141
+ };
142
+ }
143
+ //# sourceMappingURL=dispose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispose.js","sourceRoot":"","sources":["../../src/util/dispose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 模块职责:时长表达式解析
3
+ * 依赖方向:仅依赖类型包
4
+ * 生命周期:纯函数
5
+ * 注意事项:一律写 `"30d"` 而不是 `60 * 60 * 24 * 30`:手工算出来的秒数改一处要通查全部
6
+ * 调用点,而配置文件里也读不出那串数字是多久。
7
+ */
8
+ import type { DurationLike } from "@yunzai-ng/types";
9
+ /**
10
+ * 把时长表达式解析为毫秒
11
+ * @param value 毫秒数字,或形如 `"5s"` / `"30d"` 的字符串
12
+ * @param fallback 无法解析时返回的默认值,缺省 0
13
+ * @returns 毫秒数
14
+ */
15
+ export declare function parseDuration(value: DurationLike | string | undefined | null, fallback?: number): number;
16
+ /**
17
+ * 把毫秒格式化为人类可读的中文时长
18
+ * @param ms 毫秒数
19
+ * @returns 形如 `"1天2小时3分"` 的字符串;小于 1 秒时返回 `"<1秒"`
20
+ */
21
+ export declare function formatDuration(ms: number): string;
22
+ /**
23
+ * 把字节数格式化为可读大小
24
+ * @param bytes 字节数
25
+ * @returns 形如 `"12.34MB"` 的字符串
26
+ */
27
+ export declare function formatBytes(bytes: number): string;
28
+ //# sourceMappingURL=duration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../../src/util/duration.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAapD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,QAAQ,SAAI,GAAG,MAAM,CAcnG;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAgBjD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWjD"}
@@ -0,0 +1,77 @@
1
+ /** 各单位对应的毫秒数 */
2
+ const UNIT_MS = {
3
+ ms: 1,
4
+ s: 1000,
5
+ m: 60_000,
6
+ h: 3_600_000,
7
+ d: 86_400_000
8
+ };
9
+ const DURATION_RE = /^(-?\d+(?:\.\d+)?)(ms|s|m|h|d)$/;
10
+ /**
11
+ * 把时长表达式解析为毫秒
12
+ * @param value 毫秒数字,或形如 `"5s"` / `"30d"` 的字符串
13
+ * @param fallback 无法解析时返回的默认值,缺省 0
14
+ * @returns 毫秒数
15
+ */
16
+ export function parseDuration(value, fallback = 0) {
17
+ if (value === undefined || value === null)
18
+ return fallback;
19
+ if (typeof value === "number")
20
+ return Number.isFinite(value) ? value : fallback;
21
+ const trimmed = value.trim();
22
+ if (trimmed === "")
23
+ return fallback;
24
+ // 纯数字串按毫秒处理,兼容从 YAML 里读到的 "5000"
25
+ if (/^-?\d+$/.test(trimmed))
26
+ return Number(trimmed);
27
+ const m = DURATION_RE.exec(trimmed);
28
+ if (!m)
29
+ return fallback;
30
+ const [, num, unit] = m;
31
+ return Number(num) * (UNIT_MS[unit] ?? 1);
32
+ }
33
+ /**
34
+ * 把毫秒格式化为人类可读的中文时长
35
+ * @param ms 毫秒数
36
+ * @returns 形如 `"1天2小时3分"` 的字符串;小于 1 秒时返回 `"<1秒"`
37
+ */
38
+ export function formatDuration(ms) {
39
+ if (!Number.isFinite(ms) || ms < 0)
40
+ return "-";
41
+ if (ms < 1000)
42
+ return "<1秒";
43
+ const d = Math.floor(ms / UNIT_MS.d);
44
+ const h = Math.floor((ms % UNIT_MS.d) / UNIT_MS.h);
45
+ const m = Math.floor((ms % UNIT_MS.h) / UNIT_MS.m);
46
+ const s = Math.floor((ms % UNIT_MS.m) / 1000);
47
+ const parts = [];
48
+ if (d)
49
+ parts.push(`${d}天`);
50
+ if (h)
51
+ parts.push(`${h}小时`);
52
+ if (m)
53
+ parts.push(`${m}分`);
54
+ // 只有在没有更大单位时才显示秒,避免 "1天0小时0分3秒" 这种噪音
55
+ if (s && !d && !h)
56
+ parts.push(`${s}秒`);
57
+ return parts.join("") || "<1秒";
58
+ }
59
+ /**
60
+ * 把字节数格式化为可读大小
61
+ * @param bytes 字节数
62
+ * @returns 形如 `"12.34MB"` 的字符串
63
+ */
64
+ export function formatBytes(bytes) {
65
+ if (!Number.isFinite(bytes))
66
+ return "-";
67
+ const units = ["B", "KB", "MB", "GB", "TB"];
68
+ let value = Math.abs(bytes);
69
+ let i = 0;
70
+ while (value >= 1024 && i < units.length - 1) {
71
+ value /= 1024;
72
+ i++;
73
+ }
74
+ const sign = bytes < 0 ? "-" : "";
75
+ return `${sign}${i === 0 ? value : value.toFixed(2)}${units[i]}`;
76
+ }
77
+ //# sourceMappingURL=duration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duration.js","sourceRoot":"","sources":["../../src/util/duration.ts"],"names":[],"mappings":"AASA,gBAAgB;AAChB,MAAM,OAAO,GAA2B;IACtC,EAAE,EAAE,CAAC;IACL,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,UAAU;CACd,CAAA;AAED,MAAM,WAAW,GAAG,iCAAiC,CAAA;AAErD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAA+C,EAAE,QAAQ,GAAG,CAAC;IACzF,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAA;IAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAA;IAE/E,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC5B,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAA;IAEnC,iCAAiC;IACjC,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,CAAA;IAEnD,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACnC,IAAI,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAA;IACvB,MAAM,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IACvB,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAc,CAAC,IAAI,CAAC,CAAC,CAAA;AACrD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC;QAAE,OAAO,GAAG,CAAA;IAC9C,IAAI,EAAE,GAAG,IAAI;QAAE,OAAO,KAAK,CAAA;IAE3B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,CAAE,CAAC,CAAA;IACrC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAE,CAAC,GAAG,OAAO,CAAC,CAAE,CAAC,CAAA;IACpD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAE,CAAC,GAAG,OAAO,CAAC,CAAE,CAAC,CAAA;IACpD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAE,CAAC,GAAG,IAAI,CAAC,CAAA;IAE9C,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC1B,IAAI,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC3B,IAAI,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC1B,qCAAqC;IACrC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACtC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,CAAA;AAChC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAA;IACvC,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IAC3C,IAAI,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAC3B,IAAI,CAAC,GAAG,CAAC,CAAA;IACT,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7C,KAAK,IAAI,IAAI,CAAA;QACb,CAAC,EAAE,CAAA;IACL,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IACjC,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;AAClE,CAAC"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * 从未知错误里取出 errno code
3
+ * @param err 错误
4
+ * @returns 错误码,取不到时 undefined
5
+ */
6
+ export declare function errorCode(err: unknown): string | undefined;
7
+ /**
8
+ * 路径是否存在
9
+ * @param path 路径
10
+ * @returns 是否存在
11
+ */
12
+ export declare function pathExists(path: string): Promise<boolean>;
13
+ /**
14
+ * 是否是目录
15
+ * @param path 路径
16
+ * @returns 是目录则 true;不存在或不是目录则 false
17
+ */
18
+ export declare function isDirectory(path: string): Promise<boolean>;
19
+ /**
20
+ * 是否是文件
21
+ * @param path 路径
22
+ * @returns 是文件则 true
23
+ */
24
+ export declare function isFile(path: string): Promise<boolean>;
25
+ /**
26
+ * 确保目录存在
27
+ * @param path 目录路径
28
+ * @returns 目录路径本身,便于链式使用
29
+ */
30
+ export declare function ensureDir(path: string): Promise<string>;
31
+ /**
32
+ * 原子写文件
33
+ *
34
+ * 先写同目录下的 `.tmp-*` 文件再 rename 覆盖 —— 同目录是关键,
35
+ * 跨分区 rename 不是原子操作。
36
+ * @param path 目标文件路径
37
+ * @param data 内容
38
+ * @throws 写入或替换失败时抛出;此时原文件保持不变
39
+ */
40
+ export declare function atomicWrite(path: string, data: string | Uint8Array): Promise<void>;
41
+ /**
42
+ * 读文本文件
43
+ * @param path 路径
44
+ * @returns 文件内容;不存在时 undefined
45
+ * @throws 除"不存在"以外的读取错误
46
+ */
47
+ export declare function readText(path: string): Promise<string | undefined>;
48
+ /**
49
+ * 读 JSON 文件
50
+ * @param path 路径
51
+ * @returns 解析结果;文件不存在时 undefined
52
+ * @throws JSON 解析失败时抛出,错误信息带上文件路径
53
+ */
54
+ export declare function readJson<T = unknown>(path: string): Promise<T | undefined>;
55
+ /**
56
+ * 原子写 JSON 文件
57
+ * @param path 路径
58
+ * @param value 数据
59
+ * @param pretty 是否缩进,缺省 true(配置类文件要能手改)
60
+ */
61
+ export declare function writeJson(path: string, value: unknown, pretty?: boolean): Promise<void>;
62
+ /**
63
+ * 列出目录下的子目录名
64
+ * @param path 目录路径
65
+ * @returns 子目录名数组;目录不存在时空数组
66
+ */
67
+ export declare function listDirs(path: string): Promise<string[]>;
68
+ /**
69
+ * 列出目录下的文件名
70
+ * @param path 目录路径
71
+ * @param ext 可选扩展名过滤,如 `".yaml"`
72
+ * @returns 文件名数组;目录不存在时空数组
73
+ */
74
+ export declare function listFiles(path: string, ext?: string): Promise<string[]>;
75
+ /**
76
+ * 删除文件或目录(不存在时静默)
77
+ * @param path 路径
78
+ */
79
+ export declare function remove(path: string): Promise<void>;
80
+ /**
81
+ * 取文件修改时间
82
+ * @param path 路径
83
+ * @returns 毫秒时间戳;文件不存在时 undefined
84
+ */
85
+ export declare function mtimeOf(path: string): Promise<number | undefined>;
86
+ /**
87
+ * 取文件大小
88
+ * @param path 路径
89
+ * @returns 字节数;文件不存在时 undefined
90
+ */
91
+ export declare function sizeOf(path: string): Promise<number | undefined>;
92
+ /**
93
+ * 在根目录内安全拼接路径,阻止 `../` 越界
94
+ *
95
+ * WebUI 的静态资源、插件模板资源都要按外部传入的相对路径取文件,
96
+ * 不做这层校验就是任意文件读取漏洞。
97
+ * @param root 允许访问的根目录
98
+ * @param requested 外部传入的相对路径
99
+ * @returns 解析后的绝对路径
100
+ * @throws 当结果落在 root 之外,或传入了绝对路径时
101
+ */
102
+ export declare function safeJoin(root: string, requested: string): string;
103
+ /**
104
+ * 判断路径是否在某目录内
105
+ * @param root 根目录
106
+ * @param target 待判定路径
107
+ * @returns 是否在根目录内(含自身)
108
+ */
109
+ export declare function isInside(root: string, target: string): boolean;
110
+ //# sourceMappingURL=fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/util/fs.ts"],"names":[],"mappings":"AAiCA;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAM1D;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMhE;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAM3D;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG7D;AAwBD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAYxF;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAOxE;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAQhF;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1F;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQ9D;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAS7E;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAExD;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAMvE;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAMtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAShE;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAG9D"}
@@ -0,0 +1,269 @@
1
+ /**
2
+ * 模块职责:文件系统助手(原子写、目录保证、路径越界防护)
3
+ * 依赖方向:仅依赖 node:fs / node:path 与 util/defer
4
+ * 生命周期:纯函数
5
+ * 注意事项:**原子写是必需品**,一律走"写临时文件 → rename"。直接 `writeFileSync` 时,
6
+ * 进程恰在此刻被杀(Windows 关窗口、安卓被系统回收)会留下一个截断的配置文件,
7
+ * 下次启动即崩。
8
+ *
9
+ * Windows 上 rename 会因杀软/资源管理器短暂占用而抛 EPERM
10
+ * (本项目 pnpm install 就撞过一次),所以带退避重试。
11
+ */
12
+ import { constants as fsConstants } from "node:fs";
13
+ import { access, mkdir, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
14
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
15
+ import { randomId } from "./id.js";
16
+ import { sleep } from "./defer.js";
17
+ /** rename 遇到占用类错误时的重试次数 */
18
+ const RENAME_RETRIES = 5;
19
+ /** 判定为"临时占用、可重试"的错误码 */
20
+ const TRANSIENT_CODES = new Set(["EPERM", "EACCES", "EBUSY"]);
21
+ /**
22
+ * 从未知错误里取出 errno code
23
+ * @param err 错误
24
+ * @returns 错误码,取不到时 undefined
25
+ */
26
+ export function errorCode(err) {
27
+ if (err !== null && typeof err === "object" && "code" in err) {
28
+ const code = err.code;
29
+ if (typeof code === "string")
30
+ return code;
31
+ }
32
+ return undefined;
33
+ }
34
+ /**
35
+ * 路径是否存在
36
+ * @param path 路径
37
+ * @returns 是否存在
38
+ */
39
+ export async function pathExists(path) {
40
+ try {
41
+ await access(path, fsConstants.F_OK);
42
+ return true;
43
+ }
44
+ catch {
45
+ return false;
46
+ }
47
+ }
48
+ /**
49
+ * 是否是目录
50
+ * @param path 路径
51
+ * @returns 是目录则 true;不存在或不是目录则 false
52
+ */
53
+ export async function isDirectory(path) {
54
+ try {
55
+ return (await stat(path)).isDirectory();
56
+ }
57
+ catch {
58
+ return false;
59
+ }
60
+ }
61
+ /**
62
+ * 是否是文件
63
+ * @param path 路径
64
+ * @returns 是文件则 true
65
+ */
66
+ export async function isFile(path) {
67
+ try {
68
+ return (await stat(path)).isFile();
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ /**
75
+ * 确保目录存在
76
+ * @param path 目录路径
77
+ * @returns 目录路径本身,便于链式使用
78
+ */
79
+ export async function ensureDir(path) {
80
+ await mkdir(path, { recursive: true });
81
+ return path;
82
+ }
83
+ /**
84
+ * 带重试的 rename
85
+ *
86
+ * Windows 下杀软扫描、编辑器索引会短暂锁住新建文件,导致 rename 抛 EPERM。
87
+ * 这类错误重试即可,不该让一次配置保存失败。
88
+ * @param from 源路径
89
+ * @param to 目标路径
90
+ * @throws 重试用尽后抛出最后一次错误
91
+ */
92
+ async function renameWithRetry(from, to) {
93
+ for (let attempt = 1;; attempt++) {
94
+ try {
95
+ await rename(from, to);
96
+ return;
97
+ }
98
+ catch (err) {
99
+ const code = errorCode(err);
100
+ if (attempt >= RENAME_RETRIES || !code || !TRANSIENT_CODES.has(code))
101
+ throw err;
102
+ await sleep(20 * attempt);
103
+ }
104
+ }
105
+ }
106
+ /**
107
+ * 原子写文件
108
+ *
109
+ * 先写同目录下的 `.tmp-*` 文件再 rename 覆盖 —— 同目录是关键,
110
+ * 跨分区 rename 不是原子操作。
111
+ * @param path 目标文件路径
112
+ * @param data 内容
113
+ * @throws 写入或替换失败时抛出;此时原文件保持不变
114
+ */
115
+ export async function atomicWrite(path, data) {
116
+ const dir = dirname(path);
117
+ await ensureDir(dir);
118
+ const tmp = join(dir, `.tmp-${randomId(8)}`);
119
+ try {
120
+ await writeFile(tmp, data);
121
+ await renameWithRetry(tmp, path);
122
+ }
123
+ catch (err) {
124
+ // 清理临时文件,失败也不掩盖原始错误
125
+ await unlink(tmp).catch(() => undefined);
126
+ throw err;
127
+ }
128
+ }
129
+ /**
130
+ * 读文本文件
131
+ * @param path 路径
132
+ * @returns 文件内容;不存在时 undefined
133
+ * @throws 除"不存在"以外的读取错误
134
+ */
135
+ export async function readText(path) {
136
+ try {
137
+ return await readFile(path, "utf8");
138
+ }
139
+ catch (err) {
140
+ if (errorCode(err) === "ENOENT")
141
+ return undefined;
142
+ throw err;
143
+ }
144
+ }
145
+ /**
146
+ * 读 JSON 文件
147
+ * @param path 路径
148
+ * @returns 解析结果;文件不存在时 undefined
149
+ * @throws JSON 解析失败时抛出,错误信息带上文件路径
150
+ */
151
+ export async function readJson(path) {
152
+ const text = await readText(path);
153
+ if (text === undefined)
154
+ return undefined;
155
+ try {
156
+ return JSON.parse(text);
157
+ }
158
+ catch (err) {
159
+ throw new Error(`解析 JSON 失败:${path} —— ${err instanceof Error ? err.message : String(err)}`);
160
+ }
161
+ }
162
+ /**
163
+ * 原子写 JSON 文件
164
+ * @param path 路径
165
+ * @param value 数据
166
+ * @param pretty 是否缩进,缺省 true(配置类文件要能手改)
167
+ */
168
+ export async function writeJson(path, value, pretty = true) {
169
+ await atomicWrite(path, JSON.stringify(value, null, pretty ? 2 : undefined));
170
+ }
171
+ /**
172
+ * 列出目录下的子目录名
173
+ * @param path 目录路径
174
+ * @returns 子目录名数组;目录不存在时空数组
175
+ */
176
+ export async function listDirs(path) {
177
+ try {
178
+ const entries = await readdir(path, { withFileTypes: true });
179
+ return entries.filter(e => e.isDirectory()).map(e => e.name);
180
+ }
181
+ catch (err) {
182
+ if (errorCode(err) === "ENOENT")
183
+ return [];
184
+ throw err;
185
+ }
186
+ }
187
+ /**
188
+ * 列出目录下的文件名
189
+ * @param path 目录路径
190
+ * @param ext 可选扩展名过滤,如 `".yaml"`
191
+ * @returns 文件名数组;目录不存在时空数组
192
+ */
193
+ export async function listFiles(path, ext) {
194
+ try {
195
+ const entries = await readdir(path, { withFileTypes: true });
196
+ const files = entries.filter(e => e.isFile()).map(e => e.name);
197
+ return ext ? files.filter(name => name.endsWith(ext)) : files;
198
+ }
199
+ catch (err) {
200
+ if (errorCode(err) === "ENOENT")
201
+ return [];
202
+ throw err;
203
+ }
204
+ }
205
+ /**
206
+ * 删除文件或目录(不存在时静默)
207
+ * @param path 路径
208
+ */
209
+ export async function remove(path) {
210
+ await rm(path, { recursive: true, force: true });
211
+ }
212
+ /**
213
+ * 取文件修改时间
214
+ * @param path 路径
215
+ * @returns 毫秒时间戳;文件不存在时 undefined
216
+ */
217
+ export async function mtimeOf(path) {
218
+ try {
219
+ return (await stat(path)).mtimeMs;
220
+ }
221
+ catch {
222
+ return undefined;
223
+ }
224
+ }
225
+ /**
226
+ * 取文件大小
227
+ * @param path 路径
228
+ * @returns 字节数;文件不存在时 undefined
229
+ */
230
+ export async function sizeOf(path) {
231
+ try {
232
+ return (await stat(path)).size;
233
+ }
234
+ catch {
235
+ return undefined;
236
+ }
237
+ }
238
+ /**
239
+ * 在根目录内安全拼接路径,阻止 `../` 越界
240
+ *
241
+ * WebUI 的静态资源、插件模板资源都要按外部传入的相对路径取文件,
242
+ * 不做这层校验就是任意文件读取漏洞。
243
+ * @param root 允许访问的根目录
244
+ * @param requested 外部传入的相对路径
245
+ * @returns 解析后的绝对路径
246
+ * @throws 当结果落在 root 之外,或传入了绝对路径时
247
+ */
248
+ export function safeJoin(root, requested) {
249
+ if (isAbsolute(requested))
250
+ throw new Error(`路径越界:不接受绝对路径 ${requested}`);
251
+ const rootAbs = resolve(root);
252
+ const target = resolve(rootAbs, requested);
253
+ const rel = relative(rootAbs, target);
254
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
255
+ throw new Error(`路径越界:${requested}`);
256
+ }
257
+ return target;
258
+ }
259
+ /**
260
+ * 判断路径是否在某目录内
261
+ * @param root 根目录
262
+ * @param target 待判定路径
263
+ * @returns 是否在根目录内(含自身)
264
+ */
265
+ export function isInside(root, target) {
266
+ const rel = relative(resolve(root), resolve(target));
267
+ return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
268
+ }
269
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/util/fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,SAAS,IAAI,WAAW,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EACL,MAAM,EACN,KAAK,EACL,QAAQ,EACR,OAAO,EACP,MAAM,EACN,EAAE,EACF,IAAI,EACJ,MAAM,EACN,SAAS,EACV,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,2BAA2B;AAC3B,MAAM,cAAc,GAAG,CAAC,CAAA;AAExB,wBAAwB;AACxB,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;AAE7D;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,GAAY;IACpC,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QAC7D,MAAM,IAAI,GAAI,GAA0B,CAAC,IAAI,CAAA;QAC7C,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;IAC3C,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,CAAA;QACpC,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY;IAC5C,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAA;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAY;IACvC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAA;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY;IAC1C,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACtC,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;;;;GAQG;AACH,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,EAAU;IACrD,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;YACtB,OAAM;QACR,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAA;YAC3B,IAAI,OAAO,IAAI,cAAc,IAAI,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,MAAM,GAAG,CAAA;YAC/E,MAAM,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,IAAyB;IACvE,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,MAAM,SAAS,CAAC,GAAG,CAAC,CAAA;IACpB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC5C,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC1B,MAAM,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,oBAAoB;QACpB,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACxC,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACrC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAA;QACjD,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAc,IAAY;IACtD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAA;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC9F,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,KAAc,EAAE,MAAM,GAAG,IAAI;IACzE,MAAM,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAA;AAC9E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QAC5D,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IAC9D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAA;QAC1C,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,GAAY;IACxD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;QAC5D,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QAC9D,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAC/D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAA;QAC1C,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAY;IACvC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AAClD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAY;IACxC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAA;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAY;IACvC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,SAAiB;IACtD,IAAI,UAAU,CAAC,SAAS,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,SAAS,EAAE,CAAC,CAAA;IACvE,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;IAC1C,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IACrC,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,QAAQ,SAAS,EAAE,CAAC,CAAA;IACtC,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,MAAc;IACnD,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;IACpD,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;AACxF,CAAC"}