rooks
Version:
Collection of awesome react hooks
1,006 lines (999 loc) • 31.1 kB
JavaScript
import {
useBroadcastChannel
} from "./chunk-4BDODBWX.js";
// src/hooks/useSuspenseNavigatorUserAgentData.ts
var DEFAULT_HINTS = [
"architecture",
"bitness",
"formFactors",
"fullVersionList",
"model",
"platformVersion",
"uaFullVersion",
"wow64"
];
var cache = /* @__PURE__ */ new Map();
function createCacheKey(hints) {
return hints.sort().join(",");
}
function useSuspenseNavigatorUserAgentData(hints = DEFAULT_HINTS) {
if (typeof navigator === "undefined" || !navigator.userAgentData) {
throw new Error(
"Navigator User Agent Data API is not supported in this browser. This API is currently only available in Chromium-based browsers."
);
}
const cacheKey = createCacheKey(hints);
let entry = cache.get(cacheKey);
if (!entry) {
const promise = navigator.userAgentData.getHighEntropyValues(hints);
entry = {
promise,
status: "pending"
};
cache.set(cacheKey, entry);
promise.then((result) => {
entry.status = "resolved";
entry.result = result;
}).catch((error) => {
entry.status = "rejected";
entry.error = error instanceof Error ? error : new Error(String(error));
});
}
if (entry.status === "pending") {
throw entry.promise;
}
if (entry.status === "rejected") {
throw entry.error;
}
return entry.result;
}
// src/hooks/useSuspenseNavigatorBattery.ts
import { useState, useEffect } from "react";
var cacheEntry = null;
function useSuspenseNavigatorBattery() {
if (typeof navigator === "undefined" || !navigator.getBattery) {
throw new Error(
"Navigator Battery API is not supported in this browser. This API requires HTTPS and is not supported in all browsers."
);
}
const [batteryState, setBatteryState] = useState(null);
if (!cacheEntry) {
const promise = navigator.getBattery();
cacheEntry = {
promise,
status: "pending"
};
promise.then((result) => {
cacheEntry.status = "resolved";
cacheEntry.result = result;
}).catch((error) => {
cacheEntry.status = "rejected";
cacheEntry.error = error instanceof Error ? error : new Error(String(error));
});
}
if (cacheEntry.status === "pending") {
throw cacheEntry.promise;
}
if (cacheEntry.status === "rejected") {
throw cacheEntry.error;
}
const initialBattery = cacheEntry.result;
useEffect(() => {
if (!initialBattery) return;
setBatteryState({
charging: initialBattery.charging,
chargingTime: initialBattery.chargingTime,
dischargingTime: initialBattery.dischargingTime,
level: initialBattery.level,
addEventListener: initialBattery.addEventListener.bind(initialBattery),
removeEventListener: initialBattery.removeEventListener.bind(initialBattery)
});
const handleChargingChange = () => {
setBatteryState((prev) => prev ? {
...prev,
charging: initialBattery.charging
} : null);
};
const handleChargingTimeChange = () => {
setBatteryState((prev) => prev ? {
...prev,
chargingTime: initialBattery.chargingTime
} : null);
};
const handleDischargingTimeChange = () => {
setBatteryState((prev) => prev ? {
...prev,
dischargingTime: initialBattery.dischargingTime
} : null);
};
const handleLevelChange = () => {
setBatteryState((prev) => prev ? {
...prev,
level: initialBattery.level
} : null);
};
initialBattery.addEventListener("chargingchange", handleChargingChange);
initialBattery.addEventListener("chargingtimechange", handleChargingTimeChange);
initialBattery.addEventListener("dischargingtimechange", handleDischargingTimeChange);
initialBattery.addEventListener("levelchange", handleLevelChange);
return () => {
initialBattery.removeEventListener("chargingchange", handleChargingChange);
initialBattery.removeEventListener("chargingtimechange", handleChargingTimeChange);
initialBattery.removeEventListener("dischargingtimechange", handleDischargingTimeChange);
initialBattery.removeEventListener("levelchange", handleLevelChange);
};
}, [initialBattery]);
return batteryState || initialBattery;
}
// src/hooks/useSuspenseFavicon.ts
import { useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
var MANAGED_FAVICON_ATTRIBUTE = "data-rooks-managed-favicon";
var ABSOLUTE_URL_PATTERN = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
var resource = {
status: "idle",
currentValue: null,
subscribers: /* @__PURE__ */ new Set(),
originalLinks: null,
managedLink: null,
isManaging: false,
instanceCount: 0,
instanceStrategies: /* @__PURE__ */ new Map()
};
function toError(error) {
return error instanceof Error ? error : new Error(String(error));
}
function ensureBrowserEnvironment() {
if (typeof window === "undefined" || typeof document === "undefined") {
throw new Error(
"useSuspenseFavicon can only be used in a browser environment."
);
}
}
function getDocumentHead() {
ensureBrowserEnvironment();
if (!document.head) {
throw new Error("useSuspenseFavicon requires document.head to be available.");
}
return document.head;
}
function isIconLink(link) {
const relAttribute = link.getAttribute("rel");
if (!relAttribute) {
return false;
}
return relAttribute.split(/\s+/).map((token) => token.trim().toLowerCase()).filter(Boolean).includes("icon");
}
function getExplicitFaviconLinks() {
const head = getDocumentHead();
return Array.from(head.querySelectorAll("link[rel][href]")).filter(
(node) => node instanceof HTMLLinkElement && isIconLink(node)
);
}
function normalizeRelativeHref(url) {
return `${url.pathname}${url.search}${url.hash}`;
}
function createSameOriginFavicon(url, relativeHref = normalizeRelativeHref(url)) {
return {
kind: "same-origin",
relativeHref,
href: url.href
};
}
function classifyFaviconHref(href, preferredRelativeHref) {
const resolved = new URL(href, document.baseURI);
if (resolved.origin === window.location.origin) {
return createSameOriginFavicon(resolved, preferredRelativeHref);
}
return {
kind: "external",
url: resolved.href,
href: resolved.href
};
}
function discoverCurrentFavicon() {
ensureBrowserEnvironment();
const links = getExplicitFaviconLinks();
for (let index = links.length - 1; index >= 0; index -= 1) {
const href = links[index]?.getAttribute("href");
if (!href) {
continue;
}
try {
return classifyFaviconHref(href);
} catch {
continue;
}
}
return null;
}
function createInitializationPromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(discoverCurrentFavicon());
} catch (error) {
reject(toError(error));
}
}, 0);
});
}
function initializeResourceIfNeeded() {
ensureBrowserEnvironment();
if (resource.status !== "idle") {
return;
}
const promise = createInitializationPromise();
resource.status = "pending";
resource.promise = promise;
promise.then((result) => {
resource.status = "resolved";
resource.result = result;
resource.currentValue = result;
}).catch((error) => {
resource.status = "rejected";
resource.error = toError(error);
});
}
function notifySubscribers(nextValue) {
resource.currentValue = nextValue;
resource.result = nextValue;
resource.status = "resolved";
resource.error = void 0;
for (const subscriber of resource.subscribers) {
subscriber(nextValue);
}
}
function ensureManagedMode() {
if (resource.isManaging) {
return;
}
const faviconLinks = getExplicitFaviconLinks();
resource.originalLinks = faviconLinks.map((link) => {
const placeholder = document.createComment("rooks-favicon-placeholder");
link.parentNode?.insertBefore(placeholder, link);
link.remove();
return {
link,
placeholder
};
});
resource.isManaging = true;
resource.managedLink = null;
}
function getOrCreateManagedLink() {
const head = getDocumentHead();
if (resource.managedLink?.isConnected) {
return resource.managedLink;
}
const link = document.createElement("link");
link.setAttribute("rel", "icon");
link.setAttribute(MANAGED_FAVICON_ATTRIBUTE, "true");
head.appendChild(link);
resource.managedLink = link;
return link;
}
function cleanupManagedLinkReference() {
if (resource.managedLink?.isConnected) {
resource.managedLink.remove();
}
resource.managedLink = null;
}
function restoreOriginalLinks() {
const head = typeof document !== "undefined" ? document.head : null;
cleanupManagedLinkReference();
if (!resource.originalLinks) {
resource.isManaging = false;
return;
}
for (const { link, placeholder } of resource.originalLinks) {
if (placeholder.parentNode) {
placeholder.parentNode.replaceChild(link, placeholder);
} else if (head) {
head.appendChild(link);
}
}
resource.originalLinks = null;
resource.isManaging = false;
}
function discardOriginalSnapshot() {
if (resource.originalLinks) {
for (const { placeholder } of resource.originalLinks) {
placeholder.remove();
}
}
resource.originalLinks = null;
resource.isManaging = false;
resource.managedLink = null;
}
function resetInitializationState() {
resource.status = "idle";
resource.promise = void 0;
resource.result = void 0;
resource.error = void 0;
resource.currentValue = null;
}
function cleanupAfterLastUnmount(strategy) {
if (resource.isManaging || resource.originalLinks || resource.managedLink) {
if (strategy === "restore-originals") {
restoreOriginalLinks();
} else {
discardOriginalSnapshot();
}
}
resource.instanceStrategies.clear();
resource.instanceCount = 0;
resetInitializationState();
}
function resolveSameOriginRelativeHref(relativeHref) {
ensureBrowserEnvironment();
const trimmedRelativeHref = relativeHref.trim();
if (!trimmedRelativeHref) {
throw new Error(
"useSuspenseFavicon expected a non-empty same-origin relativeHref."
);
}
if (ABSOLUTE_URL_PATTERN.test(trimmedRelativeHref) || trimmedRelativeHref.startsWith("//")) {
throw new Error(
"useSuspenseFavicon expected same-origin updates to use a relative href string."
);
}
const resolved = new URL(trimmedRelativeHref, document.baseURI);
if (resolved.origin !== window.location.origin) {
throw new Error(
"useSuspenseFavicon expected same-origin updates to resolve to the current origin."
);
}
return resolved;
}
function resolveExternalUrl(url) {
ensureBrowserEnvironment();
const trimmedUrl = url.trim();
if (!trimmedUrl) {
throw new Error("useSuspenseFavicon expected a non-empty external url.");
}
const resolved = new URL(trimmedUrl, document.baseURI);
if (resolved.origin === window.location.origin) {
throw new Error(
"useSuspenseFavicon expected external updates to resolve to a different origin."
);
}
return resolved;
}
function updateFaviconURL(config) {
ensureBrowserEnvironment();
ensureManagedMode();
const managedLink = getOrCreateManagedLink();
if (config.kind === "same-origin") {
const trimmedRelativeHref = config.relativeHref.trim();
const resolved2 = resolveSameOriginRelativeHref(trimmedRelativeHref);
managedLink.setAttribute("href", trimmedRelativeHref);
notifySubscribers(createSameOriginFavicon(resolved2, trimmedRelativeHref));
return;
}
const resolved = resolveExternalUrl(config.url);
managedLink.setAttribute("href", resolved.href);
notifySubscribers({
kind: "external",
url: resolved.href,
href: resolved.href
});
}
function useSuspenseFavicon(options = {}) {
const { unmountStrategy = "restore-originals" } = options;
const instanceId = useRef(/* @__PURE__ */ Symbol("useSuspenseFavicon"));
const latestUnmountStrategy = useRef(unmountStrategy);
latestUnmountStrategy.current = unmountStrategy;
initializeResourceIfNeeded();
if (resource.status === "pending") {
throw resource.promise;
}
if (resource.status === "rejected") {
throw resource.error;
}
const [favicon, setFavicon] = useState2(resource.currentValue);
useEffect2(() => {
const subscriber = (nextFavicon) => {
setFavicon(nextFavicon);
};
resource.subscribers.add(subscriber);
resource.instanceCount += 1;
resource.instanceStrategies.set(
instanceId.current,
latestUnmountStrategy.current
);
setFavicon(resource.currentValue);
return () => {
resource.subscribers.delete(subscriber);
resource.instanceStrategies.delete(instanceId.current);
resource.instanceCount = Math.max(0, resource.instanceCount - 1);
if (resource.instanceCount === 0) {
cleanupAfterLastUnmount(latestUnmountStrategy.current);
}
};
}, []);
useEffect2(() => {
resource.instanceStrategies.set(instanceId.current, unmountStrategy);
}, [unmountStrategy]);
const controls = useMemo(
() => ({
updateFaviconURL
}),
[]
);
return [favicon, controls];
}
// src/hooks/useSuspenseLocalStorageState.ts
import { useState as useState3, useEffect as useEffect3, useCallback, useMemo as useMemo2 } from "react";
var cache2 = /* @__PURE__ */ new Map();
function getValueFromLocalStorage(key) {
if (typeof localStorage === "undefined") {
return null;
}
try {
return localStorage.getItem(key);
} catch (error) {
console.error(`Failed to get value from localStorage for key "${key}":`, error);
return null;
}
}
function saveValueToLocalStorage(key, value) {
if (typeof localStorage === "undefined") {
return;
}
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(`Failed to save value to localStorage for key "${key}":`, error);
}
}
function removeValueFromLocalStorage(key) {
if (typeof localStorage === "undefined") {
return;
}
try {
localStorage.removeItem(key);
} catch (error) {
console.error(`Failed to remove value from localStorage for key "${key}":`, error);
}
}
function createInitializationPromise2(key, initializer) {
return new Promise((resolve, reject) => {
try {
setTimeout(() => {
try {
const rawValue = getValueFromLocalStorage(key);
const initializedValue = initializer(rawValue);
resolve(initializedValue);
} catch (error) {
reject(error instanceof Error ? error : new Error(String(error)));
}
}, 0);
} catch (error) {
reject(error instanceof Error ? error : new Error(String(error)));
}
});
}
function useSuspenseLocalStorageState(key, initializer) {
let cacheEntry2 = cache2.get(key);
if (!cacheEntry2) {
const promise = createInitializationPromise2(key, initializer);
cacheEntry2 = {
promise,
status: "pending"
};
cache2.set(key, cacheEntry2);
promise.then((result) => {
const entry = cache2.get(key);
if (entry) {
entry.status = "resolved";
entry.result = result;
}
}).catch((error) => {
const entry = cache2.get(key);
if (entry) {
entry.status = "rejected";
entry.error = error instanceof Error ? error : new Error(String(error));
}
});
}
if (cacheEntry2.status === "pending") {
throw cacheEntry2.promise;
}
if (cacheEntry2.status === "rejected") {
throw cacheEntry2.error;
}
const initialValue = cacheEntry2.result;
const [value, setValue] = useState3(initialValue);
const [isDeleted, setIsDeleted] = useState3(false);
const customEventTypeName = useMemo2(() => {
return `rooks-${key}-localstorage-update`;
}, [key]);
useEffect3(() => {
if (!isDeleted) {
saveValueToLocalStorage(key, value);
}
}, [key, value, isDeleted]);
const handleStorageEvent = useCallback(
(event) => {
if (event.storageArea === localStorage && event.key === key) {
try {
if (event.newValue === null) {
const newValue = initializer(null);
setValue(newValue);
} else {
const newValue = initializer(event.newValue);
setValue(newValue);
}
} catch (error) {
console.error(`Failed to handle storage event for key "${key}":`, error);
}
}
},
[key, initializer]
);
const handleCustomEvent = useCallback(
(event) => {
try {
const { newValue } = event.detail;
setValue(newValue);
} catch (error) {
console.error(`Failed to handle custom event for key "${key}":`, error);
}
},
[key]
);
useEffect3(() => {
if (typeof window !== "undefined") {
window.addEventListener("storage", handleStorageEvent);
return () => {
window.removeEventListener("storage", handleStorageEvent);
};
}
}, [handleStorageEvent]);
useEffect3(() => {
if (typeof document !== "undefined") {
document.addEventListener(
customEventTypeName,
handleCustomEvent
);
return () => {
document.removeEventListener(
customEventTypeName,
handleCustomEvent
);
};
}
}, [customEventTypeName, handleCustomEvent]);
const broadcastValueWithinDocument = useCallback(
(newValue) => {
if (typeof document !== "undefined") {
const event = new CustomEvent(
customEventTypeName,
{ detail: { newValue } }
);
document.dispatchEvent(event);
}
},
[customEventTypeName]
);
const controls = useMemo2(() => ({
getItem: () => value,
setItem: (newValue) => {
setIsDeleted(false);
setValue(newValue);
broadcastValueWithinDocument(newValue);
},
deleteItem: () => {
removeValueFromLocalStorage(key);
const resetValue = initializer(null);
setIsDeleted(true);
setValue(resetValue);
broadcastValueWithinDocument(resetValue);
}
}), [value, key, initializer, broadcastValueWithinDocument]);
return [value, controls];
}
// src/hooks/useSuspenseSessionStorageState.ts
import { useState as useState4, useEffect as useEffect4, useCallback as useCallback2, useMemo as useMemo3 } from "react";
var cache3 = /* @__PURE__ */ new Map();
function getValueFromSessionStorage(key) {
if (typeof sessionStorage === "undefined") {
return null;
}
try {
return sessionStorage.getItem(key);
} catch (error) {
console.error(`Failed to get value from sessionStorage for key "${key}":`, error);
return null;
}
}
function saveValueToSessionStorage(key, value) {
if (typeof sessionStorage === "undefined") {
return;
}
try {
sessionStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(`Failed to save value to sessionStorage for key "${key}":`, error);
}
}
function removeValueFromSessionStorage(key) {
if (typeof sessionStorage === "undefined") {
return;
}
try {
sessionStorage.removeItem(key);
} catch (error) {
console.error(`Failed to remove value from sessionStorage for key "${key}":`, error);
}
}
function createInitializationPromise3(key, initializer) {
return new Promise((resolve, reject) => {
try {
setTimeout(() => {
try {
const rawValue = getValueFromSessionStorage(key);
const initializedValue = initializer(rawValue);
resolve(initializedValue);
} catch (error) {
reject(error instanceof Error ? error : new Error(String(error)));
}
}, 0);
} catch (error) {
reject(error instanceof Error ? error : new Error(String(error)));
}
});
}
function useSuspenseSessionStorageState(key, initializer) {
let cacheEntry2 = cache3.get(key);
if (!cacheEntry2) {
const promise = createInitializationPromise3(key, initializer);
cacheEntry2 = {
promise,
status: "pending"
};
cache3.set(key, cacheEntry2);
promise.then((result) => {
const entry = cache3.get(key);
if (entry) {
entry.status = "resolved";
entry.result = result;
}
}).catch((error) => {
const entry = cache3.get(key);
if (entry) {
entry.status = "rejected";
entry.error = error instanceof Error ? error : new Error(String(error));
}
});
}
if (cacheEntry2.status === "pending") {
throw cacheEntry2.promise;
}
if (cacheEntry2.status === "rejected") {
throw cacheEntry2.error;
}
const initialValue = cacheEntry2.result;
const [value, setValue] = useState4(initialValue);
const [isDeleted, setIsDeleted] = useState4(false);
const customEventTypeName = useMemo3(() => {
return `rooks-${key}-sessionstorage-update`;
}, [key]);
useEffect4(() => {
if (!isDeleted) {
saveValueToSessionStorage(key, value);
}
}, [key, value, isDeleted]);
const handleStorageEvent = useCallback2(
(event) => {
if (event.storageArea === sessionStorage && event.key === key) {
try {
if (event.newValue === null) {
const newValue = initializer(null);
setValue(newValue);
} else {
const newValue = initializer(event.newValue);
setValue(newValue);
}
} catch (error) {
console.error(`Failed to handle storage event for key "${key}":`, error);
}
}
},
[key, initializer]
);
const handleCustomEvent = useCallback2(
(event) => {
try {
const { newValue } = event.detail;
setValue(newValue);
} catch (error) {
console.error(`Failed to handle custom event for key "${key}":`, error);
}
},
[key]
);
useEffect4(() => {
if (typeof window !== "undefined") {
window.addEventListener("storage", handleStorageEvent);
return () => {
window.removeEventListener("storage", handleStorageEvent);
};
}
}, [handleStorageEvent]);
useEffect4(() => {
if (typeof document !== "undefined") {
document.addEventListener(
customEventTypeName,
handleCustomEvent
);
return () => {
document.removeEventListener(
customEventTypeName,
handleCustomEvent
);
};
}
}, [customEventTypeName, handleCustomEvent]);
const broadcastValueWithinDocument = useCallback2(
(newValue) => {
if (typeof document !== "undefined") {
const event = new CustomEvent(
customEventTypeName,
{ detail: { newValue } }
);
document.dispatchEvent(event);
}
},
[customEventTypeName]
);
const controls = useMemo3(() => ({
getItem: () => value,
setItem: (newValue) => {
setIsDeleted(false);
setValue(newValue);
broadcastValueWithinDocument(newValue);
},
deleteItem: () => {
removeValueFromSessionStorage(key);
const resetValue = initializer(null);
setIsDeleted(true);
setValue(resetValue);
broadcastValueWithinDocument(resetValue);
}
}), [value, key, initializer, broadcastValueWithinDocument]);
return [value, controls];
}
// src/hooks/useSuspenseIndexedDBState.ts
import { useState as useState5, useCallback as useCallback3, useMemo as useMemo4, useRef as useRef2 } from "react";
var cache4 = /* @__PURE__ */ new Map();
function isIndexedDBSupported() {
return typeof indexedDB !== "undefined";
}
function openDatabase(dbName, version, storeName) {
return new Promise((resolve, reject) => {
if (!isIndexedDBSupported()) {
reject(new Error("IndexedDB is not supported in this environment"));
return;
}
const request = indexedDB.open(dbName, version);
request.onerror = () => {
reject(new Error(`Failed to open database "${dbName}": ${request.error?.message}`));
};
request.onsuccess = () => {
resolve(request.result);
};
request.onupgradeneeded = () => {
const db = request.result;
if (!db.objectStoreNames.contains(storeName)) {
db.createObjectStore(storeName);
}
};
});
}
async function getValueFromIndexedDB(dbName, storeName, key, version) {
try {
const db = await openDatabase(dbName, version, storeName);
return new Promise((resolve, reject) => {
const transaction = db.transaction([storeName], "readonly");
const store = transaction.objectStore(storeName);
const request = store.get(key);
request.onerror = () => {
reject(new Error(`Failed to get value for key "${key}": ${request.error?.message}`));
};
request.onsuccess = () => {
resolve(request.result ?? null);
};
transaction.oncomplete = () => {
db.close();
};
});
} catch (error) {
console.error(`Failed to get value from IndexedDB for key "${key}":`, error);
return null;
}
}
async function saveValueToIndexedDB(dbName, storeName, key, value, version) {
try {
const db = await openDatabase(dbName, version, storeName);
return new Promise((resolve, reject) => {
const transaction = db.transaction([storeName], "readwrite");
const store = transaction.objectStore(storeName);
const request = store.put(value, key);
request.onerror = () => {
reject(new Error(`Failed to save value for key "${key}": ${request.error?.message}`));
};
request.onsuccess = () => {
resolve();
};
transaction.oncomplete = () => {
db.close();
};
});
} catch (error) {
console.error(`Failed to save value to IndexedDB for key "${key}":`, error);
throw error;
}
}
async function removeValueFromIndexedDB(dbName, storeName, key, version) {
try {
const db = await openDatabase(dbName, version, storeName);
return new Promise((resolve, reject) => {
const transaction = db.transaction([storeName], "readwrite");
const store = transaction.objectStore(storeName);
const request = store.delete(key);
request.onerror = () => {
reject(new Error(`Failed to remove value for key "${key}": ${request.error?.message}`));
};
request.onsuccess = () => {
resolve();
};
transaction.oncomplete = () => {
db.close();
};
});
} catch (error) {
console.error(`Failed to remove value from IndexedDB for key "${key}":`, error);
throw error;
}
}
function createInitializationPromise4(dbName, storeName, key, version, initializer) {
return new Promise((resolve, reject) => {
getValueFromIndexedDB(dbName, storeName, key, version).then((rawValue) => {
try {
const initializedValue = initializer(rawValue);
resolve(initializedValue);
} catch (error) {
reject(error instanceof Error ? error : new Error(String(error)));
}
}).catch((error) => {
reject(error instanceof Error ? error : new Error(String(error)));
});
});
}
function useSuspenseIndexedDBState(key, initializer, config = {}) {
const {
dbName = "rooks-db",
storeName = "state",
version = 1
} = config;
const cacheKey = `${dbName}:${storeName}:${key}:${version}`;
let cacheEntry2 = cache4.get(cacheKey);
if (!cacheEntry2) {
const promise = createInitializationPromise4(dbName, storeName, key, version, initializer);
cacheEntry2 = {
promise,
status: "pending"
};
cache4.set(cacheKey, cacheEntry2);
promise.then((result) => {
const entry = cache4.get(cacheKey);
if (entry) {
entry.status = "resolved";
entry.result = result;
}
}).catch((error) => {
const entry = cache4.get(cacheKey);
if (entry) {
entry.status = "rejected";
entry.error = error instanceof Error ? error : new Error(String(error));
}
});
}
if (cacheEntry2.status === "pending") {
throw cacheEntry2.promise;
}
if (cacheEntry2.status === "rejected") {
throw cacheEntry2.error;
}
const initialValue = cacheEntry2.result;
const [value, setValue] = useState5(initialValue);
const channelName = useMemo4(() => {
return `rooks-indexeddb-${dbName}-${storeName}`;
}, [dbName, storeName]);
const initializerRef = useRef2(initializer);
initializerRef.current = initializer;
const handleBroadcastMessage = useCallback3(
(data) => {
const { type, key: messageKey, value: messageValue, dbName: msgDbName, storeName: msgStoreName } = data;
if (msgDbName === dbName && msgStoreName === storeName && messageKey === key) {
try {
if (type === "SET" && messageValue !== void 0) {
setValue(messageValue);
const entry = cache4.get(cacheKey);
if (entry && entry.status === "resolved") {
entry.result = messageValue;
}
} else if (type === "DELETE") {
const resetValue = initializerRef.current(null);
setValue(resetValue);
const entry = cache4.get(cacheKey);
if (entry && entry.status === "resolved") {
entry.result = resetValue;
}
}
} catch (error) {
console.error(`Failed to handle broadcast message for key "${key}":`, error);
}
}
},
[dbName, storeName, key, cacheKey]
);
const { postMessage: broadcastMessage, isSupported: isBroadcastSupported } = useBroadcastChannel(
channelName,
{
onMessage: handleBroadcastMessage,
onError: (error) => {
console.error(`BroadcastChannel error for key "${key}":`, error);
}
}
);
const broadcastChange = useCallback3(
(type, newValue) => {
if (isBroadcastSupported) {
const message = {
type,
key,
value: newValue,
dbName,
storeName
};
broadcastMessage(message);
}
},
[broadcastMessage, isBroadcastSupported, key, dbName, storeName]
);
const controls = useMemo4(() => ({
getItem: () => value,
setItem: async (newValue) => {
try {
await saveValueToIndexedDB(dbName, storeName, key, newValue, version);
setValue(newValue);
broadcastChange("SET", newValue);
} catch (error) {
console.error(`Failed to set item in IndexedDB for key "${key}":`, error);
throw error;
}
},
deleteItem: async () => {
try {
await removeValueFromIndexedDB(dbName, storeName, key, version);
const resetValue = initializerRef.current(null);
setValue(resetValue);
broadcastChange("DELETE");
} catch (error) {
console.error(`Failed to delete item from IndexedDB for key "${key}":`, error);
throw error;
}
}
}), [value, dbName, storeName, key, version, broadcastChange]);
return [value, controls];
}
export {
useSuspenseFavicon,
useSuspenseIndexedDBState,
useSuspenseLocalStorageState,
useSuspenseNavigatorBattery,
useSuspenseNavigatorUserAgentData,
useSuspenseSessionStorageState
};