@oscarpalmer/atoms 0.157.0 → 0.158.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.
@@ -244,12 +244,12 @@ function findValues(type, array, parameters, mapper) {
244
244
  const { length } = array;
245
245
  const { bool, key, value } = getParameters(parameters);
246
246
  const callbacks = getArrayCallbacks(bool, key);
247
- if (type === FIND_VALUES_UNIQUE && callbacks?.keyed == null && length >= UNIQUE_THRESHOLD) {
247
+ if (type === "unique" && callbacks?.keyed == null && length >= UNIQUE_THRESHOLD) {
248
248
  result.matched = [...new Set(array)];
249
249
  return result;
250
250
  }
251
251
  const mapCallback = typeof mapper === "function" ? mapper : void 0;
252
- if (callbacks?.bool != null || type === FIND_VALUES_ALL && key == null) {
252
+ if (callbacks?.bool != null || type === "all" && key == null) {
253
253
  const callback = callbacks?.bool ?? ((item) => Object.is(item, value));
254
254
  for (let index = 0; index < length; index += 1) {
255
255
  const item = array[index];
@@ -262,7 +262,7 @@ function findValues(type, array, parameters, mapper) {
262
262
  for (let index = 0; index < length; index += 1) {
263
263
  const item = array[index];
264
264
  const keyed = callbacks?.keyed?.(item, index, array) ?? item;
265
- if (type === FIND_VALUES_ALL && Object.is(keyed, value) || type === FIND_VALUES_UNIQUE && !keys.has(keyed)) {
265
+ if (type === "all" && Object.is(keyed, value) || type === "unique" && !keys.has(keyed)) {
266
266
  keys.add(keyed);
267
267
  result.matched.push(mapCallback?.(item, index, array) ?? item);
268
268
  } else result.notMatched.push(item);
@@ -279,7 +279,6 @@ function getParameters(original) {
279
279
  }
280
280
  const FIND_VALUE_INDEX = "index";
281
281
  const FIND_VALUE_VALUE = "value";
282
- const FIND_VALUES_ALL = "all";
283
282
  const FIND_VALUES_UNIQUE = "unique";
284
283
  const UNIQUE_THRESHOLD = 100;
285
284
  function exists(array, ...parameters) {
@@ -287,11 +286,11 @@ function exists(array, ...parameters) {
287
286
  return findValue(FIND_VALUE_INDEX, array, parameters) > -1;
288
287
  }
289
288
  function filter(array, ...parameters) {
290
- return findValues(FIND_VALUES_ALL, array, parameters).matched;
289
+ return findValues("all", array, parameters).matched;
291
290
  }
292
291
  filter.remove = removeFiltered;
293
292
  function removeFiltered(array, ...parameters) {
294
- return findValues(FIND_VALUES_ALL, array, parameters).notMatched;
293
+ return findValues("all", array, parameters).notMatched;
295
294
  }
296
295
  function find(array, ...parameters) {
297
296
  return findValue(FIND_VALUE_VALUE, array, parameters);
@@ -366,7 +365,7 @@ function intersection(first, second, key) {
366
365
  return compareSets(COMPARE_SETS_INTERSECTION, first, second, key);
367
366
  }
368
367
  function partition(array, ...parameters) {
369
- const { matched, notMatched } = findValues(FIND_VALUES_ALL, array, parameters);
368
+ const { matched, notMatched } = findValues("all", array, parameters);
370
369
  return [matched, notMatched];
371
370
  }
372
371
  /**
@@ -379,7 +378,7 @@ function push(array, pushed) {
379
378
  return insertValues("push", array, pushed, array.length, 0);
380
379
  }
381
380
  function select(array, ...parameters) {
382
- return findValues(FIND_VALUES_ALL, array, parameters, parameters.pop()).matched;
381
+ return findValues("all", array, parameters, parameters.pop()).matched;
383
382
  }
384
383
  function drop(array, first, second) {
385
384
  return extract(EXTRACT_DROP, array, first, second);
@@ -1017,6 +1016,159 @@ function memoize(callback, options) {
1017
1016
  }
1018
1017
  const DEFAULT_CACHE_SIZE = 1024;
1019
1018
  /**
1019
+ * Asserts that a condition is true, throwing an error if it is not
1020
+ * @param condition Condition to assert
1021
+ * @param message Error message
1022
+ * @param error Error constructor
1023
+ */
1024
+ function assert(condition, message, error) {
1025
+ if (!condition()) throw new (error ?? Error)(message);
1026
+ }
1027
+ assert.condition = assertCondition;
1028
+ assert.defined = assertDefined;
1029
+ assert.instanceOf = assertInstanceOf;
1030
+ assert.is = assertIs;
1031
+ /**
1032
+ * Creates an asserter that asserts a condition is true, throwing an error if it is not
1033
+ * @param condition Condition to assert
1034
+ * @param message Error message
1035
+ * @param error Error constructor
1036
+ * @returns Asserter
1037
+ */
1038
+ function assertCondition(condition, message, error) {
1039
+ return (value) => {
1040
+ assert(() => condition(value), message, error);
1041
+ };
1042
+ }
1043
+ /**
1044
+ * Asserts that a value is defined throwing an error if it is not
1045
+ * @param value Value to assert
1046
+ * @param message Error message
1047
+ */
1048
+ function assertDefined(value, message) {
1049
+ assert(() => value != null, message ?? MESSAGE_VALUE_DEFINED);
1050
+ }
1051
+ /**
1052
+ * Creates an asserter that asserts a value is an instance of a constructor, throwing an error if it is not
1053
+ * @param constructor Constructor to check against
1054
+ * @param message Error message
1055
+ * @param error Error constructor
1056
+ * @returns Asserter
1057
+ */
1058
+ function assertInstanceOf(constructor, message, error) {
1059
+ return (value) => {
1060
+ assert(() => value instanceof constructor, message, error);
1061
+ };
1062
+ }
1063
+ /**
1064
+ * Creates an asserter that asserts a value is of a specific type, throwing an error if it is not
1065
+ * @param condition Type guard function to check the value
1066
+ * @param message Error message
1067
+ * @param error Error constructor
1068
+ * @returns Asserter
1069
+ */
1070
+ function assertIs(condition, message, error) {
1071
+ return (value) => {
1072
+ assert(() => condition(value), message, error);
1073
+ };
1074
+ }
1075
+ const MESSAGE_VALUE_DEFINED = "Expected value to be defined";
1076
+ /**
1077
+ * Create an asynchronous function that can only be called once, rejecting or resolving the same result on subsequent calls
1078
+ * @param callback Callback to use once
1079
+ * @returns Once callback
1080
+ */
1081
+ function asyncOnce(callback) {
1082
+ assert(() => typeof callback === "function", MESSAGE_EXPECTATION$1);
1083
+ const state = {
1084
+ called: false,
1085
+ cleared: false,
1086
+ error: false,
1087
+ finished: false,
1088
+ items: [],
1089
+ value: void 0
1090
+ };
1091
+ const fn = (...parameters) => {
1092
+ if (state.cleared) return Promise.reject(new Error(MESSAGE_CLEARED));
1093
+ if (state.finished) return state.error ? Promise.reject(state.value) : Promise.resolve(state.value);
1094
+ if (state.called) return new Promise((resolve, reject) => {
1095
+ state.items.push({
1096
+ reject,
1097
+ resolve
1098
+ });
1099
+ });
1100
+ state.called = true;
1101
+ return new Promise((resolve, reject) => {
1102
+ state.items.push({
1103
+ reject,
1104
+ resolve
1105
+ });
1106
+ callback(...parameters).then((value) => {
1107
+ handleResult$1(state, value, false);
1108
+ }).catch((error) => {
1109
+ handleResult$1(state, error, true);
1110
+ });
1111
+ });
1112
+ };
1113
+ Object.defineProperties(fn, {
1114
+ called: { get: () => state.called },
1115
+ cleared: { get: () => state.cleared },
1116
+ error: { get: () => state.error },
1117
+ finished: { get: () => state.finished }
1118
+ });
1119
+ fn.clear = () => {
1120
+ if (!state.called || !state.finished || state.cleared) return;
1121
+ state.cleared = true;
1122
+ state.value = void 0;
1123
+ };
1124
+ return fn;
1125
+ }
1126
+ function handleResult$1(state, value, error) {
1127
+ state.error = error;
1128
+ state.finished = true;
1129
+ state.value = value;
1130
+ const items = state.items.splice(0);
1131
+ const { length } = items;
1132
+ for (let index = 0; index < length; index += 1) {
1133
+ const { reject, resolve } = items[index];
1134
+ if (error) reject(value);
1135
+ else resolve(value);
1136
+ }
1137
+ }
1138
+ /**
1139
+ * Create a function that can only be called once, returning the same value on subsequent calls
1140
+ * @param callback Callback to use once
1141
+ * @returns Once callback
1142
+ */
1143
+ function once(callback) {
1144
+ assert(() => typeof callback === "function", MESSAGE_EXPECTATION$1);
1145
+ const state = {
1146
+ called: false,
1147
+ cleared: false,
1148
+ value: void 0
1149
+ };
1150
+ const fn = (...parameters) => {
1151
+ if (state.cleared) throw new Error(MESSAGE_CLEARED);
1152
+ if (state.called) return state.value;
1153
+ state.called = true;
1154
+ state.value = callback(...parameters);
1155
+ return state.value;
1156
+ };
1157
+ Object.defineProperties(fn, {
1158
+ called: { get: () => state.called },
1159
+ cleared: { get: () => state.cleared }
1160
+ });
1161
+ fn.clear = () => {
1162
+ if (!state.called || state.cleared) return;
1163
+ state.cleared = true;
1164
+ state.value = void 0;
1165
+ };
1166
+ return fn;
1167
+ }
1168
+ once.async = asyncOnce;
1169
+ const MESSAGE_CLEARED = "Once has been cleared";
1170
+ const MESSAGE_EXPECTATION$1 = "Once expected a function";
1171
+ /**
1020
1172
  * Debounce a function, ensuring it is only called after `time` milliseconds have passed
1021
1173
  *
1022
1174
  * On subsequent calls, the timer is reset and will wait another `time` milliseconds _(and so on...)_
@@ -1135,64 +1287,6 @@ function isOk(value) {
1135
1287
  function isResult(value) {
1136
1288
  return _isResult(value, true) || _isResult(value, false);
1137
1289
  }
1138
- /**
1139
- * Asserts that a condition is true, throwing an error if it is not
1140
- * @param condition Condition to assert
1141
- * @param message Error message
1142
- * @param error Error constructor
1143
- */
1144
- function assert(condition, message, error) {
1145
- if (!condition()) throw new (error ?? Error)(message);
1146
- }
1147
- assert.condition = assertCondition;
1148
- assert.defined = assertDefined;
1149
- assert.instanceOf = assertInstanceOf;
1150
- assert.is = assertIs;
1151
- /**
1152
- * Creates an asserter that asserts a condition is true, throwing an error if it is not
1153
- * @param condition Condition to assert
1154
- * @param message Error message
1155
- * @param error Error constructor
1156
- * @returns Asserter
1157
- */
1158
- function assertCondition(condition, message, error) {
1159
- return (value) => {
1160
- assert(() => condition(value), message, error);
1161
- };
1162
- }
1163
- /**
1164
- * Asserts that a value is defined throwing an error if it is not
1165
- * @param value Value to assert
1166
- * @param message Error message
1167
- */
1168
- function assertDefined(value, message) {
1169
- assert(() => value != null, message ?? MESSAGE_VALUE_DEFINED);
1170
- }
1171
- /**
1172
- * Creates an asserter that asserts a value is an instance of a constructor, throwing an error if it is not
1173
- * @param constructor Constructor to check against
1174
- * @param message Error message
1175
- * @param error Error constructor
1176
- * @returns Asserter
1177
- */
1178
- function assertInstanceOf(constructor, message, error) {
1179
- return (value) => {
1180
- assert(() => value instanceof constructor, message, error);
1181
- };
1182
- }
1183
- /**
1184
- * Creates an asserter that asserts a value is of a specific type, throwing an error if it is not
1185
- * @param condition Type guard function to check the value
1186
- * @param message Error message
1187
- * @param error Error constructor
1188
- * @returns Asserter
1189
- */
1190
- function assertIs(condition, message, error) {
1191
- return (value) => {
1192
- assert(() => condition(value), message, error);
1193
- };
1194
- }
1195
- const MESSAGE_VALUE_DEFINED = "Expected value to be defined";
1196
1290
  function asyncFlow(...fns) {
1197
1291
  assertFlowFunctions(fns);
1198
1292
  return (...args) => asyncWork(args.map((value) => {
@@ -2288,14 +2382,10 @@ function getObserver(first, second, third) {
2288
2382
  }
2289
2383
  const MESSAGE_BEACON = "Cannot retrieve observable from a destroyed beacon";
2290
2384
  const MESSAGE_OBSERVABLE = "Cannot subscribe to a destroyed observable";
2291
- const ALPHA_FULL_HEX_SHORT = "f";
2292
- const ALPHA_FULL_HEX_LONG = `${ALPHA_FULL_HEX_SHORT}${ALPHA_FULL_HEX_SHORT}`;
2293
- const ALPHA_FULL_VALUE = 1;
2294
- const ALPHA_NONE_HEX = "00";
2295
- const ALPHA_NONE_VALUE = 0;
2385
+ const ALPHA_FULL_HEX_LONG = `ff`;
2296
2386
  const DEFAULT_ALPHA = {
2297
2387
  hex: ALPHA_FULL_HEX_LONG,
2298
- value: ALPHA_FULL_VALUE
2388
+ value: 1
2299
2389
  };
2300
2390
  const DEFAULT_HSL = {
2301
2391
  hue: 0,
@@ -2312,8 +2402,6 @@ const EXPRESSION_HEX_SHORT = /^#?([a-f0-9]{3,4})$/i;
2312
2402
  const EXPRESSION_PREFIX = /^#/;
2313
2403
  const HEX_BLACK = "000000";
2314
2404
  const HEX_WHITE = "ffffff";
2315
- const LENGTH_LONG = 6;
2316
- const LENGTH_SHORT = 3;
2317
2405
  const KEYS_HSL = [
2318
2406
  "hue",
2319
2407
  "saturation",
@@ -2326,18 +2414,10 @@ const KEYS_RGB = [
2326
2414
  "blue"
2327
2415
  ];
2328
2416
  const KEYS_RGBA = [...KEYS_RGB, "alpha"];
2329
- const MAX_DEGREE = 360;
2330
- const MAX_HEX = 255;
2331
- const MAX_PERCENT = 100;
2332
- const SRGB_LUMINANCE_BLUE = .0722;
2333
2417
  const SRGB_LUMINANCE_EXPONENT = 2.4;
2334
- const SRGB_LUMINANCE_GREEN = .7152;
2335
- const SRGB_LUMINANCE_MINIMUM = .03928;
2336
2418
  const SRGB_LUMINANCE_MODIFIER = 1.055;
2337
2419
  const SRGB_LUMINANCE_MULTIPLIER = 12.92;
2338
2420
  const SRGB_LUMINANCE_OFFSET = .055;
2339
- const SRGB_LUMINANCE_RED = .2126;
2340
- const SRGB_LUMINANCE_THRESHOLD = .625;
2341
2421
  function formatColor(space, color, alpha) {
2342
2422
  const suffix = alpha ? ` / ${color.alpha}` : "";
2343
2423
  const value = color[space];
@@ -2351,14 +2431,14 @@ function getAlpha(value) {
2351
2431
  if (typeof value === "number") return getAlphaFromValue(value);
2352
2432
  if (typeof value === "string" && value !== ALPHA_FULL_HEX_LONG) return {
2353
2433
  hex: value,
2354
- value: Number.parseInt(value, 16) / MAX_HEX
2434
+ value: Number.parseInt(value, 16) / 255
2355
2435
  };
2356
2436
  return { ...DEFAULT_ALPHA };
2357
2437
  }
2358
2438
  function getAlphaHexadecimal(value) {
2359
- if (value === ALPHA_NONE_VALUE) return ALPHA_NONE_HEX;
2360
- if (value === ALPHA_FULL_VALUE) return ALPHA_FULL_HEX_LONG;
2361
- return Math.round(value * MAX_HEX).toString(16);
2439
+ if (value === 0) return "00";
2440
+ if (value === 1) return ALPHA_FULL_HEX_LONG;
2441
+ return Math.round(value * 255).toString(16);
2362
2442
  }
2363
2443
  function getAlphaFromValue(value) {
2364
2444
  const alpha = getAlphaValue(value);
@@ -2368,18 +2448,18 @@ function getAlphaFromValue(value) {
2368
2448
  };
2369
2449
  }
2370
2450
  function getAlphaValue(original) {
2371
- if (Number.isNaN(original) || original >= MAX_PERCENT || original === ALPHA_FULL_VALUE) return ALPHA_FULL_VALUE;
2372
- if (original < ALPHA_NONE_VALUE) return ALPHA_NONE_VALUE;
2373
- return original <= ALPHA_FULL_VALUE ? original : original / MAX_PERCENT;
2451
+ if (Number.isNaN(original) || original >= 100 || original === 1) return 1;
2452
+ if (original < 0) return 0;
2453
+ return original <= 1 ? original : original / 100;
2374
2454
  }
2375
2455
  function hasKeys(value, keys) {
2376
2456
  return typeof value === "object" && value !== null && keys.every((key) => key in value);
2377
2457
  }
2378
2458
  function isAlpha(value) {
2379
- return typeof value === "number" && between(value, ALPHA_NONE_VALUE, ALPHA_FULL_VALUE);
2459
+ return typeof value === "number" && between(value, 0, 1);
2380
2460
  }
2381
2461
  function isBytey(value) {
2382
- return typeof value === "number" && between(value, 0, MAX_HEX);
2462
+ return typeof value === "number" && between(value, 0, 255);
2383
2463
  }
2384
2464
  /**
2385
2465
  * Is the value a Color?
@@ -2401,7 +2481,7 @@ function isColorValue(obj, properties) {
2401
2481
  return true;
2402
2482
  }
2403
2483
  function isDegree(value) {
2404
- return typeof value === "number" && between(value, 0, MAX_DEGREE);
2484
+ return typeof value === "number" && between(value, 0, 360);
2405
2485
  }
2406
2486
  /**
2407
2487
  * Is the value a hex color?
@@ -2412,7 +2492,7 @@ function isDegree(value) {
2412
2492
  function isHexColor(value, alpha) {
2413
2493
  if (typeof value !== "string") return false;
2414
2494
  if (!(EXPRESSION_HEX_SHORT.test(value) || EXPRESSION_HEX_LONG.test(value))) return false;
2415
- if (alpha === false) return value.length === LENGTH_SHORT || value.length === LENGTH_LONG;
2495
+ if (alpha === false) return value.length === 3 || value.length === 6;
2416
2496
  return true;
2417
2497
  }
2418
2498
  /**
@@ -2454,7 +2534,7 @@ function isRgbLike(value) {
2454
2534
  return hasKeys(value, KEYS_RGB);
2455
2535
  }
2456
2536
  function isPercentage(value) {
2457
- return typeof value === "number" && between(value, 0, MAX_PERCENT);
2537
+ return typeof value === "number" && between(value, 0, 100);
2458
2538
  }
2459
2539
  const validators = {
2460
2540
  alpha: isAlpha,
@@ -2476,17 +2556,17 @@ function getClampedValue(value, minimum, maximum) {
2476
2556
  function getForegroundColor(value) {
2477
2557
  const { blue, green, red } = getState(value).rgb;
2478
2558
  const values = [
2479
- blue / MAX_HEX,
2480
- green / MAX_HEX,
2481
- red / MAX_HEX
2559
+ blue / 255,
2560
+ green / 255,
2561
+ red / 255
2482
2562
  ];
2483
2563
  const { length } = values;
2484
2564
  for (let index = 0; index < length; index += 1) {
2485
2565
  const color = values[index];
2486
- if (color <= SRGB_LUMINANCE_MINIMUM) values[index] /= SRGB_LUMINANCE_MULTIPLIER;
2566
+ if (color <= .03928) values[index] /= SRGB_LUMINANCE_MULTIPLIER;
2487
2567
  else values[index] = ((color + SRGB_LUMINANCE_OFFSET) / SRGB_LUMINANCE_MODIFIER) ** SRGB_LUMINANCE_EXPONENT;
2488
2568
  }
2489
- return new Color(SRGB_LUMINANCE_RED * values[2] + SRGB_LUMINANCE_GREEN * values[1] + SRGB_LUMINANCE_BLUE * values[0] > SRGB_LUMINANCE_THRESHOLD ? HEX_BLACK : HEX_WHITE);
2569
+ return new Color(.2126 * values[2] + .7152 * values[1] + .0722 * values[0] > .625 ? HEX_BLACK : HEX_WHITE);
2490
2570
  }
2491
2571
  /**
2492
2572
  * Get the hex color _(with alpha channel)_ from any kind of value
@@ -2506,10 +2586,10 @@ function getHexColor(value) {
2506
2586
  return getState(value).hex;
2507
2587
  }
2508
2588
  function getHexValue(value) {
2509
- return getClampedValue(value, 0, MAX_HEX);
2589
+ return getClampedValue(value, 0, 255);
2510
2590
  }
2511
2591
  function getDegrees(value) {
2512
- return getClampedValue(value, 0, MAX_DEGREE);
2592
+ return getClampedValue(value, 0, 360);
2513
2593
  }
2514
2594
  /**
2515
2595
  * Get the HSLA color from any kind of value
@@ -2532,7 +2612,7 @@ function getHslColor(value) {
2532
2612
  return getState(value).hsl;
2533
2613
  }
2534
2614
  function getPercentage(value) {
2535
- return getClampedValue(value, 0, MAX_PERCENT);
2615
+ return getClampedValue(value, 0, 100);
2536
2616
  }
2537
2617
  /**
2538
2618
  * Get the RGBA color from any kind of value
@@ -2569,9 +2649,9 @@ function convertRgbToHex(rgb, alpha) {
2569
2649
  }
2570
2650
  function convertRgbToHsla(value) {
2571
2651
  const rgb = isRgbLike(value) ? getRgbValue(value) : { ...DEFAULT_RGB };
2572
- const blue = rgb.blue / MAX_HEX;
2573
- const green = rgb.green / MAX_HEX;
2574
- const red = rgb.red / MAX_HEX;
2652
+ const blue = rgb.blue / 255;
2653
+ const green = rgb.green / 255;
2654
+ const red = rgb.red / 255;
2575
2655
  const maxHex = Math.max(blue, green, red);
2576
2656
  const minHex = Math.min(blue, green, red);
2577
2657
  const delta = maxHex - minHex;
@@ -2594,10 +2674,10 @@ function convertRgbToHsla(value) {
2594
2674
  hue *= 60;
2595
2675
  }
2596
2676
  return {
2597
- alpha: getAlphaValue(value.alpha ?? ALPHA_FULL_VALUE),
2677
+ alpha: getAlphaValue(value.alpha ?? 1),
2598
2678
  hue: +hue.toFixed(2),
2599
- lightness: +(lightness * MAX_PERCENT).toFixed(2),
2600
- saturation: +(saturation * MAX_PERCENT).toFixed(2)
2679
+ lightness: +(lightness * 100).toFixed(2),
2680
+ saturation: +(saturation * 100).toFixed(2)
2601
2681
  };
2602
2682
  }
2603
2683
  function getRgbValue(value) {
@@ -2648,7 +2728,7 @@ function convertHexToRgba(value) {
2648
2728
  const { length } = pairs;
2649
2729
  for (let index = 1; index < length; index += 1) values.push(Number.parseInt(pairs[index], 16));
2650
2730
  return {
2651
- alpha: values[3] / MAX_HEX,
2731
+ alpha: values[3] / 255,
2652
2732
  blue: values[2],
2653
2733
  green: values[1],
2654
2734
  red: values[0]
@@ -2664,8 +2744,8 @@ function getNormalizedHex(value, alpha) {
2664
2744
  const includeAlpha = alpha ?? false;
2665
2745
  if (!isHexColor(value)) return `${HEX_BLACK}${includeAlpha ? ALPHA_FULL_HEX_LONG : ""}`;
2666
2746
  const normalized = value.replace(EXPRESSION_PREFIX, "");
2667
- if (normalized.length < LENGTH_LONG) return join(`${normalized.slice(0, LENGTH_SHORT)}${includeAlpha ? normalized[LENGTH_SHORT] ?? ALPHA_FULL_HEX_SHORT : ""}`.split("").map((character) => character.repeat(2)));
2668
- return `${normalized.slice(0, LENGTH_LONG)}${includeAlpha ? normalized.slice(LENGTH_LONG) || ALPHA_FULL_HEX_LONG : ""}`;
2747
+ if (normalized.length < 6) return join(`${normalized.slice(0, 3)}${includeAlpha ? normalized[3] ?? "f" : ""}`.split("").map((character) => character.repeat(2)));
2748
+ return `${normalized.slice(0, 6)}${includeAlpha ? normalized.slice(6) || ALPHA_FULL_HEX_LONG : ""}`;
2669
2749
  }
2670
2750
  function hexToHsl(value) {
2671
2751
  const { hue, lightness, saturation } = hexToHsla(value);
@@ -2701,11 +2781,11 @@ function hexToRgba(value) {
2701
2781
  }
2702
2782
  function convertHslToRgba(value) {
2703
2783
  const hsl = isHslLike(value) ? getHslValue(value) : { ...DEFAULT_HSL };
2704
- const hue = hsl.hue % MAX_DEGREE;
2705
- const saturation = hsl.saturation / MAX_PERCENT;
2706
- const lightness = hsl.lightness / MAX_PERCENT;
2784
+ const hue = hsl.hue % 360;
2785
+ const saturation = hsl.saturation / 100;
2786
+ const lightness = hsl.lightness / 100;
2707
2787
  return {
2708
- alpha: getAlphaValue(value.alpha ?? ALPHA_FULL_VALUE),
2788
+ alpha: getAlphaValue(value.alpha ?? 1),
2709
2789
  blue: getHexValue(Math.round(getHexyValue(hue, lightness, saturation, 4))),
2710
2790
  green: getHexValue(Math.round(getHexyValue(hue, lightness, saturation, 8))),
2711
2791
  red: getHexValue(Math.round(getHexyValue(hue, lightness, saturation, 0)))
@@ -2713,7 +2793,7 @@ function convertHslToRgba(value) {
2713
2793
  }
2714
2794
  function getHexyValue(hue, lightness, saturation, value) {
2715
2795
  const part = (value + hue / 30) % 12;
2716
- return (lightness - saturation * Math.min(lightness, 1 - lightness) * Math.max(-1, Math.min(part - 3, 9 - part, 1))) * MAX_HEX;
2796
+ return (lightness - saturation * Math.min(lightness, 1 - lightness) * Math.max(-1, Math.min(part - 3, 9 - part, 1))) * 255;
2717
2797
  }
2718
2798
  function getHslValue(value) {
2719
2799
  return {
@@ -2753,12 +2833,12 @@ function hslToRgba(hsl) {
2753
2833
  function getState(value) {
2754
2834
  if (typeof value === "string") {
2755
2835
  const normalized = getNormalizedHex(value, true);
2756
- const hex = normalized.slice(0, LENGTH_LONG);
2836
+ const hex = normalized.slice(0, 6);
2757
2837
  const rgb = hexToRgb(hex);
2758
2838
  return {
2759
2839
  hex,
2760
2840
  rgb,
2761
- alpha: getAlpha(normalized.slice(LENGTH_LONG)),
2841
+ alpha: getAlpha(normalized.slice(6)),
2762
2842
  hsl: rgbToHsl(rgb)
2763
2843
  };
2764
2844
  }
@@ -2784,7 +2864,7 @@ function getState(value) {
2784
2864
  return state;
2785
2865
  }
2786
2866
  }
2787
- state.alpha ??= getAlpha(ALPHA_FULL_VALUE);
2867
+ state.alpha ??= getAlpha(1);
2788
2868
  state.hex ??= String(HEX_BLACK);
2789
2869
  state.hsl ??= { ...DEFAULT_HSL };
2790
2870
  state.rgb ??= { ...DEFAULT_RGB };
@@ -2793,12 +2873,12 @@ function getState(value) {
2793
2873
  function setHexColor(state, value, alpha) {
2794
2874
  if (!isHexColor(value) || !alpha && value === state.hex) return;
2795
2875
  const normalized = getNormalizedHex(value, true);
2796
- const hex = normalized.slice(0, LENGTH_LONG);
2876
+ const hex = normalized.slice(0, 6);
2797
2877
  const rgb = hexToRgb(hex);
2798
2878
  state.hex = hex;
2799
2879
  state.hsl = rgbToHsl(rgb);
2800
2880
  state.rgb = rgb;
2801
- if (alpha) state.alpha = getAlpha(normalized.slice(LENGTH_LONG));
2881
+ if (alpha) state.alpha = getAlpha(normalized.slice(6));
2802
2882
  }
2803
2883
  function setHSLColor(state, value, alpha) {
2804
2884
  if (!isHslLike(value)) return;
@@ -3329,11 +3409,11 @@ function cancelable(executor) {
3329
3409
  function handleResult(status, parameters) {
3330
3410
  const { abort, complete, data, handlers, index, signal, value } = parameters;
3331
3411
  if (signal?.aborted ?? false) return;
3332
- if (!complete && status === PROMISE_TYPE_REJECTED) {
3412
+ if (!complete && status === "rejected") {
3333
3413
  settlePromise(abort, handlers.reject, value, signal);
3334
3414
  return;
3335
3415
  }
3336
- data.result[index] = !complete ? value : status === PROMISE_TYPE_FULFILLED ? {
3416
+ data.result[index] = !complete ? value : status === "fulfilled" ? {
3337
3417
  status,
3338
3418
  value
3339
3419
  } : {
@@ -3923,4 +4003,4 @@ var SizedSet = class extends Set {
3923
4003
  }
3924
4004
  }
3925
4005
  };
3926
- export { CancelablePromise, PromiseTimeoutError, QueueError, RetryError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, difference, drop, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, toResult, fromQuery, toPromise as fromResult, toPromise, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hasValue, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, slice, smush, snakeCase, sort, splice, startsWith, sum, take, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toSet, toggle, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
4006
+ export { CancelablePromise, PromiseTimeoutError, QueueError, RetryError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, difference, drop, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, toResult, fromQuery, toPromise as fromResult, toPromise, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hasValue, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, once, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, slice, smush, snakeCase, sort, splice, startsWith, sum, take, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toSet, toggle, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
@@ -1,6 +1,7 @@
1
1
  import { noop } from "../internal/function/misc.js";
2
2
  import { TIMER_DEBOUNCE, TIMER_THROTTLE, getTimer } from "../internal/function/timer.js";
3
3
  import { memoize } from "./memoize.js";
4
+ import { once } from "./once.js";
4
5
  /**
5
6
  * Debounce a function, ensuring it is only called after `time` milliseconds have passed
6
7
  *
@@ -21,4 +22,4 @@ function debounce(callback, time) {
21
22
  function throttle(callback, time) {
22
23
  return getTimer(TIMER_THROTTLE, callback, time);
23
24
  }
24
- export { debounce, memoize, noop, throttle };
25
+ export { debounce, memoize, noop, once, throttle };
@@ -0,0 +1,97 @@
1
+ import { assert } from "./assert.js";
2
+ /**
3
+ * Create an asynchronous function that can only be called once, rejecting or resolving the same result on subsequent calls
4
+ * @param callback Callback to use once
5
+ * @returns Once callback
6
+ */
7
+ function asyncOnce(callback) {
8
+ assert(() => typeof callback === "function", MESSAGE_EXPECTATION);
9
+ const state = {
10
+ called: false,
11
+ cleared: false,
12
+ error: false,
13
+ finished: false,
14
+ items: [],
15
+ value: void 0
16
+ };
17
+ const fn = (...parameters) => {
18
+ if (state.cleared) return Promise.reject(new Error(MESSAGE_CLEARED));
19
+ if (state.finished) return state.error ? Promise.reject(state.value) : Promise.resolve(state.value);
20
+ if (state.called) return new Promise((resolve, reject) => {
21
+ state.items.push({
22
+ reject,
23
+ resolve
24
+ });
25
+ });
26
+ state.called = true;
27
+ return new Promise((resolve, reject) => {
28
+ state.items.push({
29
+ reject,
30
+ resolve
31
+ });
32
+ callback(...parameters).then((value) => {
33
+ handleResult(state, value, false);
34
+ }).catch((error) => {
35
+ handleResult(state, error, true);
36
+ });
37
+ });
38
+ };
39
+ Object.defineProperties(fn, {
40
+ called: { get: () => state.called },
41
+ cleared: { get: () => state.cleared },
42
+ error: { get: () => state.error },
43
+ finished: { get: () => state.finished }
44
+ });
45
+ fn.clear = () => {
46
+ if (!state.called || !state.finished || state.cleared) return;
47
+ state.cleared = true;
48
+ state.value = void 0;
49
+ };
50
+ return fn;
51
+ }
52
+ function handleResult(state, value, error) {
53
+ state.error = error;
54
+ state.finished = true;
55
+ state.value = value;
56
+ const items = state.items.splice(0);
57
+ const { length } = items;
58
+ for (let index = 0; index < length; index += 1) {
59
+ const { reject, resolve } = items[index];
60
+ if (error) reject(value);
61
+ else resolve(value);
62
+ }
63
+ }
64
+ /**
65
+ * Create a function that can only be called once, returning the same value on subsequent calls
66
+ * @param callback Callback to use once
67
+ * @returns Once callback
68
+ */
69
+ function once(callback) {
70
+ assert(() => typeof callback === "function", MESSAGE_EXPECTATION);
71
+ const state = {
72
+ called: false,
73
+ cleared: false,
74
+ value: void 0
75
+ };
76
+ const fn = (...parameters) => {
77
+ if (state.cleared) throw new Error(MESSAGE_CLEARED);
78
+ if (state.called) return state.value;
79
+ state.called = true;
80
+ state.value = callback(...parameters);
81
+ return state.value;
82
+ };
83
+ Object.defineProperties(fn, {
84
+ called: { get: () => state.called },
85
+ cleared: { get: () => state.cleared }
86
+ });
87
+ fn.clear = () => {
88
+ if (!state.called || state.cleared) return;
89
+ state.cleared = true;
90
+ state.value = void 0;
91
+ };
92
+ return fn;
93
+ }
94
+ once.async = asyncOnce;
95
+ var MESSAGE_CLEARED = "Once has been cleared";
96
+ var MESSAGE_EXPECTATION = "Once expected a function";
97
+ export { once };
package/dist/index.js CHANGED
@@ -42,6 +42,7 @@ import { hslToHex, hslToRgb, hslToRgba } from "./color/space/hsl.js";
42
42
  import { getColor } from "./color/index.js";
43
43
  import { SizedMap } from "./sized/map.js";
44
44
  import { memoize } from "./function/memoize.js";
45
+ import { once } from "./function/once.js";
45
46
  import { debounce, throttle } from "./function/index.js";
46
47
  import { RetryError, retry } from "./function/retry.js";
47
48
  import { isError, isOk, isResult } from "./internal/result.js";
@@ -76,4 +77,4 @@ import { QueueError, queue } from "./queue.js";
76
77
  import { getRandomBoolean, getRandomCharacters, getRandomColor, getRandomHex, getRandomItem, getRandomItems } from "./random.js";
77
78
  import { attempt } from "./result/index.js";
78
79
  import { SizedSet } from "./sized/set.js";
79
- export { CancelablePromise, PromiseTimeoutError, QueueError, RetryError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, difference, drop, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, fromQuery, toPromise as fromResult, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hasValue, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, slice, smush, snakeCase, sort, splice, startsWith, sum, take, template, throttle, timed, times, titleCase, toMap, toPromise, toQuery, toRecord, toResult, toSet, toggle, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
80
+ export { CancelablePromise, PromiseTimeoutError, QueueError, RetryError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, difference, drop, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, fromQuery, toPromise as fromResult, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hasValue, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, once, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, retry, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, slice, smush, snakeCase, sort, splice, startsWith, sum, take, template, throttle, timed, times, titleCase, toMap, toPromise, toQuery, toRecord, toResult, toSet, toggle, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "jsdom": "^28.1",
11
11
  "oxfmt": "^0.36",
12
12
  "oxlint": "^1.51",
13
- "rolldown": "1.0.0-rc.6",
13
+ "rolldown": "1.0.0-rc.7",
14
14
  "tslib": "^2.8",
15
15
  "typescript": "^5.9",
16
16
  "vite": "8.0.0-beta.16",
@@ -184,5 +184,5 @@
184
184
  },
185
185
  "type": "module",
186
186
  "types": "./types/index.d.ts",
187
- "version": "0.157.0"
188
- }
187
+ "version": "0.158.0"
188
+ }
@@ -37,5 +37,6 @@ export function throttle<Callback extends GenericCallback>(
37
37
 
38
38
  export {noop} from '../internal/function/misc';
39
39
  export {memoize, type Memoized, type MemoizedOptions} from './memoize';
40
+ export {once} from './once';
40
41
 
41
42
  // #endregion
@@ -0,0 +1,188 @@
1
+ import type {
2
+ GenericAsyncCallback,
3
+ GenericCallback,
4
+ OnceAsyncCallback,
5
+ OnceCallback,
6
+ } from '../models';
7
+ import {assert} from './assert';
8
+
9
+ // #region Types
10
+
11
+ type OnceAsyncItem<Value> = {
12
+ reject: (reason?: unknown) => void;
13
+ resolve: (value: Value) => void;
14
+ };
15
+
16
+ type OnceAsyncState<Value> = {
17
+ error: boolean;
18
+ finished: boolean;
19
+ items: OnceAsyncItem<Value>[];
20
+ } & OnceState<Value>;
21
+
22
+ type OnceState<Value> = {
23
+ called: boolean;
24
+ cleared: boolean;
25
+ value: Value;
26
+ };
27
+
28
+ // #endregion
29
+
30
+ // #region Functions
31
+
32
+ /**
33
+ * Create an asynchronous function that can only be called once, rejecting or resolving the same result on subsequent calls
34
+ * @param callback Callback to use once
35
+ * @returns Once callback
36
+ */
37
+ function asyncOnce<Callback extends GenericAsyncCallback>(
38
+ callback: Callback,
39
+ ): OnceAsyncCallback<Callback> {
40
+ assert(() => typeof callback === 'function', MESSAGE_EXPECTATION);
41
+
42
+ const state: OnceAsyncState<Awaited<ReturnType<Callback>>> = {
43
+ called: false,
44
+ cleared: false,
45
+ error: false,
46
+ finished: false,
47
+ items: [],
48
+ value: undefined as never,
49
+ };
50
+
51
+ const fn = (...parameters: Parameters<Callback>): Promise<Awaited<ReturnType<Callback>>> => {
52
+ if (state.cleared) {
53
+ return Promise.reject(new Error(MESSAGE_CLEARED));
54
+ }
55
+
56
+ if (state.finished) {
57
+ return state.error ? Promise.reject(state.value) : Promise.resolve(state.value);
58
+ }
59
+
60
+ if (state.called) {
61
+ return new Promise<Awaited<ReturnType<Callback>>>((resolve, reject) => {
62
+ state.items.push({reject, resolve});
63
+ });
64
+ }
65
+
66
+ state.called = true;
67
+
68
+ return new Promise<Awaited<ReturnType<Callback>>>((resolve, reject) => {
69
+ state.items.push({reject, resolve});
70
+
71
+ void callback(...parameters)
72
+ .then(value => {
73
+ handleResult(state, value, false);
74
+ })
75
+ .catch(error => {
76
+ handleResult(state, error, true);
77
+ });
78
+ });
79
+ };
80
+
81
+ Object.defineProperties(fn, {
82
+ called: {
83
+ get: (): boolean => state.called,
84
+ },
85
+ cleared: {
86
+ get: (): boolean => state.cleared,
87
+ },
88
+ error: {
89
+ get: (): boolean => state.error,
90
+ },
91
+ finished: {
92
+ get: (): boolean => state.finished,
93
+ },
94
+ });
95
+
96
+ fn.clear = (): void => {
97
+ if (!state.called || !state.finished || state.cleared) {
98
+ return;
99
+ }
100
+
101
+ state.cleared = true;
102
+ state.value = undefined as never;
103
+ };
104
+
105
+ return fn as OnceAsyncCallback<Callback>;
106
+ }
107
+
108
+ function handleResult<Value>(state: OnceAsyncState<Value>, value: unknown, error: boolean): void {
109
+ state.error = error;
110
+ state.finished = true;
111
+ state.value = value as Value;
112
+
113
+ const items = state.items.splice(0);
114
+ const {length} = items;
115
+
116
+ for (let index = 0; index < length; index += 1) {
117
+ const {reject, resolve} = items[index];
118
+
119
+ if (error) {
120
+ reject(value);
121
+ } else {
122
+ resolve(value as Value);
123
+ }
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Create a function that can only be called once, returning the same value on subsequent calls
129
+ * @param callback Callback to use once
130
+ * @returns Once callback
131
+ */
132
+ export function once<Callback extends GenericCallback>(callback: Callback): OnceCallback<Callback> {
133
+ assert(() => typeof callback === 'function', MESSAGE_EXPECTATION);
134
+
135
+ const state: OnceState<ReturnType<Callback>> = {
136
+ called: false,
137
+ cleared: false,
138
+ value: undefined as never,
139
+ };
140
+
141
+ const fn = (...parameters: Parameters<Callback>): ReturnType<Callback> => {
142
+ if (state.cleared) {
143
+ throw new Error(MESSAGE_CLEARED);
144
+ }
145
+
146
+ if (state.called) {
147
+ return state.value;
148
+ }
149
+
150
+ state.called = true;
151
+
152
+ state.value = callback(...parameters);
153
+
154
+ return state.value;
155
+ };
156
+
157
+ Object.defineProperties(fn, {
158
+ called: {
159
+ get: (): boolean => state.called,
160
+ },
161
+ cleared: {
162
+ get: (): boolean => state.cleared,
163
+ },
164
+ });
165
+
166
+ fn.clear = (): void => {
167
+ if (!state.called || state.cleared) {
168
+ return;
169
+ }
170
+
171
+ state.cleared = true;
172
+ state.value = undefined as never;
173
+ };
174
+
175
+ return fn as OnceCallback<Callback>;
176
+ }
177
+
178
+ once.async = asyncOnce;
179
+
180
+ // #endregion
181
+
182
+ // #region Variables
183
+
184
+ const MESSAGE_CLEARED = 'Once has been cleared';
185
+
186
+ const MESSAGE_EXPECTATION = 'Once expected a function';
187
+
188
+ // #endregion
package/src/models.ts CHANGED
@@ -140,6 +140,41 @@ export type NumericalValues<Item extends PlainObject> = {
140
140
  [Key in keyof Item as Item[Key] extends number ? Key : never]: Item[Key];
141
141
  };
142
142
 
143
+ /**
144
+ * An asynchronous function that can only be called once, returning the same value on subsequent calls
145
+ */
146
+ export type OnceAsyncCallback<Callback extends GenericAsyncCallback> = {
147
+ /**
148
+ * Did the callback's promise reject?
149
+ */
150
+ readonly error: boolean;
151
+ /**
152
+ * Has the callback finished?
153
+ */
154
+ readonly finished: boolean;
155
+ } & Callback &
156
+ OnceCallbackProperties;
157
+
158
+ /**
159
+ * A callback function that can only be called once, returning the same value on subsequent calls
160
+ */
161
+ export type OnceCallback<Callback extends GenericCallback> = Callback & OnceCallbackProperties;
162
+
163
+ type OnceCallbackProperties = {
164
+ /**
165
+ * Has the callback been called?
166
+ */
167
+ readonly called: boolean;
168
+ /**
169
+ * Has the callback's value been cleared?
170
+ */
171
+ readonly cleared: boolean;
172
+ /**
173
+ * Clear the callback's cached value
174
+ */
175
+ clear: () => void;
176
+ };
177
+
143
178
  /**
144
179
  * A generic object
145
180
  */
@@ -17,3 +17,4 @@ export declare function debounce<Callback extends GenericCallback>(callback: Cal
17
17
  export declare function throttle<Callback extends GenericCallback>(callback: Callback, time?: number): CancelableCallback<Callback>;
18
18
  export { noop } from '../internal/function/misc';
19
19
  export { memoize, type Memoized, type MemoizedOptions } from './memoize';
20
+ export { once } from './once';
@@ -0,0 +1,17 @@
1
+ import type { GenericAsyncCallback, GenericCallback, OnceAsyncCallback, OnceCallback } from '../models';
2
+ /**
3
+ * Create an asynchronous function that can only be called once, rejecting or resolving the same result on subsequent calls
4
+ * @param callback Callback to use once
5
+ * @returns Once callback
6
+ */
7
+ declare function asyncOnce<Callback extends GenericAsyncCallback>(callback: Callback): OnceAsyncCallback<Callback>;
8
+ /**
9
+ * Create a function that can only be called once, returning the same value on subsequent calls
10
+ * @param callback Callback to use once
11
+ * @returns Once callback
12
+ */
13
+ export declare function once<Callback extends GenericCallback>(callback: Callback): OnceCallback<Callback>;
14
+ export declare namespace once {
15
+ var async: typeof asyncOnce;
16
+ }
17
+ export {};
package/types/models.d.ts CHANGED
@@ -83,6 +83,37 @@ export type NumericalKeys<Value> = {
83
83
  export type NumericalValues<Item extends PlainObject> = {
84
84
  [Key in keyof Item as Item[Key] extends number ? Key : never]: Item[Key];
85
85
  };
86
+ /**
87
+ * An asynchronous function that can only be called once, returning the same value on subsequent calls
88
+ */
89
+ export type OnceAsyncCallback<Callback extends GenericAsyncCallback> = {
90
+ /**
91
+ * Did the callback's promise reject?
92
+ */
93
+ readonly error: boolean;
94
+ /**
95
+ * Has the callback finished?
96
+ */
97
+ readonly finished: boolean;
98
+ } & Callback & OnceCallbackProperties;
99
+ /**
100
+ * A callback function that can only be called once, returning the same value on subsequent calls
101
+ */
102
+ export type OnceCallback<Callback extends GenericCallback> = Callback & OnceCallbackProperties;
103
+ type OnceCallbackProperties = {
104
+ /**
105
+ * Has the callback been called?
106
+ */
107
+ readonly called: boolean;
108
+ /**
109
+ * Has the callback's value been cleared?
110
+ */
111
+ readonly cleared: boolean;
112
+ /**
113
+ * Clear the callback's cached value
114
+ */
115
+ clear: () => void;
116
+ };
86
117
  /**
87
118
  * A generic object
88
119
  */