@react-hive/honey-utils 3.3.3 → 3.5.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.
@@ -419,11 +419,15 @@ __webpack_require__.r(__webpack_exports__);
419
419
  /* harmony export */ getDOMRectIntersectionRatio: () => (/* binding */ getDOMRectIntersectionRatio),
420
420
  /* harmony export */ getElementOffsetRect: () => (/* binding */ getElementOffsetRect),
421
421
  /* harmony export */ getFocusableHtmlElements: () => (/* binding */ getFocusableHtmlElements),
422
+ /* harmony export */ getLocalStorageCapabilities: () => (/* binding */ getLocalStorageCapabilities),
422
423
  /* harmony export */ isAnchorHtmlElement: () => (/* binding */ isAnchorHtmlElement),
423
424
  /* harmony export */ isContentEditableHtmlElement: () => (/* binding */ isContentEditableHtmlElement),
424
425
  /* harmony export */ isHtmlElementFocusable: () => (/* binding */ isHtmlElementFocusable),
426
+ /* harmony export */ isLocalStorageReadable: () => (/* binding */ isLocalStorageReadable),
425
427
  /* harmony export */ parse2DMatrix: () => (/* binding */ parse2DMatrix)
426
428
  /* harmony export */ });
429
+ /* harmony import */ var _function__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./function */ "./src/function.ts");
430
+
427
431
  const FOCUSABLE_HTML_TAGS = ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON', 'A'];
428
432
  /**
429
433
  * Extracts transformation values (translate, scale, skew) from the 2D transformation matrix of a given HTML element.
@@ -584,6 +588,72 @@ const isHtmlElementFocusable = (element) => {
584
588
  * @returns An array of focusable HTMLElements in DOM order.
585
589
  */
586
590
  const getFocusableHtmlElements = (container) => Array.from(container.querySelectorAll('*')).filter(isHtmlElementFocusable);
591
+ /**
592
+ * Determines whether the browser environment allows safe read access to
593
+ * `localStorage`. Some platforms (e.g., Safari Private Mode, sandboxed iframes)
594
+ * expose `localStorage` but still throw when accessed.
595
+ *
596
+ * This function **only tests read access**, making it safe even when write
597
+ * operations would fail due to `QuotaExceededError` or storage restrictions.
598
+ *
599
+ * @returns `true` if `localStorage` exists and calling `getItem()` does not
600
+ * throw; otherwise `false`.
601
+ */
602
+ const isLocalStorageReadable = () => {
603
+ if (typeof window === 'undefined' || !window.localStorage) {
604
+ return false;
605
+ }
606
+ try {
607
+ window.localStorage.getItem('__non_existing_key__');
608
+ return true;
609
+ }
610
+ catch {
611
+ return false;
612
+ }
613
+ };
614
+ const localStorageCapabilities = {
615
+ readable: false,
616
+ writable: false,
617
+ };
618
+ /**
619
+ * Detects and returns the browser's `localStorage` read and write capabilities.
620
+ * The function is wrapped in `once()`, so capability detection is performed
621
+ * only on the first invocation and cached for all future calls.
622
+ *
623
+ * Detection logic:
624
+ * - **Readable**: Successfully calling `getItem()` without exceptions.
625
+ * - **Writable**: Successfully calling `setItem()` and `removeItem()` without
626
+ * triggering `QuotaExceededError` or other security restrictions.
627
+ *
628
+ * Edge-case behavior:
629
+ * - In some environments, storage may be readable but not writable (e.g.,
630
+ * Safari Private Mode or quota-full situations).
631
+ * - In SSR contexts or restricted frames, both capabilities will be `false`.
632
+ *
633
+ * @returns An object describing the available capabilities.
634
+ */
635
+ const getLocalStorageCapabilities = (0,_function__WEBPACK_IMPORTED_MODULE_0__.once)(() => {
636
+ if (typeof window === 'undefined' || !window.localStorage) {
637
+ return localStorageCapabilities;
638
+ }
639
+ try {
640
+ window.localStorage.getItem('__test_read__');
641
+ localStorageCapabilities.readable = true;
642
+ }
643
+ catch {
644
+ return localStorageCapabilities;
645
+ }
646
+ try {
647
+ const key = '__test_write__';
648
+ window.localStorage.setItem(key, '1');
649
+ window.localStorage.removeItem(key);
650
+ localStorageCapabilities.writable = true;
651
+ }
652
+ catch {
653
+ // Readable but not writable (QuotaExceededError or some restrictions)
654
+ }
655
+ return localStorageCapabilities;
656
+ });
587
657
 
588
658
 
589
659
  /***/ }),
