@planet-matrix/mobius-model 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +123 -36
  3. package/dist/index.js +45 -4
  4. package/dist/index.js.map +183 -11
  5. package/oxlint.config.ts +6 -0
  6. package/package.json +16 -10
  7. package/src/abort/README.md +92 -0
  8. package/src/abort/abort-manager.ts +278 -0
  9. package/src/abort/abort-signal-listener-manager.ts +81 -0
  10. package/src/abort/index.ts +2 -0
  11. package/src/basic/README.md +69 -118
  12. package/src/basic/function.ts +81 -62
  13. package/src/basic/is.ts +152 -71
  14. package/src/basic/promise.ts +29 -8
  15. package/src/basic/string.ts +2 -33
  16. package/src/color/README.md +105 -0
  17. package/src/color/index.ts +3 -0
  18. package/src/color/internal.ts +42 -0
  19. package/src/color/rgb/analyze.ts +236 -0
  20. package/src/color/rgb/construct.ts +130 -0
  21. package/src/color/rgb/convert.ts +227 -0
  22. package/src/color/rgb/derive.ts +303 -0
  23. package/src/color/rgb/index.ts +6 -0
  24. package/src/color/rgb/internal.ts +208 -0
  25. package/src/color/rgb/parse.ts +302 -0
  26. package/src/color/rgb/serialize.ts +144 -0
  27. package/src/color/types.ts +57 -0
  28. package/src/color/xyz/analyze.ts +80 -0
  29. package/src/color/xyz/construct.ts +19 -0
  30. package/src/color/xyz/convert.ts +71 -0
  31. package/src/color/xyz/index.ts +3 -0
  32. package/src/color/xyz/internal.ts +23 -0
  33. package/src/css/README.md +93 -0
  34. package/src/css/class.ts +559 -0
  35. package/src/css/index.ts +1 -0
  36. package/src/encoding/README.md +66 -79
  37. package/src/encoding/base64.ts +13 -4
  38. package/src/environment/README.md +97 -0
  39. package/src/environment/basic.ts +26 -0
  40. package/src/environment/device.ts +311 -0
  41. package/src/environment/feature.ts +285 -0
  42. package/src/environment/geo.ts +337 -0
  43. package/src/environment/index.ts +7 -0
  44. package/src/environment/runtime.ts +400 -0
  45. package/src/environment/snapshot.ts +60 -0
  46. package/src/environment/variable.ts +239 -0
  47. package/src/event/README.md +90 -0
  48. package/src/event/class-event-proxy.ts +228 -0
  49. package/src/event/common.ts +19 -0
  50. package/src/event/event-manager.ts +203 -0
  51. package/src/event/index.ts +4 -0
  52. package/src/event/instance-event-proxy.ts +186 -0
  53. package/src/event/internal.ts +24 -0
  54. package/src/exception/README.md +96 -0
  55. package/src/exception/browser.ts +219 -0
  56. package/src/exception/index.ts +4 -0
  57. package/src/exception/nodejs.ts +169 -0
  58. package/src/exception/normalize.ts +106 -0
  59. package/src/exception/types.ts +99 -0
  60. package/src/identifier/README.md +92 -0
  61. package/src/identifier/id.ts +119 -0
  62. package/src/identifier/index.ts +2 -0
  63. package/src/identifier/uuid.ts +187 -0
  64. package/src/index.ts +16 -1
  65. package/src/log/README.md +79 -0
  66. package/src/log/index.ts +5 -0
  67. package/src/log/log-emitter.ts +72 -0
  68. package/src/log/log-record.ts +10 -0
  69. package/src/log/log-scheduler.ts +74 -0
  70. package/src/log/log-type.ts +8 -0
  71. package/src/log/logger.ts +543 -0
  72. package/src/orchestration/README.md +89 -0
  73. package/src/orchestration/coordination/barrier.ts +214 -0
  74. package/src/orchestration/coordination/count-down-latch.ts +215 -0
  75. package/src/orchestration/coordination/errors.ts +98 -0
  76. package/src/orchestration/coordination/index.ts +16 -0
  77. package/src/orchestration/coordination/internal/wait-constraints.ts +95 -0
  78. package/src/orchestration/coordination/internal/wait-queue.ts +109 -0
  79. package/src/orchestration/coordination/keyed-lock.ts +168 -0
  80. package/src/orchestration/coordination/mutex.ts +257 -0
  81. package/src/orchestration/coordination/permit.ts +127 -0
  82. package/src/orchestration/coordination/read-write-lock.ts +444 -0
  83. package/src/orchestration/coordination/semaphore.ts +280 -0
  84. package/src/orchestration/index.ts +1 -0
  85. package/src/random/README.md +55 -86
  86. package/src/random/index.ts +1 -1
  87. package/src/random/string.ts +35 -0
  88. package/src/reactor/README.md +4 -0
  89. package/src/reactor/reactor-core/primitive.ts +9 -9
  90. package/src/reactor/reactor-core/reactive-system.ts +5 -5
  91. package/src/singleton/README.md +79 -0
  92. package/src/singleton/factory.ts +55 -0
  93. package/src/singleton/index.ts +2 -0
  94. package/src/singleton/manager.ts +204 -0
  95. package/src/storage/README.md +107 -0
  96. package/src/storage/index.ts +1 -0
  97. package/src/storage/table.ts +449 -0
  98. package/src/timer/README.md +86 -0
  99. package/src/timer/expiration/expiration-manager.ts +594 -0
  100. package/src/timer/expiration/index.ts +3 -0
  101. package/src/timer/expiration/min-heap.ts +208 -0
  102. package/src/timer/expiration/remaining-manager.ts +241 -0
  103. package/src/timer/index.ts +1 -0
  104. package/src/type/README.md +54 -307
  105. package/src/type/class.ts +2 -2
  106. package/src/type/index.ts +14 -14
  107. package/src/type/is.ts +265 -2
  108. package/src/type/object.ts +37 -0
  109. package/src/type/string.ts +7 -2
  110. package/src/type/tuple.ts +6 -6
  111. package/src/type/union.ts +16 -0
  112. package/src/web/README.md +77 -0
  113. package/src/web/capture.ts +35 -0
  114. package/src/web/clipboard.ts +97 -0
  115. package/src/web/dom.ts +117 -0
  116. package/src/web/download.ts +16 -0
  117. package/src/web/event.ts +46 -0
  118. package/src/web/index.ts +10 -0
  119. package/src/web/local-storage.ts +113 -0
  120. package/src/web/location.ts +28 -0
  121. package/src/web/permission.ts +172 -0
  122. package/src/web/script-loader.ts +432 -0
  123. package/tests/unit/abort/abort-manager.spec.ts +225 -0
  124. package/tests/unit/abort/abort-signal-listener-manager.spec.ts +62 -0
  125. package/tests/unit/basic/array.spec.ts +1 -1
  126. package/tests/unit/basic/stream.spec.ts +1 -1
  127. package/tests/unit/basic/string.spec.ts +0 -9
  128. package/tests/unit/color/rgb/analyze.spec.ts +110 -0
  129. package/tests/unit/color/rgb/construct.spec.ts +56 -0
  130. package/tests/unit/color/rgb/convert.spec.ts +60 -0
  131. package/tests/unit/color/rgb/derive.spec.ts +103 -0
  132. package/tests/unit/color/rgb/parse.spec.ts +66 -0
  133. package/tests/unit/color/rgb/serialize.spec.ts +46 -0
  134. package/tests/unit/color/xyz/analyze.spec.ts +33 -0
  135. package/tests/unit/color/xyz/construct.spec.ts +10 -0
  136. package/tests/unit/color/xyz/convert.spec.ts +18 -0
  137. package/tests/unit/css/class.spec.ts +157 -0
  138. package/tests/unit/environment/basic.spec.ts +20 -0
  139. package/tests/unit/environment/device.spec.ts +146 -0
  140. package/tests/unit/environment/feature.spec.ts +388 -0
  141. package/tests/unit/environment/geo.spec.ts +111 -0
  142. package/tests/unit/environment/runtime.spec.ts +364 -0
  143. package/tests/unit/environment/snapshot.spec.ts +4 -0
  144. package/tests/unit/environment/variable.spec.ts +190 -0
  145. package/tests/unit/event/class-event-proxy.spec.ts +225 -0
  146. package/tests/unit/event/event-manager.spec.ts +246 -0
  147. package/tests/unit/event/instance-event-proxy.spec.ts +187 -0
  148. package/tests/unit/exception/browser.spec.ts +213 -0
  149. package/tests/unit/exception/nodejs.spec.ts +144 -0
  150. package/tests/unit/exception/normalize.spec.ts +57 -0
  151. package/tests/unit/identifier/id.spec.ts +71 -0
  152. package/tests/unit/identifier/uuid.spec.ts +85 -0
  153. package/tests/unit/log/log-emitter.spec.ts +33 -0
  154. package/tests/unit/log/log-scheduler.spec.ts +40 -0
  155. package/tests/unit/log/log-type.spec.ts +7 -0
  156. package/tests/unit/log/logger.spec.ts +222 -0
  157. package/tests/unit/orchestration/coordination/barrier.spec.ts +96 -0
  158. package/tests/unit/orchestration/coordination/count-down-latch.spec.ts +63 -0
  159. package/tests/unit/orchestration/coordination/errors.spec.ts +29 -0
  160. package/tests/unit/orchestration/coordination/keyed-lock.spec.ts +109 -0
  161. package/tests/unit/orchestration/coordination/mutex.spec.ts +132 -0
  162. package/tests/unit/orchestration/coordination/permit.spec.ts +43 -0
  163. package/tests/unit/orchestration/coordination/read-write-lock.spec.ts +154 -0
  164. package/tests/unit/orchestration/coordination/semaphore.spec.ts +135 -0
  165. package/tests/unit/random/string.spec.ts +11 -0
  166. package/tests/unit/reactor/alien-signals-effect.spec.ts +11 -10
  167. package/tests/unit/reactor/preact-signal.spec.ts +1 -2
  168. package/tests/unit/singleton/singleton.spec.ts +49 -0
  169. package/tests/unit/storage/table.spec.ts +620 -0
  170. package/tests/unit/timer/expiration/expiration-manager.spec.ts +464 -0
  171. package/tests/unit/timer/expiration/min-heap.spec.ts +71 -0
  172. package/tests/unit/timer/expiration/remaining-manager.spec.ts +234 -0
  173. package/.oxlintrc.json +0 -5
  174. package/src/random/uuid.ts +0 -103
  175. package/tests/unit/random/uuid.spec.ts +0 -37
