@xivdyetools/core 1.3.7
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/LICENSE +37 -0
- package/README.md +400 -0
- package/dist/constants/index.d.ts +56 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +103 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/data/colors_xiv.json +3130 -0
- package/dist/data/locales/de.json +231 -0
- package/dist/data/locales/en.json +231 -0
- package/dist/data/locales/fr.json +231 -0
- package/dist/data/locales/ja.json +231 -0
- package/dist/data/locales/ko.json +233 -0
- package/dist/data/locales/zh.json +233 -0
- package/dist/data/presets.json +390 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/services/APIService.d.ts +246 -0
- package/dist/services/APIService.d.ts.map +1 -0
- package/dist/services/APIService.js +499 -0
- package/dist/services/APIService.js.map +1 -0
- package/dist/services/ColorService.d.ts +146 -0
- package/dist/services/ColorService.d.ts.map +1 -0
- package/dist/services/ColorService.js +209 -0
- package/dist/services/ColorService.js.map +1 -0
- package/dist/services/DyeService.d.ts +230 -0
- package/dist/services/DyeService.d.ts.map +1 -0
- package/dist/services/DyeService.js +326 -0
- package/dist/services/DyeService.js.map +1 -0
- package/dist/services/LocalizationService.d.ts +338 -0
- package/dist/services/LocalizationService.d.ts.map +1 -0
- package/dist/services/LocalizationService.js +449 -0
- package/dist/services/LocalizationService.js.map +1 -0
- package/dist/services/PaletteService.d.ts +137 -0
- package/dist/services/PaletteService.d.ts.map +1 -0
- package/dist/services/PaletteService.js +349 -0
- package/dist/services/PaletteService.js.map +1 -0
- package/dist/services/PresetService.d.ts +196 -0
- package/dist/services/PresetService.d.ts.map +1 -0
- package/dist/services/PresetService.js +261 -0
- package/dist/services/PresetService.js.map +1 -0
- package/dist/services/color/ColorAccessibility.d.ts +39 -0
- package/dist/services/color/ColorAccessibility.d.ts.map +1 -0
- package/dist/services/color/ColorAccessibility.js +71 -0
- package/dist/services/color/ColorAccessibility.js.map +1 -0
- package/dist/services/color/ColorConverter.d.ts +146 -0
- package/dist/services/color/ColorConverter.d.ts.map +1 -0
- package/dist/services/color/ColorConverter.js +393 -0
- package/dist/services/color/ColorConverter.js.map +1 -0
- package/dist/services/color/ColorManipulator.d.ts +36 -0
- package/dist/services/color/ColorManipulator.d.ts.map +1 -0
- package/dist/services/color/ColorManipulator.js +56 -0
- package/dist/services/color/ColorManipulator.js.map +1 -0
- package/dist/services/color/ColorblindnessSimulator.d.ts +35 -0
- package/dist/services/color/ColorblindnessSimulator.d.ts.map +1 -0
- package/dist/services/color/ColorblindnessSimulator.js +110 -0
- package/dist/services/color/ColorblindnessSimulator.js.map +1 -0
- package/dist/services/dye/DyeDatabase.d.ts +131 -0
- package/dist/services/dye/DyeDatabase.d.ts.map +1 -0
- package/dist/services/dye/DyeDatabase.js +367 -0
- package/dist/services/dye/DyeDatabase.js.map +1 -0
- package/dist/services/dye/DyeSearch.d.ts +55 -0
- package/dist/services/dye/DyeSearch.d.ts.map +1 -0
- package/dist/services/dye/DyeSearch.js +196 -0
- package/dist/services/dye/DyeSearch.js.map +1 -0
- package/dist/services/dye/HarmonyGenerator.d.ts +110 -0
- package/dist/services/dye/HarmonyGenerator.d.ts.map +1 -0
- package/dist/services/dye/HarmonyGenerator.js +221 -0
- package/dist/services/dye/HarmonyGenerator.js.map +1 -0
- package/dist/services/localization/LocaleLoader.d.ts +38 -0
- package/dist/services/localization/LocaleLoader.d.ts.map +1 -0
- package/dist/services/localization/LocaleLoader.js +83 -0
- package/dist/services/localization/LocaleLoader.js.map +1 -0
- package/dist/services/localization/LocaleRegistry.d.ts +73 -0
- package/dist/services/localization/LocaleRegistry.d.ts.map +1 -0
- package/dist/services/localization/LocaleRegistry.js +84 -0
- package/dist/services/localization/LocaleRegistry.js.map +1 -0
- package/dist/services/localization/TranslationProvider.d.ts +157 -0
- package/dist/services/localization/TranslationProvider.d.ts.map +1 -0
- package/dist/services/localization/TranslationProvider.js +289 -0
- package/dist/services/localization/TranslationProvider.js.map +1 -0
- package/dist/types/index.d.ts +409 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +87 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/logger.d.ts +84 -0
- package/dist/types/logger.d.ts.map +1 -0
- package/dist/types/logger.js +54 -0
- package/dist/types/logger.js.map +1 -0
- package/dist/utils/index.d.ts +441 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +577 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/kd-tree.d.ts +76 -0
- package/dist/utils/kd-tree.d.ts.map +1 -0
- package/dist/utils/kd-tree.js +195 -0
- package/dist/utils/kd-tree.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @xivdyetools/core - Shared Utilities
|
|
3
|
+
*
|
|
4
|
+
* Reusable utility functions (environment-agnostic)
|
|
5
|
+
*
|
|
6
|
+
* @module utils
|
|
7
|
+
*/
|
|
8
|
+
import { RGB_MIN, RGB_MAX, HUE_MIN, HUE_MAX, SATURATION_MIN, SATURATION_MAX, VALUE_MIN, VALUE_MAX, PATTERNS, } from '../constants/index.js';
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Math Utilities
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Clamp a number between min and max values
|
|
14
|
+
*
|
|
15
|
+
* @param value - The value to clamp
|
|
16
|
+
* @param min - Minimum allowed value
|
|
17
|
+
* @param max - Maximum allowed value
|
|
18
|
+
* @returns Clamped value between min and max
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* clamp(150, 0, 100) // Returns 100
|
|
23
|
+
* clamp(-10, 0, 100) // Returns 0
|
|
24
|
+
* clamp(50, 0, 100) // Returns 50
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Edge cases:
|
|
28
|
+
* - NaN values return NaN
|
|
29
|
+
* - Infinity is clamped to max
|
|
30
|
+
* - -Infinity is clamped to min
|
|
31
|
+
*/
|
|
32
|
+
export function clamp(value, min, max) {
|
|
33
|
+
if (isNaN(value) || isNaN(min) || isNaN(max)) {
|
|
34
|
+
return NaN;
|
|
35
|
+
}
|
|
36
|
+
return Math.min(Math.max(value, min), max);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Linear interpolation between two values
|
|
40
|
+
*
|
|
41
|
+
* @param a - Start value
|
|
42
|
+
* @param b - End value
|
|
43
|
+
* @param t - Interpolation factor (0 = a, 1 = b, 0.5 = midpoint)
|
|
44
|
+
* @returns Interpolated value
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* lerp(0, 100, 0.5) // Returns 50
|
|
49
|
+
* lerp(0, 100, 0) // Returns 0
|
|
50
|
+
* lerp(0, 100, 1) // Returns 100
|
|
51
|
+
* lerp(10, 20, 0.25) // Returns 12.5
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* Edge cases:
|
|
55
|
+
* - t can be outside [0, 1] for extrapolation
|
|
56
|
+
* - NaN values return NaN
|
|
57
|
+
* - Handles Infinity correctly
|
|
58
|
+
*/
|
|
59
|
+
export function lerp(a, b, t) {
|
|
60
|
+
if (isNaN(a) || isNaN(b) || isNaN(t)) {
|
|
61
|
+
return NaN;
|
|
62
|
+
}
|
|
63
|
+
return a + (b - a) * t;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Round a number to a specific decimal place
|
|
67
|
+
*
|
|
68
|
+
* @param value - The number to round
|
|
69
|
+
* @param decimals - Number of decimal places (default: 0)
|
|
70
|
+
* @returns Rounded number
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* round(3.14159, 2) // Returns 3.14
|
|
75
|
+
* round(2.5) // Returns 3 (rounds to nearest integer)
|
|
76
|
+
* round(123.456, 1) // Returns 123.5
|
|
77
|
+
* round(-2.5) // Returns -2
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* Edge cases:
|
|
81
|
+
* - NaN returns NaN
|
|
82
|
+
* - Infinity returns Infinity/-Infinity
|
|
83
|
+
* - Negative decimals round to left of decimal point
|
|
84
|
+
*/
|
|
85
|
+
export function round(value, decimals = 0) {
|
|
86
|
+
if (isNaN(value)) {
|
|
87
|
+
return NaN;
|
|
88
|
+
}
|
|
89
|
+
if (!isFinite(value)) {
|
|
90
|
+
return value; // Preserve Infinity/-Infinity
|
|
91
|
+
}
|
|
92
|
+
const factor = Math.pow(10, decimals);
|
|
93
|
+
return Math.round(value * factor) / factor;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Calculate Euclidean distance between two points in 2D space
|
|
97
|
+
*
|
|
98
|
+
* @param x1 - X coordinate of first point
|
|
99
|
+
* @param y1 - Y coordinate of first point
|
|
100
|
+
* @param x2 - X coordinate of second point
|
|
101
|
+
* @param y2 - Y coordinate of second point
|
|
102
|
+
* @returns Distance between the two points (always >= 0)
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* distance(0, 0, 3, 4) // Returns 5 (Pythagorean theorem)
|
|
107
|
+
* distance(0, 0, 0, 0) // Returns 0 (same point)
|
|
108
|
+
* distance(1, 1, 4, 5) // Returns 5
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* Edge cases:
|
|
112
|
+
* - NaN values return NaN
|
|
113
|
+
* - Infinity values may return Infinity
|
|
114
|
+
* - Always returns non-negative value
|
|
115
|
+
*/
|
|
116
|
+
export function distance(x1, y1, x2, y2) {
|
|
117
|
+
if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) {
|
|
118
|
+
return NaN;
|
|
119
|
+
}
|
|
120
|
+
const dx = x2 - x1;
|
|
121
|
+
const dy = y2 - y1;
|
|
122
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
123
|
+
}
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// Array Utilities
|
|
126
|
+
// ============================================================================
|
|
127
|
+
/**
|
|
128
|
+
* Get unique values from an array
|
|
129
|
+
*
|
|
130
|
+
* @param array - Input array (may contain duplicates)
|
|
131
|
+
* @returns New array with only unique values (preserves first occurrence order)
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* unique([1, 2, 2, 3, 1]) // Returns [1, 2, 3]
|
|
136
|
+
* unique(['a', 'b', 'a']) // Returns ['a', 'b']
|
|
137
|
+
* unique([]) // Returns []
|
|
138
|
+
* ```
|
|
139
|
+
*
|
|
140
|
+
* Edge cases:
|
|
141
|
+
* - Empty array returns empty array
|
|
142
|
+
* - Uses Set equality (NaN === NaN, +0 === -0)
|
|
143
|
+
* - Preserves object/array references
|
|
144
|
+
*/
|
|
145
|
+
export function unique(array) {
|
|
146
|
+
return Array.from(new Set(array));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Group array items by a key function
|
|
150
|
+
*
|
|
151
|
+
* @param array - Input array to group
|
|
152
|
+
* @param keyFn - Function that extracts grouping key from each item
|
|
153
|
+
* @returns Object with keys as groups and values as arrays of items
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* const items = [
|
|
158
|
+
* { type: 'fruit', name: 'apple' },
|
|
159
|
+
* { type: 'fruit', name: 'banana' },
|
|
160
|
+
* { type: 'vegetable', name: 'carrot' }
|
|
161
|
+
* ];
|
|
162
|
+
* groupBy(items, item => item.type)
|
|
163
|
+
* // Returns { fruit: [...], vegetable: [...] }
|
|
164
|
+
* ```
|
|
165
|
+
*
|
|
166
|
+
* Edge cases:
|
|
167
|
+
* - Empty array returns empty object
|
|
168
|
+
* - Handles undefined/null keys as string keys
|
|
169
|
+
*/
|
|
170
|
+
export function groupBy(array, keyFn) {
|
|
171
|
+
return array.reduce((acc, item) => {
|
|
172
|
+
const key = keyFn(item);
|
|
173
|
+
if (!acc[key]) {
|
|
174
|
+
acc[key] = [];
|
|
175
|
+
}
|
|
176
|
+
acc[key].push(item);
|
|
177
|
+
return acc;
|
|
178
|
+
}, {});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Sort array by property value
|
|
182
|
+
*
|
|
183
|
+
* @param array - Input array to sort
|
|
184
|
+
* @param property - Property key to sort by
|
|
185
|
+
* @param order - Sort order ('asc' or 'desc', default: 'asc')
|
|
186
|
+
* @returns New sorted array (does not mutate original)
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```typescript
|
|
190
|
+
* const items = [{ age: 30 }, { age: 20 }, { age: 25 }];
|
|
191
|
+
* sortByProperty(items, 'age') // Sorted by age ascending
|
|
192
|
+
* sortByProperty(items, 'age', 'desc') // Sorted by age descending
|
|
193
|
+
* ```
|
|
194
|
+
*
|
|
195
|
+
* Edge cases:
|
|
196
|
+
* - Returns shallow copy of array
|
|
197
|
+
* - Handles undefined properties (sorted to end)
|
|
198
|
+
* - Stable sort (preserves relative order of equal elements)
|
|
199
|
+
*/
|
|
200
|
+
export function sortByProperty(array, property, order = 'asc') {
|
|
201
|
+
return [...array].sort((a, b) => {
|
|
202
|
+
const aVal = a[property];
|
|
203
|
+
const bVal = b[property];
|
|
204
|
+
const comparison = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
205
|
+
return order === 'asc' ? comparison : -comparison;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Filter array items, removing null and undefined values
|
|
210
|
+
*
|
|
211
|
+
* @param array - Input array with possibly null/undefined items
|
|
212
|
+
* @returns New array with null and undefined values removed
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```typescript
|
|
216
|
+
* filterNulls([1, null, 2, undefined, 3]) // Returns [1, 2, 3]
|
|
217
|
+
* filterNulls([null, undefined]) // Returns []
|
|
218
|
+
* filterNulls([0, false, '']) // Returns [0, false, ''] (keeps falsy values)
|
|
219
|
+
* ```
|
|
220
|
+
*
|
|
221
|
+
* Edge cases:
|
|
222
|
+
* - Keeps falsy values (0, false, empty string)
|
|
223
|
+
* - Type guard ensures return type doesn't include null|undefined
|
|
224
|
+
*/
|
|
225
|
+
export function filterNulls(array) {
|
|
226
|
+
return array.filter((item) => item !== null && item !== undefined);
|
|
227
|
+
}
|
|
228
|
+
// ============================================================================
|
|
229
|
+
// Validation Utilities
|
|
230
|
+
// ============================================================================
|
|
231
|
+
/**
|
|
232
|
+
* Validate a hexadecimal color string
|
|
233
|
+
*
|
|
234
|
+
* @param hex - Hex color string to validate
|
|
235
|
+
* @returns true if valid hex color, false otherwise
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```typescript
|
|
239
|
+
* isValidHexColor('#FF0000') // Returns true
|
|
240
|
+
* isValidHexColor('#F00') // Returns true (shorthand)
|
|
241
|
+
* isValidHexColor('FF0000') // Returns false (missing #)
|
|
242
|
+
* isValidHexColor('#GGGGGG') // Returns false (invalid characters)
|
|
243
|
+
* isValidHexColor('') // Returns false
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* Accepts:
|
|
247
|
+
* - Full format: #RRGGBB (e.g., #FF0000)
|
|
248
|
+
* - Shorthand format: #RGB (e.g., #F00)
|
|
249
|
+
* - Case insensitive (A-F or a-f)
|
|
250
|
+
*/
|
|
251
|
+
export function isValidHexColor(hex) {
|
|
252
|
+
if (typeof hex !== 'string') {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
return PATTERNS.HEX_COLOR.test(hex);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Validate RGB color values
|
|
259
|
+
*
|
|
260
|
+
* @param r - Red value
|
|
261
|
+
* @param g - Green value
|
|
262
|
+
* @param b - Blue value
|
|
263
|
+
* @returns true if all values are valid (0-255, finite, not NaN), false otherwise
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```typescript
|
|
267
|
+
* isValidRGB(255, 0, 0) // Returns true
|
|
268
|
+
* isValidRGB(0, 128, 255) // Returns true
|
|
269
|
+
* isValidRGB(256, 0, 0) // Returns false (r > 255)
|
|
270
|
+
* isValidRGB(-1, 0, 0) // Returns false (r < 0)
|
|
271
|
+
* isValidRGB(NaN, 0, 0) // Returns false
|
|
272
|
+
* isValidRGB(Infinity, 0, 0) // Returns false
|
|
273
|
+
* ```
|
|
274
|
+
*
|
|
275
|
+
* Valid range: 0-255 (inclusive) for all channels
|
|
276
|
+
*/
|
|
277
|
+
export function isValidRGB(r, g, b) {
|
|
278
|
+
return (Number.isFinite(r) &&
|
|
279
|
+
Number.isFinite(g) &&
|
|
280
|
+
Number.isFinite(b) &&
|
|
281
|
+
r >= RGB_MIN &&
|
|
282
|
+
r <= RGB_MAX &&
|
|
283
|
+
g >= RGB_MIN &&
|
|
284
|
+
g <= RGB_MAX &&
|
|
285
|
+
b >= RGB_MIN &&
|
|
286
|
+
b <= RGB_MAX);
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Validate HSV color values
|
|
290
|
+
*
|
|
291
|
+
* @param h - Hue value (0-360)
|
|
292
|
+
* @param s - Saturation value (0-100)
|
|
293
|
+
* @param v - Value/brightness (0-100)
|
|
294
|
+
* @returns true if all values are valid (finite, not NaN, within ranges), false otherwise
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* ```typescript
|
|
298
|
+
* isValidHSV(180, 50, 100) // Returns true
|
|
299
|
+
* isValidHSV(0, 0, 0) // Returns true
|
|
300
|
+
* isValidHSV(360, 100, 100) // Returns true (edge of range)
|
|
301
|
+
* isValidHSV(361, 50, 50) // Returns false (h > 360)
|
|
302
|
+
* isValidHSV(180, -1, 50) // Returns false (s < 0)
|
|
303
|
+
* isValidHSV(NaN, 50, 50) // Returns false
|
|
304
|
+
* ```
|
|
305
|
+
*
|
|
306
|
+
* Valid ranges:
|
|
307
|
+
* - Hue: 0-360 (degrees)
|
|
308
|
+
* - Saturation: 0-100 (percent)
|
|
309
|
+
* - Value: 0-100 (percent)
|
|
310
|
+
*/
|
|
311
|
+
export function isValidHSV(h, s, v) {
|
|
312
|
+
return (Number.isFinite(h) &&
|
|
313
|
+
Number.isFinite(s) &&
|
|
314
|
+
Number.isFinite(v) &&
|
|
315
|
+
h >= HUE_MIN &&
|
|
316
|
+
h <= HUE_MAX &&
|
|
317
|
+
s >= SATURATION_MIN &&
|
|
318
|
+
s <= SATURATION_MAX &&
|
|
319
|
+
v >= VALUE_MIN &&
|
|
320
|
+
v <= VALUE_MAX);
|
|
321
|
+
}
|
|
322
|
+
// ============================================================================
|
|
323
|
+
// Type Guards
|
|
324
|
+
// ============================================================================
|
|
325
|
+
/**
|
|
326
|
+
* Type guard: Check if a value is a string
|
|
327
|
+
*
|
|
328
|
+
* @param value - Value to check
|
|
329
|
+
* @returns true if value is a string, false otherwise
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```typescript
|
|
333
|
+
* isString('hello') // Returns true
|
|
334
|
+
* isString('') // Returns true (empty string)
|
|
335
|
+
* isString(123) // Returns false
|
|
336
|
+
* isString(new String('')) // Returns false (String object, not primitive)
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
export function isString(value) {
|
|
340
|
+
return typeof value === 'string';
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Type guard: Check if a value is a finite number (excludes NaN and Infinity)
|
|
344
|
+
*
|
|
345
|
+
* @param value - Value to check
|
|
346
|
+
* @returns true if value is a number and finite (not NaN, not Infinity), false otherwise
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* ```typescript
|
|
350
|
+
* isNumber(42) // Returns true
|
|
351
|
+
* isNumber(0) // Returns true
|
|
352
|
+
* isNumber(-3.14) // Returns true
|
|
353
|
+
* isNumber(NaN) // Returns false
|
|
354
|
+
* isNumber(Infinity) // Returns false
|
|
355
|
+
* isNumber('123') // Returns false (string)
|
|
356
|
+
* isNumber(new Number(5)) // Returns false (Number object)
|
|
357
|
+
* ```
|
|
358
|
+
*
|
|
359
|
+
* Note: This excludes NaN and Infinity for safer numeric operations
|
|
360
|
+
*/
|
|
361
|
+
export function isNumber(value) {
|
|
362
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Type guard: Check if a value is an array
|
|
366
|
+
*
|
|
367
|
+
* @param value - Value to check
|
|
368
|
+
* @returns true if value is an array, false otherwise
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```typescript
|
|
372
|
+
* isArray([1, 2, 3]) // Returns true
|
|
373
|
+
* isArray([]) // Returns true
|
|
374
|
+
* isArray('not array') // Returns false
|
|
375
|
+
* isArray({ length: 0 }) // Returns false (array-like object)
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
export function isArray(value) {
|
|
379
|
+
return Array.isArray(value);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Type guard: Check if a value is a plain object (not array, not null)
|
|
383
|
+
*
|
|
384
|
+
* @param value - Value to check
|
|
385
|
+
* @returns true if value is a plain object, false otherwise
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* ```typescript
|
|
389
|
+
* isObject({ a: 1 }) // Returns true
|
|
390
|
+
* isObject({}) // Returns true
|
|
391
|
+
* isObject([]) // Returns false (array)
|
|
392
|
+
* isObject(null) // Returns false
|
|
393
|
+
* isObject(new Date()) // Returns true (object instance)
|
|
394
|
+
* ```
|
|
395
|
+
*/
|
|
396
|
+
export function isObject(value) {
|
|
397
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Type guard: Check if a value is null or undefined
|
|
401
|
+
*
|
|
402
|
+
* @param value - Value to check
|
|
403
|
+
* @returns true if value is null or undefined, false otherwise
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```typescript
|
|
407
|
+
* isNullish(null) // Returns true
|
|
408
|
+
* isNullish(undefined) // Returns true
|
|
409
|
+
* isNullish(0) // Returns false
|
|
410
|
+
* isNullish('') // Returns false
|
|
411
|
+
* isNullish(false) // Returns false
|
|
412
|
+
* ```
|
|
413
|
+
*
|
|
414
|
+
* Note: More precise than falsy check - only null/undefined, not 0/false/''
|
|
415
|
+
*/
|
|
416
|
+
export function isNullish(value) {
|
|
417
|
+
return value === null || value === undefined;
|
|
418
|
+
}
|
|
419
|
+
// ============================================================================
|
|
420
|
+
// Async Utilities
|
|
421
|
+
// ============================================================================
|
|
422
|
+
/**
|
|
423
|
+
* Sleep for a specified duration (async delay)
|
|
424
|
+
*
|
|
425
|
+
* @param ms - Milliseconds to sleep (must be non-negative)
|
|
426
|
+
* @returns Promise that resolves after the specified delay
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```typescript
|
|
430
|
+
* await sleep(1000); // Wait 1 second
|
|
431
|
+
* await sleep(0); // Immediate next tick
|
|
432
|
+
*
|
|
433
|
+
* // Use with async/await
|
|
434
|
+
* async function delayed() {
|
|
435
|
+
* console.log('Start');
|
|
436
|
+
* await sleep(2000);
|
|
437
|
+
* console.log('After 2 seconds');
|
|
438
|
+
* }
|
|
439
|
+
* ```
|
|
440
|
+
*
|
|
441
|
+
* Note: Negative values are clamped to 0 (immediate resolution)
|
|
442
|
+
*/
|
|
443
|
+
export function sleep(ms) {
|
|
444
|
+
const delay = Math.max(0, ms); // Clamp to non-negative
|
|
445
|
+
return new Promise((resolve) => setTimeout(resolve, delay));
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Check if an error is an AbortError (from AbortController timeout)
|
|
449
|
+
*
|
|
450
|
+
* @param error - Error to check
|
|
451
|
+
* @returns true if error is an AbortError, false otherwise
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* ```typescript
|
|
455
|
+
* try {
|
|
456
|
+
* await fetch(url, { signal: controller.signal });
|
|
457
|
+
* } catch (error) {
|
|
458
|
+
* if (isAbortError(error)) {
|
|
459
|
+
* console.log('Request timed out or was aborted');
|
|
460
|
+
* }
|
|
461
|
+
* }
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
export function isAbortError(error) {
|
|
465
|
+
return (error instanceof Error &&
|
|
466
|
+
(error.name === 'AbortError' ||
|
|
467
|
+
error.name === 'TimeoutError' ||
|
|
468
|
+
(error instanceof DOMException && error.code === DOMException.ABORT_ERR)));
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Retry a function multiple times with exponential backoff
|
|
472
|
+
*
|
|
473
|
+
* @param fn - Async function to retry
|
|
474
|
+
* @param maxAttempts - Maximum number of attempts (default: 3, min: 1)
|
|
475
|
+
* @param delayMs - Initial delay in milliseconds (default: 1000, doubles each retry)
|
|
476
|
+
* @returns Promise resolving to function result
|
|
477
|
+
* @throws Last error if all attempts fail
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* ```typescript
|
|
481
|
+
* // Retry API call up to 3 times
|
|
482
|
+
* const data = await retry(
|
|
483
|
+
* () => fetch('https://api.example.com/data').then(r => r.json()),
|
|
484
|
+
* 3,
|
|
485
|
+
* 1000
|
|
486
|
+
* );
|
|
487
|
+
* // Delays: 0ms (try 1), 1000ms (try 2), 2000ms (try 3)
|
|
488
|
+
*
|
|
489
|
+
* // Custom retry logic
|
|
490
|
+
* const result = await retry(
|
|
491
|
+
* async () => {
|
|
492
|
+
* const response = await riskyOperation();
|
|
493
|
+
* if (!response.ok) throw new Error('Not OK');
|
|
494
|
+
* return response;
|
|
495
|
+
* },
|
|
496
|
+
* 5,
|
|
497
|
+
* 500
|
|
498
|
+
* );
|
|
499
|
+
* ```
|
|
500
|
+
*
|
|
501
|
+
* Backoff schedule:
|
|
502
|
+
* - Attempt 1: Immediate (no delay)
|
|
503
|
+
* - Attempt 2: delayMs * 2^0 = delayMs
|
|
504
|
+
* - Attempt 3: delayMs * 2^1 = delayMs * 2
|
|
505
|
+
* - Attempt 4: delayMs * 2^2 = delayMs * 4
|
|
506
|
+
* - etc.
|
|
507
|
+
*
|
|
508
|
+
* @remarks
|
|
509
|
+
* Retries on all errors including AbortError (timeout), allowing
|
|
510
|
+
* transient network issues to be recovered from.
|
|
511
|
+
*/
|
|
512
|
+
export async function retry(fn, maxAttempts = 3, delayMs = 1000) {
|
|
513
|
+
const attempts = Math.max(1, Math.floor(maxAttempts)); // Ensure at least 1 attempt
|
|
514
|
+
let lastError = null;
|
|
515
|
+
for (let i = 0; i < attempts; i++) {
|
|
516
|
+
try {
|
|
517
|
+
return await fn();
|
|
518
|
+
}
|
|
519
|
+
catch (error) {
|
|
520
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
521
|
+
// Log timeout errors specifically for debugging
|
|
522
|
+
if (isAbortError(error)) {
|
|
523
|
+
console.warn(`Request timed out (attempt ${i + 1}/${attempts})`);
|
|
524
|
+
}
|
|
525
|
+
if (i < attempts - 1) {
|
|
526
|
+
await sleep(delayMs * Math.pow(2, i)); // Exponential backoff
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
throw lastError;
|
|
531
|
+
}
|
|
532
|
+
// ============================================================================
|
|
533
|
+
// Data Integrity Utilities
|
|
534
|
+
// ============================================================================
|
|
535
|
+
/**
|
|
536
|
+
* Generate a simple checksum for data integrity checking
|
|
537
|
+
*
|
|
538
|
+
* Uses a non-cryptographic hash function (djb2-like algorithm)
|
|
539
|
+
* Suitable for cache validation and detecting data corruption
|
|
540
|
+
*
|
|
541
|
+
* @param data - Any JSON-serializable data
|
|
542
|
+
* @returns Base-36 encoded hash string
|
|
543
|
+
* @throws Error if data contains circular references or cannot be stringified
|
|
544
|
+
*
|
|
545
|
+
* @example
|
|
546
|
+
* ```typescript
|
|
547
|
+
* const checksum1 = generateChecksum({ a: 1, b: 2 }); // "abc123"
|
|
548
|
+
* const checksum2 = generateChecksum({ a: 1, b: 2 }); // "abc123" (same)
|
|
549
|
+
* const checksum3 = generateChecksum({ a: 1, b: 3 }); // "xyz789" (different)
|
|
550
|
+
*
|
|
551
|
+
* // Use for cache validation
|
|
552
|
+
* const cachedData = { checksum: "abc123", data: {...} };
|
|
553
|
+
* const computedChecksum = generateChecksum(cachedData.data);
|
|
554
|
+
* if (computedChecksum !== cachedData.checksum) {
|
|
555
|
+
* console.warn('Cache corruption detected!');
|
|
556
|
+
* }
|
|
557
|
+
* ```
|
|
558
|
+
*
|
|
559
|
+
* Important notes:
|
|
560
|
+
* - NOT cryptographically secure (do not use for security)
|
|
561
|
+
* - Deterministic: same input always produces same output
|
|
562
|
+
* - Fast and lightweight
|
|
563
|
+
* - Collision-resistant for typical cache validation use cases
|
|
564
|
+
* - Throws on circular references (by JSON.stringify)
|
|
565
|
+
* - Per Issue #7: Uses |0 to properly convert to 32-bit integer
|
|
566
|
+
*/
|
|
567
|
+
export function generateChecksum(data) {
|
|
568
|
+
const str = JSON.stringify(data); // Throws on circular references
|
|
569
|
+
let hash = 0;
|
|
570
|
+
for (let i = 0; i < str.length; i++) {
|
|
571
|
+
const char = str.charCodeAt(i);
|
|
572
|
+
hash = (hash << 5) - hash + char;
|
|
573
|
+
hash = hash | 0; // Per Issue #7: Convert to 32-bit signed integer (|0 is idiomatic)
|
|
574
|
+
}
|
|
575
|
+
return Math.abs(hash).toString(36);
|
|
576
|
+
}
|
|
577
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* k-d Tree implementation for 3D color space (RGB)
|
|
3
|
+
* Per P-7: Spatial indexing for fast nearest neighbor search
|
|
4
|
+
*
|
|
5
|
+
* k-d tree provides O(log n) average case for nearest neighbor queries
|
|
6
|
+
* vs O(n) for linear search, significant improvement for color matching
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Point in 3D space (RGB color)
|
|
10
|
+
*/
|
|
11
|
+
export interface Point3D {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
z: number;
|
|
15
|
+
data?: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* k-d Tree for 3D color space (RGB)
|
|
19
|
+
* Optimized for nearest neighbor search in color matching
|
|
20
|
+
*/
|
|
21
|
+
export declare class KDTree {
|
|
22
|
+
private root;
|
|
23
|
+
private size;
|
|
24
|
+
/**
|
|
25
|
+
* Build k-d tree from array of points
|
|
26
|
+
* @param points - Array of 3D points (RGB colors)
|
|
27
|
+
*/
|
|
28
|
+
constructor(points: Point3D[]);
|
|
29
|
+
/**
|
|
30
|
+
* Recursively build k-d tree using index arrays
|
|
31
|
+
* CORE-PERF-002: Optimized to reduce temporary array allocations
|
|
32
|
+
* @param points - Full points array (never copied)
|
|
33
|
+
* @param indices - Indices into points array for current subtree
|
|
34
|
+
* @param depth - Current depth (determines splitting dimension)
|
|
35
|
+
*/
|
|
36
|
+
private buildTreeOptimized;
|
|
37
|
+
/**
|
|
38
|
+
* Calculate Euclidean distance between two points
|
|
39
|
+
*/
|
|
40
|
+
private distance;
|
|
41
|
+
/**
|
|
42
|
+
* Find nearest neighbor to target point
|
|
43
|
+
* @param target - Target point to search for
|
|
44
|
+
* @param excludeData - Optional function to exclude certain data points
|
|
45
|
+
* @returns Nearest point or null if tree is empty
|
|
46
|
+
*/
|
|
47
|
+
nearestNeighbor(target: Point3D, excludeData?: (data: unknown) => boolean): Point3D | null;
|
|
48
|
+
/**
|
|
49
|
+
* Recursive nearest neighbor search
|
|
50
|
+
*/
|
|
51
|
+
private searchNearest;
|
|
52
|
+
/**
|
|
53
|
+
* Find all points within a distance threshold
|
|
54
|
+
* @param target - Target point
|
|
55
|
+
* @param maxDistance - Maximum distance
|
|
56
|
+
* @param excludeData - Optional function to exclude certain data points
|
|
57
|
+
* @returns Array of points within distance, sorted by distance
|
|
58
|
+
*/
|
|
59
|
+
pointsWithinDistance(target: Point3D, maxDistance: number, excludeData?: (data: unknown) => boolean): Array<{
|
|
60
|
+
point: Point3D;
|
|
61
|
+
distance: number;
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* Recursive search for points within distance
|
|
65
|
+
*/
|
|
66
|
+
private searchWithinDistance;
|
|
67
|
+
/**
|
|
68
|
+
* Get tree size
|
|
69
|
+
*/
|
|
70
|
+
getSize(): number;
|
|
71
|
+
/**
|
|
72
|
+
* Check if tree is empty
|
|
73
|
+
*/
|
|
74
|
+
isEmpty(): boolean;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=kd-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kd-tree.d.ts","sourceRoot":"","sources":["../../src/utils/kd-tree.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAiBD;;;GAGG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,IAAI,CAAa;IAEzB;;;OAGG;gBACS,MAAM,EAAE,OAAO,EAAE;IAW7B;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;IAoC1B;;OAEG;IACH,OAAO,CAAC,QAAQ;IAOhB;;;;;OAKG;IACH,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,GAAG,IAAI;IAS1F;;OAEG;IACH,OAAO,CAAC,aAAa;IA8CrB;;;;;;OAMG;IACH,oBAAoB,CAClB,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,GACvC,KAAK,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAa9C;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA4C5B;;OAEG;IACH,OAAO,IAAI,MAAM;IAIjB;;OAEG;IACH,OAAO,IAAI,OAAO;CAGnB"}
|