@planet-matrix/mobius-model 0.4.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 (179) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +134 -21
  3. package/dist/index.js +45 -4
  4. package/dist/index.js.map +186 -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 -117
  12. package/src/basic/enhance.ts +10 -0
  13. package/src/basic/function.ts +81 -62
  14. package/src/basic/index.ts +2 -0
  15. package/src/basic/is.ts +152 -71
  16. package/src/basic/object.ts +82 -0
  17. package/src/basic/promise.ts +29 -8
  18. package/src/basic/string.ts +2 -33
  19. package/src/color/README.md +105 -0
  20. package/src/color/index.ts +3 -0
  21. package/src/color/internal.ts +42 -0
  22. package/src/color/rgb/analyze.ts +236 -0
  23. package/src/color/rgb/construct.ts +130 -0
  24. package/src/color/rgb/convert.ts +227 -0
  25. package/src/color/rgb/derive.ts +303 -0
  26. package/src/color/rgb/index.ts +6 -0
  27. package/src/color/rgb/internal.ts +208 -0
  28. package/src/color/rgb/parse.ts +302 -0
  29. package/src/color/rgb/serialize.ts +144 -0
  30. package/src/color/types.ts +57 -0
  31. package/src/color/xyz/analyze.ts +80 -0
  32. package/src/color/xyz/construct.ts +19 -0
  33. package/src/color/xyz/convert.ts +71 -0
  34. package/src/color/xyz/index.ts +3 -0
  35. package/src/color/xyz/internal.ts +23 -0
  36. package/src/css/README.md +93 -0
  37. package/src/css/class.ts +559 -0
  38. package/src/css/index.ts +1 -0
  39. package/src/encoding/README.md +92 -0
  40. package/src/encoding/base64.ts +107 -0
  41. package/src/encoding/index.ts +1 -0
  42. package/src/environment/README.md +97 -0
  43. package/src/environment/basic.ts +26 -0
  44. package/src/environment/device.ts +311 -0
  45. package/src/environment/feature.ts +285 -0
  46. package/src/environment/geo.ts +337 -0
  47. package/src/environment/index.ts +7 -0
  48. package/src/environment/runtime.ts +400 -0
  49. package/src/environment/snapshot.ts +60 -0
  50. package/src/environment/variable.ts +239 -0
  51. package/src/event/README.md +90 -0
  52. package/src/event/class-event-proxy.ts +228 -0
  53. package/src/event/common.ts +19 -0
  54. package/src/event/event-manager.ts +203 -0
  55. package/src/event/index.ts +4 -0
  56. package/src/event/instance-event-proxy.ts +186 -0
  57. package/src/event/internal.ts +24 -0
  58. package/src/exception/README.md +96 -0
  59. package/src/exception/browser.ts +219 -0
  60. package/src/exception/index.ts +4 -0
  61. package/src/exception/nodejs.ts +169 -0
  62. package/src/exception/normalize.ts +106 -0
  63. package/src/exception/types.ts +99 -0
  64. package/src/identifier/README.md +92 -0
  65. package/src/identifier/id.ts +119 -0
  66. package/src/identifier/index.ts +2 -0
  67. package/src/identifier/uuid.ts +187 -0
  68. package/src/index.ts +18 -1
  69. package/src/log/README.md +79 -0
  70. package/src/log/index.ts +5 -0
  71. package/src/log/log-emitter.ts +72 -0
  72. package/src/log/log-record.ts +10 -0
  73. package/src/log/log-scheduler.ts +74 -0
  74. package/src/log/log-type.ts +8 -0
  75. package/src/log/logger.ts +543 -0
  76. package/src/orchestration/README.md +89 -0
  77. package/src/orchestration/coordination/barrier.ts +214 -0
  78. package/src/orchestration/coordination/count-down-latch.ts +215 -0
  79. package/src/orchestration/coordination/errors.ts +98 -0
  80. package/src/orchestration/coordination/index.ts +16 -0
  81. package/src/orchestration/coordination/internal/wait-constraints.ts +95 -0
  82. package/src/orchestration/coordination/internal/wait-queue.ts +109 -0
  83. package/src/orchestration/coordination/keyed-lock.ts +168 -0
  84. package/src/orchestration/coordination/mutex.ts +257 -0
  85. package/src/orchestration/coordination/permit.ts +127 -0
  86. package/src/orchestration/coordination/read-write-lock.ts +444 -0
  87. package/src/orchestration/coordination/semaphore.ts +280 -0
  88. package/src/orchestration/index.ts +1 -0
  89. package/src/random/README.md +78 -0
  90. package/src/random/index.ts +1 -0
  91. package/src/random/string.ts +35 -0
  92. package/src/reactor/README.md +4 -0
  93. package/src/reactor/reactor-core/primitive.ts +9 -9
  94. package/src/reactor/reactor-core/reactive-system.ts +5 -5
  95. package/src/singleton/README.md +79 -0
  96. package/src/singleton/factory.ts +55 -0
  97. package/src/singleton/index.ts +2 -0
  98. package/src/singleton/manager.ts +204 -0
  99. package/src/storage/README.md +107 -0
  100. package/src/storage/index.ts +1 -0
  101. package/src/storage/table.ts +449 -0
  102. package/src/timer/README.md +86 -0
  103. package/src/timer/expiration/expiration-manager.ts +594 -0
  104. package/src/timer/expiration/index.ts +3 -0
  105. package/src/timer/expiration/min-heap.ts +208 -0
  106. package/src/timer/expiration/remaining-manager.ts +241 -0
  107. package/src/timer/index.ts +1 -0
  108. package/src/type/README.md +54 -307
  109. package/src/type/class.ts +2 -2
  110. package/src/type/index.ts +14 -14
  111. package/src/type/is.ts +265 -2
  112. package/src/type/object.ts +37 -0
  113. package/src/type/string.ts +7 -2
  114. package/src/type/tuple.ts +6 -6
  115. package/src/type/union.ts +16 -0
  116. package/src/web/README.md +77 -0
  117. package/src/web/capture.ts +35 -0
  118. package/src/web/clipboard.ts +97 -0
  119. package/src/web/dom.ts +117 -0
  120. package/src/web/download.ts +16 -0
  121. package/src/web/event.ts +46 -0
  122. package/src/web/index.ts +10 -0
  123. package/src/web/local-storage.ts +113 -0
  124. package/src/web/location.ts +28 -0
  125. package/src/web/permission.ts +172 -0
  126. package/src/web/script-loader.ts +432 -0
  127. package/tests/unit/abort/abort-manager.spec.ts +225 -0
  128. package/tests/unit/abort/abort-signal-listener-manager.spec.ts +62 -0
  129. package/tests/unit/basic/array.spec.ts +1 -1
  130. package/tests/unit/basic/object.spec.ts +32 -1
  131. package/tests/unit/basic/stream.spec.ts +1 -1
  132. package/tests/unit/basic/string.spec.ts +0 -9
  133. package/tests/unit/color/rgb/analyze.spec.ts +110 -0
  134. package/tests/unit/color/rgb/construct.spec.ts +56 -0
  135. package/tests/unit/color/rgb/convert.spec.ts +60 -0
  136. package/tests/unit/color/rgb/derive.spec.ts +103 -0
  137. package/tests/unit/color/rgb/parse.spec.ts +66 -0
  138. package/tests/unit/color/rgb/serialize.spec.ts +46 -0
  139. package/tests/unit/color/xyz/analyze.spec.ts +33 -0
  140. package/tests/unit/color/xyz/construct.spec.ts +10 -0
  141. package/tests/unit/color/xyz/convert.spec.ts +18 -0
  142. package/tests/unit/css/class.spec.ts +157 -0
  143. package/tests/unit/encoding/base64.spec.ts +40 -0
  144. package/tests/unit/environment/basic.spec.ts +20 -0
  145. package/tests/unit/environment/device.spec.ts +146 -0
  146. package/tests/unit/environment/feature.spec.ts +388 -0
  147. package/tests/unit/environment/geo.spec.ts +111 -0
  148. package/tests/unit/environment/runtime.spec.ts +364 -0
  149. package/tests/unit/environment/snapshot.spec.ts +4 -0
  150. package/tests/unit/environment/variable.spec.ts +190 -0
  151. package/tests/unit/event/class-event-proxy.spec.ts +225 -0
  152. package/tests/unit/event/event-manager.spec.ts +246 -0
  153. package/tests/unit/event/instance-event-proxy.spec.ts +187 -0
  154. package/tests/unit/exception/browser.spec.ts +213 -0
  155. package/tests/unit/exception/nodejs.spec.ts +144 -0
  156. package/tests/unit/exception/normalize.spec.ts +57 -0
  157. package/tests/unit/identifier/id.spec.ts +71 -0
  158. package/tests/unit/identifier/uuid.spec.ts +85 -0
  159. package/tests/unit/log/log-emitter.spec.ts +33 -0
  160. package/tests/unit/log/log-scheduler.spec.ts +40 -0
  161. package/tests/unit/log/log-type.spec.ts +7 -0
  162. package/tests/unit/log/logger.spec.ts +222 -0
  163. package/tests/unit/orchestration/coordination/barrier.spec.ts +96 -0
  164. package/tests/unit/orchestration/coordination/count-down-latch.spec.ts +63 -0
  165. package/tests/unit/orchestration/coordination/errors.spec.ts +29 -0
  166. package/tests/unit/orchestration/coordination/keyed-lock.spec.ts +109 -0
  167. package/tests/unit/orchestration/coordination/mutex.spec.ts +132 -0
  168. package/tests/unit/orchestration/coordination/permit.spec.ts +43 -0
  169. package/tests/unit/orchestration/coordination/read-write-lock.spec.ts +154 -0
  170. package/tests/unit/orchestration/coordination/semaphore.spec.ts +135 -0
  171. package/tests/unit/random/string.spec.ts +11 -0
  172. package/tests/unit/reactor/alien-signals-effect.spec.ts +11 -10
  173. package/tests/unit/reactor/preact-signal.spec.ts +1 -2
  174. package/tests/unit/singleton/singleton.spec.ts +49 -0
  175. package/tests/unit/storage/table.spec.ts +620 -0
  176. package/tests/unit/timer/expiration/expiration-manager.spec.ts +464 -0
  177. package/tests/unit/timer/expiration/min-heap.spec.ts +71 -0
  178. package/tests/unit/timer/expiration/remaining-manager.spec.ts +234 -0
  179. package/.oxlintrc.json +0 -5
