From ac5955a922d4380df88be1762e9fc970ab545a08 Mon Sep 17 00:00:00 2001 From: David Snelling Date: Wed, 28 May 2025 11:37:28 -0700 Subject: [PATCH] feat: add persistent storage support for OPFS Introduced an optional `requestPersistentStorage` flag to enable persistent storage permission requests in the browser. Updated `OPFSStorage` to include methods for requesting and checking persistent storage status, and modified `createStorage` to handle the new flag. --- src/brainyData.ts | 14 ++++++++- src/storage/opfsStorage.ts | 62 +++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/brainyData.ts b/src/brainyData.ts index 47c1f5e9..bcab8c63 100644 --- a/src/brainyData.ts +++ b/src/brainyData.ts @@ -29,6 +29,12 @@ export interface BrainyDataConfig { * Embedding function to convert data to vectors */ embeddingFunction?: EmbeddingFunction + + /** + * Request persistent storage when running in a browser + * This will prompt the user for permission to use persistent storage + */ + requestPersistentStorage?: boolean } export class BrainyData { @@ -36,6 +42,7 @@ export class BrainyData { private storage: StorageAdapter | null = null private isInitialized = false private embeddingFunction: EmbeddingFunction + private requestPersistentStorage: boolean /** * Create a new vector database @@ -52,6 +59,9 @@ export class BrainyData { // Set embedding function if provided, otherwise use default this.embeddingFunction = config.embeddingFunction || defaultEmbeddingFunction + + // Set persistent storage request flag + this.requestPersistentStorage = config.requestPersistentStorage || false } /** @@ -66,7 +76,9 @@ export class BrainyData { try { // Initialize storage if not provided in constructor if (!this.storage) { - this.storage = await createStorage() + this.storage = await createStorage({ + requestPersistentStorage: this.requestPersistentStorage + }) } // Initialize storage diff --git a/src/storage/opfsStorage.ts b/src/storage/opfsStorage.ts index 56cf00fc..faf09658 100644 --- a/src/storage/opfsStorage.ts +++ b/src/storage/opfsStorage.ts @@ -19,6 +19,8 @@ export class OPFSStorage implements StorageAdapter { private metadataDir: FileSystemDirectoryHandle | null = null private isInitialized = false private isAvailable = false + private isPersistentRequested = false + private isPersistentGranted = false constructor() { // Check if OPFS is available @@ -78,6 +80,51 @@ export class OPFSStorage implements StorageAdapter { return this.isAvailable } + /** + * Request persistent storage permission from the user + * @returns Promise that resolves to true if permission was granted, false otherwise + */ + public async requestPersistentStorage(): Promise { + if (!this.isAvailable) { + console.warn('Cannot request persistent storage: OPFS is not available') + return false + } + + try { + // Check if persistence is already granted + this.isPersistentGranted = await navigator.storage.persisted() + + if (!this.isPersistentGranted) { + // Request permission for persistent storage + this.isPersistentGranted = await navigator.storage.persist() + } + + this.isPersistentRequested = true + return this.isPersistentGranted + } catch (error) { + console.warn('Failed to request persistent storage:', error) + return false + } + } + + /** + * Check if persistent storage is granted + * @returns Promise that resolves to true if persistent storage is granted, false otherwise + */ + public async isPersistent(): Promise { + if (!this.isAvailable) { + return false + } + + try { + this.isPersistentGranted = await navigator.storage.persisted() + return this.isPersistentGranted + } catch (error) { + console.warn('Failed to check persistent storage status:', error) + return false + } + } + /** * Save a node to storage */ @@ -650,8 +697,12 @@ export class MemoryStorage implements StorageAdapter { /** * Factory function to create the appropriate storage adapter based on the environment + * @param options Configuration options for storage + * @returns Promise that resolves to a StorageAdapter instance */ -export async function createStorage(): Promise { +export async function createStorage(options: { + requestPersistentStorage?: boolean +} = {}): Promise { // Check if we're in a Node.js environment const isNode = typeof process !== 'undefined' && process.versions != null && @@ -671,6 +722,15 @@ export async function createStorage(): Promise { const opfsStorage = new OPFSStorage() if (opfsStorage.isOPFSAvailable()) { + // Request persistent storage if specified + if (options.requestPersistentStorage) { + const isPersistentGranted = await opfsStorage.requestPersistentStorage() + if (isPersistentGranted) { + console.log('Persistent storage permission granted') + } else { + console.warn('Persistent storage permission denied') + } + } return opfsStorage } else { console.warn('OPFS is not available, falling back to in-memory storage')