@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
+ import {
2
+ internalAssertAlpha,
3
+ internalAssertColorUnit,
4
+ internalAssertFinite,
5
+ internalRoundColorFloat,
6
+ } from "../internal.ts"
7
+
8
+ import type {
9
+ HslColorValue,
10
+ HsvColorValue,
11
+ LinearRgbColorValue,
12
+ SrgbColorValue,
13
+ } from "../types.ts"
14
+
15
+ /**
16
+ * 匹配模块支持的十六进制颜色字符串格式。
17
+ */
18
+ export const internalHexPattern: RegExp = /^#?(?:[\da-fA-F]{3}|[\da-fA-F]{4}|[\da-fA-F]{6}|[\da-fA-F]{8})$/
19
+
20
+ /**
21
+ * 断言一个 sRGB 通道值是合法的 8 位整数。
22
+ */
23
+ export const internalAssertByte = (value: number, channelName: string): void => {
24
+ if (Number.isInteger(value) === false || value < 0 || value > 255) {
25
+ throw new RangeError(`Color channel ${channelName} must be an integer between 0 and 255`)
26
+ }
27
+ }
28
+
29
+ /**
30
+ * 将色相角度规范化到 `0` 到 `360` 之间,`360` 会被折叠为 `0`。
31
+ */
32
+ export const internalNormalizeHue = (value: number): number => {
33
+ internalAssertFinite(value, "Color hue")
34
+
35
+ const normalizedValue = value % 360
36
+ if (normalizedValue < 0) {
37
+ return normalizedValue + 360
38
+ }
39
+
40
+ return normalizedValue
41
+ }
42
+
43
+ /**
44
+ * 规范化 Linear RGB 颜色值结构。
45
+ */
46
+ export const internalNormalizeLinearRgbColorValue = (color: LinearRgbColorValue): LinearRgbColorValue => {
47
+ internalAssertFinite(color.red, "Color channel red")
48
+ internalAssertFinite(color.green, "Color channel green")
49
+ internalAssertFinite(color.blue, "Color channel blue")
50
+ internalAssertAlpha(color.alpha)
51
+
52
+ return {
53
+ red: color.red,
54
+ green: color.green,
55
+ blue: color.blue,
56
+ alpha: color.alpha,
57
+ }
58
+ }
59
+
60
+ /**
61
+ * 规范化 sRGB 颜色值结构。
62
+ */
63
+ export const internalNormalizeSrgbColorValue = (color: SrgbColorValue): SrgbColorValue => {
64
+ internalAssertByte(color.red, "red")
65
+ internalAssertByte(color.green, "green")
66
+ internalAssertByte(color.blue, "blue")
67
+ internalAssertAlpha(color.alpha)
68
+
69
+ return {
70
+ red: color.red,
71
+ green: color.green,
72
+ blue: color.blue,
73
+ alpha: color.alpha,
74
+ }
75
+ }
76
+
77
+ /**
78
+ * 规范化 HSL 颜色值结构。
79
+ */
80
+ export const internalNormalizeHslColorValue = (color: HslColorValue): HslColorValue => {
81
+ internalAssertColorUnit(color.saturation, "saturation")
82
+ internalAssertColorUnit(color.lightness, "lightness")
83
+ internalAssertAlpha(color.alpha)
84
+
85
+ return {
86
+ hue: internalNormalizeHue(color.hue),
87
+ saturation: internalRoundColorFloat(color.saturation),
88
+ lightness: internalRoundColorFloat(color.lightness),
89
+ alpha: color.alpha,
90
+ }
91
+ }
92
+
93
+ /**
94
+ * 规范化 HSV 颜色值结构。
95
+ */
96
+ export const internalNormalizeHsvColorValue = (color: HsvColorValue): HsvColorValue => {
97
+ internalAssertColorUnit(color.saturation, "saturation")
98
+ internalAssertColorUnit(color.value, "value")
99
+ internalAssertAlpha(color.alpha)
100
+
101
+ return {
102
+ hue: internalNormalizeHue(color.hue),
103
+ saturation: internalRoundColorFloat(color.saturation),
104
+ value: internalRoundColorFloat(color.value),
105
+ alpha: color.alpha,
106
+ }
107
+ }
108
+
109
+ /**
110
+ * 将短格式十六进制颜色扩展为长格式。
111
+ */
112
+ export const internalExpandShortHex = (input: string): string => {
113
+ return input.split("").map(char => `${char}${char}`).join("")
114
+ }
115
+
116
+ /**
117
+ * 将 8 位通道值格式化为两位十六进制字符串。
118
+ */
119
+ export const internalFormatHexByte = (value: number): string => {
120
+ return value.toString(16).padStart(2, "0").toUpperCase()
121
+ }
122
+
123
+ /**
124
+ * 将浮点数格式化为适合颜色字符串的紧凑小数字符串。
125
+ */
126
+ export const internalFormatAlpha = (value: number): string => {
127
+ return Number(value.toFixed(4)).toString()
128
+ }
129
+
130
+ /**
131
+ * 将 `0` 到 `1` 的单位值格式化为百分比数值字符串,不附带 `%` 符号。
132
+ */
133
+ export const internalFormatPercentUnit = (value: number): string => {
134
+ internalAssertColorUnit(value, "unit")
135
+ return Number((value * 100).toFixed(2)).toString()
136
+ }
137
+
138
+ /**
139
+ * 将 RGB 字符串通道解析为合法的 8 位通道值。
140
+ */
141
+ export const internalParseRgbChannel = (input: string, channelName: string): number => {
142
+ const value = Number(input)
143
+ internalAssertByte(value, channelName)
144
+ return value
145
+ }
146
+
147
+ /**
148
+ * 将百分比字符串解析为 `0` 到 `1` 之间的单位值。
149
+ */
150
+ export const internalParsePercentUnit = (input: string, channelName: string): number => {
151
+ if (input.endsWith("%") === false) {
152
+ throw new TypeError(`Invalid ${channelName} percentage input`)
153
+ }
154
+
155
+ return internalRoundColorFloat(Number(input.slice(0, -1)) / 100)
156
+ }
157
+
158
+ /**
159
+ * 将 8 位 sRGB 通道值转换为线性光通道值。
160
+ */
161
+ export const internalGetLinearRgbChannelBySrgbChannel = (value: number): number => {
162
+ internalAssertByte(value, "sRGB")
163
+ const normalizedValue = value / 255
164
+
165
+ if (normalizedValue <= 0.040_45) {
166
+ return internalRoundColorFloat(normalizedValue / 12.92)
167
+ }
168
+
169
+ return internalRoundColorFloat(((normalizedValue + 0.055) / 1.055) ** 2.4)
170
+ }
171
+
172
+ /**
173
+ * 将线性光通道值转换为 8 位 sRGB 通道值。
174
+ */
175
+ export const internalGetSrgbChannelByLinearRgbChannel = (value: number, channelName: string): number => {
176
+ internalAssertFinite(value, `Linear RGB channel ${channelName}`)
177
+
178
+ if (value < 0 || value > 1) {
179
+ throw new RangeError(`Linear RGB channel ${channelName} must be between 0 and 1 to convert to sRGB`)
180
+ }
181
+
182
+ if (value <= 0.003_130_8) {
183
+ return Math.round(value * 12.92 * 255)
184
+ }
185
+
186
+ return Math.round(((1.055 * (value ** (1 / 2.4))) - 0.055) * 255)
187
+ }
188
+
189
+ /**
190
+ * 根据输入字符串稳定生成一个 sRGB 颜色值。
191
+ */
192
+ export const internalGetStableSrgbColorValueByString = (input: string): SrgbColorValue => {
193
+ let codePointSum = 0
194
+
195
+ for (const char of input) {
196
+ const codePoint = char.codePointAt(0)
197
+ if (codePoint !== undefined) {
198
+ codePointSum = codePointSum + codePoint
199
+ }
200
+ }
201
+
202
+ return {
203
+ red: Math.round(Math.abs(Math.sin(codePointSum) * 255)),
204
+ green: Math.round(Math.abs(Math.sin(codePointSum + 1) * 255)),
205
+ blue: Math.round(Math.abs(Math.sin(codePointSum + 2) * 255)),
206
+ alpha: 1,
207
+ }
208
+ }
@@ -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
+ }>