@@ -0,0 +1,214 @@
1
+ import type { InternalWaitQueueEntry } from "./internal/wait-queue.ts"
2
+ import type { InternalWaitConstraintsEntry, InternalWaitConstraintsOptions } from "./internal/wait-constraints.ts"
3
+
4
+ import { InternalWaitQueue } from "./internal/wait-queue.ts"
5
+ import { BrokenBarrierError, CoordinationAbortError } from "./errors.ts"
6
+ import { internalAssertTimeoutOption, internalBindWaitConstraints, internalCleanupWaitConstraints } from "./internal/wait-constraints.ts"
7
+
8
+ /**
9
+ * 断言栅栏的参与者数量是一个有效的正整数。
10
+ */
11
+ const internalAssertParticipantCount = (participantCount: number): void => {
12
+ if (
13
+ Number.isFinite(participantCount) === false
14
+ || Number.isInteger(participantCount) === false
15
+ || participantCount <= 0
16
+ ) {
17
+ throw new RangeError("Barrier participantCount must be a finite integer greater than 0.")
18
+ }
19
+ }
20
+
21
+ /**
22
+ * 表示某一代栅栏中单个等待者对应的内部节点。
23
+ *
24
+ * 每个节点既要能挂入等待队列,也要能绑定超时与中止约束,最终在整代完成或整代破坏时统一结算。
25
+ */
26
+ interface InternalBarrierQueueEntry extends InternalWaitQueueEntry<InternalBarrierQueueEntry>, InternalWaitConstraintsEntry {
27
+ resolve: (generation: number) => void
28
+ reject: (reason: unknown) => void
29
+ isSettled: boolean
30
+ }
31
+
32
+ /**
33
+ * 表示调用 `Barrier.signalAndWait()` 时可选的等待约束。
34
+ */
35
+ export interface BarrierWaitOptions extends InternalWaitConstraintsOptions {
36
+ /**
37
+ * 单次等待允许持续的最长时间,单位为毫秒。
38
+ */
39
+ timeout?: number | undefined
40
+
41
+ /**
42
+ * 用于主动中止当前等待的信号。
43
+ */
44
+ abortSignal?: AbortSignal | undefined
45
+ }
46
+
47
+ /**
48
+ * 表示一个循环栅栏,用于让固定数量的参与者在同一同步点会合。
49
+ *
50
+ * 每次当所有参与者都调用 `signalAndWait()` 后,当前这一代会被一次性完成,
51
+ * 所有等待者都会收到相同的代号,然后栅栏自动进入下一代继续复用。
52
+ */
53
+ export class Barrier {
54
+ private readonly queue: InternalWaitQueue<InternalBarrierQueueEntry>
55
+
56
+ private internalArrivedCount: number
57
+
58
+ private internalGeneration: number
59
+
60
+ private readonly participantCount: number
61
+
62
+ /**
63
+ * 创建一个需要固定参与者数量才能完成一代的栅栏。
64
+ */
65
+ constructor(participantCount: number) {
66
+ internalAssertParticipantCount(participantCount)
67
+
68
+ this.queue = new InternalWaitQueue<InternalBarrierQueueEntry>()
69
+ this.internalArrivedCount = 0
70
+ this.internalGeneration = 0
71
+ this.participantCount = participantCount
72
+ }
73
+
74
+ /**
75
+ * 读取每一代完成所需的参与者总数。
76
+ */
77
+ getParticipantCount(): number {
78
+ return this.participantCount
79
+ }
80
+
81
+ /**
82
+ * 读取当前所处的代号。
83
+ *
84
+ * 该值会在一代正常完成或被破坏后递增。
85
+ */
86
+ getGeneration(): number {
87
+ return this.internalGeneration
88
+ }
89
+
90
+ /**
91
+ * 读取当前这一代中仍在等待其余参与者的任务数量。
92
+ */
93
+ getPendingCount(): number {
94
+ return this.queue.getCount()
95
+ }
96
+
97
+ /**
98
+ * 读取当前这一代距离完成还差多少参与者。
99
+ */
100
+ getRemainingCount(): number {
101
+ return this.participantCount - this.internalArrivedCount
102
+ }
103
+
104
+ /**
105
+ * 以成功状态完成当前代。
106
+ *
107
+ * 完成时会先重置计数并推进代号,再唤醒所有尚未结算的等待者,确保下一代可以立即开始接收新的参与者。
108
+ */
109
+ private completeGeneration(completedGeneration: number): void {
110
+ this.internalArrivedCount = 0
111
+ this.internalGeneration = this.internalGeneration + 1
112
+
113
+ while (this.queue.isEmpty() === false) {
114
+ const entry = this.queue.shift()
115
+ if (entry === undefined) {
116
+ return
117
+ }
118
+
119
+ if (entry.isSettled === true) {
120
+ continue
121
+ }
122
+
123
+ entry.isSettled = true
124
+ internalCleanupWaitConstraints(entry)
125
+ entry.resolve(completedGeneration)
126
+ }
127
+ }
128
+
129
+ /**
130
+ * 以失败状态破坏当前代。
131
+ *
132
+ * 触发 abort 或 timeout 的参与者收到其原始错误,其余同代等待者统一收到 `BrokenBarrierError`,
133
+ * 这样调用方可以区分“我自己失败了”与“别人导致这一代失效”。
134
+ */
135
+ private breakGeneration(failingEntry: InternalBarrierQueueEntry, reason: unknown): void {
136
+ if (failingEntry.isSettled === true) {
137
+ return
138
+ }
139
+
140
+ const brokenReason = new BrokenBarrierError(reason)
141
+ this.internalArrivedCount = 0
142
+ this.internalGeneration = this.internalGeneration + 1
143
+
144
+ while (this.queue.isEmpty() === false) {
145
+ const entry = this.queue.shift()
146
+ if (entry === undefined) {
147
+ return
148
+ }
149
+
150
+ if (entry.isSettled === true) {
151
+ continue
152
+ }
153
+
154
+ entry.isSettled = true
155
+ internalCleanupWaitConstraints(entry)
156
+
157
+ if (entry === failingEntry) {
158
+ entry.reject(reason)
159
+ }
160
+ else {
161
+ entry.reject(brokenReason)
162
+ }
163
+ }
164
+ }
165
+
166
+ /**
167
+ * 声明当前参与者已经到达同步点,并等待整代完成。
168
+ *
169
+ * 返回值是本次完成时对应的代号;如果本调用导致最后一个参与者到达,则会立即完成整代并同步返回。
170
+ */
171
+ async signalAndWait(options: BarrierWaitOptions = {}): Promise<number> {
172
+ internalAssertTimeoutOption("Barrier wait", options.timeout)
173
+
174
+ if (options.abortSignal?.aborted === true) {
175
+ throw new CoordinationAbortError("Barrier wait", options.abortSignal.reason)
176
+ }
177
+
178
+ const currentGeneration = this.internalGeneration
179
+ this.internalArrivedCount = this.internalArrivedCount + 1
180
+
181
+ if (this.internalArrivedCount === this.participantCount) {
182
+ this.completeGeneration(currentGeneration)
183
+ return currentGeneration
184
+ }
185
+
186
+ const generation = await new Promise<number>((resolve, reject) => {
187
+ const entry: InternalBarrierQueueEntry = {
188
+ resolve,
189
+ reject,
190
+ isSettled: false,
191
+ isQueued: false,
192
+ previous: undefined,
193
+ next: undefined,
194
+ timeoutId: undefined,
195
+ abortSignal: undefined,
196
+ abortListener: undefined,
197
+ }
198
+
199
+ internalBindWaitConstraints(entry, options, {
200
+ abortOperation: "Barrier wait",
201
+ timeoutOperation: "Barrier wait",
202
+ onAbort: error => {
203
+ this.breakGeneration(entry, error)
204
+ },
205
+ onTimeout: error => {
206
+ this.breakGeneration(entry, error)
207
+ },
208
+ })
209
+ this.queue.enqueue(entry)
210
+ })
211
+
212
+ return generation
213
+ }
214
+ }
@@ -0,0 +1,215 @@
1
+ import type { InternalWaitQueueEntry } from "./internal/wait-queue.ts"
2
+ import type { InternalWaitConstraintsEntry, InternalWaitConstraintsOptions } from "./internal/wait-constraints.ts"
3
+
4
+ import { InternalWaitQueue } from "./internal/wait-queue.ts"
5
+ import {
6
+ internalAssertTimeoutOption,
7
+ internalBindWaitConstraints,
8
+ internalCleanupWaitConstraints,
9
+ internalThrowIfAborted,
10
+ } from "./internal/wait-constraints.ts"
11
+
12
+ /**
13
+ * 断言闭锁初始计数为合法的非负整数。
14
+ */
15
+ const internalAssertInitialCount = (count: number): void => {
16
+ if (
17
+ Number.isFinite(count) === false
18
+ || Number.isInteger(count) === false
19
+ || count < 0
20
+ ) {
21
+ throw new RangeError("CountDownLatch initialCount must be a finite integer greater than or equal to 0.")
22
+ }
23
+ }
24
+
25
+ /**
26
+ * 断言单次递减步长为合法的正整数。
27
+ */
28
+ const internalAssertDelta = (count: number): void => {
29
+ if (
30
+ Number.isFinite(count) === false
31
+ || Number.isInteger(count) === false
32
+ || count <= 0
33
+ ) {
34
+ throw new RangeError("CountDownLatch count must be a finite integer greater than 0.")
35
+ }
36
+ }
37
+
38
+ /**
39
+ * 表示 `CountDownLatch` 等待者在内部队列中的节点。
40
+ */
41
+ interface InternalCountDownLatchQueueEntry extends InternalWaitQueueEntry<InternalCountDownLatchQueueEntry>, InternalWaitConstraintsEntry {
42
+ resolve: () => void
43
+ reject: (reason: unknown) => void
44
+ isSettled: boolean
45
+ }
46
+
47
+ /**
48
+ * 表示调用 `CountDownLatch.wait()` 时可选的等待约束。
49
+ */
50
+ export interface CountDownLatchWaitOptions extends InternalWaitConstraintsOptions {
51
+ /**
52
+ * 等待闭锁打开的最长时间,单位为毫秒。
53
+ */
54
+ timeout?: number | undefined
55
+
56
+ /**
57
+ * 用于主动取消等待的信号。
58
+ */
59
+ abortSignal?: AbortSignal | undefined
60
+ }
61
+
62
+ /**
63
+ * 表示一个倒计时闭锁。
64
+ *
65
+ * 闭锁在剩余计数降到 0 前保持关闭,所有等待者都会被挂起;
66
+ * 一旦打开,当前及后续等待都会立即通过,且该实例不会再次关闭。
67
+ */
68
+ export class CountDownLatch {
69
+ private readonly queue: InternalWaitQueue<InternalCountDownLatchQueueEntry>
70
+
71
+ private internalRemainingCount: number
72
+
73
+ /**
74
+ * 使用给定初始计数创建闭锁。
75
+ */
76
+ constructor(initialCount: number) {
77
+ internalAssertInitialCount(initialCount)
78
+
79
+ this.queue = new InternalWaitQueue<InternalCountDownLatchQueueEntry>()
80
+ this.internalRemainingCount = initialCount
81
+ }
82
+
83
+ /**
84
+ * 判断闭锁是否已经打开。
85
+ */
86
+ isOpen(): boolean {
87
+ return this.internalRemainingCount === 0
88
+ }
89
+
90
+ /**
91
+ * 以同步方式检测当前是否可以继续执行而无需等待。
92
+ */
93
+ tryWait(): boolean {
94
+ return this.internalRemainingCount === 0
95
+ }
96
+
97
+ /**
98
+ * 读取距离打开闭锁还差多少次 `countDown()`。
99
+ */
100
+ getRemainingCount(): number {
101
+ return this.internalRemainingCount
102
+ }
103
+
104
+ /**
105
+ * 读取当前挂起的等待者数量。
106
+ */
107
+ getPendingCount(): number {
108
+ return this.queue.getCount()
109
+ }
110
+
111
+ /**
112
+ * 在闭锁打开时一次性唤醒全部等待者。
113
+ */
114
+ private resolveAll(): void {
115
+ while (this.queue.isEmpty() === false) {
116
+ const entry = this.queue.shift()
117
+ if (entry === undefined) {
118
+ return
119
+ }
120
+
121
+ if (entry.isSettled === true) {
122
+ continue
123
+ }
124
+
125
+ entry.isSettled = true
126
+ internalCleanupWaitConstraints(entry)
127
+ entry.resolve()
128
+ }
129
+ }
130
+
131
+ /**
132
+ * 以失败状态结束指定等待者,并从队列中摘除。
133
+ */
134
+ private rejectEntry(entry: InternalCountDownLatchQueueEntry, reason: unknown): void {
135
+ if (entry.isSettled === true) {
136
+ return
137
+ }
138
+
139
+ entry.isSettled = true
140
+ internalCleanupWaitConstraints(entry)
141
+ this.queue.remove(entry)
142
+ entry.reject(reason)
143
+ }
144
+
145
+ /**
146
+ * 将闭锁剩余计数减少指定值。
147
+ *
148
+ * 当剩余计数降到 0 时,所有等待者会立即被放行。
149
+ */
150
+ countDown(count: number = 1): number {
151
+ internalAssertDelta(count)
152
+
153
+ if (this.internalRemainingCount === 0) {
154
+ return 0
155
+ }
156
+
157
+ this.internalRemainingCount = Math.max(0, this.internalRemainingCount - count)
158
+
159
+ if (this.internalRemainingCount === 0) {
160
+ this.resolveAll()
161
+ }
162
+
163
+ return this.internalRemainingCount
164
+ }
165
+
166
+ /**
167
+ * `countDown()` 的语义化别名,用于强调“到达一次事件”。
168
+ */
169
+ arrive(count: number = 1): number {
170
+ return this.countDown(count)
171
+ }
172
+
173
+ /**
174
+ * 等待直到闭锁打开。
175
+ */
176
+ async wait(options: CountDownLatchWaitOptions = {}): Promise<void> {
177
+ internalAssertTimeoutOption("CountDownLatch wait", options.timeout)
178
+
179
+ internalThrowIfAborted("CountDownLatch wait", options.abortSignal)
180
+
181
+ if (this.internalRemainingCount === 0) {
182
+ return
183
+ }
184
+
185
+ await new Promise<void>((resolve, reject) => {
186
+ const entry: InternalCountDownLatchQueueEntry = {
187
+ resolve,
188
+ reject,
189
+ isSettled: false,
190
+ isQueued: false,
191
+ previous: undefined,
192
+ next: undefined,
193
+ timeoutId: undefined,
194
+ abortSignal: undefined,
195
+ abortListener: undefined,
196
+ }
197
+
198
+ internalBindWaitConstraints(entry, options, {
199
+ abortOperation: "CountDownLatch wait",
200
+ timeoutOperation: "CountDownLatch wait",
201
+ onAbort: error => {
202
+ this.rejectEntry(entry, error)
203
+ },
204
+ onTimeout: error => {
205
+ this.rejectEntry(entry, error)
206
+ },
207
+ })
208
+ this.queue.enqueue(entry)
209
+
210
+ if (this.internalRemainingCount === 0) {
211
+ this.resolveAll()
212
+ }
213
+ })
214
+ }
215
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * 把可读的失败原因拼接到错误消息中。
3
+ *
4
+ * 这里尽量保留基础类型与 `Error.message`,避免把复杂对象直接序列化成噪音文本。
5
+ */
6
+ const internalAppendReasonMessage = (message: string, reason: unknown): string => {
7
+ if (reason === undefined) {
8
+ return message
9
+ }
10
+
11
+ if (reason instanceof Error && reason.message !== "") {
12
+ return `${message} ${reason.message}`
13
+ }
14
+
15
+ if (
16
+ typeof reason === "string"
17
+ || typeof reason === "number"
18
+ || typeof reason === "boolean"
19
+ || typeof reason === "bigint"
20
+ || typeof reason === "symbol"
21
+ ) {
22
+ return `${message} ${String(reason)}`
23
+ }
24
+
25
+ return message
26
+ }
27
+
28
+ /**
29
+ * 表示协调原语等待过程中的中止错误。
30
+ */
31
+ export class CoordinationAbortError extends Error {
32
+ /**
33
+ * 触发中止的操作名称。
34
+ */
35
+ readonly operation: string
36
+
37
+ /**
38
+ * 中止信号携带的原始原因。
39
+ */
40
+ readonly reason: unknown
41
+
42
+ /**
43
+ * 创建一个表示等待被主动中止的错误。
44
+ */
45
+ constructor(operation: string, reason: unknown) {
46
+ super(internalAppendReasonMessage(`${operation} aborted.`, reason))
47
+ this.name = "CoordinationAbortError"
48
+ Object.setPrototypeOf(this, new.target.prototype)
49
+ this.operation = operation
50
+ this.reason = reason
51
+ }
52
+ }
53
+
54
+ /**
55
+ * 表示协调原语等待过程中的超时错误。
56
+ */
57
+ export class CoordinationTimeoutError extends Error {
58
+ /**
59
+ * 发生超时的操作名称。
60
+ */
61
+ readonly operation: string
62
+
63
+ /**
64
+ * 触发超时时等待的毫秒数。
65
+ */
66
+ readonly timeout: number
67
+
68
+ /**
69
+ * 创建一个表示等待超时的错误。
70
+ */
71
+ constructor(operation: string, timeout: number) {
72
+ super(`${operation} timeout after ${timeout}ms.`)
73
+ this.name = "CoordinationTimeoutError"
74
+ Object.setPrototypeOf(this, new.target.prototype)
75
+ this.operation = operation
76
+ this.timeout = timeout
77
+ }
78
+ }
79
+
80
+ /**
81
+ * 表示栅栏某一代被参与者破坏后的错误。
82
+ */
83
+ export class BrokenBarrierError extends Error {
84
+ /**
85
+ * 导致当前栅栏代失效的原始原因。
86
+ */
87
+ readonly reason: unknown
88
+
89
+ /**
90
+ * 创建一个表示“当前代已被其他参与者破坏”的错误。
91
+ */
92
+ constructor(reason: unknown) {
93
+ super(internalAppendReasonMessage("Barrier generation was broken by another participant.", reason))
94
+ this.name = "BrokenBarrierError"
95
+ Object.setPrototypeOf(this, new.target.prototype)
96
+ this.reason = reason
97
+ }
98
+ }
@@ -0,0 +1,16 @@
1
+ export * from "./errors.ts"
2
+
3
+ /**
4
+ * 栅栏与闭锁类原语。
5
+ */
6
+ export * from "./barrier.ts"
7
+ export * from "./count-down-latch.ts"
8
+
9
+ /**
10
+ * permit 与各类锁/信号量原语。
11
+ */
12
+ export * from "./permit.ts"
13
+ export * from "./mutex.ts"
14
+ export * from "./semaphore.ts"
15
+ export * from "./keyed-lock.ts"
16
+ export * from "./read-write-lock.ts"
@@ -0,0 +1,95 @@
1
+ import { CoordinationAbortError, CoordinationTimeoutError } from "../errors.ts"
2
+
3
+ /**
4
+ * 表示等待节点上与 timeout / abort 绑定相关的内部状态。
5
+ */
6
+ export interface InternalWaitConstraintsEntry {
7
+ timeoutId: ReturnType<typeof setTimeout> | undefined
8
+ abortSignal: AbortSignal | undefined
9
+ abortListener: (() => void) | undefined
10
+ }
11
+
12
+ /**
13
+ * 表示等待约束的输入参数。
14
+ */
15
+ export interface InternalWaitConstraintsOptions {
16
+ timeout?: number | undefined
17
+ abortSignal?: AbortSignal | undefined
18
+ }
19
+
20
+ /**
21
+ * 表示将等待约束绑定到某个节点时需要提供的回调集合。
22
+ */
23
+ export interface InternalWaitConstraintBindings {
24
+ abortOperation: string
25
+ timeoutOperation: string
26
+ onAbort: (error: CoordinationAbortError) => void
27
+ onTimeout: (error: CoordinationTimeoutError) => void
28
+ }
29
+
30
+ /**
31
+ * 断言 timeout 参数是合法的非负有限数值。
32
+ */
33
+ export const internalAssertTimeoutOption = (operation: string, timeout: number | undefined): void => {
34
+ if (timeout === undefined) {
35
+ return
36
+ }
37
+
38
+ if (Number.isFinite(timeout) === false || timeout < 0) {
39
+ throw new RangeError(`${operation} timeout must be a finite number greater than or equal to 0.`)
40
+ }
41
+ }
42
+
43
+ /**
44
+ * 如果传入的 `AbortSignal` 已经处于中止状态,则立即抛出中止错误。
45
+ */
46
+ export const internalThrowIfAborted = (operation: string, abortSignal: AbortSignal | undefined): void => {
47
+ if (abortSignal?.aborted === true) {
48
+ throw new CoordinationAbortError(operation, abortSignal.reason)
49
+ }
50
+ }
51
+
52
+ /**
53
+ * 清理已经绑定到等待节点上的超时器与中止监听器。
54
+ *
55
+ * 该函数应当在节点成功结算或失败结算时都被调用,以避免悬挂的定时器与事件监听器泄漏。
56
+ */
57
+ export const internalCleanupWaitConstraints = (entry: InternalWaitConstraintsEntry): void => {
58
+ if (entry.timeoutId !== undefined) {
59
+ clearTimeout(entry.timeoutId)
60
+ entry.timeoutId = undefined
61
+ }
62
+
63
+ if (entry.abortSignal !== undefined && entry.abortListener !== undefined) {
64
+ entry.abortSignal.removeEventListener("abort", entry.abortListener)
65
+ entry.abortSignal = undefined
66
+ entry.abortListener = undefined
67
+ }
68
+ }
69
+
70
+ /**
71
+ * 把 timeout 与 abort 约束绑定到某个等待节点。
72
+ *
73
+ * 绑定本身只负责注册外部约束,真正如何让节点失败由调用方通过 `bindings` 中的回调决定。
74
+ */
75
+ export const internalBindWaitConstraints = (
76
+ entry: InternalWaitConstraintsEntry,
77
+ options: InternalWaitConstraintsOptions,
78
+ bindings: InternalWaitConstraintBindings,
79
+ ): void => {
80
+ const { abortSignal, timeout } = options
81
+
82
+ if (abortSignal !== undefined) {
83
+ entry.abortSignal = abortSignal
84
+ entry.abortListener = (): void => {
85
+ bindings.onAbort(new CoordinationAbortError(bindings.abortOperation, abortSignal.reason))
86
+ }
87
+ abortSignal.addEventListener("abort", entry.abortListener, { once: true })
88
+ }
89
+
90
+ if (timeout !== undefined) {
91
+ entry.timeoutId = setTimeout(() => {
92
+ bindings.onTimeout(new CoordinationTimeoutError(bindings.timeoutOperation, timeout))
93
+ }, timeout)
94
+ }
95
+ }