@@ -708,13 +778,8 @@ const readFileSystemDirectoryEntries = async (directoryEntry) => {
708
778
  * Directories themselves are not returned. To avoid unnecessary noise, certain system or
709
779
  * OS-generated files can be excluded via the `skipFiles` option.
710
780
  *
711
- * A progress callback (`onProgress`) may be provided to receive updates each time a file
712
- * is discovered. This is useful when working with large folders or deeply nested structures.
713
- *
714
781
  * @param directoryEntry - The starting directory entry to traverse.
715
782
  * @param options - Optional settings that control traversal behavior.
716
- * @param processed - Internal counter used to track the number of processed files
717
- * during recursive traversal. Not intended to be provided manually.
718
783
  *
719
784
  * @returns A promise resolving to a flat array of all collected `File` objects.
720
785
  */
@@ -727,28 +792,19 @@ const traverseFileSystemDirectory = async (directoryEntry, { skipFiles = [
727
792
  '.Trashes',
728
793
  '.fseventsd',
729
794
  '__MACOSX',
730
- ], onProgress, } = {}, processed = 0) => {
795
+ ], } = {}) => {
731
796
  const skipFilesSet = new Set(skipFiles);
732
797
  const entries = await readFileSystemDirectoryEntries(directoryEntry);
733
798
  const filePromises = await (0,_async__WEBPACK_IMPORTED_MODULE_0__.runParallel)(entries, async (entry) => {
734
799
  if (entry.isDirectory) {
735
800
  return traverseFileSystemDirectory(entry, {
736
801
  skipFiles,
737
- onProgress,
738
- }, processed);
802
+ });
739
803
  }
740
804
  else if (!skipFilesSet.has(entry.name)) {
741
805
  const file = await new Promise((resolve, reject) => {
742
806
  entry.file(resolve, reject);
743
807
  });
744
- if (onProgress) {
745
- processed++;
746
- onProgress({
747
- processed,
748
- path: `${directoryEntry.fullPath}/${entry.name}`,
749
- currentFile: file,
750
- });
751
- }
752
808
  return [file];
753
809
  }
754
810
  return [];
@@ -764,13 +820,9 @@ const traverseFileSystemDirectory = async (directoryEntry, { skipFiles = [
764
820
  * they are traversed recursively using `traverseFileSystemDirectory`, returning a
765
821
  * fully flattened list of nested files.
766
822
  *
767
- * The `traverseOptions` parameter can be used to skip system files or receive
768
- * progress updates during directory traversal.
769
- *
770
823
  * @param dataTransfer - The `DataTransfer` instance from a drop or paste event.
771
824
  * If `null` or missing items, an empty array is returned.
772
825
  * @param traverseOptions - Optional settings passed to directory traversal.
773
- * Useful for skipping unwanted files or enabling `onProgress` callbacks.
774
826
  *
775
827
  * @returns A promise that resolves to a flat array of all extracted `File` objects.
776
828
  * This includes:
@@ -783,7 +835,6 @@ const readFilesFromDataTransfer = async (dataTransfer, traverseOptions = {}) =>
783
835
  if (!items) {
784
836
  return [];
785
837
  }
786
- let processed = 0;
787
838
  const tasks = [];
788
839
  for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
789
840
  const item = items[itemIndex];
@@ -793,35 +844,11 @@ const readFilesFromDataTransfer = async (dataTransfer, traverseOptions = {}) =>
793
844
  // ?.() -> avoids throwing on Safari weirdness
794
845
  const entry = item.webkitGetAsEntry?.();
795
846
  if (entry?.isDirectory) {
796
- tasks.push(traverseFileSystemDirectory(entry, {
797
- ...traverseOptions,
798
- onProgress: progress => {
799
- if (traverseOptions.onProgress) {
800
- processed = progress.processed;
801
- traverseOptions.onProgress({
802
- ...progress,
803
- processed,
804
- });
805
- }
806
- },
807
- }, processed));
847
+ tasks.push(traverseFileSystemDirectory(entry, traverseOptions));
808
848
  continue;
809
849
  }
810
850
  if (entry?.isFile) {
811
- tasks.push(new Promise((resolve, reject) => {
812
- const fileEntry = entry;
813
- fileEntry.file(file => {
814
- resolve([file]);
815
- if (traverseOptions.onProgress) {
816
- processed++;
817
- traverseOptions.onProgress({
818
- processed,
819
- path: `${fileEntry.fullPath}/${fileEntry.name}`,
820
- currentFile: file,
821
- });
822
- }
823
- }, reject);
824
- }));
851
+ tasks.push(new Promise((resolve, reject) => entry.file(file => resolve([file]), reject)));
825
852
  continue;
826
853
  }
827
854
  }
@@ -850,6 +877,7 @@ __webpack_require__.r(__webpack_exports__);
850
877
  /* harmony export */ isFunction: () => (/* binding */ isFunction),
851
878
  /* harmony export */ noop: () => (/* binding */ noop),
852
879
  /* harmony export */ not: () => (/* binding */ not),
880
+ /* harmony export */ once: () => (/* binding */ once),
853
881
  /* harmony export */ retry: () => (/* binding */ retry),
854
882
  /* harmony export */ timeout: () => (/* binding */ timeout)
855
883
  /* harmony export */ });
@@ -1016,6 +1044,35 @@ const retry = (task, { maxAttempts = 3, delayMs = 300, backoff = true, onRetry }
1016
1044
  throw lastError;
1017
1045
  };
1018
1046
  };
