@zk-tech/bedrock 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (390) hide show
  1. package/README.md +24 -0
  2. package/dist/array/index.cjs +22 -0
  3. package/dist/array/index.cjs.map +1 -0
  4. package/dist/array/index.d.cts +13 -0
  5. package/dist/array/index.d.ts +13 -0
  6. package/dist/array/index.js +19 -0
  7. package/dist/array/index.js.map +1 -0
  8. package/dist/assert/index.cjs +29 -0
  9. package/dist/assert/index.cjs.map +1 -0
  10. package/dist/assert/index.d.cts +25 -0
  11. package/dist/assert/index.d.ts +25 -0
  12. package/dist/assert/index.js +24 -0
  13. package/dist/assert/index.js.map +1 -0
  14. package/dist/async/index.cjs +746 -0
  15. package/dist/async/index.cjs.map +1 -0
  16. package/dist/async/index.d.cts +47 -0
  17. package/dist/async/index.d.ts +47 -0
  18. package/dist/async/index.js +738 -0
  19. package/dist/async/index.js.map +1 -0
  20. package/dist/barrier-316Xonfd.d.cts +18 -0
  21. package/dist/barrier-316Xonfd.d.ts +18 -0
  22. package/dist/byte/index.cjs +59 -0
  23. package/dist/byte/index.cjs.map +1 -0
  24. package/dist/byte/index.d.cts +12 -0
  25. package/dist/byte/index.d.ts +12 -0
  26. package/dist/byte/index.js +49 -0
  27. package/dist/byte/index.js.map +1 -0
  28. package/dist/cache/index.cjs +418 -0
  29. package/dist/cache/index.cjs.map +1 -0
  30. package/dist/cache/index.d.cts +40 -0
  31. package/dist/cache/index.d.ts +40 -0
  32. package/dist/cache/index.js +415 -0
  33. package/dist/cache/index.js.map +1 -0
  34. package/dist/cancellation-BIIv2UJm.d.cts +25 -0
  35. package/dist/cancellation-ClqPPsV1.d.ts +25 -0
  36. package/dist/context/index.cjs +59 -0
  37. package/dist/context/index.cjs.map +1 -0
  38. package/dist/context/index.d.cts +33 -0
  39. package/dist/context/index.d.ts +33 -0
  40. package/dist/context/index.js +51 -0
  41. package/dist/context/index.js.map +1 -0
  42. package/dist/di/index.cjs +1965 -0
  43. package/dist/di/index.cjs.map +1 -0
  44. package/dist/di/index.d.cts +140 -0
  45. package/dist/di/index.d.ts +140 -0
  46. package/dist/di/index.js +1949 -0
  47. package/dist/di/index.js.map +1 -0
  48. package/dist/disposable-t-B15Nu57y.d.ts +93 -0
  49. package/dist/disposable-t-CVsiyHPL.d.cts +93 -0
  50. package/dist/dispose/index.cjs +356 -0
  51. package/dist/dispose/index.cjs.map +1 -0
  52. package/dist/dispose/index.d.cts +26 -0
  53. package/dist/dispose/index.d.ts +26 -0
  54. package/dist/dispose/index.js +340 -0
  55. package/dist/dispose/index.js.map +1 -0
  56. package/dist/dispose-base-CAeXDpjg.d.cts +6 -0
  57. package/dist/dispose-base-CAeXDpjg.d.ts +6 -0
  58. package/dist/emitter-CAfCtSTg.d.cts +35 -0
  59. package/dist/emitter-DeM5mlEm.d.ts +35 -0
  60. package/dist/error/index.cjs +145 -0
  61. package/dist/error/index.cjs.map +1 -0
  62. package/dist/error/index.d.cts +45 -0
  63. package/dist/error/index.d.ts +45 -0
  64. package/dist/error/index.js +126 -0
  65. package/dist/error/index.js.map +1 -0
  66. package/dist/error-base-B4zaiJ5m.d.cts +32 -0
  67. package/dist/error-base-B4zaiJ5m.d.ts +32 -0
  68. package/dist/event/index.cjs +550 -0
  69. package/dist/event/index.cjs.map +1 -0
  70. package/dist/event/index.d.cts +139 -0
  71. package/dist/event/index.d.ts +139 -0
  72. package/dist/event/index.js +538 -0
  73. package/dist/event/index.js.map +1 -0
  74. package/dist/function/index.cjs +132 -0
  75. package/dist/function/index.cjs.map +1 -0
  76. package/dist/function/index.d.cts +26 -0
  77. package/dist/function/index.d.ts +26 -0
  78. package/dist/function/index.js +129 -0
  79. package/dist/function/index.js.map +1 -0
  80. package/dist/graph-BGbNOniY.d.cts +23 -0
  81. package/dist/graph-BGbNOniY.d.ts +23 -0
  82. package/dist/hash/index.cjs +54 -0
  83. package/dist/hash/index.cjs.map +1 -0
  84. package/dist/hash/index.d.cts +5 -0
  85. package/dist/hash/index.d.ts +5 -0
  86. package/dist/hash/index.js +50 -0
  87. package/dist/hash/index.js.map +1 -0
  88. package/dist/instantiation-service.interface-CVFMBUUD.d.cts +78 -0
  89. package/dist/instantiation-service.interface-CVFMBUUD.d.ts +78 -0
  90. package/dist/json/index.cjs +28 -0
  91. package/dist/json/index.cjs.map +1 -0
  92. package/dist/json/index.d.cts +8 -0
  93. package/dist/json/index.d.ts +8 -0
  94. package/dist/json/index.js +26 -0
  95. package/dist/json/index.js.map +1 -0
  96. package/dist/launch/index.cjs +213 -0
  97. package/dist/launch/index.cjs.map +1 -0
  98. package/dist/launch/index.d.cts +46 -0
  99. package/dist/launch/index.d.ts +46 -0
  100. package/dist/launch/index.js +211 -0
  101. package/dist/launch/index.js.map +1 -0
  102. package/dist/linked-list-CUkue5DZ.d.cts +24 -0
  103. package/dist/linked-list-CUkue5DZ.d.ts +24 -0
  104. package/dist/lock/index.cjs +662 -0
  105. package/dist/lock/index.cjs.map +1 -0
  106. package/dist/lock/index.d.cts +145 -0
  107. package/dist/lock/index.d.ts +145 -0
  108. package/dist/lock/index.js +656 -0
  109. package/dist/lock/index.js.map +1 -0
  110. package/dist/lodash-es/index.cjs +14 -0
  111. package/dist/lodash-es/index.cjs.map +1 -0
  112. package/dist/lodash-es/index.d.cts +1 -0
  113. package/dist/lodash-es/index.d.ts +1 -0
  114. package/dist/lodash-es/index.js +3 -0
  115. package/dist/lodash-es/index.js.map +1 -0
  116. package/dist/math/index.cjs +161 -0
  117. package/dist/math/index.cjs.map +1 -0
  118. package/dist/math/index.d.cts +76 -0
  119. package/dist/math/index.d.ts +76 -0
  120. package/dist/math/index.js +156 -0
  121. package/dist/math/index.js.map +1 -0
  122. package/dist/network/index.cjs +91 -0
  123. package/dist/network/index.cjs.map +1 -0
  124. package/dist/network/index.d.cts +62 -0
  125. package/dist/network/index.d.ts +62 -0
  126. package/dist/network/index.js +82 -0
  127. package/dist/network/index.js.map +1 -0
  128. package/dist/objects/index.cjs +80 -0
  129. package/dist/objects/index.cjs.map +1 -0
  130. package/dist/objects/index.d.cts +11 -0
  131. package/dist/objects/index.d.ts +11 -0
  132. package/dist/objects/index.js +77 -0
  133. package/dist/objects/index.js.map +1 -0
  134. package/dist/platform/index.cjs +62 -0
  135. package/dist/platform/index.cjs.map +1 -0
  136. package/dist/platform/index.d.cts +21 -0
  137. package/dist/platform/index.d.ts +21 -0
  138. package/dist/platform/index.js +48 -0
  139. package/dist/platform/index.js.map +1 -0
  140. package/dist/promise/index.cjs +639 -0
  141. package/dist/promise/index.cjs.map +1 -0
  142. package/dist/promise/index.d.cts +63 -0
  143. package/dist/promise/index.d.ts +63 -0
  144. package/dist/promise/index.js +633 -0
  145. package/dist/promise/index.js.map +1 -0
  146. package/dist/scheduler/index.cjs +599 -0
  147. package/dist/scheduler/index.cjs.map +1 -0
  148. package/dist/scheduler/index.d.cts +57 -0
  149. package/dist/scheduler/index.d.ts +57 -0
  150. package/dist/scheduler/index.js +594 -0
  151. package/dist/scheduler/index.js.map +1 -0
  152. package/dist/sprintf/index.cjs +101 -0
  153. package/dist/sprintf/index.cjs.map +1 -0
  154. package/dist/sprintf/index.d.cts +3 -0
  155. package/dist/sprintf/index.d.ts +3 -0
  156. package/dist/sprintf/index.js +99 -0
  157. package/dist/sprintf/index.js.map +1 -0
  158. package/dist/structure/index.cjs +300 -0
  159. package/dist/structure/index.cjs.map +1 -0
  160. package/dist/structure/index.d.cts +21 -0
  161. package/dist/structure/index.d.ts +21 -0
  162. package/dist/structure/index.js +296 -0
  163. package/dist/structure/index.js.map +1 -0
  164. package/dist/type/index.cjs +4 -0
  165. package/dist/type/index.cjs.map +1 -0
  166. package/dist/type/index.d.cts +20 -0
  167. package/dist/type/index.d.ts +20 -0
  168. package/dist/type/index.js +3 -0
  169. package/dist/type/index.js.map +1 -0
  170. package/dist/undo-redo-stack/index.cjs +545 -0
  171. package/dist/undo-redo-stack/index.cjs.map +1 -0
  172. package/dist/undo-redo-stack/index.d.cts +130 -0
  173. package/dist/undo-redo-stack/index.d.ts +130 -0
  174. package/dist/undo-redo-stack/index.js +542 -0
  175. package/dist/undo-redo-stack/index.js.map +1 -0
  176. package/dist/uuid/index.cjs +67 -0
  177. package/dist/uuid/index.cjs.map +1 -0
  178. package/dist/uuid/index.d.cts +17 -0
  179. package/dist/uuid/index.d.ts +17 -0
  180. package/dist/uuid/index.js +61 -0
  181. package/dist/uuid/index.js.map +1 -0
  182. package/dist/worker/index.cjs +271 -0
  183. package/dist/worker/index.cjs.map +1 -0
  184. package/dist/worker/index.d.cts +66 -0
  185. package/dist/worker/index.d.ts +66 -0
  186. package/dist/worker/index.js +267 -0
  187. package/dist/worker/index.js.map +1 -0
  188. package/package.json +285 -0
  189. package/src/_internal/logger.ts +59 -0
  190. package/src/array/array.test.ts +35 -0
  191. package/src/array/array.ts +25 -0
  192. package/src/array/index.ts +1 -0
  193. package/src/assert/assert.test.ts +86 -0
  194. package/src/assert/assert.ts +42 -0
  195. package/src/assert/index.ts +2 -0
  196. package/src/async/barrier.test.ts +90 -0
  197. package/src/async/barrier.ts +58 -0
  198. package/src/async/cancellation.test.ts +85 -0
  199. package/src/async/cancellation.ts +193 -0
  200. package/src/async/index.ts +18 -0
  201. package/src/async/queue/queue.test.ts +70 -0
  202. package/src/async/queue/queue.ts +56 -0
  203. package/src/async/queue/task.test.ts +155 -0
  204. package/src/async/queue/task.ts +67 -0
  205. package/src/async/utils.test.ts +28 -0
  206. package/src/async/utils.ts +8 -0
  207. package/src/async/wait.ts +9 -0
  208. package/src/byte/format.test.ts +64 -0
  209. package/src/byte/format.ts +44 -0
  210. package/src/byte/index.ts +2 -0
  211. package/src/byte/node_modules/.vitest/results.json +1 -0
  212. package/src/byte/var.ts +11 -0
  213. package/src/cache/index.ts +2 -0
  214. package/src/cache/lru-with-timeout.test.ts +88 -0
  215. package/src/cache/lru-with-timeout.ts +85 -0
  216. package/src/cache/lru.test.ts +56 -0
  217. package/src/cache/lru.ts +59 -0
  218. package/src/context/context.test.ts +17 -0
  219. package/src/context/context.ts +60 -0
  220. package/src/context/index.ts +8 -0
  221. package/src/di/base.ts +73 -0
  222. package/src/di/container-service.test.ts +179 -0
  223. package/src/di/context.web.tsx +41 -0
  224. package/src/di/descriptor.ts +31 -0
  225. package/src/di/idle-value.test.ts +73 -0
  226. package/src/di/idle-value.ts +63 -0
  227. package/src/di/index.common.ts +32 -0
  228. package/src/di/index.ts +2 -0
  229. package/src/di/instantiation-service.interface.ts +46 -0
  230. package/src/di/instantiation-service.test.ts +337 -0
  231. package/src/di/instantiation-service.ts +468 -0
  232. package/src/di/lazy/foo.mock.ts +28 -0
  233. package/src/di/lazy/idle-load.ts +39 -0
  234. package/src/di/lazy/index.ts +4 -0
  235. package/src/di/lazy/lazy-service.test.ts +65 -0
  236. package/src/di/lazy/lazy-service.ts +71 -0
  237. package/src/di/lazy/type.ts +5 -0
  238. package/src/di/node_modules/.vitest/results.json +1 -0
  239. package/src/di/proxy-builder.test.ts +45 -0
  240. package/src/di/proxy-builder.ts +38 -0
  241. package/src/di/service-collection.test.ts +27 -0
  242. package/src/di/service-collection.ts +46 -0
  243. package/src/di/service-ownership-collection.test.ts +39 -0
  244. package/src/di/service-ownership-collection.ts +38 -0
  245. package/src/di/service-registry.test.ts +66 -0
  246. package/src/di/service-registry.ts +99 -0
  247. package/src/di/trace.ts +85 -0
  248. package/src/dispose/disposable-store.test.ts +57 -0
  249. package/src/dispose/disposable-store.ts +80 -0
  250. package/src/dispose/disposable-t.test.ts +123 -0
  251. package/src/dispose/disposable-t.ts +238 -0
  252. package/src/dispose/disposable-utils.test.ts +15 -0
  253. package/src/dispose/disposable-utils.ts +28 -0
  254. package/src/dispose/dispose-base.ts +9 -0
  255. package/src/dispose/index.ts +34 -0
  256. package/src/dispose/logger.test.ts +65 -0
  257. package/src/dispose/logger.ts +39 -0
  258. package/src/dispose/timer.test.ts +30 -0
  259. package/src/dispose/timer.ts +16 -0
  260. package/src/dispose/tracker.test.ts +51 -0
  261. package/src/dispose/tracker.ts +105 -0
  262. package/src/error/error-base.ts +45 -0
  263. package/src/error/error-code.ts +39 -0
  264. package/src/error/error-const.test.ts +30 -0
  265. package/src/error/error-const.ts +16 -0
  266. package/src/error/error-or.test.ts +44 -0
  267. package/src/error/error-or.ts +2 -0
  268. package/src/error/error-t.test.ts +116 -0
  269. package/src/error/error-t.ts +100 -0
  270. package/src/error/index.ts +24 -0
  271. package/src/error/node_modules/.vitest/results.json +1 -0
  272. package/src/event/disposable-linked-list.ts +29 -0
  273. package/src/event/emitter.test.ts +191 -0
  274. package/src/event/emitter.ts +162 -0
  275. package/src/event/error-handler.ts +22 -0
  276. package/src/event/index.ts +34 -0
  277. package/src/event/once.ts +29 -0
  278. package/src/event/phase-emitter.test.ts +212 -0
  279. package/src/event/phase-emitter.ts +209 -0
  280. package/src/event/shortcut-event-utils.ts +33 -0
  281. package/src/event/utils.ts +6 -0
  282. package/src/event/when.ts +40 -0
  283. package/src/function/debounce.test.ts +274 -0
  284. package/src/function/debounce.ts +168 -0
  285. package/src/function/index.ts +2 -0
  286. package/src/function/node_modules/.vitest/results.json +1 -0
  287. package/src/function/throttle.test.ts +179 -0
  288. package/src/function/throttle.ts +26 -0
  289. package/src/hash/hash-t.test.ts +100 -0
  290. package/src/hash/hash-t.ts +51 -0
  291. package/src/hash/index.ts +3 -0
  292. package/src/json/index.ts +1 -0
  293. package/src/json/node_modules/.vitest/results.json +1 -0
  294. package/src/json/parse.ts +19 -0
  295. package/src/launch/abstract-job.ts +45 -0
  296. package/src/launch/cost-recorder.ts +22 -0
  297. package/src/launch/index.ts +2 -0
  298. package/src/launch/job-scheduler.test.ts +122 -0
  299. package/src/launch/job-scheduler.ts +118 -0
  300. package/src/launch/node_modules/.vitest/deps/_metadata.json +8 -0
  301. package/src/launch/node_modules/.vitest/deps/package.json +3 -0
  302. package/src/launch/node_modules/.vitest/results.json +1 -0
  303. package/src/lock/README.md +11 -0
  304. package/src/lock/capability.test.ts +110 -0
  305. package/src/lock/capability.ts +89 -0
  306. package/src/lock/index.ts +15 -0
  307. package/src/lock/node_modules/.vitest/results.json +1 -0
  308. package/src/lock/semaphore.ts +21 -0
  309. package/src/lock/shared-mutex.test.ts +537 -0
  310. package/src/lock/shared-mutex.ts +242 -0
  311. package/src/lock/utils.test.ts +165 -0
  312. package/src/lock/utils.ts +135 -0
  313. package/src/lodash-es/index.ts +1 -0
  314. package/src/math/degree.ts +16 -0
  315. package/src/math/index.ts +7 -0
  316. package/src/math/math.test.ts +40 -0
  317. package/src/math/math.ts +64 -0
  318. package/src/math/node_modules/.vitest/results.json +1 -0
  319. package/src/math/vector.test.ts +73 -0
  320. package/src/math/vector.ts +114 -0
  321. package/src/network/client.interface.ts +104 -0
  322. package/src/network/client.web.ts +24 -0
  323. package/src/network/index.common.ts +10 -0
  324. package/src/network/index.ts +2 -0
  325. package/src/network/plugins/retry.ts +98 -0
  326. package/src/objects/deep-clone.test.ts +40 -0
  327. package/src/objects/deep-clone.ts +13 -0
  328. package/src/objects/deep-equal.test.ts +86 -0
  329. package/src/objects/deep-equal.ts +60 -0
  330. package/src/objects/index.ts +4 -0
  331. package/src/platform/index.ts +64 -0
  332. package/src/promise/index.ts +16 -0
  333. package/src/promise/promise.test.ts +254 -0
  334. package/src/promise/promise.ts +212 -0
  335. package/src/scheduler/callback-token.ts +31 -0
  336. package/src/scheduler/core/actuator-args.test.ts +47 -0
  337. package/src/scheduler/core/actuator.test.ts +82 -0
  338. package/src/scheduler/core/actuator.ts +58 -0
  339. package/src/scheduler/core/chunk-scheduler.test.ts +54 -0
  340. package/src/scheduler/core/chunk-scheduler.ts +28 -0
  341. package/src/scheduler/core/node_modules/.vitest/results.json +1 -0
  342. package/src/scheduler/core/scheduler.test.ts +328 -0
  343. package/src/scheduler/core/scheduler.ts +172 -0
  344. package/src/scheduler/core/task-queue.test.ts +78 -0
  345. package/src/scheduler/core/task-queue.ts +44 -0
  346. package/src/scheduler/core/task.test.ts +34 -0
  347. package/src/scheduler/core/task.ts +52 -0
  348. package/src/scheduler/core/utils.ts +48 -0
  349. package/src/scheduler/executor/abstract-executor.test.ts +44 -0
  350. package/src/scheduler/executor/abstract-executor.ts +38 -0
  351. package/src/scheduler/executor/executor.interface.ts +39 -0
  352. package/src/scheduler/executor/idle-callback-executor.test.ts +70 -0
  353. package/src/scheduler/executor/idle-callback-executor.ts +98 -0
  354. package/src/scheduler/executor/make-executor.ts +18 -0
  355. package/src/scheduler/executor/post-message-executor.test.ts +66 -0
  356. package/src/scheduler/executor/post-message-executor.ts +52 -0
  357. package/src/scheduler/index.ts +15 -0
  358. package/src/scheduler/lv-scheduler-callback.ts +19 -0
  359. package/src/scheduler/lv-scheduler-config.ts +17 -0
  360. package/src/scheduler/type.ts +48 -0
  361. package/src/sprintf/index.ts +2 -0
  362. package/src/sprintf/sprintf.test.ts +95 -0
  363. package/src/sprintf/sprintf.ts +97 -0
  364. package/src/structure/graph.test.ts +181 -0
  365. package/src/structure/graph.ts +105 -0
  366. package/src/structure/index.ts +8 -0
  367. package/src/structure/linked-list.test.ts +74 -0
  368. package/src/structure/linked-list.ts +145 -0
  369. package/src/structure/min-heap.test.ts +71 -0
  370. package/src/structure/min-heap.ts +91 -0
  371. package/src/type/REAME.md +2 -0
  372. package/src/type/distributive-omit.interface.ts +4 -0
  373. package/src/type/index.ts +3 -0
  374. package/src/type/object-key-paths.interface.ts +40 -0
  375. package/src/undo-redo-stack/README.md +61 -0
  376. package/src/undo-redo-stack/action-stack.test.ts +330 -0
  377. package/src/undo-redo-stack/action-stack.ts +150 -0
  378. package/src/undo-redo-stack/element.ts +4 -0
  379. package/src/undo-redo-stack/index.ts +7 -0
  380. package/src/undo-redo-stack/state-stack.test.ts +118 -0
  381. package/src/undo-redo-stack/state-stack.ts +133 -0
  382. package/src/uuid/index.ts +7 -0
  383. package/src/uuid/uuid.ts +86 -0
  384. package/src/worker/cors-worker.ts +38 -0
  385. package/src/worker/index.ts +4 -0
  386. package/src/worker/node_modules/.vitest/results.json +1 -0
  387. package/src/worker/promise-worker-main-thread.test.ts +91 -0
  388. package/src/worker/promise-worker-main-thread.ts +76 -0
  389. package/src/worker/promise-worker-worker-thread.ts +64 -0
  390. package/src/worker/promise-worker.interface.ts +15 -0
