@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.
- package/CHANGELOG.md +32 -0
- package/README.md +134 -21
- package/dist/index.js +45 -4
- package/dist/index.js.map +186 -11
- package/oxlint.config.ts +6 -0
- package/package.json +16 -10
- package/src/abort/README.md +92 -0
- package/src/abort/abort-manager.ts +278 -0
- package/src/abort/abort-signal-listener-manager.ts +81 -0
- package/src/abort/index.ts +2 -0
- package/src/basic/README.md +69 -117
- package/src/basic/enhance.ts +10 -0
- package/src/basic/function.ts +81 -62
- package/src/basic/index.ts +2 -0
- package/src/basic/is.ts +152 -71
- package/src/basic/object.ts +82 -0
- package/src/basic/promise.ts +29 -8
- package/src/basic/string.ts +2 -33
- package/src/color/README.md +105 -0
- package/src/color/index.ts +3 -0
- package/src/color/internal.ts +42 -0
- package/src/color/rgb/analyze.ts +236 -0
- package/src/color/rgb/construct.ts +130 -0
- package/src/color/rgb/convert.ts +227 -0
- package/src/color/rgb/derive.ts +303 -0
- package/src/color/rgb/index.ts +6 -0
- package/src/color/rgb/internal.ts +208 -0
- package/src/color/rgb/parse.ts +302 -0
- package/src/color/rgb/serialize.ts +144 -0
- package/src/color/types.ts +57 -0
- package/src/color/xyz/analyze.ts +80 -0
- package/src/color/xyz/construct.ts +19 -0
- package/src/color/xyz/convert.ts +71 -0
- package/src/color/xyz/index.ts +3 -0
- package/src/color/xyz/internal.ts +23 -0
- package/src/css/README.md +93 -0
- package/src/css/class.ts +559 -0
- package/src/css/index.ts +1 -0
- package/src/encoding/README.md +92 -0
- package/src/encoding/base64.ts +107 -0
- package/src/encoding/index.ts +1 -0
- package/src/environment/README.md +97 -0
- package/src/environment/basic.ts +26 -0
- package/src/environment/device.ts +311 -0
- package/src/environment/feature.ts +285 -0
- package/src/environment/geo.ts +337 -0
- package/src/environment/index.ts +7 -0
- package/src/environment/runtime.ts +400 -0
- package/src/environment/snapshot.ts +60 -0
- package/src/environment/variable.ts +239 -0
- package/src/event/README.md +90 -0
- package/src/event/class-event-proxy.ts +228 -0
- package/src/event/common.ts +19 -0
- package/src/event/event-manager.ts +203 -0
- package/src/event/index.ts +4 -0
- package/src/event/instance-event-proxy.ts +186 -0
- package/src/event/internal.ts +24 -0
- package/src/exception/README.md +96 -0
- package/src/exception/browser.ts +219 -0
- package/src/exception/index.ts +4 -0
- package/src/exception/nodejs.ts +169 -0
- package/src/exception/normalize.ts +106 -0
- package/src/exception/types.ts +99 -0
- package/src/identifier/README.md +92 -0
- package/src/identifier/id.ts +119 -0
- package/src/identifier/index.ts +2 -0
- package/src/identifier/uuid.ts +187 -0
- package/src/index.ts +18 -1
- package/src/log/README.md +79 -0
- package/src/log/index.ts +5 -0
- package/src/log/log-emitter.ts +72 -0
- package/src/log/log-record.ts +10 -0
- package/src/log/log-scheduler.ts +74 -0
- package/src/log/log-type.ts +8 -0
- package/src/log/logger.ts +543 -0
- package/src/orchestration/README.md +89 -0
- package/src/orchestration/coordination/barrier.ts +214 -0
- package/src/orchestration/coordination/count-down-latch.ts +215 -0
- package/src/orchestration/coordination/errors.ts +98 -0
- package/src/orchestration/coordination/index.ts +16 -0
- package/src/orchestration/coordination/internal/wait-constraints.ts +95 -0
- package/src/orchestration/coordination/internal/wait-queue.ts +109 -0
- package/src/orchestration/coordination/keyed-lock.ts +168 -0
- package/src/orchestration/coordination/mutex.ts +257 -0
- package/src/orchestration/coordination/permit.ts +127 -0
- package/src/orchestration/coordination/read-write-lock.ts +444 -0
- package/src/orchestration/coordination/semaphore.ts +280 -0
- package/src/orchestration/index.ts +1 -0
- package/src/random/README.md +78 -0
- package/src/random/index.ts +1 -0
- package/src/random/string.ts +35 -0
- package/src/reactor/README.md +4 -0
- package/src/reactor/reactor-core/primitive.ts +9 -9
- package/src/reactor/reactor-core/reactive-system.ts +5 -5
- package/src/singleton/README.md +79 -0
- package/src/singleton/factory.ts +55 -0
- package/src/singleton/index.ts +2 -0
- package/src/singleton/manager.ts +204 -0
- package/src/storage/README.md +107 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/table.ts +449 -0
- package/src/timer/README.md +86 -0
- package/src/timer/expiration/expiration-manager.ts +594 -0
- package/src/timer/expiration/index.ts +3 -0
- package/src/timer/expiration/min-heap.ts +208 -0
- package/src/timer/expiration/remaining-manager.ts +241 -0
- package/src/timer/index.ts +1 -0
- package/src/type/README.md +54 -307
- package/src/type/class.ts +2 -2
- package/src/type/index.ts +14 -14
- package/src/type/is.ts +265 -2
- package/src/type/object.ts +37 -0
- package/src/type/string.ts +7 -2
- package/src/type/tuple.ts +6 -6
- package/src/type/union.ts +16 -0
- package/src/web/README.md +77 -0
- package/src/web/capture.ts +35 -0
- package/src/web/clipboard.ts +97 -0
- package/src/web/dom.ts +117 -0
- package/src/web/download.ts +16 -0
- package/src/web/event.ts +46 -0
- package/src/web/index.ts +10 -0
- package/src/web/local-storage.ts +113 -0
- package/src/web/location.ts +28 -0
- package/src/web/permission.ts +172 -0
- package/src/web/script-loader.ts +432 -0
- package/tests/unit/abort/abort-manager.spec.ts +225 -0
- package/tests/unit/abort/abort-signal-listener-manager.spec.ts +62 -0
- package/tests/unit/basic/array.spec.ts +1 -1
- package/tests/unit/basic/object.spec.ts +32 -1
- package/tests/unit/basic/stream.spec.ts +1 -1
- package/tests/unit/basic/string.spec.ts +0 -9
- package/tests/unit/color/rgb/analyze.spec.ts +110 -0
- package/tests/unit/color/rgb/construct.spec.ts +56 -0
- package/tests/unit/color/rgb/convert.spec.ts +60 -0
- package/tests/unit/color/rgb/derive.spec.ts +103 -0
- package/tests/unit/color/rgb/parse.spec.ts +66 -0
- package/tests/unit/color/rgb/serialize.spec.ts +46 -0
- package/tests/unit/color/xyz/analyze.spec.ts +33 -0
- package/tests/unit/color/xyz/construct.spec.ts +10 -0
- package/tests/unit/color/xyz/convert.spec.ts +18 -0
- package/tests/unit/css/class.spec.ts +157 -0
- package/tests/unit/encoding/base64.spec.ts +40 -0
- package/tests/unit/environment/basic.spec.ts +20 -0
- package/tests/unit/environment/device.spec.ts +146 -0
- package/tests/unit/environment/feature.spec.ts +388 -0
- package/tests/unit/environment/geo.spec.ts +111 -0
- package/tests/unit/environment/runtime.spec.ts +364 -0
- package/tests/unit/environment/snapshot.spec.ts +4 -0
- package/tests/unit/environment/variable.spec.ts +190 -0
- package/tests/unit/event/class-event-proxy.spec.ts +225 -0
- package/tests/unit/event/event-manager.spec.ts +246 -0
- package/tests/unit/event/instance-event-proxy.spec.ts +187 -0
- package/tests/unit/exception/browser.spec.ts +213 -0
- package/tests/unit/exception/nodejs.spec.ts +144 -0
- package/tests/unit/exception/normalize.spec.ts +57 -0
- package/tests/unit/identifier/id.spec.ts +71 -0
- package/tests/unit/identifier/uuid.spec.ts +85 -0
- package/tests/unit/log/log-emitter.spec.ts +33 -0
- package/tests/unit/log/log-scheduler.spec.ts +40 -0
- package/tests/unit/log/log-type.spec.ts +7 -0
- package/tests/unit/log/logger.spec.ts +222 -0
- package/tests/unit/orchestration/coordination/barrier.spec.ts +96 -0
- package/tests/unit/orchestration/coordination/count-down-latch.spec.ts +63 -0
- package/tests/unit/orchestration/coordination/errors.spec.ts +29 -0
- package/tests/unit/orchestration/coordination/keyed-lock.spec.ts +109 -0
- package/tests/unit/orchestration/coordination/mutex.spec.ts +132 -0
- package/tests/unit/orchestration/coordination/permit.spec.ts +43 -0
- package/tests/unit/orchestration/coordination/read-write-lock.spec.ts +154 -0
- package/tests/unit/orchestration/coordination/semaphore.spec.ts +135 -0
- package/tests/unit/random/string.spec.ts +11 -0
- package/tests/unit/reactor/alien-signals-effect.spec.ts +11 -10
- package/tests/unit/reactor/preact-signal.spec.ts +1 -2
- package/tests/unit/singleton/singleton.spec.ts +49 -0
- package/tests/unit/storage/table.spec.ts +620 -0
- package/tests/unit/timer/expiration/expiration-manager.spec.ts +464 -0
- package/tests/unit/timer/expiration/min-heap.spec.ts +71 -0
- package/tests/unit/timer/expiration/remaining-manager.spec.ts +234 -0
- package/.oxlintrc.json +0 -5
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ABORT_MANAGER_MODE,
|
|
5
|
+
AbortManager,
|
|
6
|
+
all,
|
|
7
|
+
any,
|
|
8
|
+
fromAbortFriendlyOptions,
|
|
9
|
+
fromAbortable,
|
|
10
|
+
fromWithAbortSignal,
|
|
11
|
+
getAbortSignal,
|
|
12
|
+
isAborted,
|
|
13
|
+
} from "#Source/abort/index.ts"
|
|
14
|
+
|
|
15
|
+
test("getAbortSignal resolves AbortSignal from supported abortables", () => {
|
|
16
|
+
const abortController = new AbortController()
|
|
17
|
+
const abortManager = new AbortManager({})
|
|
18
|
+
|
|
19
|
+
expect(getAbortSignal(abortController.signal)).toBe(abortController.signal)
|
|
20
|
+
expect(getAbortSignal(abortController)).toBe(abortController.signal)
|
|
21
|
+
expect(getAbortSignal(abortManager)).toBe(abortManager.abortSignal)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test("isAborted reflects abort state from abortables", () => {
|
|
25
|
+
const abortController = new AbortController()
|
|
26
|
+
|
|
27
|
+
expect(isAborted(abortController.signal)).toBe(false)
|
|
28
|
+
expect(isAborted(abortController)).toBe(false)
|
|
29
|
+
|
|
30
|
+
abortController.abort("stop")
|
|
31
|
+
|
|
32
|
+
expect(isAborted(abortController.signal)).toBe(true)
|
|
33
|
+
expect(isAborted(abortController)).toBe(true)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test("AbortManager constructor links upstream abortManager from options", () => {
|
|
37
|
+
const upstream = new AbortManager({})
|
|
38
|
+
const abortManager = new AbortManager({ abortManager: upstream })
|
|
39
|
+
|
|
40
|
+
expect(abortManager.isAborted()).toBe(false)
|
|
41
|
+
|
|
42
|
+
upstream.abort("upstream")
|
|
43
|
+
|
|
44
|
+
expect(abortManager.isAborted()).toBe(true)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test("AbortManager.isAborted mirrors internal abortSignal", () => {
|
|
48
|
+
const abortManager = new AbortManager({})
|
|
49
|
+
|
|
50
|
+
expect(abortManager.isAborted()).toBe(false)
|
|
51
|
+
|
|
52
|
+
abortManager.abort("done")
|
|
53
|
+
|
|
54
|
+
expect(abortManager.isAborted()).toBe(true)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test("AbortManager.setMode sets mode and supports chaining", () => {
|
|
58
|
+
const abortManager = new AbortManager({})
|
|
59
|
+
const upstream1 = new AbortController()
|
|
60
|
+
const upstream2 = new AbortController()
|
|
61
|
+
|
|
62
|
+
const returned = abortManager
|
|
63
|
+
.setMode(ABORT_MANAGER_MODE.ANY)
|
|
64
|
+
.addUpstreams([upstream1.signal, upstream2.signal])
|
|
65
|
+
|
|
66
|
+
expect(returned).toBe(abortManager)
|
|
67
|
+
|
|
68
|
+
upstream1.abort("one")
|
|
69
|
+
|
|
70
|
+
expect(abortManager.isAborted()).toBe(true)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test("AbortManager.addUpstreams makes manager abort when upstream condition is met", () => {
|
|
74
|
+
const abortManager = new AbortManager({})
|
|
75
|
+
const upstream = new AbortController()
|
|
76
|
+
|
|
77
|
+
abortManager.addUpstreams([upstream.signal])
|
|
78
|
+
|
|
79
|
+
expect(abortManager.isAborted()).toBe(false)
|
|
80
|
+
|
|
81
|
+
upstream.abort("upstream")
|
|
82
|
+
|
|
83
|
+
expect(abortManager.isAborted()).toBe(true)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test("AbortManager.removeUpstreams detaches selected upstreams", () => {
|
|
87
|
+
const abortManager = new AbortManager({})
|
|
88
|
+
const upstream1 = new AbortController()
|
|
89
|
+
const upstream2 = new AbortController()
|
|
90
|
+
|
|
91
|
+
abortManager
|
|
92
|
+
.setMode(ABORT_MANAGER_MODE.ANY)
|
|
93
|
+
.addUpstreams([upstream1.signal, upstream2.signal])
|
|
94
|
+
.removeUpstreams([upstream1.signal])
|
|
95
|
+
|
|
96
|
+
upstream1.abort("ignored")
|
|
97
|
+
|
|
98
|
+
expect(abortManager.isAborted()).toBe(false)
|
|
99
|
+
|
|
100
|
+
upstream2.abort("active")
|
|
101
|
+
|
|
102
|
+
expect(abortManager.isAborted()).toBe(true)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test("AbortManager.removeAllUpstreams detaches all upstreams", () => {
|
|
106
|
+
const abortManager = new AbortManager({})
|
|
107
|
+
const upstream1 = new AbortController()
|
|
108
|
+
const upstream2 = new AbortController()
|
|
109
|
+
|
|
110
|
+
abortManager
|
|
111
|
+
.setMode(ABORT_MANAGER_MODE.ANY)
|
|
112
|
+
.addUpstreams([upstream1.signal, upstream2.signal])
|
|
113
|
+
.removeAllUpstreams()
|
|
114
|
+
|
|
115
|
+
upstream1.abort("ignored")
|
|
116
|
+
upstream2.abort("ignored")
|
|
117
|
+
|
|
118
|
+
expect(abortManager.isAborted()).toBe(false)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test("AbortManager.tryToAbort evaluates mode conditions", () => {
|
|
122
|
+
const abortManager = new AbortManager({})
|
|
123
|
+
const upstream1 = new AbortController()
|
|
124
|
+
const upstream2 = new AbortController()
|
|
125
|
+
|
|
126
|
+
abortManager
|
|
127
|
+
.setMode(ABORT_MANAGER_MODE.ALL)
|
|
128
|
+
.addUpstreams([upstream1.signal, upstream2.signal])
|
|
129
|
+
|
|
130
|
+
upstream1.abort("first")
|
|
131
|
+
abortManager.tryToAbort()
|
|
132
|
+
|
|
133
|
+
expect(abortManager.isAborted()).toBe(false)
|
|
134
|
+
|
|
135
|
+
upstream2.abort("second")
|
|
136
|
+
abortManager.tryToAbort()
|
|
137
|
+
|
|
138
|
+
expect(abortManager.isAborted()).toBe(true)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
test("AbortManager.abort aborts and applies auto-remove policy", () => {
|
|
142
|
+
class TestAbortManager extends AbortManager {
|
|
143
|
+
removeAllUpstreamsCalled = 0
|
|
144
|
+
|
|
145
|
+
override removeAllUpstreams(): this {
|
|
146
|
+
this.removeAllUpstreamsCalled = this.removeAllUpstreamsCalled + 1
|
|
147
|
+
return super.removeAllUpstreams()
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const enabled = new TestAbortManager({ autoRemoveUpstreamsAfterAbort: true })
|
|
152
|
+
enabled.addUpstreams([new AbortController().signal])
|
|
153
|
+
enabled.abort("enabled")
|
|
154
|
+
|
|
155
|
+
const disabled = new TestAbortManager({ autoRemoveUpstreamsAfterAbort: false })
|
|
156
|
+
disabled.addUpstreams([new AbortController().signal])
|
|
157
|
+
disabled.abort("disabled")
|
|
158
|
+
|
|
159
|
+
expect(enabled.isAborted()).toBe(true)
|
|
160
|
+
expect(enabled.removeAllUpstreamsCalled).toBe(1)
|
|
161
|
+
expect(disabled.isAborted()).toBe(true)
|
|
162
|
+
expect(disabled.removeAllUpstreamsCalled).toBe(0)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
test("fromAbortable creates manager from a single upstream abortable", () => {
|
|
166
|
+
const upstream = new AbortController()
|
|
167
|
+
const abortManager = fromAbortable(upstream)
|
|
168
|
+
|
|
169
|
+
expect(abortManager.isAborted()).toBe(false)
|
|
170
|
+
|
|
171
|
+
upstream.abort("stop")
|
|
172
|
+
|
|
173
|
+
expect(abortManager.isAborted()).toBe(true)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test("any creates manager that aborts when any upstream aborts", () => {
|
|
177
|
+
const upstream1 = new AbortController()
|
|
178
|
+
const upstream2 = new AbortController()
|
|
179
|
+
const abortManager = any([upstream1.signal, null, undefined, upstream2.signal])
|
|
180
|
+
|
|
181
|
+
expect(abortManager.isAborted()).toBe(false)
|
|
182
|
+
|
|
183
|
+
upstream1.abort("first")
|
|
184
|
+
|
|
185
|
+
expect(abortManager.isAborted()).toBe(true)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
test("all creates manager that aborts only when all upstreams abort", () => {
|
|
189
|
+
const upstream1 = new AbortController()
|
|
190
|
+
const upstream2 = new AbortController()
|
|
191
|
+
const abortManager = all([upstream1.signal, null, undefined, upstream2.signal])
|
|
192
|
+
|
|
193
|
+
upstream1.abort("first")
|
|
194
|
+
|
|
195
|
+
expect(abortManager.isAborted()).toBe(false)
|
|
196
|
+
|
|
197
|
+
upstream2.abort("second")
|
|
198
|
+
|
|
199
|
+
expect(abortManager.isAborted()).toBe(true)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
test("fromWithAbortSignal creates manager from options.abortSignal", () => {
|
|
203
|
+
const upstream = new AbortController()
|
|
204
|
+
const withSignal = fromWithAbortSignal({ abortSignal: upstream.signal })
|
|
205
|
+
const withoutSignal = fromWithAbortSignal({})
|
|
206
|
+
|
|
207
|
+
expect(withSignal.isAborted()).toBe(false)
|
|
208
|
+
expect(withoutSignal.isAborted()).toBe(false)
|
|
209
|
+
|
|
210
|
+
upstream.abort("stop")
|
|
211
|
+
|
|
212
|
+
expect(withSignal.isAborted()).toBe(true)
|
|
213
|
+
expect(withoutSignal.isAborted()).toBe(false)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
test("fromAbortFriendlyOptions creates manager using abort-friendly options", () => {
|
|
217
|
+
const upstream = new AbortManager({})
|
|
218
|
+
const abortManager = fromAbortFriendlyOptions({ abortManager: upstream })
|
|
219
|
+
|
|
220
|
+
expect(abortManager.isAborted()).toBe(false)
|
|
221
|
+
|
|
222
|
+
upstream.abort("stop")
|
|
223
|
+
|
|
224
|
+
expect(abortManager.isAborted()).toBe(true)
|
|
225
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AbortSignalListenerManager,
|
|
5
|
+
getGlobalAbortSignalListenerManager,
|
|
6
|
+
} from "#Source/abort/index.ts"
|
|
7
|
+
|
|
8
|
+
test("AbortSignalListenerManager.hasEventListener checks managed listener state", () => {
|
|
9
|
+
const manager = new AbortSignalListenerManager()
|
|
10
|
+
const abortController = new AbortController()
|
|
11
|
+
const listener = (): void => {
|
|
12
|
+
void abortController
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
expect(manager.hasEventListener(abortController.signal, listener)).toBe(false)
|
|
16
|
+
|
|
17
|
+
manager.addEventListener(abortController.signal, listener)
|
|
18
|
+
|
|
19
|
+
expect(manager.hasEventListener(abortController.signal, listener)).toBe(true)
|
|
20
|
+
|
|
21
|
+
manager.removeEventListener(abortController.signal, listener)
|
|
22
|
+
|
|
23
|
+
expect(manager.hasEventListener(abortController.signal, listener)).toBe(false)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test("AbortSignalListenerManager.addEventListener registers listener for abort events", () => {
|
|
27
|
+
const manager = new AbortSignalListenerManager()
|
|
28
|
+
const abortController = new AbortController()
|
|
29
|
+
let count = 0
|
|
30
|
+
const listener = (): void => {
|
|
31
|
+
count = count + 1
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
manager.addEventListener(abortController.signal, listener)
|
|
35
|
+
|
|
36
|
+
abortController.abort("stop")
|
|
37
|
+
|
|
38
|
+
expect(count).toBe(1)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test("AbortSignalListenerManager.removeEventListener unregisters listener", () => {
|
|
42
|
+
const manager = new AbortSignalListenerManager()
|
|
43
|
+
const abortController = new AbortController()
|
|
44
|
+
let count = 0
|
|
45
|
+
const listener = (): void => {
|
|
46
|
+
count = count + 1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
manager.addEventListener(abortController.signal, listener)
|
|
50
|
+
manager.removeEventListener(abortController.signal, listener)
|
|
51
|
+
|
|
52
|
+
abortController.abort("stop")
|
|
53
|
+
|
|
54
|
+
expect(count).toBe(0)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test("getGlobalAbortSignalListenerManager returns singleton instance", () => {
|
|
58
|
+
const first = getGlobalAbortSignalListenerManager()
|
|
59
|
+
const second = getGlobalAbortSignalListenerManager()
|
|
60
|
+
|
|
61
|
+
expect(first).toBe(second)
|
|
62
|
+
})
|
|
@@ -192,7 +192,7 @@ test("arrayAny aliases arraySome", () => {
|
|
|
192
192
|
|
|
193
193
|
test("arrayForEach applies the callback", () => {
|
|
194
194
|
const target: number[] = []
|
|
195
|
-
arrayForEach((item) => target.push(item * 2), [1, 2, 3])
|
|
195
|
+
arrayForEach((item) => { target.push(item * 2) }, [1, 2, 3])
|
|
196
196
|
expect(target).toEqual([2, 4, 6])
|
|
197
197
|
})
|
|
198
198
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { expect, test } from "vitest"
|
|
2
2
|
|
|
3
|
-
import { excludeFields, includeFields } from "#Source/basic/object.ts"
|
|
3
|
+
import { excludeFields, includeFields, objectDateFieldsToNumber } from "#Source/basic/object.ts"
|
|
4
4
|
|
|
5
5
|
test("includeFields picks specified keys", () => {
|
|
6
6
|
expect(includeFields({ a: 1, b: 2, c: 3 }, ["a", "c"])).toEqual({ a: 1, c: 3 })
|
|
@@ -13,3 +13,34 @@ test("excludeFields omits specified keys", () => {
|
|
|
13
13
|
// @ts-expect-error - Testing behavior with undefined input
|
|
14
14
|
expect(excludeFields(undefined, ["a"])).toEqual({})
|
|
15
15
|
})
|
|
16
|
+
|
|
17
|
+
test("objectDateFieldsToNumber converts top-level Date fields", () => {
|
|
18
|
+
const createdAt = new Date("2024-01-02T03:04:05.678Z")
|
|
19
|
+
const updatedAt = new Date("2024-02-03T04:05:06.789Z")
|
|
20
|
+
const nestedDate = new Date("2024-03-04T05:06:07.890Z")
|
|
21
|
+
|
|
22
|
+
const source = {
|
|
23
|
+
id: "x-1",
|
|
24
|
+
createdAt,
|
|
25
|
+
updatedAt,
|
|
26
|
+
nested: {
|
|
27
|
+
occurredAt: nestedDate,
|
|
28
|
+
},
|
|
29
|
+
optional: undefined as Date | undefined,
|
|
30
|
+
nullable: null as Date | null,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const result = objectDateFieldsToNumber(source)
|
|
34
|
+
|
|
35
|
+
expect(result).toEqual({
|
|
36
|
+
id: "x-1",
|
|
37
|
+
createdAt: createdAt.getTime(),
|
|
38
|
+
updatedAt: updatedAt.getTime(),
|
|
39
|
+
nested: {
|
|
40
|
+
occurredAt: nestedDate,
|
|
41
|
+
},
|
|
42
|
+
optional: undefined,
|
|
43
|
+
nullable: null,
|
|
44
|
+
})
|
|
45
|
+
expect(result).toBe(source)
|
|
46
|
+
})
|
|
@@ -99,7 +99,7 @@ test("streamConsumeInAsyncMacroTask consumes stream and forwards callback failur
|
|
|
99
99
|
test("streamTransformInAsyncMacroTask transforms values and handles invalid inputs/errors", async () => {
|
|
100
100
|
expect(() => {
|
|
101
101
|
streamTransformInAsyncMacroTask<number, number>({})
|
|
102
|
-
}).
|
|
102
|
+
}).toThrow("Either readableStream or reader must be provided")
|
|
103
103
|
|
|
104
104
|
const transformed = streamTransformInAsyncMacroTask<number, number>({
|
|
105
105
|
readableStream: streamFromArray([1, 2, 3]),
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
stringCamelCaseToKebabCase,
|
|
6
6
|
stringHelloWord,
|
|
7
7
|
stringKebabCaseToCamelCase,
|
|
8
|
-
stringRandom,
|
|
9
8
|
stringSliceByUnits,
|
|
10
9
|
stringSmartSplit,
|
|
11
10
|
stringSplit,
|
|
@@ -13,14 +12,6 @@ import {
|
|
|
13
12
|
stringTruncateByUnits,
|
|
14
13
|
} from "#Source/basic/index.ts"
|
|
15
14
|
|
|
16
|
-
test("stringRandom returns expected output", () => {
|
|
17
|
-
const value = stringRandom(12)
|
|
18
|
-
expect(value).toHaveLength(12)
|
|
19
|
-
|
|
20
|
-
const constrained = stringRandom(10, "ab")
|
|
21
|
-
expect(constrained.split("").every(char => char === "a" || char === "b")).toBe(true)
|
|
22
|
-
})
|
|
23
|
-
|
|
24
15
|
test("stringCamelCaseToKebabCase converts correctly", () => {
|
|
25
16
|
expect(stringCamelCaseToKebabCase("helloWorld")).toBe("hello-world")
|
|
26
17
|
expect(stringCamelCaseToKebabCase("ab2Cd")).toBe("ab2-cd")
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
doSrgbColorValuesMeetContrastRatio,
|
|
5
|
+
isLightSrgbColorValue,
|
|
6
|
+
linearRgbColorValueToRelativeLuminance,
|
|
7
|
+
linearRgbColorValueToTristimulusSum,
|
|
8
|
+
linearRgbColorValueToXyChromaticityValue,
|
|
9
|
+
pickReadableSrgbTextColorValue,
|
|
10
|
+
srgbColorValueToRelativeLuminance,
|
|
11
|
+
srgbColorValueToTristimulusSum,
|
|
12
|
+
srgbColorValueToXyChromaticityValue,
|
|
13
|
+
srgbColorValuesToContrastRatio,
|
|
14
|
+
} from "#Source/color/index.ts"
|
|
15
|
+
|
|
16
|
+
test("linearRgbColorValueToRelativeLuminance follows the explicit RGB branch pipeline", () => {
|
|
17
|
+
expect(linearRgbColorValueToRelativeLuminance({ red: 1, green: 0, blue: 0, alpha: 1 })).toBe(0.212_673)
|
|
18
|
+
expect(linearRgbColorValueToRelativeLuminance({ red: 1, green: 1, blue: 1, alpha: 1 })).toBe(1)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test("srgbColorValueToRelativeLuminance follows the explicit RGB branch pipeline", () => {
|
|
22
|
+
expect(srgbColorValueToRelativeLuminance({ red: 51, green: 102, blue: 153, alpha: 1 })).toBe(0.125_053)
|
|
23
|
+
expect(srgbColorValueToRelativeLuminance({ red: 255, green: 255, blue: 255, alpha: 1 })).toBe(1)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test("srgbColorValuesToContrastRatio supports accessibility-oriented comparisons", () => {
|
|
27
|
+
expect(srgbColorValuesToContrastRatio(
|
|
28
|
+
{ red: 0, green: 0, blue: 0, alpha: 1 },
|
|
29
|
+
{ red: 255, green: 255, blue: 255, alpha: 1 },
|
|
30
|
+
)).toBe(21)
|
|
31
|
+
|
|
32
|
+
expect(srgbColorValuesToContrastRatio(
|
|
33
|
+
{ red: 51, green: 102, blue: 153, alpha: 1 },
|
|
34
|
+
{ red: 51, green: 102, blue: 153, alpha: 1 },
|
|
35
|
+
)).toBe(1)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test("doSrgbColorValuesMeetContrastRatio validates thresholds and comparisons", () => {
|
|
39
|
+
expect(() => doSrgbColorValuesMeetContrastRatio(
|
|
40
|
+
{ red: 0, green: 0, blue: 0, alpha: 1 },
|
|
41
|
+
{ red: 255, green: 255, blue: 255, alpha: 1 },
|
|
42
|
+
0.99,
|
|
43
|
+
)).toThrow(RangeError)
|
|
44
|
+
|
|
45
|
+
expect(doSrgbColorValuesMeetContrastRatio(
|
|
46
|
+
{ red: 0, green: 0, blue: 0, alpha: 1 },
|
|
47
|
+
{ red: 255, green: 255, blue: 255, alpha: 1 },
|
|
48
|
+
7,
|
|
49
|
+
)).toBe(true)
|
|
50
|
+
|
|
51
|
+
expect(doSrgbColorValuesMeetContrastRatio(
|
|
52
|
+
{ red: 120, green: 120, blue: 120, alpha: 1 },
|
|
53
|
+
{ red: 255, green: 255, blue: 255, alpha: 1 },
|
|
54
|
+
4.5,
|
|
55
|
+
)).toBe(false)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test("pickReadableSrgbTextColorValue chooses the more readable candidate", () => {
|
|
59
|
+
expect(pickReadableSrgbTextColorValue({ red: 0, green: 102, blue: 204, alpha: 1 })).toEqual({
|
|
60
|
+
red: 255,
|
|
61
|
+
green: 255,
|
|
62
|
+
blue: 255,
|
|
63
|
+
alpha: 1,
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
expect(pickReadableSrgbTextColorValue(
|
|
67
|
+
{ red: 250, green: 240, blue: 210, alpha: 1 },
|
|
68
|
+
{
|
|
69
|
+
darkColor: { red: 32, green: 32, blue: 32, alpha: 1 },
|
|
70
|
+
lightColor: { red: 255, green: 255, blue: 255, alpha: 1 },
|
|
71
|
+
},
|
|
72
|
+
)).toEqual({
|
|
73
|
+
red: 32,
|
|
74
|
+
green: 32,
|
|
75
|
+
blue: 32,
|
|
76
|
+
alpha: 1,
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test("linearRgbColorValueToTristimulusSum follows the explicit RGB branch pipeline", () => {
|
|
81
|
+
expect(linearRgbColorValueToTristimulusSum({ red: 1, green: 0, blue: 0, alpha: 1 })).toBe(0.644_463)
|
|
82
|
+
expect(linearRgbColorValueToTristimulusSum({ red: 1, green: 1, blue: 1, alpha: 1 })).toBe(3.039_3)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test("srgbColorValueToTristimulusSum follows the explicit RGB branch pipeline", () => {
|
|
86
|
+
expect(srgbColorValueToTristimulusSum({ red: 51, green: 102, blue: 153, alpha: 1 })).toBe(0.562_889)
|
|
87
|
+
expect(srgbColorValueToTristimulusSum({ red: 255, green: 255, blue: 255, alpha: 1 })).toBe(3.039_3)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test("linearRgbColorValueToXyChromaticityValue follows the explicit RGB branch pipeline", () => {
|
|
91
|
+
expect(linearRgbColorValueToXyChromaticityValue({ red: 1, green: 0, blue: 0, alpha: 1 })).toEqual({
|
|
92
|
+
x: 0.64,
|
|
93
|
+
y: 0.33,
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test("srgbColorValueToXyChromaticityValue follows the explicit RGB branch pipeline", () => {
|
|
98
|
+
expect(srgbColorValueToXyChromaticityValue({ red: 51, green: 102, blue: 153, alpha: 1 })).toEqual({
|
|
99
|
+
x: 0.210_775,
|
|
100
|
+
y: 0.222_163,
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test("isLightSrgbColorValue uses relative luminance threshold", () => {
|
|
105
|
+
expect(isLightSrgbColorValue({ red: 255, green: 255, blue: 255, alpha: 1 })).toBe(true)
|
|
106
|
+
expect(isLightSrgbColorValue({ red: 0, green: 0, blue: 0, alpha: 1 })).toBe(false)
|
|
107
|
+
expect(isLightSrgbColorValue({ red: 128, green: 128, blue: 128, alpha: 1 }, 0.2)).toBe(true)
|
|
108
|
+
|
|
109
|
+
expect(() => isLightSrgbColorValue({ red: 128, green: 128, blue: 128, alpha: 1 }, 1.1)).toThrow(RangeError)
|
|
110
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createHslColorValue,
|
|
5
|
+
createHslColorValueByString,
|
|
6
|
+
createHsvColorValue,
|
|
7
|
+
createHsvColorValueByString,
|
|
8
|
+
createLinearRgbColorValue,
|
|
9
|
+
createSrgbColorValue,
|
|
10
|
+
createSrgbColorValueByString,
|
|
11
|
+
} from "#Source/color/index.ts"
|
|
12
|
+
|
|
13
|
+
test("createLinearRgbColorValue accepts finite linear channels", () => {
|
|
14
|
+
expect(createLinearRgbColorValue({ red: -0.25, green: 0.4, blue: 1.2, alpha: 1 }))
|
|
15
|
+
.toEqual({ red: -0.25, green: 0.4, blue: 1.2, alpha: 1 })
|
|
16
|
+
|
|
17
|
+
expect(createLinearRgbColorValue({ red: 0, green: 0, blue: 0, alpha: 0.5 }))
|
|
18
|
+
.toEqual({ red: 0, green: 0, blue: 0, alpha: 0.5 })
|
|
19
|
+
|
|
20
|
+
expect(() => createLinearRgbColorValue({ red: Number.NaN, green: 0.4, blue: 1.2, alpha: 1 })).toThrow(RangeError)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test("createSrgbColorValue validates byte channels", () => {
|
|
24
|
+
expect(createSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 0.5 }))
|
|
25
|
+
.toEqual({ red: 51, green: 102, blue: 153, alpha: 0.5 })
|
|
26
|
+
|
|
27
|
+
expect(() => createSrgbColorValue({ red: -1, green: 0, blue: 0, alpha: 1 })).toThrow(RangeError)
|
|
28
|
+
expect(() => createSrgbColorValue({ red: 0, green: 0, blue: 256, alpha: 1 })).toThrow(RangeError)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test("createHslColorValue normalizes hue and validates units", () => {
|
|
32
|
+
expect(createHslColorValue({ hue: -30, saturation: 0.5, lightness: 0.4, alpha: 1 }))
|
|
33
|
+
.toEqual({ hue: 330, saturation: 0.5, lightness: 0.4, alpha: 1 })
|
|
34
|
+
expect(() => createHslColorValue({ hue: 0, saturation: 1.5, lightness: 0.4, alpha: 1 })).toThrow(RangeError)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test("createHsvColorValue normalizes hue and validates units", () => {
|
|
38
|
+
expect(createHsvColorValue({ hue: 390, saturation: 0.5, value: 0.6, alpha: 1 }))
|
|
39
|
+
.toEqual({ hue: 30, saturation: 0.5, value: 0.6, alpha: 1 })
|
|
40
|
+
expect(() => createHsvColorValue({ hue: 0, saturation: 0.5, value: 1.5, alpha: 1 })).toThrow(RangeError)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test("createSrgbColorValueByString stays deterministic", () => {
|
|
44
|
+
expect(createSrgbColorValueByString("mobius")).toEqual(createSrgbColorValueByString("mobius"))
|
|
45
|
+
expect(createSrgbColorValueByString("mobius")).not.toEqual(createSrgbColorValueByString("planet"))
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test("createHslColorValueByString stays deterministic", () => {
|
|
49
|
+
expect(createHslColorValueByString("mobius")).toEqual(createHslColorValueByString("mobius"))
|
|
50
|
+
expect(createHslColorValueByString("mobius")).not.toEqual(createHslColorValueByString("planet"))
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test("createHsvColorValueByString stays deterministic", () => {
|
|
54
|
+
expect(createHsvColorValueByString("mobius")).toEqual(createHsvColorValueByString("mobius"))
|
|
55
|
+
expect(createHsvColorValueByString("mobius")).not.toEqual(createHsvColorValueByString("planet"))
|
|
56
|
+
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
hslColorValueToSrgbColorValue,
|
|
5
|
+
hsvColorValueToSrgbColorValue,
|
|
6
|
+
linearRgbColorValueToSrgbColorValue,
|
|
7
|
+
srgbColorValueToHslColorValue,
|
|
8
|
+
srgbColorValueToHsvColorValue,
|
|
9
|
+
srgbColorValueToLinearRgbColorValue,
|
|
10
|
+
} from "#Source/color/index.ts"
|
|
11
|
+
|
|
12
|
+
test("linearRgbColorValueToSrgbColorValue converts in-gamut linear values to sRGB", () => {
|
|
13
|
+
expect(linearRgbColorValueToSrgbColorValue({ red: 1, green: 0, blue: 0, alpha: 0.5 }))
|
|
14
|
+
.toEqual({ red: 255, green: 0, blue: 0, alpha: 0.5 })
|
|
15
|
+
|
|
16
|
+
expect(linearRgbColorValueToSrgbColorValue({ red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5 }))
|
|
17
|
+
.toEqual({ red: 188, green: 188, blue: 188, alpha: 0.5 })
|
|
18
|
+
|
|
19
|
+
expect(() => linearRgbColorValueToSrgbColorValue({ red: 1.2, green: 0, blue: 0, alpha: 1 })).toThrow(RangeError)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test("srgbColorValueToLinearRgbColorValue converts sRGB values to linear values", () => {
|
|
23
|
+
expect(srgbColorValueToLinearRgbColorValue({ red: 255, green: 0, blue: 0, alpha: 0.5 }))
|
|
24
|
+
.toEqual({ red: 1, green: 0, blue: 0, alpha: 0.5 })
|
|
25
|
+
|
|
26
|
+
expect(srgbColorValueToLinearRgbColorValue({ red: 128, green: 128, blue: 128, alpha: 0.5 }))
|
|
27
|
+
.toEqual({ red: 0.215_861, green: 0.215_861, blue: 0.215_861, alpha: 0.5 })
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test("srgbColorValueToHslColorValue converts sRGB values to HSL", () => {
|
|
31
|
+
expect(srgbColorValueToHslColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }))
|
|
32
|
+
.toEqual({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 1 })
|
|
33
|
+
|
|
34
|
+
expect(srgbColorValueToHslColorValue({ red: 128, green: 128, blue: 128, alpha: 1 }))
|
|
35
|
+
.toEqual({ hue: 0, saturation: 0, lightness: 0.501_961, alpha: 1 })
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test("hslColorValueToSrgbColorValue converts HSL values to sRGB", () => {
|
|
39
|
+
expect(hslColorValueToSrgbColorValue({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 1 }))
|
|
40
|
+
.toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
|
|
41
|
+
|
|
42
|
+
expect(hslColorValueToSrgbColorValue({ hue: 0, saturation: 0, lightness: 0.501_961, alpha: 1 }))
|
|
43
|
+
.toEqual({ red: 128, green: 128, blue: 128, alpha: 1 })
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test("srgbColorValueToHsvColorValue converts sRGB values to HSV", () => {
|
|
47
|
+
expect(srgbColorValueToHsvColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }))
|
|
48
|
+
.toEqual({ hue: 210, saturation: 0.666_667, value: 0.6, alpha: 1 })
|
|
49
|
+
|
|
50
|
+
expect(srgbColorValueToHsvColorValue({ red: 128, green: 128, blue: 128, alpha: 1 }))
|
|
51
|
+
.toEqual({ hue: 0, saturation: 0, value: 0.501_961, alpha: 1 })
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test("hsvColorValueToSrgbColorValue converts HSV values to sRGB", () => {
|
|
55
|
+
expect(hsvColorValueToSrgbColorValue({ hue: 210, saturation: 0.666_667, value: 0.6, alpha: 1 }))
|
|
56
|
+
.toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
|
|
57
|
+
|
|
58
|
+
expect(hsvColorValueToSrgbColorValue({ hue: 0, saturation: 0, value: 0.501_961, alpha: 1 }))
|
|
59
|
+
.toEqual({ red: 128, green: 128, blue: 128, alpha: 1 })
|
|
60
|
+
})
|