UNPKG

@shopify/cli-kit

Version:

A set of utilities, interfaces, and models that are common across all the platform features

115 lines 4.16 kB
import { AbortError, BugError } from './error.js'; import { fileHasWritePermissions, unixFileIsOwnedByCurrentUser } from './fs.js'; import { dirname } from './path.js'; import Config from 'conf'; /** * A wrapper around the `conf` package that provides a strongly-typed interface * for accessing the local storage. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export class LocalStorage { constructor(options) { this.config = new Config(options); } /** * Get a value from the local storage. * * @param key - The key to get. * @returns The value. * @throws AbortError if a permission error occurs. * @throws BugError if an unexpected error occurs. */ get(key) { try { return this.config.get(key); // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { this.handleError(error, 'get'); } } /** * Set a value in the local storage. * * @param key - The key to set. * @param value - The value to set. * @throws AbortError if a permission error occurs. * @throws BugError if an unexpected error occurs. */ set(key, value) { try { this.config.set(key, value); // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { this.handleError(error, 'set'); } } /** * Delete a value from the local storage. * * @param key - The key to delete. * @throws AbortError if a permission error occurs. * @throws BugError if an unexpected error occurs. */ delete(key) { try { this.config.delete(key); // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { this.handleError(error, 'delete'); } } /** * Clear the local storage (delete all values). * * @throws AbortError if a permission error occurs. * @throws BugError if an unexpected error occurs. */ clear() { try { this.config.clear(); // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { this.handleError(error, 'clear'); } } /** * Handle errors from config operations. * If the error is permission-related, throw an AbortError with helpful hints. * Otherwise, throw a BugError. * * @param error - The error that occurred. * @param operation - The operation that failed. * @throws AbortError if the error is permission-related. * @throws BugError if the error is not permission-related. */ handleError(error, operation) { if (this.isPermissionError()) { throw new AbortError(`Failed to access local storage (${operation}): ${error}`, this.tryMessage()); } else { throw new BugError(`Unexpected error while accessing local storage at ${this.config.path} (${operation}): ${error}`); } } isPermissionError() { const canAccessFile = fileHasWritePermissions(this.config.path); const canAccessFolder = fileHasWritePermissions(dirname(this.config.path)); const ownsFile = unixFileIsOwnedByCurrentUser(this.config.path); return !canAccessFile || !canAccessFolder || ownsFile === false; } tryMessage() { const ownsFile = unixFileIsOwnedByCurrentUser(this.config.path); const ownsFolder = unixFileIsOwnedByCurrentUser(dirname(this.config.path)); const message = [`Check that you have write permissions for`, { filePath: this.config.path }]; if (ownsFile === false || ownsFolder === false) { message.push('- The file is owned by a different user. This typically happens when Shopify CLI was previously run with elevated permissions (e.g., sudo).'); } message.push('\n\nTo resolve this, remove the Shopify CLI preferences folder:'); message.push({ command: `rm -rf ${dirname(this.config.path)}` }); return message; } } //# sourceMappingURL=local-storage.js.map