import type { StringAutoCompletable } from "#Source/type/index.ts" import type { Use } from "./basic.ts" import { useFactory } from "./basic.ts" /** * @description Define built-in and custom feature keys for capability detection. */ export type Feature = | StringAutoCompletable | "process" | "crypto" | "navigator" | "permissions" | "document" | "css" interface FeatureRegistryItem { feature: Feature detect: () => boolean } const internalFeatureRegistry = new Map() /** * @description Check whether a feature is registered. */ export const isFeatureRegistered = (feature: Feature): boolean => { return internalFeatureRegistry.has(feature) } /** * @description Register a feature detector. */ export const registerFeature = (item: FeatureRegistryItem): void => { internalFeatureRegistry.set(item.feature, item) } /** * @description Unregister a feature detector. */ export const unregisterFeature = (feature: Feature): void => { internalFeatureRegistry.delete(feature) } /** * @description List all registered feature keys. */ export const listFeatures = (): Feature[] => { return Array.from(internalFeatureRegistry.keys()) } /** * @description Get a feature registry item by feature key. */ export const getFeatureRegistryItem = (feature: Feature): FeatureRegistryItem | undefined => { return internalFeatureRegistry.get(feature) } /** * @description Define detection results for registered features. */ export type FeatureFlags = Record /** * @description Get the flags for all registered features. */ export const getFeatureFlags = (): FeatureFlags => { const flags: FeatureFlags = {} as FeatureFlags for (const [name, item] of internalFeatureRegistry) { flags[name] = item.detect() } return flags } /** * @description Check if the current environment satisfies the specified feature. */ export const isSatisfiesFeature = (feature: Feature): boolean => { const featureRegistryItem = getFeatureRegistryItem(feature) if (featureRegistryItem === undefined) { throw new Error(`Feature "${feature}" is not registered.`) } return featureRegistryItem.detect() === true } /** * @description Check if the current environment satisfies the specified feature conditions. */ export const isSatisfiesFeatures = (condition: Partial): boolean => { let result = true Object.keys(condition).forEach((feature) => { const featureRegistryItem = getFeatureRegistryItem(feature as Feature) if (featureRegistryItem === undefined) { throw new Error(`Feature "${feature}" is not registered.`) } if (featureRegistryItem.detect() !== condition[feature]) { result = false } }) return result } /** * @description Check if the `process` object is available in the environment. */ export const supportProcess = (): boolean => { return typeof process !== "undefined" && process !== null && typeof process === "object" } /** * @description Get the `process` object from the current environment. */ export const getProcess = (): NodeJS.Process => { return globalThis.process } /** * @description Get the `process` object if it is available, otherwise throw an error. */ export const ensureProcess = (): NodeJS.Process => { if (supportProcess() === true) { return getProcess() } else { throw new Error("The process object is not supported in the current environment.") } } /** * @description Execute a function with the `process` object if it is available. */ export const useProcess: Use = useFactory(getProcess, supportProcess) registerFeature({ feature: "process", detect: supportProcess, }) /** * @description Check if the `crypto` object is available in the environment. */ export const supportCrypto = (): boolean => { return typeof crypto !== "undefined" && crypto !== null && typeof crypto === "object" } /** * @description Get the `crypto` object from the current environment. */ export const getCrypto = (): Crypto => { return globalThis.crypto } /** * @description Get the `crypto` object if it is available, otherwise throw an error. */ export const ensureCrypto = (): Crypto => { if (supportCrypto() === true) { return getCrypto() } else { throw new Error("The crypto object is not supported in the current environment.") } } /** * @description Execute a function with the `crypto` object if it is available. */ export const useCrypto: Use = useFactory(getCrypto, supportCrypto) registerFeature({ feature: "crypto", detect: supportCrypto, }) /** * @description Check if the `navigator` object is available in the environment. */ export const supportNavigator = (): boolean => { return typeof navigator !== "undefined" && navigator !== null && typeof navigator === "object" } /** * @description Get the `navigator` object from the current environment. */ export const getNavigator = (): Navigator => { return globalThis.navigator } /** * @description Get the `navigator` object if it is available, otherwise throw an error. */ export const ensureNavigator = (): Navigator => { if (supportNavigator() === true) { return getNavigator() } else { throw new Error("The navigator object is not supported in the current environment.") } } /** * @description Execute a function with the `navigator` object if it is available. */ export const useNavigator: Use = useFactory(getNavigator, supportNavigator) registerFeature({ feature: "navigator", detect: supportNavigator, }) /** * @description Check if the `clipboard` API is available in the environment. */ export const supportClipboard = (): boolean => { return supportNavigator() && "clipboard" in navigator } /** * @description Get the `clipboard` API from the current environment. */ export const getClipboard = (): Clipboard => { return navigator.clipboard } /** * @description Get the `clipboard` API if it is available, otherwise throw an error. */ export const ensureClipboard = (): Clipboard => { if (supportClipboard() === true) { return getClipboard() } else { throw new Error("The clipboard API is not supported in the current environment.") } } /** * @description Execute a function with the `clipboard` API if it is available. */ export const useClipboard: Use = useFactory(getClipboard, supportClipboard) registerFeature({ feature: "clipboard", detect: supportClipboard, }) /** * @description Check if the `permissions` API is available in the environment. */ export const supportPermissions = (): boolean => { return supportNavigator() && "permissions" in navigator } /** * @description Get the `permissions` API from the current environment. */ export const getPermissions = (): Permissions => { return navigator.permissions } /** * @description Get the `permissions` API if it is available, otherwise throw an error. */ export const ensurePermissions = (): Permissions => { if (supportPermissions() === true) { return getPermissions() } else { throw new Error("The permissions API is not supported in the current environment.") } } /** * @description Execute a function with the `permissions` API if it is available. */ export const usePermissions: Use = useFactory(getPermissions, supportPermissions) registerFeature({ feature: "permissions", detect: supportPermissions, }) /** * @description Check if the `document` object is available in the environment. */ export const supportDocument = (): boolean => { return typeof document !== "undefined" && document !== null && typeof document === "object" } /** * @description Get the `document` object from the current environment. */ export const getDocument = (): Document => { return globalThis.document } /** * @description Get the `document` object if it is available, otherwise throw an error. */ export const ensureDocument = (): Document => { if (supportDocument() === true) { return getDocument() } else { throw new Error("The document object is not supported in the current environment.") } } /** * @description Execute a function with the `document` object if it is available. */ export const useDocument: Use = useFactory(getDocument, supportDocument) registerFeature({ feature: "document", detect: supportDocument, }) /** * @description Check if the `CSS` object is available in the environment. */ export const supportCSS = (): boolean => { return typeof CSS !== "undefined" && CSS !== null && typeof CSS === "object" } /** * @description Get the `CSS` object from the current environment. */ export const getCSS = (): typeof CSS => { return globalThis.CSS } /** * @description Get the `CSS` object if it is available, otherwise throw an error. */ export const ensureCSS = (): typeof CSS => { if (supportCSS() === true) { return getCSS() } else { throw new Error("The CSS object is not supported in the current environment.") } } /** * @description Execute a function with the `CSS` object if it is available. */ export const useCSS: Use = useFactory(getCSS, supportCSS) registerFeature({ feature: "css", detect: supportCSS, })