@@ -0,0 +1,208 @@
1
+ /**
2
+ * 表示最小堆中的一个节点。
3
+ *
4
+ * `name` 用于在 `indexMap` 中快速定位节点,
5
+ * `endAt` 用作最小堆的排序键,值越小表示越早到期,优先级越高。
6
+ */
7
+ interface HeapNode<Name extends string> {
8
+ name: Name
9
+ endAt: number
10
+ }
11
+
12
+ /**
13
+ * 提供一个按 `endAt` 升序排列的最小堆。
14
+ *
15
+ * 该实现同时维护:
16
+ * 1. `heap`:保存二叉堆结构本身,用于快速获取最小值。
17
+ * 2. `indexMap`:记录名称到数组索引的映射,用于按名称 $O(1)$ 定位节点,
18
+ * 从而支持高效的更新和删除。
19
+ *
20
+ * 这个堆特别适合“按时间先后触发”的场景,例如过期调度器。
21
+ */
22
+ export class MinHeap<Name extends string> {
23
+ /**
24
+ * 使用数组存储完全二叉树结构。
25
+ * 父子节点关系遵循:
26
+ * - parent = Math.floor((index - 1) / 2)
27
+ * - left = index * 2 + 1
28
+ * - right = index * 2 + 2
29
+ */
30
+ private heap: Array<HeapNode<Name>> = []
31
+
32
+ /**
33
+ * 记录节点名称在 `heap` 数组中的当前位置。
34
+ *
35
+ * 这样删除指定名称的节点时,不需要线性扫描整个堆。
36
+ */
37
+ private indexMap = new Map<Name, number>()
38
+
39
+ /**
40
+ * 返回当前堆中的节点数量。
41
+ */
42
+ size(): number {
43
+ return this.heap.length
44
+ }
45
+
46
+ /**
47
+ * 查看堆顶节点但不移除。
48
+ *
49
+ * 堆顶始终是当前 `endAt` 最小的节点,也就是最早应被处理的节点。
50
+ */
51
+ peek(): HeapNode<Name> | undefined {
52
+ return this.heap[0]
53
+ }
54
+
55
+ /**
56
+ * 向堆中插入一个新节点。
57
+ *
58
+ * 新节点会先追加到数组末尾,再通过上浮恢复最小堆性质。
59
+ */
60
+ push(node: HeapNode<Name>): void {
61
+ const index = this.heap.length
62
+ this.heap.push(node)
63
+ this.indexMap.set(node.name, index)
64
+ this.bubbleUp(index)
65
+ }
66
+
67
+ /**
68
+ * 按名称移除一个节点。
69
+ *
70
+ * 过程如下:
71
+ * 1. 借助 `indexMap` 找到目标节点位置。
72
+ * 2. 用最后一个节点填补被删除的位置。
73
+ * 3. 根据替换节点与父/子节点的大小关系,分别尝试下沉和上浮,恢复堆结构。
74
+ */
75
+ remove(name: Name): void {
76
+ const index = this.indexMap.get(name)
77
+ if (index === undefined) {
78
+ return
79
+ }
80
+
81
+ const last = this.heap.pop()!
82
+ this.indexMap.delete(name)
83
+
84
+ // 被删除节点本来就在数组末尾时,弹出后无需再做任何调整。
85
+ if (index === this.heap.length) {
86
+ return
87
+ }
88
+
89
+ this.heap[index] = last
90
+ this.indexMap.set(last.name, index)
91
+
92
+ // 替换进来的节点可能比子节点大,也可能比父节点小,
93
+ // 因此两个方向都尝试一次,以覆盖所有位置变化情况。
94
+ this.bubbleDown(index)
95
+ this.bubbleUp(index)
96
+ }
97
+
98
+ /**
99
+ * 移除并返回堆顶节点。
100
+ *
101
+ * 当堆为空时返回 `undefined`。
102
+ */
103
+ pop(): HeapNode<Name> | undefined {
104
+ if (this.heap.length === 0) {
105
+ return undefined
106
+ }
107
+
108
+ const top = this.heap[0]
109
+ if (top === undefined) {
110
+ return undefined
111
+ }
112
+
113
+ this.remove(top.name)
114
+ return top
115
+ }
116
+
117
+ /**
118
+ * 交换两个索引位置上的节点,并同步更新它们在 `indexMap` 中的记录。
119
+ */
120
+ private swap(i: number, j: number): void {
121
+ const a = this.heap[i]
122
+ const b = this.heap[j]
123
+
124
+ if (a === undefined || b === undefined) {
125
+ return
126
+ }
127
+
128
+ this.heap[i] = b
129
+ this.heap[j] = a
130
+
131
+ this.indexMap.set(a.name, j)
132
+ this.indexMap.set(b.name, i)
133
+ }
134
+
135
+ /**
136
+ * 让指定索引处的节点持续向上移动,直到满足最小堆性质。
137
+ *
138
+ * 适用于插入新节点,或某个节点的排序键变小之后的重排。
139
+ */
140
+ private bubbleUp(index: number): void {
141
+ let currentIndex = index
142
+
143
+ while (currentIndex > 0) {
144
+ const parent = Math.floor((currentIndex - 1) / 2)
145
+ const parentNode = this.heap[parent]
146
+ const currentNode = this.heap[currentIndex]
147
+
148
+ if (parentNode === undefined || currentNode === undefined) {
149
+ break
150
+ }
151
+
152
+ if (parentNode.endAt <= currentNode.endAt) {
153
+ break
154
+ }
155
+
156
+ this.swap(parent, currentIndex)
157
+ currentIndex = parent
158
+ }
159
+ }
160
+
161
+ /**
162
+ * 让指定索引处的节点持续向下移动,直到满足最小堆性质。
163
+ *
164
+ * 每次从当前节点与左右子节点中选出 `endAt` 最小者,
165
+ * 若最小者不是当前节点,则交换并继续向下检查。
166
+ */
167
+ private bubbleDown(index: number): void {
168
+ const length = this.heap.length
169
+ let currentIndex = index
170
+
171
+ while (true) {
172
+ let smallest = currentIndex
173
+ const left = currentIndex * 2 + 1
174
+ const right = currentIndex * 2 + 2
175
+ const smallestNode = this.heap[smallest]
176
+ const leftNode = this.heap[left]
177
+ const rightNode = this.heap[right]
178
+
179
+ if (smallestNode === undefined) {
180
+ break
181
+ }
182
+
183
+ // 先用左子节点与当前最小值比较。
184
+ if (left < length && leftNode !== undefined && leftNode.endAt < smallestNode.endAt) {
185
+ smallest = left
186
+ }
187
+
188
+ const nextSmallestNode = this.heap[smallest]
189
+
190
+ // 再基于最新的最小候选与右子节点比较。
191
+ if (
192
+ right < length &&
193
+ rightNode !== undefined &&
194
+ nextSmallestNode !== undefined &&
195
+ rightNode.endAt < nextSmallestNode.endAt
196
+ ) {
197
+ smallest = right
198
+ }
199
+
200
+ if (smallest === currentIndex) {
201
+ break
202
+ }
203
+
204
+ this.swap(currentIndex, smallest)
205
+ currentIndex = smallest
206
+ }
207
+ }
208
+ }
@@ -0,0 +1,241 @@
1
+ import { EventManager } from "#Source/event/index.ts"
2
+
3
+ import type { BaseEvents, SubscriberEntry } from "#Source/event/index.ts"
4
+ import type { ExpirationManager, ExpirationManagerEvents } from "./expiration-manager.ts"
5
+
6
+ /**
7
+ * 表示一个以秒为单位的剩余时长。
8
+ */
9
+ export type RemainingTime = number
10
+
11
+ /**
12
+ * 描述一组按名称索引的派生剩余时长字典。
13
+ */
14
+ export type RemainingDict<ExpirationName extends string> = {
15
+ [K in ExpirationName]?: RemainingTime | undefined
16
+ }
17
+
18
+ /**
19
+ * 描述 `RemainingManager` 对外发出的事件表。
20
+ */
21
+ export interface RemainingManagerEvents<ExpirationName extends string> extends BaseEvents {
22
+ /**
23
+ * 在剩余时间快照发生变化时发出最新结果。
24
+ */
25
+ remaining: (remaining: RemainingDict<ExpirationName>) => void
26
+ }
27
+
28
+ /**
29
+ * 描述创建 `RemainingManager` 时可用的配置项。
30
+ */
31
+ export interface RemainingManagerOptions<ExpirationName extends string> {
32
+ /**
33
+ * 提供过期状态真相来源的过期管理器。
34
+ */
35
+ expirationManager: ExpirationManager<ExpirationName>
36
+
37
+ /**
38
+ * 控制是否在创建后立即启用周期性剩余时间检查。
39
+ */
40
+ enabled?: boolean | undefined
41
+ }
42
+
43
+ const isRemainingDictEqual = <ExpirationName extends string>(
44
+ left: RemainingDict<ExpirationName>,
45
+ right: RemainingDict<ExpirationName>,
46
+ ): boolean => {
47
+ const leftKeys = Object.keys(left)
48
+ const rightKeys = Object.keys(right)
49
+
50
+ if (leftKeys.length !== rightKeys.length) {
51
+ return false
52
+ }
53
+
54
+ for (const key of leftKeys) {
55
+ // oxlint-disable-next-line no-unsafe-type-assertion
56
+ const expirationName = key as ExpirationName
57
+
58
+ if (left[expirationName] !== right[expirationName]) {
59
+ return false
60
+ }
61
+ }
62
+
63
+ return true
64
+ }
65
+
66
+ /**
67
+ * 管理基于 `ExpirationManager` 状态派生出的剩余时间查询与周期性通知。
68
+ *
69
+ * `RemainingManager` 不拥有独立的过期真相来源,而是始终从 `ExpirationManager`
70
+ * 读取当前过期状态,并在需要时把这些状态换算为以秒为单位的剩余值。
71
+ *
72
+ * 这里的 `enabled` 只影响内部的周期性检查是否运行,不影响过期状态变化时
73
+ * 立即触发的派生结果更新。
74
+ */
75
+ export class RemainingManager<ExpirationName extends string = string> {
76
+ readonly expirationManager: ExpirationManager<ExpirationName>
77
+ readonly event: EventManager<RemainingManagerEvents<ExpirationName>>
78
+
79
+ private enabled: boolean
80
+ private lastEmittedRemainingSnapshot: RemainingDict<ExpirationName>
81
+ private timer: ReturnType<typeof setInterval> | null
82
+ private terminated: boolean
83
+ private readonly expirationStateSubscriberEntry: SubscriberEntry<
84
+ ExpirationManagerEvents<ExpirationName>,
85
+ "expirationState"
86
+ >
87
+
88
+ constructor(options: RemainingManagerOptions<ExpirationName>) {
89
+ const { expirationManager, enabled } = options
90
+
91
+ this.expirationManager = expirationManager
92
+ this.event = new EventManager()
93
+
94
+ this.enabled = enabled === true
95
+ this.lastEmittedRemainingSnapshot = {}
96
+ this.timer = null
97
+ this.terminated = false
98
+ this.expirationStateSubscriberEntry = this.expirationManager.event.subscribe(
99
+ "expirationState",
100
+ (): void => {
101
+ this.emit()
102
+ },
103
+ )
104
+
105
+ if (this.enabled === true) {
106
+ this.resume()
107
+ }
108
+ }
109
+
110
+ /**
111
+ * 返回某个具名过期项当前的秒级剩余时长。
112
+ *
113
+ * 当目标不存在,或其状态不是 `active` 时,返回 `undefined`。
114
+ */
115
+ getRemaining(name: ExpirationName): number | undefined {
116
+ const expirationState = this.expirationManager.getExpirationState(name)
117
+ if (expirationState === undefined) {
118
+ return undefined
119
+ }
120
+ if (expirationState.state !== "active") {
121
+ return undefined
122
+ }
123
+
124
+ return Math.max(0, Math.ceil((expirationState.endAt - this.expirationManager.getNow()) / 1_000))
125
+ }
126
+
127
+ /**
128
+ * 返回当前所有活跃过期项的秒级剩余时长快照。
129
+ */
130
+ getRemainingSnapshot(): RemainingDict<ExpirationName> {
131
+ const expirationDict = this.expirationManager.getExpirationSnapshot()
132
+ const result: RemainingDict<ExpirationName> = {}
133
+
134
+ for (const name in expirationDict) {
135
+ if (Object.hasOwn(expirationDict, name) === false) {
136
+ continue
137
+ }
138
+
139
+ const expirationName = name as ExpirationName
140
+ const remaining = this.getRemaining(expirationName)
141
+
142
+ if (remaining === undefined) {
143
+ continue
144
+ }
145
+
146
+ result[expirationName] = remaining
147
+ }
148
+
149
+ return result
150
+ }
151
+
152
+ /**
153
+ * 在剩余时间快照发生变化时发出一次 `remaining` 事件。
154
+ *
155
+ * 若当前实例已经终止,或新旧快照完全一致,则不会重复发出事件。
156
+ */
157
+ emit(): void {
158
+ if (this.terminated === true) {
159
+ return
160
+ }
161
+
162
+ const remainingSnapshot = this.getRemainingSnapshot()
163
+ if (isRemainingDictEqual(this.lastEmittedRemainingSnapshot, remainingSnapshot) === true) {
164
+ return
165
+ }
166
+
167
+ this.lastEmittedRemainingSnapshot = remainingSnapshot
168
+ this.event.emit("remaining", remainingSnapshot)
169
+ }
170
+
171
+ /**
172
+ * 启用周期性剩余时间检查。
173
+ *
174
+ * 这不会跳过过期状态变更时本来就会触发的即时派生通知。
175
+ */
176
+ start(): void {
177
+ if (this.terminated === true) {
178
+ return
179
+ }
180
+
181
+ this.enabled = true
182
+ this.resume()
183
+ }
184
+
185
+ /**
186
+ * 停止周期性剩余时间检查,并将启用标记设为关闭。
187
+ */
188
+ stop(): void {
189
+ this.enabled = false
190
+
191
+ if (this.timer === null) {
192
+ return
193
+ }
194
+
195
+ clearInterval(this.timer)
196
+ this.timer = null
197
+ }
198
+
199
+ /**
200
+ * 暂停当前周期性定时器,但保留启用标记。
201
+ *
202
+ * 这通常用于由外层过期管理器在暂停期间临时停止派生检查,
203
+ * 以便之后通过 `resume()` 继续运行。
204
+ */
205
+ pause(): void {
206
+ if (this.timer !== null) {
207
+ clearInterval(this.timer)
208
+ this.timer = null
209
+ }
210
+ }
211
+
212
+ /**
213
+ * 在实例已启用且尚未终止时恢复周期性剩余时间检查。
214
+ */
215
+ resume(): void {
216
+ if (this.terminated === true || this.enabled !== true || this.timer !== null) {
217
+ return
218
+ }
219
+
220
+ this.timer = setInterval(() => {
221
+ if (Object.keys(this.expirationManager.getExpirationSnapshot()).length === 0) {
222
+ return
223
+ }
224
+
225
+ this.emit()
226
+ }, 1_000)
227
+ }
228
+
229
+ /**
230
+ * 终止实例并释放订阅与定时器资源。
231
+ */
232
+ terminate(): void {
233
+ if (this.terminated === true) {
234
+ return
235
+ }
236
+
237
+ this.terminated = true
238
+ this.expirationStateSubscriberEntry.unsubscribe()
239
+ this.stop()
240
+ }
241
+ }
@@ -0,0 +1 @@
1
+ export * from "./expiration/index.ts"