1047
+ /**
1048
+ * Wraps a function so that it can only be executed once.
1049
+ * The wrapped function remembers (caches) the result of the first invocation
1050
+ * and returns that same result for all subsequent calls, regardless of the arguments provided.
1051
+ *
1052
+ * Common use cases include:
1053
+ * - initializing singletons
1054
+ * - running setup logic only once
1055
+ * - avoiding repeated expensive computations
1056
+ *
1057
+ * @template T - A function type whose return value should be cached.
1058
+ *
1059
+ * @param fn - The function to execute at most once.
1060
+ *
1061
+ * @returns A new function with the same signature as `fn`, but guaranteed to
1062
+ * execute `fn` only on the first call and return the cached result
1063
+ * thereafter.
1064
+ */
1065
+ const once = (fn) => {
1066
+ let called = false;
1067
+ let result;
1068
+ return function (...args) {
1069
+ if (!called) {
1070
+ called = true;
1071
+ result = fn.apply(this, args);
1072
+ }
1073
+ return result;
1074
+ };
1075
+ };
1019
1076
 
1020
1077
 
1021
1078
  /***/ }),
@@ -1029,10 +1086,12 @@ const retry = (task, { maxAttempts = 3, delayMs = 300, backoff = true, onRetry }
1029
1086
  __webpack_require__.r(__webpack_exports__);
1030
1087
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1031
1088
  /* harmony export */ assert: () => (/* binding */ assert),
1089
+ /* harmony export */ isBlob: () => (/* binding */ isBlob),
1032
1090
  /* harmony export */ isBool: () => (/* binding */ isBool),
1033
1091
  /* harmony export */ isDate: () => (/* binding */ isDate),
1034
1092
  /* harmony export */ isDefined: () => (/* binding */ isDefined),
1035
1093
  /* harmony export */ isEmptyObject: () => (/* binding */ isEmptyObject),
1094
+ /* harmony export */ isError: () => (/* binding */ isError),
1036
1095
  /* harmony export */ isFiniteNumber: () => (/* binding */ isFiniteNumber),
1037
1096
  /* harmony export */ isInteger: () => (/* binding */ isInteger),
1038
1097
  /* harmony export */ isMap: () => (/* binding */ isMap),
@@ -1075,6 +1134,14 @@ const isNil = (value) => value === undefined || value === null;
1075
1134
  * @returns `true` if the value is defined (not `null` or `undefined`); otherwise, `false`.
1076
1135
  */
1077
1136
  const isDefined = (value) => value !== null && value !== undefined;
1137
+ /**
1138
+ * Checks if a value is undefined.
1139
+ *
1140
+ * @param value - The value to check.
1141
+ *
1142
+ * @returns `true` if the value is undefined; otherwise, `false`.
1143
+ */
1144
+ const isUndefined = (value) => value === undefined;
1078
1145
  /**
1079
1146
  * Checks if a value is a number.
1080
1147
  *
@@ -1115,6 +1182,22 @@ const isEmptyObject = (value) => isObject(value) && !isNull(value) && Object.key
1115
1182
  * @returns `true` if the value is a Date object; otherwise, `false`.
1116
1183
  */
1117
1184
  const isDate = (value) => value instanceof Date;
1185
+ /**
1186
+ * Checks if a value is a `Blob`.
1187
+ *
1188
+ * @param value - The value to check.
1189
+ *
1190
+ * @returns `true` if the value is a Blob object; otherwise, `false`.
1191
+ */
1192
+ const isBlob = (value) => value instanceof Blob;
1193
+ /**
1194
+ * Checks if a value is an `Error` object.
1195
+ *
1196
+ * @param value - The value to check.
1197
+ *
1198
+ * @returns `true` if the value is an Error instance; otherwise, `false`.
1199
+ */
1200
+ const isError = (value) => value instanceof Error;
1118
1201
  /**
1119
1202
  * Checks if a value is a valid Date object (not Invalid Date).
1120
1203
  *
@@ -1155,14 +1238,6 @@ const isSet = (value) => value instanceof Set;
1155
1238
  * @returns `true` if the value is a Symbol; otherwise, `false`.
1156
1239
  */
1157
1240
  const isSymbol = (value) => typeof value === 'symbol';
1158
- /**
1159
- * Checks if a value is undefined.
1160
- *
1161
- * @param value - The value to check.
1162
- *
1163
- * @returns `true` if the value is undefined; otherwise, `false`.
1164
- */
1165
- const isUndefined = (value) => value === undefined;
1166
1241
  /**
1167
1242
  * Checks if a value is a finite number.
1168
1243
  *
@@ -1468,22 +1543,26 @@ __webpack_require__.r(__webpack_exports__);
1468
1543
  /* harmony export */ getDOMRectIntersectionRatio: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.getDOMRectIntersectionRatio),
