@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,302 @@
1
+ import { internalAssertAlpha } from "../internal.ts"
2
+ import {
3
+ createHslColorValue,
4
+ createHsvColorValue,
5
+ createSrgbColorValue,
6
+ } from "./construct.ts"
7
+ import {
8
+ hslColorValueToSrgbColorValue,
9
+ hsvColorValueToSrgbColorValue,
10
+ } from "./convert.ts"
11
+ import {
12
+ internalExpandShortHex,
13
+ internalHexPattern,
14
+ internalParsePercentUnit,
15
+ internalParseRgbChannel,
16
+ } from "./internal.ts"
17
+
18
+ import type {
19
+ HslColorValue,
20
+ HsvColorValue,
21
+ SrgbColorValue,
22
+ } from "../types.ts"
23
+
24
+ /**
25
+ * 判断输入是否为合法的十六进制颜色字符串。
26
+ *
27
+ * @example
28
+ * ```
29
+ * // Expect: true
30
+ * const example1 = isHexColorString("#F0A")
31
+ *
32
+ * // Expect: false
33
+ * const example2 = isHexColorString("#12")
34
+ * ```
35
+ */
36
+ export const isHexColorString = (input: string): boolean => {
37
+ return internalHexPattern.test(input.trim())
38
+ }
39
+
40
+ /**
41
+ * 将十六进制颜色字符串解析为 sRGB 颜色值对象。
42
+ *
43
+ * @example
44
+ * ```
45
+ * // Expect: { red: 255, green: 0, blue: 170, alpha: 1 }
46
+ * const example1 = hexColorStringToSrgbColorValue("#F0A")
47
+ *
48
+ * // Expect: { red: 17, green: 34, blue: 51, alpha: 0.2667 }
49
+ * const example2 = hexColorStringToSrgbColorValue("#11223344")
50
+ * ```
51
+ */
52
+ export const hexColorStringToSrgbColorValue = (input: string): SrgbColorValue => {
53
+ const normalizedInput = input.trim()
54
+ if (isHexColorString(normalizedInput) === false) {
55
+ throw new TypeError("Invalid hex color input")
56
+ }
57
+
58
+ const hex = normalizedInput.startsWith("#") ? normalizedInput.slice(1) : normalizedInput
59
+ const expandedHex = hex.length === 3 || hex.length === 4 ? internalExpandShortHex(hex) : hex
60
+ const red = Number.parseInt(expandedHex.slice(0, 2), 16)
61
+ const green = Number.parseInt(expandedHex.slice(2, 4), 16)
62
+ const blue = Number.parseInt(expandedHex.slice(4, 6), 16)
63
+ const alpha = expandedHex.length === 8
64
+ ? Number((Number.parseInt(expandedHex.slice(6, 8), 16) / 255).toFixed(4))
65
+ : 1
66
+
67
+ return createSrgbColorValue({ red, green, blue, alpha })
68
+ }
69
+
70
+ const internalParseRgbParts = (input: string, allowAlpha: boolean): SrgbColorValue => {
71
+ const rgbMatch = /^rgba?\((.*)\)$/i.exec(input)
72
+ if (rgbMatch === null) {
73
+ throw new TypeError("Invalid color string input")
74
+ }
75
+
76
+ const partsSource = rgbMatch[1]
77
+ if (partsSource === undefined) {
78
+ throw new TypeError("Invalid color string input")
79
+ }
80
+
81
+ const parts = partsSource.split(",").map(part => part.trim())
82
+ if (allowAlpha === true && parts.length !== 4) {
83
+ throw new TypeError("Invalid RGBA color string input")
84
+ }
85
+
86
+ if (allowAlpha === false && parts.length !== 3) {
87
+ throw new TypeError("Invalid RGB color string input")
88
+ }
89
+
90
+ const [redPart = "", greenPart = "", bluePart = "", alphaPart] = parts
91
+ const red = internalParseRgbChannel(redPart, "red")
92
+ const green = internalParseRgbChannel(greenPart, "green")
93
+ const blue = internalParseRgbChannel(bluePart, "blue")
94
+ const alpha = allowAlpha === true ? Number(alphaPart) : 1
95
+
96
+ return createSrgbColorValue({ red, green, blue, alpha })
97
+ }
98
+
99
+ /**
100
+ * 将 `rgb(r, g, b)` 字符串解析为 sRGB 颜色值对象。
101
+ *
102
+ * @example
103
+ * ```
104
+ * // Expect: { red: 51, green: 102, blue: 153, alpha: 1 }
105
+ * const example1 = rgbColorStringToSrgbColorValue("rgb(51, 102, 153)")
106
+ *
107
+ * // Expect: { red: 0, green: 0, blue: 0, alpha: 1 }
108
+ * const example2 = rgbColorStringToSrgbColorValue("rgb(0, 0, 0)")
109
+ * ```
110
+ */
111
+ export const rgbColorStringToSrgbColorValue = (input: string): SrgbColorValue => {
112
+ return internalParseRgbParts(input.trim(), false)
113
+ }
114
+
115
+ /**
116
+ * 将 `rgba(r, g, b, a)` 字符串解析为 sRGB 颜色值对象。
117
+ *
118
+ * @example
119
+ * ```
120
+ * // Expect: { red: 51, green: 102, blue: 153, alpha: 0.5 }
121
+ * const example1 = rgbaColorStringToSrgbColorValue("rgba(51, 102, 153, 0.5)")
122
+ *
123
+ * // Expect: { red: 255, green: 255, blue: 255, alpha: 1 }
124
+ * const example2 = rgbaColorStringToSrgbColorValue("rgba(255, 255, 255, 1)")
125
+ * ```
126
+ */
127
+ export const rgbaColorStringToSrgbColorValue = (input: string): SrgbColorValue => {
128
+ return internalParseRgbParts(input.trim(), true)
129
+ }
130
+
131
+ const internalParseHslParts = (input: string, allowAlpha: boolean): HslColorValue => {
132
+ const hslMatch = /^hsla?\((.*)\)$/i.exec(input)
133
+ if (hslMatch === null) {
134
+ throw new TypeError("Invalid color string input")
135
+ }
136
+
137
+ const partsSource = hslMatch[1]
138
+ if (partsSource === undefined) {
139
+ throw new TypeError("Invalid color string input")
140
+ }
141
+
142
+ const parts = partsSource.split(",").map(part => part.trim())
143
+ if (allowAlpha === true && parts.length !== 4) {
144
+ throw new TypeError("Invalid HSLA color string input")
145
+ }
146
+
147
+ if (allowAlpha === false && parts.length !== 3) {
148
+ throw new TypeError("Invalid HSL color string input")
149
+ }
150
+
151
+ const [huePart = "", saturationPart = "", lightnessPart = "", alphaPart] = parts
152
+ const hue = Number(huePart)
153
+ const saturation = internalParsePercentUnit(saturationPart, "saturation")
154
+ const lightness = internalParsePercentUnit(lightnessPart, "lightness")
155
+ const alpha = allowAlpha === true ? Number(alphaPart) : 1
156
+ internalAssertAlpha(alpha)
157
+
158
+ return createHslColorValue({ hue, saturation, lightness, alpha })
159
+ }
160
+
161
+ /**
162
+ * 将 `hsl(h, s%, l%)` 字符串解析为 HSL 颜色值对象。
163
+ *
164
+ * @example
165
+ * ```
166
+ * // Expect: { hue: 210, saturation: 0.5, lightness: 0.4, alpha: 1 }
167
+ * const example1 = hslColorStringToHslColorValue("hsl(210, 50%, 40%)")
168
+ *
169
+ * // Expect: { hue: 330, saturation: 0.5, lightness: 0.4, alpha: 1 }
170
+ * const example2 = hslColorStringToHslColorValue("hsl(-30, 50%, 40%)")
171
+ * ```
172
+ */
173
+ export const hslColorStringToHslColorValue = (input: string): HslColorValue => {
174
+ return internalParseHslParts(input.trim(), false)
175
+ }
176
+
177
+ /**
178
+ * 将 `hsla(h, s%, l%, a)` 字符串解析为 HSL 颜色值对象。
179
+ *
180
+ * @example
181
+ * ```
182
+ * // Expect: { hue: 210, saturation: 0.5, lightness: 0.4, alpha: 0.5 }
183
+ * const example1 = hslaColorStringToHslColorValue("hsla(210, 50%, 40%, 0.5)")
184
+ *
185
+ * // Expect: { hue: 0, saturation: 0, lightness: 1, alpha: 1 }
186
+ * const example2 = hslaColorStringToHslColorValue("hsla(0, 0%, 100%, 1)")
187
+ * ```
188
+ */
189
+ export const hslaColorStringToHslColorValue = (input: string): HslColorValue => {
190
+ return internalParseHslParts(input.trim(), true)
191
+ }
192
+
193
+ const internalParseHsvParts = (input: string, allowAlpha: boolean): HsvColorValue => {
194
+ const hsvMatch = /^hsva?\((.*)\)$/i.exec(input)
195
+ if (hsvMatch === null) {
196
+ throw new TypeError("Invalid color string input")
197
+ }
198
+
199
+ const partsSource = hsvMatch[1]
200
+ if (partsSource === undefined) {
201
+ throw new TypeError("Invalid color string input")
202
+ }
203
+
204
+ const parts = partsSource.split(",").map(part => part.trim())
205
+ if (allowAlpha === true && parts.length !== 4) {
206
+ throw new TypeError("Invalid HSVA color string input")
207
+ }
208
+
209
+ if (allowAlpha === false && parts.length !== 3) {
210
+ throw new TypeError("Invalid HSV color string input")
211
+ }
212
+
213
+ const [huePart = "", saturationPart = "", valuePart = "", alphaPart] = parts
214
+ const hue = Number(huePart)
215
+ const saturation = internalParsePercentUnit(saturationPart, "saturation")
216
+ const value = internalParsePercentUnit(valuePart, "value")
217
+ const alpha = allowAlpha === true ? Number(alphaPart) : 1
218
+ internalAssertAlpha(alpha)
219
+
220
+ return createHsvColorValue({ hue, saturation, value, alpha })
221
+ }
222
+
223
+ /**
224
+ * 将 `hsv(h, s%, v%)` 字符串解析为 HSV 颜色值对象。
225
+ *
226
+ * @example
227
+ * ```
228
+ * // Expect: { hue: 210, saturation: 0.6667, value: 0.6, alpha: 1 }
229
+ * const example1 = hsvColorStringToHsvColorValue("hsv(210, 66.67%, 60%)")
230
+ *
231
+ * // Expect: { hue: 30, saturation: 0.5, value: 0.6, alpha: 1 }
232
+ * const example2 = hsvColorStringToHsvColorValue("hsv(390, 50%, 60%)")
233
+ * ```
234
+ */
235
+ export const hsvColorStringToHsvColorValue = (input: string): HsvColorValue => {
236
+ return internalParseHsvParts(input.trim(), false)
237
+ }
238
+
239
+ /**
240
+ * 将 `hsva(h, s%, v%, a)` 字符串解析为 HSV 颜色值对象。
241
+ *
242
+ * @example
243
+ * ```
244
+ * // Expect: { hue: 210, saturation: 0.6667, value: 0.6, alpha: 0.5 }
245
+ * const example1 = hsvaColorStringToHsvColorValue("hsva(210, 66.67%, 60%, 0.5)")
246
+ *
247
+ * // Expect: { hue: 0, saturation: 0, value: 1, alpha: 1 }
248
+ * const example2 = hsvaColorStringToHsvColorValue("hsva(0, 0%, 100%, 1)")
249
+ * ```
250
+ */
251
+ export const hsvaColorStringToHsvColorValue = (input: string): HsvColorValue => {
252
+ return internalParseHsvParts(input.trim(), true)
253
+ }
254
+
255
+ /**
256
+ * 将异构颜色字符串归一化为 sRGB 颜色值对象。
257
+ *
258
+ * 这是一个面向最终显示层的归一化入口。它会有意把十六进制、RGB、HSL 与 HSV
259
+ * 等不同语义层的字符串统一折叠为 sRGB;如果调用方需要保留输入本身的颜色语义,
260
+ * 应改用对应的专用解析函数。
261
+ *
262
+ * @example
263
+ * ```
264
+ * // Expect: { red: 51, green: 102, blue: 153, alpha: 1 }
265
+ * const example1 = colorStringToSrgbColorValue("#336699")
266
+ *
267
+ * // Expect: { red: 51, green: 102, blue: 153, alpha: 1 }
268
+ * const example2 = colorStringToSrgbColorValue("hsl(210, 50%, 40%)")
269
+ * ```
270
+ */
271
+ export const colorStringToSrgbColorValue = (input: string): SrgbColorValue => {
272
+ const normalizedInput = input.trim()
273
+ if (normalizedInput.startsWith("#") || isHexColorString(normalizedInput) === true) {
274
+ return hexColorStringToSrgbColorValue(normalizedInput)
275
+ }
276
+
277
+ if (normalizedInput.toLowerCase().startsWith("rgba(")) {
278
+ return rgbaColorStringToSrgbColorValue(normalizedInput)
279
+ }
280
+
281
+ if (normalizedInput.toLowerCase().startsWith("rgb(")) {
282
+ return rgbColorStringToSrgbColorValue(normalizedInput)
283
+ }
284
+
285
+ if (normalizedInput.toLowerCase().startsWith("hsla(")) {
286
+ return hslColorValueToSrgbColorValue(hslaColorStringToHslColorValue(normalizedInput))
287
+ }
288
+
289
+ if (normalizedInput.toLowerCase().startsWith("hsl(")) {
290
+ return hslColorValueToSrgbColorValue(hslColorStringToHslColorValue(normalizedInput))
291
+ }
292
+
293
+ if (normalizedInput.toLowerCase().startsWith("hsva(")) {
294
+ return hsvColorValueToSrgbColorValue(hsvaColorStringToHsvColorValue(normalizedInput))
295
+ }
296
+
297
+ if (normalizedInput.toLowerCase().startsWith("hsv(")) {
298
+ return hsvColorValueToSrgbColorValue(hsvColorStringToHsvColorValue(normalizedInput))
299
+ }
300
+
301
+ throw new TypeError("Invalid color string input")
302
+ }
@@ -0,0 +1,144 @@
1
+ import {
2
+ createHslColorValue,
3
+ createHsvColorValue,
4
+ createSrgbColorValue,
5
+ } from "./construct.ts"
6
+ import {
7
+ internalFormatAlpha,
8
+ internalFormatHexByte,
9
+ internalFormatPercentUnit,
10
+ } from "./internal.ts"
11
+
12
+ import type {
13
+ HslColorValue,
14
+ HsvColorValue,
15
+ SrgbColorValue,
16
+ } from "../types.ts"
17
+
18
+ /**
19
+ * 将 sRGB 颜色值序列化为十六进制颜色字符串。
20
+ *
21
+ * @example
22
+ * ```
23
+ * // Expect: #336699
24
+ * const example1 = srgbColorValueToHexColorString({ red: 51, green: 102, blue: 153, alpha: 0.5 })
25
+ *
26
+ * // Expect: #33669980
27
+ * const example2 = srgbColorValueToHexColorString({ red: 51, green: 102, blue: 153, alpha: 0.5 }, { includeAlpha: true })
28
+ * ```
29
+ */
30
+ export const srgbColorValueToHexColorString = (color: SrgbColorValue, options: { includeAlpha?: boolean } = {}): string => {
31
+ const normalizedColor = createSrgbColorValue(color)
32
+ const hex = [normalizedColor.red, normalizedColor.green, normalizedColor.blue]
33
+ .map(value => internalFormatHexByte(value))
34
+ .join("")
35
+
36
+ if (options.includeAlpha === true) {
37
+ const alphaByte = Math.round(normalizedColor.alpha * 255)
38
+ return `#${hex}${internalFormatHexByte(alphaByte)}`
39
+ }
40
+
41
+ return `#${hex}`
42
+ }
43
+
44
+ /**
45
+ * 将 sRGB 颜色值序列化为 `rgb(...)` 字符串。
46
+ *
47
+ * @example
48
+ * ```
49
+ * // Expect: rgb(51, 102, 153)
50
+ * const example1 = srgbColorValueToRgbColorString({ red: 51, green: 102, blue: 153, alpha: 0.5 })
51
+ *
52
+ * // Expect: rgb(0, 0, 0)
53
+ * const example2 = srgbColorValueToRgbColorString({ red: 0, green: 0, blue: 0, alpha: 1 })
54
+ * ```
55
+ */
56
+ export const srgbColorValueToRgbColorString = (color: SrgbColorValue): string => {
57
+ const normalizedColor = createSrgbColorValue(color)
58
+ return `rgb(${normalizedColor.red}, ${normalizedColor.green}, ${normalizedColor.blue})`
59
+ }
60
+
61
+ /**
62
+ * 将 sRGB 颜色值序列化为 `rgba(...)` 字符串。
63
+ *
64
+ * @example
65
+ * ```
66
+ * // Expect: rgba(51, 102, 153, 0.5)
67
+ * const example1 = srgbColorValueToRgbaColorString({ red: 51, green: 102, blue: 153, alpha: 0.5 })
68
+ *
69
+ * // Expect: rgba(255, 255, 255, 1)
70
+ * const example2 = srgbColorValueToRgbaColorString({ red: 255, green: 255, blue: 255, alpha: 1 })
71
+ * ```
72
+ */
73
+ export const srgbColorValueToRgbaColorString = (color: SrgbColorValue): string => {
74
+ const normalizedColor = createSrgbColorValue(color)
75
+ return `rgba(${normalizedColor.red}, ${normalizedColor.green}, ${normalizedColor.blue}, ${internalFormatAlpha(normalizedColor.alpha)})`
76
+ }
77
+
78
+ /**
79
+ * 将 HSL 颜色值序列化为 `hsl(...)` 字符串。
80
+ *
81
+ * @example
82
+ * ```
83
+ * // Expect: hsl(210, 50%, 40%)
84
+ * const example1 = hslColorValueToHslColorString({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 0.5 })
85
+ *
86
+ * // Expect: hsl(0, 0%, 100%)
87
+ * const example2 = hslColorValueToHslColorString({ hue: 0, saturation: 0, lightness: 1, alpha: 1 })
88
+ * ```
89
+ */
90
+ export const hslColorValueToHslColorString = (color: HslColorValue): string => {
91
+ const normalizedColor = createHslColorValue(color)
92
+ return `hsl(${normalizedColor.hue}, ${internalFormatPercentUnit(normalizedColor.saturation)}%, ${internalFormatPercentUnit(normalizedColor.lightness)}%)`
93
+ }
94
+
95
+ /**
96
+ * 将 HSL 颜色值序列化为 `hsla(...)` 字符串。
97
+ *
98
+ * @example
99
+ * ```
100
+ * // Expect: hsla(210, 50%, 40%, 0.5)
101
+ * const example1 = hslColorValueToHslaColorString({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 0.5 })
102
+ *
103
+ * // Expect: hsla(0, 0%, 100%, 1)
104
+ * const example2 = hslColorValueToHslaColorString({ hue: 0, saturation: 0, lightness: 1, alpha: 1 })
105
+ * ```
106
+ */
107
+ export const hslColorValueToHslaColorString = (color: HslColorValue): string => {
108
+ const normalizedColor = createHslColorValue(color)
109
+ return `hsla(${normalizedColor.hue}, ${internalFormatPercentUnit(normalizedColor.saturation)}%, ${internalFormatPercentUnit(normalizedColor.lightness)}%, ${internalFormatAlpha(normalizedColor.alpha)})`
110
+ }
111
+
112
+ /**
113
+ * 将 HSV 颜色值序列化为 `hsv(...)` 字符串。
114
+ *
115
+ * @example
116
+ * ```
117
+ * // Expect: hsv(210, 66.67%, 60%)
118
+ * const example1 = hsvColorValueToHsvColorString({ hue: 210, saturation: 0.6667, value: 0.6, alpha: 0.5 })
119
+ *
120
+ * // Expect: hsv(0, 0%, 100%)
121
+ * const example2 = hsvColorValueToHsvColorString({ hue: 0, saturation: 0, value: 1, alpha: 1 })
122
+ * ```
123
+ */
124
+ export const hsvColorValueToHsvColorString = (color: HsvColorValue): string => {
125
+ const normalizedColor = createHsvColorValue(color)
126
+ return `hsv(${normalizedColor.hue}, ${internalFormatPercentUnit(normalizedColor.saturation)}%, ${internalFormatPercentUnit(normalizedColor.value)}%)`
127
+ }
128
+
129
+ /**
130
+ * 将 HSV 颜色值序列化为 `hsva(...)` 字符串。
131
+ *
132
+ * @example
133
+ * ```
134
+ * // Expect: hsva(210, 66.67%, 60%, 0.5)
135
+ * const example1 = hsvColorValueToHsvaColorString({ hue: 210, saturation: 0.6667, value: 0.6, alpha: 0.5 })
136
+ *
137
+ * // Expect: hsva(0, 0%, 100%, 1)
138
+ * const example2 = hsvColorValueToHsvaColorString({ hue: 0, saturation: 0, value: 1, alpha: 1 })
139
+ * ```
140
+ */
141
+ export const hsvColorValueToHsvaColorString = (color: HsvColorValue): string => {
142
+ const normalizedColor = createHsvColorValue(color)
143
+ return `hsva(${normalizedColor.hue}, ${internalFormatPercentUnit(normalizedColor.saturation)}%, ${internalFormatPercentUnit(normalizedColor.value)}%, ${internalFormatAlpha(normalizedColor.alpha)})`
144
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * 表示 XYZ 颜色值结构,使用 `x`、`y`、`z` 三个通道和 `alpha` 透明度通道。
3
+ */
4
+ export type XyzColorValue = Readonly<{
5
+ x: number
6
+ y: number
7
+ z: number
8
+ alpha: number
9
+ }>
10
+
11
+ /**
12
+ * 表示基于 XYZ 颜色值计算得到的 `xy` 色度坐标结构。
13
+ */
14
+ export type XyChromaticityValue = Readonly<{
15
+ x: number
16
+ y: number
17
+ }>
18
+
19
+ /**
20
+ * 表示 Linear RGB 颜色值结构,使用线性 `red`、`green`、`blue` 三个通道和 `alpha` 透明度通道。
21
+ */
22
+ export type LinearRgbColorValue = Readonly<{
23
+ red: number
24
+ green: number
25
+ blue: number
26
+ alpha: number
27
+ }>
28
+
29
+ /**
30
+ * 表示 sRGB 颜色值结构,使用 `red`、`green`、`blue` 三个 8 位通道和 `alpha` 透明度通道。
31
+ */
32
+ export type SrgbColorValue = Readonly<{
33
+ red: number
34
+ green: number
35
+ blue: number
36
+ alpha: number
37
+ }>
38
+
39
+ /**
40
+ * 表示 HSL 颜色值结构,使用 `hue`、`saturation`、`lightness` 和 `alpha` 四个通道。
41
+ */
42
+ export type HslColorValue = Readonly<{
43
+ hue: number
44
+ saturation: number
45
+ lightness: number
46
+ alpha: number
47
+ }>
48
+
49
+ /**
50
+ * 表示 HSV 颜色值结构,使用 `hue`、`saturation`、`value` 和 `alpha` 四个通道。
51
+ */
52
+ export type HsvColorValue = Readonly<{
53
+ hue: number
54
+ saturation: number
55
+ value: number
56
+ alpha: number
57
+ }>
@@ -0,0 +1,80 @@
1
+ import { createXyzColorValue } from "./construct.ts"
2
+ import { internalRoundColorFloat } from "../internal.ts"
3
+
4
+ import type {
5
+ XyChromaticityValue,
6
+ XyzColorValue,
7
+ } from "../types.ts"
8
+
9
+ const internalGetNormalizedXyzColorData = (color: XyzColorValue): { color: XyzColorValue, tristimulusSum: number } => {
10
+ const normalizedColor = createXyzColorValue(color)
11
+ const tristimulusSum = internalRoundColorFloat(normalizedColor.x + normalizedColor.y + normalizedColor.z)
12
+
13
+ return {
14
+ color: normalizedColor,
15
+ tristimulusSum,
16
+ }
17
+ }
18
+
19
+ /**
20
+ * 读取 XYZ 颜色值的相对亮度。
21
+ *
22
+ * 在当前模块约定下,`Y` 通道直接表示相对亮度。
23
+ *
24
+ * @example
25
+ * ```
26
+ * // Expect: 0.3
27
+ * const example1 = xyzColorValueToRelativeLuminance({ x: 0.25, y: 0.3, z: 0.4, alpha: 1 })
28
+ *
29
+ * // Expect: 0
30
+ * const example2 = xyzColorValueToRelativeLuminance({ x: 0, y: 0, z: 0, alpha: 1 })
31
+ * ```
32
+ */
33
+ export const xyzColorValueToRelativeLuminance = (color: XyzColorValue): number => {
34
+ return createXyzColorValue(color).y
35
+ }
36
+
37
+ /**
38
+ * 读取 XYZ 颜色值的三刺激总量。
39
+ *
40
+ * 在当前模块约定下,结果等于 `x + y + z`。
41
+ *
42
+ * @example
43
+ * ```
44
+ * // Expect: 0.95
45
+ * const example1 = xyzColorValueToTristimulusSum({ x: 0.25, y: 0.3, z: 0.4, alpha: 1 })
46
+ *
47
+ * // Expect: 0
48
+ * const example2 = xyzColorValueToTristimulusSum({ x: 0, y: 0, z: 0, alpha: 1 })
49
+ * ```
50
+ */
51
+ export const xyzColorValueToTristimulusSum = (color: XyzColorValue): number => {
52
+ return internalGetNormalizedXyzColorData(color).tristimulusSum
53
+ }
54
+
55
+ /**
56
+ * 读取 XYZ 颜色值对应的 `xy` 色度坐标。
57
+ *
58
+ * 当三刺激总量为 `0` 时,该结果没有定义,因此会抛出异常。
59
+ *
60
+ * @example
61
+ * ```
62
+ * // Expect: { x: 0.263158, y: 0.315789 }
63
+ * const example1 = xyzColorValueToXyChromaticityValue({ x: 0.25, y: 0.3, z: 0.4, alpha: 1 })
64
+ *
65
+ * // Expect: { x: 0.64, y: 0.33 }
66
+ * const example2 = xyzColorValueToXyChromaticityValue({ x: 0.412456, y: 0.212673, z: 0.019334, alpha: 1 })
67
+ * ```
68
+ */
69
+ export const xyzColorValueToXyChromaticityValue = (color: XyzColorValue): XyChromaticityValue => {
70
+ const normalizedData = internalGetNormalizedXyzColorData(color)
71
+
72
+ if (normalizedData.tristimulusSum === 0) {
73
+ throw new RangeError("XYZ tristimulus sum must be greater than 0 to compute xy chromaticity")
74
+ }
75
+
76
+ return {
77
+ x: internalRoundColorFloat(normalizedData.color.x / normalizedData.tristimulusSum),
78
+ y: internalRoundColorFloat(normalizedData.color.y / normalizedData.tristimulusSum),
79
+ }
80
+ }
@@ -0,0 +1,19 @@
1
+ import { internalNormalizeXyzColorValue } from "./internal.ts"
2
+
3
+ import type { XyzColorValue } from "../types.ts"
4
+
5
+ /**
6
+ * 构造一个 XYZ 颜色值对象。
7
+ *
8
+ * @example
9
+ * ```
10
+ * // Expect: { x: 0.25, y: 0.3, z: 0.4, alpha: 1 }
11
+ * const example1 = createXyzColorValue({ x: 0.25, y: 0.3, z: 0.4, alpha: 1 })
12
+ *
13
+ * // Expect: { x: 0, y: 0, z: 0, alpha: 0.5 }
14
+ * const example2 = createXyzColorValue({ x: 0, y: 0, z: 0, alpha: 0.5 })
15
+ * ```
16
+ */
17
+ export const createXyzColorValue = (color: XyzColorValue): XyzColorValue => {
18
+ return internalNormalizeXyzColorValue(color)
19
+ }
@@ -0,0 +1,71 @@
1
+ import { internalNormalizeXyzColorValue } from "./internal.ts"
2
+ import {
3
+ internalAssertAlpha,
4
+ internalAssertFinite,
5
+ internalRoundColorFloat,
6
+ } from "../internal.ts"
7
+
8
+ import type {
9
+ LinearRgbColorValue,
10
+ XyzColorValue,
11
+ } from "../types.ts"
12
+
13
+ const internalNormalizeLinearRgbColorValueForXyzConversion = (color: LinearRgbColorValue): LinearRgbColorValue => {
14
+ internalAssertFinite(color.red, "Color channel red")
15
+ internalAssertFinite(color.green, "Color channel green")
16
+ internalAssertFinite(color.blue, "Color channel blue")
17
+ internalAssertAlpha(color.alpha)
18
+
19
+ return {
20
+ red: color.red,
21
+ green: color.green,
22
+ blue: color.blue,
23
+ alpha: color.alpha,
24
+ }
25
+ }
26
+
27
+ /**
28
+ * 将 XYZ 颜色值转换为 Linear RGB 颜色值。
29
+ *
30
+ * @example
31
+ * ```
32
+ * // Expect: { red: 0.2, green: 0.4, blue: 0.6, alpha: 1 }
33
+ * const example1 = xyzColorValueToLinearRgbColorValue({ x: 0.333784, y: 0.3719, z: 0.621726, alpha: 1 })
34
+ *
35
+ * // Expect: { red: 1, green: 0, blue: 0, alpha: 1 }
36
+ * const example2 = xyzColorValueToLinearRgbColorValue({ x: 0.412456, y: 0.212673, z: 0.019334, alpha: 1 })
37
+ * ```
38
+ */
39
+ export const xyzColorValueToLinearRgbColorValue = (color: XyzColorValue): LinearRgbColorValue => {
40
+ const normalizedColor = internalNormalizeXyzColorValue(color)
41
+
42
+ return internalNormalizeLinearRgbColorValueForXyzConversion({
43
+ red: internalRoundColorFloat((3.240_454_2 * normalizedColor.x) - (1.537_138_5 * normalizedColor.y) - (0.498_531_4 * normalizedColor.z)),
44
+ green: internalRoundColorFloat((-0.969_266 * normalizedColor.x) + (1.876_010_8 * normalizedColor.y) + (0.041_556 * normalizedColor.z)),
45
+ blue: internalRoundColorFloat((0.055_643_4 * normalizedColor.x) - (0.204_025_9 * normalizedColor.y) + (1.057_225_2 * normalizedColor.z)),
46
+ alpha: normalizedColor.alpha,
47
+ })
48
+ }
49
+
50
+ /**
51
+ * 将 Linear RGB 颜色值转换为 XYZ 颜色值。
52
+ *
53
+ * @example
54
+ * ```
55
+ * // Expect: { x: 0.333784, y: 0.3719, z: 0.621726, alpha: 1 }
56
+ * const example1 = linearRgbColorValueToXyzColorValue({ red: 0.2, green: 0.4, blue: 0.6, alpha: 1 })
57
+ *
58
+ * // Expect: { x: 0.412456, y: 0.212673, z: 0.019334, alpha: 1 }
59
+ * const example2 = linearRgbColorValueToXyzColorValue({ red: 1, green: 0, blue: 0, alpha: 1 })
60
+ * ```
61
+ */
62
+ export const linearRgbColorValueToXyzColorValue = (color: LinearRgbColorValue): XyzColorValue => {
63
+ const normalizedColor = internalNormalizeLinearRgbColorValueForXyzConversion(color)
64
+
65
+ return internalNormalizeXyzColorValue({
66
+ x: internalRoundColorFloat((0.412_456_4 * normalizedColor.red) + (0.357_576_1 * normalizedColor.green) + (0.180_437_5 * normalizedColor.blue)),
67
+ y: internalRoundColorFloat((0.212_672_9 * normalizedColor.red) + (0.715_152_2 * normalizedColor.green) + (0.072_175 * normalizedColor.blue)),
68
+ z: internalRoundColorFloat((0.019_333_9 * normalizedColor.red) + (0.119_192 * normalizedColor.green) + (0.950_304_1 * normalizedColor.blue)),
69
+ alpha: normalizedColor.alpha,
70
+ })
71
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./construct.ts"
2
+ export * from "./convert.ts"
3
+ export * from "./analyze.ts"