@@ -0,0 +1,191 @@
1
+ import type { Event } from './emitter';
2
+ import { Emitter } from './emitter';
3
+ import { listenOnce } from './once';
4
+ import { listenWhen } from './when';
5
+ import { asyncUnexpectedError, ignoreUnexpectedError, syncUnexpectedError } from './error-handler';
6
+
7
+ interface IChangeData {
8
+ code: number;
9
+ msg: string;
10
+ }
11
+
12
+ class Foo {
13
+ readonly onChange: Event<[IChangeData]>;
14
+ private readonly _onChangeEmitter: Emitter<[IChangeData]>;
15
+
16
+ constructor(errorHandler = asyncUnexpectedError) {
17
+ this._onChangeEmitter = new Emitter({
18
+ onListenerError: errorHandler,
19
+ });
20
+ this.onChange = this._onChangeEmitter.event;
21
+ }
22
+
23
+ fire() {
24
+ this._onChangeEmitter.fire({
25
+ code: 0,
26
+ msg: '',
27
+ });
28
+ }
29
+ }
30
+
31
+ class Foo2 {
32
+ readonly onChange: Event<[number, string]>;
33
+ private readonly _onChangeEmitter: Emitter<[number, string]> = new Emitter();
34
+
35
+ constructor() {
36
+ this.onChange = this._onChangeEmitter.event;
37
+ }
38
+
39
+ fire() {
40
+ this._onChangeEmitter.fire(100, 'hello');
41
+ }
42
+
43
+ fireCustom(...args: [number, string]) {
44
+ this._onChangeEmitter.fire(...args);
45
+ }
46
+ }
47
+
48
+ describe('Emitter', () => {
49
+ test('Listen and unlisten event', () => {
50
+ const foo = new Foo();
51
+ let count = 0;
52
+ const disposable = foo.onChange(() => {
53
+ count++;
54
+ });
55
+
56
+ foo.fire();
57
+ foo.fire();
58
+ expect(count).toBe(2);
59
+
60
+ disposable.dispose();
61
+ foo.fire();
62
+ foo.fire();
63
+ expect(count).toBe(2);
64
+ });
65
+
66
+ test('Listen, unlisten, and re-listen event', () => {
67
+ const foo = new Foo();
68
+ let count = 0;
69
+
70
+ const disposable1 = foo.onChange(() => {
71
+ count++;
72
+ });
73
+ // Initially has one listener, can trigger normally
74
+ foo.fire();
75
+ expect(count).toBe(1);
76
+
77
+ const disposable2 = foo.onChange(() => {
78
+ count++;
79
+ });
80
+ // Now has two listeners, can still trigger normally
81
+ foo.fire();
82
+ expect(count).toBe(3);
83
+
84
+ disposable1.dispose();
85
+ // One listener removed, back to one listener, can still trigger normally
86
+ foo.fire();
87
+ expect(count).toBe(4);
88
+
89
+ disposable2.dispose();
90
+ // No listeners left, won't trigger
91
+ foo.fire();
92
+ expect(count).toBe(4);
93
+ });
94
+
95
+ test('listen to event only once', () => {
96
+ const foo = new Foo();
97
+ let count = 0;
98
+ listenOnce(foo.onChange)(() => {
99
+ count++;
100
+ });
101
+
102
+ foo.fire();
103
+ foo.fire();
104
+ expect(count).toBe(1);
105
+ });
106
+
107
+ test('listenWhen should only fire once when the predicate returns true', () => {
108
+ const foo2 = new Foo2();
109
+ let result = 0;
110
+ let count = 0;
111
+
112
+ listenWhen(foo2.onChange, (num) => {
113
+ return num === 200;
114
+ })((num) => {
115
+ count++;
116
+ result = num;
117
+ });
118
+
119
+ foo2.fire();
120
+ foo2.fireCustom(200, 'Yes');
121
+ foo2.fire();
122
+
123
+ expect(count).toBe(1);
124
+ expect(result).toBe(200);
125
+ });
126
+
127
+ test('multiple parameters', () => {
128
+ const foo = new Foo2();
129
+ let code;
130
+ let msg;
131
+ foo.onChange((c, m) => {
132
+ code = c;
133
+ msg = m;
134
+ });
135
+ foo.fire();
136
+
137
+ expect(code).toBe(100);
138
+ expect(msg).toBe('hello');
139
+ });
140
+ });
141
+
142
+ describe('ErrorHandler', () => {
143
+ beforeEach(() => {
144
+ vi.useFakeTimers();
145
+ });
146
+
147
+ afterEach(() => {
148
+ vi.useRealTimers();
149
+ });
150
+ test('async error', () => {
151
+ const foo = new Foo();
152
+ let count = 0;
153
+ foo.onChange(() => {
154
+ throw new Error('error');
155
+ });
156
+ foo.onChange(() => {
157
+ count++;
158
+ });
159
+
160
+ foo.fire();
161
+ expect(count).toBe(1);
162
+ });
163
+
164
+ test('sync error', () => {
165
+ const foo = new Foo(syncUnexpectedError);
166
+ let count = 0;
167
+ foo.onChange(() => {
168
+ throw new Error('error');
169
+ });
170
+ foo.onChange(() => {
171
+ count++;
172
+ });
173
+
174
+ expect(() => foo.fire()).toThrowError();
175
+ expect(count).toBe(0);
176
+ });
177
+
178
+ test('ignore error', () => {
179
+ const foo = new Foo(ignoreUnexpectedError);
180
+ let count = 0;
181
+ foo.onChange(() => {
182
+ throw new Error('error');
183
+ });
184
+ foo.onChange(() => {
185
+ count++;
186
+ });
187
+
188
+ foo.fire();
189
+ expect(count).toBe(1);
190
+ });
191
+ });
@@ -0,0 +1,162 @@
1
+ import { makeSafeDisposable } from '@/dispose';
2
+ import type { IDisposable } from '@/dispose';
3
+ import { DisposableLinkedList } from './disposable-linked-list';
4
+ import { asyncUnexpectedError } from './error-handler';
5
+
6
+ export interface EmitterOptions {
7
+ onAddListener?: (...args: any) => any;
8
+ onRemoveListener?: (...args: any) => any;
9
+ onListenerError?: (e: any) => void;
10
+ }
11
+
12
+ //
13
+ // 事件监听中的回调实体
14
+ //
15
+ class Listener<TArgs extends any[]> {
16
+ private readonly _callback: (...args: TArgs) => void;
17
+ private readonly _callbackThis: any | undefined;
18
+
19
+ constructor(callback: (...args: TArgs) => void, callbackThis: any | undefined) {
20
+ this._callback = callback;
21
+ this._callbackThis = callbackThis;
22
+ }
23
+
24
+ invoke(...args: TArgs): void {
25
+ this._callback.call(this._callbackThis, ...args);
26
+ }
27
+ }
28
+
29
+ //
30
+ // 存放在EventDeliveryQueue中的元素
31
+ //
32
+ class EventDeliveryQueueElement<TArgs extends any[]> {
33
+ readonly emitter: Emitter<TArgs>;
34
+ readonly listener: Listener<TArgs>;
35
+ readonly event: TArgs;
36
+ constructor(emitter: Emitter<TArgs>, listener: Listener<TArgs>, event: TArgs) {
37
+ this.emitter = emitter;
38
+ this.listener = listener;
39
+ this.event = event;
40
+ }
41
+ }
42
+
43
+ export class EventDeliveryQueue {
44
+ protected _queue = new DisposableLinkedList<EventDeliveryQueueElement<any>>();
45
+
46
+ constructor(private readonly _onListenerError: (e: unknown) => void = asyncUnexpectedError) {}
47
+
48
+ get size(): number {
49
+ return this._queue.size;
50
+ }
51
+
52
+ push<TArgs extends any[]>(emitter: Emitter<TArgs>, listener: Listener<TArgs>, event: TArgs): void {
53
+ this._queue.push(new EventDeliveryQueueElement(emitter, listener, event));
54
+ }
55
+
56
+ clear<TArgs extends any[]>(emitter: Emitter<TArgs>): void {
57
+ const newQueue = new DisposableLinkedList<EventDeliveryQueueElement<TArgs>>();
58
+ for (const element of this._queue) {
59
+ if (element.emitter !== emitter) {
60
+ newQueue.push(element);
61
+ }
62
+ }
63
+ this._queue = newQueue;
64
+ }
65
+
66
+ deliver(): void {
67
+ while (this._queue.size > 0) {
68
+ const element = this._queue.shift()!;
69
+ try {
70
+ element.listener.invoke(...element.event);
71
+ } catch (e) {
72
+ this._onListenerError(e);
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ export interface Event<T extends any[]> {
79
+ (listener: (...args: T) => any, thisArgs?: any): IDisposable;
80
+ }
81
+
82
+ export class Emitter<TArgs extends any[]> {
83
+ protected _listeners?: DisposableLinkedList<Listener<TArgs>>;
84
+ private readonly _options?: EmitterOptions;
85
+ private _disposed = false;
86
+ private _event?: Event<TArgs>;
87
+ private _deliveryQueue?: EventDeliveryQueue;
88
+
89
+ constructor(options?: EmitterOptions) {
90
+ this._options = options;
91
+ }
92
+
93
+ get event(): Event<TArgs> {
94
+ if (this._event) {
95
+ return this._event;
96
+ }
97
+
98
+ this._event = (callback: (...args: TArgs) => any, thisArgs?: any): IDisposable => {
99
+ const listener = new Listener(callback, thisArgs);
100
+
101
+ if (!this._listeners) {
102
+ this._listeners = new DisposableLinkedList();
103
+ }
104
+
105
+ const removeListener = this._listeners.pushAndGetDisposableNode(listener);
106
+
107
+ if (this._options?.onAddListener) {
108
+ this._options.onAddListener(this, callback, thisArgs);
109
+ }
110
+
111
+ // 生成可销毁函数返回
112
+ const result = () => {
113
+ if (!this._disposed) {
114
+ removeListener();
115
+ if (this._options?.onRemoveListener) {
116
+ this._options.onRemoveListener(this, callback, thisArgs);
117
+ }
118
+ }
119
+ };
120
+
121
+ return makeSafeDisposable(result);
122
+ };
123
+
124
+ return this._event;
125
+ }
126
+
127
+ dispose(): void {
128
+ if (this._disposed) {
129
+ return;
130
+ }
131
+ this._disposed = true;
132
+ this._listeners?.clear();
133
+ this._deliveryQueue?.clear(this);
134
+ }
135
+
136
+ fire(...event: TArgs): void {
137
+ if (!this._listeners || this._listeners.size === 0) {
138
+ return;
139
+ }
140
+ // 绝大部分场景事件只会有一个监听器,针对性进行性能优化,没必要构造DeliveryQueue结构
141
+ if (this._listeners.size === 1) {
142
+ const listener = this._listeners.firstNode!;
143
+ try {
144
+ listener.value.invoke(...event);
145
+ } catch (e) {
146
+ if (this._options?.onListenerError) {
147
+ this._options.onListenerError(e);
148
+ } else {
149
+ asyncUnexpectedError(e);
150
+ }
151
+ }
152
+ return;
153
+ }
154
+
155
+ this._deliveryQueue ??= new EventDeliveryQueue(this._options?.onListenerError);
156
+
157
+ for (const listener of this._listeners) {
158
+ this._deliveryQueue.push(this, listener, event);
159
+ }
160
+ this._deliveryQueue.deliver();
161
+ }
162
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 针对未捕获的错误,异步抛出,不阻塞事件响应主流程
3
+ * 默认模式
4
+ */
5
+ export function asyncUnexpectedError(e: any): void {
6
+ setTimeout(() => {
7
+ throw e;
8
+ }, 0);
9
+ }
10
+
11
+ /**
12
+ * 针对未捕获的错误,同步抛出,阻塞事件响应主流程
13
+ */
14
+ export function syncUnexpectedError(e: any): void {
15
+ throw e;
16
+ }
17
+
18
+ /**
19
+ * 针对未捕获的错误,静默掉,不进行处理
20
+ */
21
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
22
+ export function ignoreUnexpectedError(e: any): void {}
@@ -0,0 +1,34 @@
1
+ // 事件触发器
2
+ export { Emitter } from './emitter';
3
+ // 事件监听
4
+ export type { Event } from './emitter';
5
+
6
+ // 状态扭转事件触发器
7
+ // 需要绑定生命周期,默认提供事件补发
8
+ export { PhaseEmitter } from './phase-emitter';
9
+ // 状态扭转事件监听
10
+ export type { PhaseEvent } from './phase-emitter';
11
+ export type { WhenPhaseEvent } from './phase-emitter';
12
+
13
+ // 快捷事件类型
14
+ export { ShortcutEventMode } from './phase-emitter';
15
+ // 辅助函数,生成快捷事件
16
+ // 生成一个异步快捷事件
17
+ export { makeAsyncShortcutEvent } from './shortcut-event-utils';
18
+ // 生成一个同步快捷事件
19
+ export { makeSyncShortcutEvent } from './shortcut-event-utils';
20
+
21
+ // 辅助函数,监听事件一次后解绑
22
+ export { listenOnce } from './once';
23
+ // 辅助函数,监听事件满足回调后解绑
24
+ export { listenWhen } from './when';
25
+ // 辅助常量,空事件监听,永不触发
26
+ export { NeverEvent } from './utils';
27
+
28
+ // 针对事件抛出过程中,遇到错误,对应的处理方案(构建Emitter对象中参数)
29
+ // 异步抛出错误
30
+ export { asyncUnexpectedError } from './error-handler';
31
+ // 同步抛出错误
32
+ export { syncUnexpectedError } from './error-handler';
33
+ // 忽略错误
34
+ export { ignoreUnexpectedError } from './error-handler';
@@ -0,0 +1,29 @@
1
+ import type { Event } from './emitter';
2
+ import type { IDisposable } from '@/dispose';
3
+
4
+ // 辅助能力:只监听某个事件一次
5
+ export function listenOnce<TArgs extends any[]>(event: Event<TArgs>): Event<TArgs> {
6
+ return (listener, thisArgs = null) => {
7
+ let didFire = false;
8
+ // 必须这样写,事件可能同步触发
9
+ // eslint-disable-next-line no-undef-init
10
+ let result: IDisposable | undefined = undefined;
11
+ result = event((...args) => {
12
+ if (didFire) {
13
+ return;
14
+ } else if (result) {
15
+ result.dispose();
16
+ } else {
17
+ didFire = true;
18
+ }
19
+
20
+ return listener.call(thisArgs, ...args);
21
+ }, null);
22
+
23
+ if (didFire) {
24
+ result.dispose();
25
+ }
26
+
27
+ return result;
28
+ };
29
+ }
@@ -0,0 +1,212 @@
1
+ import { PhaseEmitter, type IPhaseChecker, type WhenPhaseEvent } from './phase-emitter';
2
+ import type { Event } from './emitter';
3
+
4
+ enum Phase {
5
+ Aaa,
6
+ Bbb,
7
+ Ccc,
8
+ }
9
+
10
+ class Lifecycle {
11
+ readonly onBbb: Event<[]>;
12
+ readonly onCcc: Event<[]>;
13
+ readonly whenPhase: WhenPhaseEvent<Phase>;
14
+ readonly onPhaseChange: Event<[Phase]>;
15
+ private readonly _phaseEmitter: PhaseEmitter<Phase>;
16
+
17
+ constructor(checker?: IPhaseChecker<Phase>) {
18
+ this._phaseEmitter = new PhaseEmitter(Phase.Aaa, {
19
+ checker,
20
+ });
21
+ this.onBbb = this._phaseEmitter.when(Phase.Bbb);
22
+ this.onCcc = this._phaseEmitter.when(Phase.Ccc);
23
+ this.whenPhase = this._phaseEmitter.whenPhase;
24
+ this.onPhaseChange = this._phaseEmitter.event;
25
+ }
26
+
27
+ toB() {
28
+ this._phaseEmitter.setPhase(Phase.Bbb);
29
+ }
30
+
31
+ toC() {
32
+ this._phaseEmitter.setPhase(Phase.Ccc);
33
+ }
34
+ }
35
+
36
+ describe('PhaseEmitter', () => {
37
+ beforeEach(() => {
38
+ vi.useFakeTimers();
39
+ });
40
+
41
+ test('listens to state transition 1', () => {
42
+ const lifecycle = new Lifecycle();
43
+ let count = 0;
44
+ lifecycle.onBbb(() => {
45
+ count += 1;
46
+ });
47
+ lifecycle.onCcc(() => {
48
+ count += 10;
49
+ });
50
+
51
+ lifecycle.toB();
52
+ lifecycle.toC();
53
+ expect(count).toBe(11);
54
+ });
55
+
56
+ test('listens to state transition 2', () => {
57
+ const lifecycle = new Lifecycle();
58
+ let count = 0;
59
+ lifecycle.whenPhase(Phase.Bbb, () => {
60
+ count += 1;
61
+ });
62
+ lifecycle.whenPhase(Phase.Ccc, () => {
63
+ count += 10;
64
+ });
65
+
66
+ lifecycle.toB();
67
+ lifecycle.toC();
68
+ expect(count).toBe(11);
69
+ });
70
+
71
+ test('listens to state transition 3', () => {
72
+ const lifecycle = new Lifecycle();
73
+ let count = 0;
74
+ lifecycle.onPhaseChange((phase) => {
75
+ if (phase === Phase.Bbb) {
76
+ count += 1;
77
+ } else if (phase === Phase.Ccc) {
78
+ count += 10;
79
+ }
80
+ });
81
+
82
+ lifecycle.toB();
83
+ lifecycle.toC();
84
+ expect(count).toBe(11);
85
+ });
86
+
87
+ test('state replay 1', async () => {
88
+ const lifecycle = new Lifecycle();
89
+ lifecycle.toB();
90
+ lifecycle.toC();
91
+
92
+ // Since final state is C, only state C will be replayed
93
+ let count = 0;
94
+ lifecycle.onBbb(() => {
95
+ count += 1;
96
+ });
97
+ lifecycle.onCcc(() => {
98
+ count += 10;
99
+ });
100
+
101
+ vi.advanceTimersByTime(6);
102
+ expect(count).toBe(10);
103
+ });
104
+
105
+ test('state replay 2', async () => {
106
+ const lifecycle = new Lifecycle();
107
+ lifecycle.toC();
108
+ lifecycle.toB();
109
+
110
+ // Since final state is B, only state B will be replayed
111
+ let count = 0;
112
+ lifecycle.whenPhase(Phase.Bbb, () => {
113
+ count += 1;
114
+ });
115
+ lifecycle.whenPhase(Phase.Ccc, () => {
116
+ count += 10;
117
+ });
118
+
119
+ vi.advanceTimersByTime(6);
120
+ expect(count).toBe(1);
121
+ });
122
+
123
+ test('state replay 3', async () => {
124
+ const lifecycle = new Lifecycle();
125
+ lifecycle.toB();
126
+ lifecycle.toC();
127
+
128
+ // Since we're listening to state changes, no replay will occur
129
+ let count = 0;
130
+ lifecycle.onPhaseChange((phase) => {
131
+ if (phase === Phase.Bbb) {
132
+ count += 1;
133
+ } else if (phase === Phase.Ccc) {
134
+ count += 10;
135
+ }
136
+ });
137
+
138
+ vi.advanceTimersByTime(6);
139
+ expect(count).toBe(0);
140
+ });
141
+
142
+ test('state replay 4', async () => {
143
+ const lifecycle = new Lifecycle();
144
+ // Listen and replay trigger simultaneously
145
+ let count = 0;
146
+ lifecycle.whenPhase(Phase.Bbb, () => {
147
+ count += 1;
148
+ });
149
+
150
+ lifecycle.toB();
151
+ lifecycle.toC();
152
+
153
+ lifecycle.whenPhase(Phase.Ccc, () => {
154
+ count += 10;
155
+ });
156
+
157
+ vi.advanceTimersByTime(6);
158
+ expect(count).toBe(11);
159
+ });
160
+
161
+ test('state replay 5', async () => {
162
+ const lifecycle = new Lifecycle();
163
+ // Multiple replay triggers
164
+ let count = 0;
165
+ lifecycle.toB();
166
+ lifecycle.whenPhase(Phase.Bbb, () => {
167
+ count += 10;
168
+ });
169
+ lifecycle.onBbb(() => {
170
+ count += 10;
171
+ });
172
+
173
+ lifecycle.toC();
174
+ lifecycle.whenPhase(Phase.Ccc, () => {
175
+ count += 10;
176
+ });
177
+ lifecycle.onCcc(() => {
178
+ count += 10;
179
+ });
180
+
181
+ vi.advanceTimersByTime(6);
182
+ expect(count).toBe(40);
183
+ });
184
+
185
+ test('repeated transitions do not trigger', () => {
186
+ const lifecycle = new Lifecycle();
187
+ let count = 0;
188
+ lifecycle.onBbb(() => {
189
+ count += 1;
190
+ });
191
+
192
+ lifecycle.toB();
193
+ lifecycle.toB();
194
+ expect(count).toBe(1);
195
+ });
196
+
197
+ test('invalid transition throws error', () => {
198
+ const lifecycle = new Lifecycle((before, after) => {
199
+ return after > before;
200
+ });
201
+ let count = 0;
202
+ lifecycle.onBbb(() => {
203
+ count += 1;
204
+ });
205
+
206
+ lifecycle.toC();
207
+ expect(() => {
208
+ lifecycle.toB();
209
+ }).toThrowError();
210
+ expect(count).toBe(0);
211
+ });
212
+ });