1469
1544
  /* harmony export */ getElementOffsetRect: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.getElementOffsetRect),
1470
1545
  /* harmony export */ getFocusableHtmlElements: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.getFocusableHtmlElements),
1546
+ /* harmony export */ getLocalStorageCapabilities: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.getLocalStorageCapabilities),
1471
1547
  /* harmony export */ hashString: () => (/* reexport safe */ _string__WEBPACK_IMPORTED_MODULE_1__.hashString),
1472
1548
  /* harmony export */ intersection: () => (/* reexport safe */ _array__WEBPACK_IMPORTED_MODULE_2__.intersection),
1473
1549
  /* harmony export */ invokeIfFunction: () => (/* reexport safe */ _function__WEBPACK_IMPORTED_MODULE_3__.invokeIfFunction),
1474
1550
  /* harmony export */ isAnchorHtmlElement: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.isAnchorHtmlElement),
1475
1551
  /* harmony export */ isArray: () => (/* reexport safe */ _array__WEBPACK_IMPORTED_MODULE_2__.isArray),
1552
+ /* harmony export */ isBlob: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isBlob),
1476
1553
  /* harmony export */ isBool: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isBool),
1477
1554
  /* harmony export */ isContentEditableHtmlElement: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.isContentEditableHtmlElement),
1478
1555
  /* harmony export */ isDate: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isDate),
1479
1556
  /* harmony export */ isDefined: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isDefined),
1480
1557
  /* harmony export */ isEmptyArray: () => (/* reexport safe */ _array__WEBPACK_IMPORTED_MODULE_2__.isEmptyArray),
1481
1558
  /* harmony export */ isEmptyObject: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isEmptyObject),
1559
+ /* harmony export */ isError: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isError),
1482
1560
  /* harmony export */ isFile: () => (/* reexport safe */ _file__WEBPACK_IMPORTED_MODULE_7__.isFile),
1483
1561
  /* harmony export */ isFiniteNumber: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isFiniteNumber),
1484
1562
  /* harmony export */ isFunction: () => (/* reexport safe */ _function__WEBPACK_IMPORTED_MODULE_3__.isFunction),
1485
1563
  /* harmony export */ isHtmlElementFocusable: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.isHtmlElementFocusable),
1486
1564
  /* harmony export */ isInteger: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isInteger),
1565
+ /* harmony export */ isLocalStorageReadable: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.isLocalStorageReadable),
1487
1566
  /* harmony export */ isMap: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isMap),
1488
1567
  /* harmony export */ isNil: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isNil),
1489
1568
  /* harmony export */ isNilOrEmptyString: () => (/* reexport safe */ _string__WEBPACK_IMPORTED_MODULE_1__.isNilOrEmptyString),
@@ -1499,6 +1578,7 @@ __webpack_require__.r(__webpack_exports__);
1499
1578
  /* harmony export */ isValidDate: () => (/* reexport safe */ _guards__WEBPACK_IMPORTED_MODULE_4__.isValidDate),
1500
1579
  /* harmony export */ noop: () => (/* reexport safe */ _function__WEBPACK_IMPORTED_MODULE_3__.noop),
1501
1580
  /* harmony export */ not: () => (/* reexport safe */ _function__WEBPACK_IMPORTED_MODULE_3__.not),
1581
+ /* harmony export */ once: () => (/* reexport safe */ _function__WEBPACK_IMPORTED_MODULE_3__.once),
1502
1582
  /* harmony export */ parse2DMatrix: () => (/* reexport safe */ _dom__WEBPACK_IMPORTED_MODULE_6__.parse2DMatrix),
1503
1583
  /* harmony export */ parseFileName: () => (/* reexport safe */ _file__WEBPACK_IMPORTED_MODULE_7__.parseFileName),
1504
1584
  /* harmony export */ pipe: () => (/* reexport safe */ _array__WEBPACK_IMPORTED_MODULE_2__.pipe),