mirror of
				https://github.com/SillyTavern/SillyTavern.git
				synced 2025-06-05 21:59:27 +02:00 
			
		
		
		
	Replace object stores for Chroma and token cache
This commit is contained in:
		| @@ -40,6 +40,7 @@ | |||||||
|     <script src="scripts/select2.min.js"></script> |     <script src="scripts/select2.min.js"></script> | ||||||
|     <script src="scripts/seedrandom.min.js"></script> |     <script src="scripts/seedrandom.min.js"></script> | ||||||
|     <script src="scripts/droll.js"></script> |     <script src="scripts/droll.js"></script> | ||||||
|  |     <script src="scripts/localforage.min.js"></script> | ||||||
|     <script type="module" src="scripts/eventemitter.js"></script> |     <script type="module" src="scripts/eventemitter.js"></script> | ||||||
|     <script type="module" src="scripts/power-user.js"></script> |     <script type="module" src="scripts/power-user.js"></script> | ||||||
|     <script type="module" src="scripts/swiped-events.js"></script> |     <script type="module" src="scripts/swiped-events.js"></script> | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| import { saveSettingsDebounced, getCurrentChatId, system_message_types, extension_prompt_types, eventSource, event_types, getRequestHeaders, CHARACTERS_PER_TOKEN_RATIO, substituteParams, max_context, } from "../../../script.js"; | import { saveSettingsDebounced, getCurrentChatId, system_message_types, extension_prompt_types, eventSource, event_types, getRequestHeaders, CHARACTERS_PER_TOKEN_RATIO, substituteParams, max_context, } from "../../../script.js"; | ||||||
| import { humanizedDateTime } from "../../RossAscends-mods.js"; | import { humanizedDateTime } from "../../RossAscends-mods.js"; | ||||||
| import { getApiUrl, extension_settings, getContext, doExtrasFetch } from "../../extensions.js"; | import { getApiUrl, extension_settings, getContext, doExtrasFetch } from "../../extensions.js"; | ||||||
| import { getFileText, onlyUnique, splitRecursive, IndexedDBStore } from "../../utils.js"; | import { getFileText, onlyUnique, splitRecursive } from "../../utils.js"; | ||||||
| export { MODULE_NAME }; | export { MODULE_NAME }; | ||||||
|  |  | ||||||
| const MODULE_NAME = 'chromadb'; | const MODULE_NAME = 'chromadb'; | ||||||
| const dbStore = new IndexedDBStore('SillyTavern', MODULE_NAME); | const dbStore = localforage.createInstance({ name: 'SillyTavern_ChromaDB' }); | ||||||
|  |  | ||||||
| const defaultSettings = { | const defaultSettings = { | ||||||
|     strategy: 'original', |     strategy: 'original', | ||||||
| @@ -59,7 +59,7 @@ async function invalidateMessageSyncState(messageId) { | |||||||
|     console.log('CHROMADB: invalidating message sync state', messageId); |     console.log('CHROMADB: invalidating message sync state', messageId); | ||||||
|     const state = await getChatSyncState(); |     const state = await getChatSyncState(); | ||||||
|     state[messageId] = 0; |     state[messageId] = 0; | ||||||
|     await dbStore.put(getCurrentChatId(), state); |     await dbStore.setItem(getCurrentChatId(), state); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function getChatSyncState() { | async function getChatSyncState() { | ||||||
| @@ -69,7 +69,7 @@ async function getChatSyncState() { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const context = getContext(); |     const context = getContext(); | ||||||
|     const chatState = (await dbStore.get(currentChatId)) || []; |     const chatState = (await dbStore.getItem(currentChatId)) || []; | ||||||
|  |  | ||||||
|     // if the chat length has decreased, it means that some messages were deleted |     // if the chat length has decreased, it means that some messages were deleted | ||||||
|     if (chatState.length > context.chat.length) { |     if (chatState.length > context.chat.length) { | ||||||
| @@ -92,7 +92,7 @@ async function getChatSyncState() { | |||||||
|             chatState[i] = 0; |             chatState[i] = 0; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     await dbStore.put(currentChatId, chatState); |     await dbStore.setItem(currentChatId, chatState); | ||||||
|  |  | ||||||
|     return chatState; |     return chatState; | ||||||
| } | } | ||||||
| @@ -304,7 +304,7 @@ async function filterSyncedMessages(splitMessages) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     console.debug('CHROMADB: sync state', syncState.map((v, i) => ({ id: i, synced: v }))); |     console.debug('CHROMADB: sync state', syncState.map((v, i) => ({ id: i, synced: v }))); | ||||||
|     await dbStore.put(getCurrentChatId(), syncState); |     await dbStore.setItem(getCurrentChatId(), syncState); | ||||||
|  |  | ||||||
|     // remove messages that are already synced |     // remove messages that are already synced | ||||||
|     return splitMessages.filter((_, i) => !removeIndices.includes(i)); |     return splitMessages.filter((_, i) => !removeIndices.includes(i)); | ||||||
| @@ -325,7 +325,7 @@ async function onPurgeClick() { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     if (purgeResult.ok) { |     if (purgeResult.ok) { | ||||||
|         await dbStore.delete(chat_id); |         await dbStore.removeItem(chat_id); | ||||||
|         toastr.success('ChromaDB context has been successfully cleared'); |         toastr.success('ChromaDB context has been successfully cleared'); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								public/scripts/localforage.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								public/scripts/localforage.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -26,7 +26,7 @@ import { | |||||||
|     event_types, |     event_types, | ||||||
|     substituteParams, |     substituteParams, | ||||||
| } from "../script.js"; | } from "../script.js"; | ||||||
| import {groups, selected_group} from "./group-chats.js"; | import { groups, selected_group } from "./group-chats.js"; | ||||||
|  |  | ||||||
| import { | import { | ||||||
|     promptManagerDefaultPromptOrders, |     promptManagerDefaultPromptOrders, | ||||||
| @@ -45,7 +45,6 @@ import { | |||||||
| } from "./secrets.js"; | } from "./secrets.js"; | ||||||
|  |  | ||||||
| import { | import { | ||||||
|     IndexedDBStore, |  | ||||||
|     delay, |     delay, | ||||||
|     download, |     download, | ||||||
|     getFileText, |     getFileText, | ||||||
| @@ -121,35 +120,36 @@ const openrouter_website_model = 'OR_Website'; | |||||||
|  |  | ||||||
| let biasCache = undefined; | let biasCache = undefined; | ||||||
| let model_list = []; | let model_list = []; | ||||||
| const objectStore = new IndexedDBStore('SillyTavern', 'chat_completions'); | const objectStore = new localforage.createInstance({ name: "SillyTavern_ChatCompletions" }); | ||||||
|  |  | ||||||
| let tokenCache = {}; | let tokenCache = {}; | ||||||
|  |  | ||||||
| async function loadTokenCache() { | async function loadTokenCache() { | ||||||
|     try { |     try { | ||||||
|         console.debug('Chat Completions: loading token cache from IndexedDB') |         console.debug('Chat Completions: loading token cache') | ||||||
|         tokenCache = await objectStore.get('tokenCache') || {}; |         tokenCache = await objectStore.getItem('tokenCache') || {}; | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|         console.log('Chat Completions: unable to load token cache from IndexedDB, using default value', e); |         console.log('Chat Completions: unable to load token cache, using default value', e); | ||||||
|         tokenCache = {}; |         tokenCache = {}; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| async function saveTokenCache() { | async function saveTokenCache() { | ||||||
|     try { |     try { | ||||||
|         console.debug('Chat Completions: saving token cache to IndexedDB') |         console.debug('Chat Completions: saving token cache') | ||||||
|         await objectStore.put('tokenCache', tokenCache); |         await objectStore.setItem('tokenCache', tokenCache); | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|         console.log('Chat Completions: unable to save token cache to IndexedDB', e); |         console.log('Chat Completions: unable to save token cache', e); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| async function resetTokenCache() { | async function resetTokenCache() { | ||||||
|     try { |     try { | ||||||
|         console.debug('Chat Completions: resetting token cache in IndexedDB'); |         console.debug('Chat Completions: resetting token cache'); | ||||||
|         Object.keys(tokenCache).forEach(key => delete tokenCache[key]); |         Object.keys(tokenCache).forEach(key => delete tokenCache[key]); | ||||||
|         await objectStore.delete('tokenCache'); |         await objectStore.removeItem('tokenCache'); | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|         console.log('Chat Completions: unable to reset token cache in IndexedDB', e); |         console.log('Chat Completions: unable to reset token cache', e); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -298,7 +298,7 @@ function setOpenAIMessages(chat) { | |||||||
|         // Apply the "wrap in quotes" option |         // Apply the "wrap in quotes" option | ||||||
|         if (role == 'user' && oai_settings.wrap_in_quotes) content = `"${content}"`; |         if (role == 'user' && oai_settings.wrap_in_quotes) content = `"${content}"`; | ||||||
|         const name = chat[j]['name']; |         const name = chat[j]['name']; | ||||||
|         openai_msgs[i] = { "role": role, "content": content, name: name}; |         openai_msgs[i] = { "role": role, "content": content, name: name }; | ||||||
|         j++; |         j++; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -532,7 +532,7 @@ function populateDialogueExamples(prompts, chatCompletion) { | |||||||
|         chatCompletion.freeBudget(newExampleChat); |         chatCompletion.freeBudget(newExampleChat); | ||||||
|  |  | ||||||
|         const chatExamples = chatCompletion.getMessages().getItemByIdentifier('dialogueExamples').getCollection(); |         const chatExamples = chatCompletion.getMessages().getItemByIdentifier('dialogueExamples').getCollection(); | ||||||
|         if(chatExamples.length) chatCompletion.insertAtStart(newExampleChat,'dialogueExamples'); |         if (chatExamples.length) chatCompletion.insertAtStart(newExampleChat, 'dialogueExamples'); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -546,7 +546,7 @@ function populateDialogueExamples(prompts, chatCompletion) { | |||||||
|  * @param {string} options.quietPrompt - Instruction prompt for extras |  * @param {string} options.quietPrompt - Instruction prompt for extras | ||||||
|  * @param {string} options.type - The type of the chat, can be 'impersonate'. |  * @param {string} options.type - The type of the chat, can be 'impersonate'. | ||||||
|  */ |  */ | ||||||
| function populateChatCompletion (prompts, chatCompletion, {bias, quietPrompt, type, cyclePrompt} = {}) { | function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, type, cyclePrompt } = {}) { | ||||||
|     // Helper function for preparing a prompt, that already exists within the prompt collection, for completion |     // Helper function for preparing a prompt, that already exists within the prompt collection, for completion | ||||||
|     const addToChatCompletion = (source, target = null) => { |     const addToChatCompletion = (source, target = null) => { | ||||||
|         // We need the prompts array to determine a position for the source. |         // We need the prompts array to determine a position for the source. | ||||||
| @@ -616,7 +616,7 @@ function populateChatCompletion (prompts, chatCompletion, {bias, quietPrompt, ty | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Persona Description |     // Persona Description | ||||||
|     if(power_user.persona_description) { |     if (power_user.persona_description) { | ||||||
|         const personaDescription = Message.fromPrompt(prompts.get('personaDescription')); |         const personaDescription = Message.fromPrompt(prompts.get('personaDescription')); | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
| @@ -678,16 +678,16 @@ function preparePromptsForChatCompletion(Scenario, charPersonality, name2, world | |||||||
|     // Create entries for system prompts |     // Create entries for system prompts | ||||||
|     const systemPrompts = [ |     const systemPrompts = [ | ||||||
|         // Ordered prompts for which a marker should exist |         // Ordered prompts for which a marker should exist | ||||||
|         {role: 'system', content: formatWorldInfo(worldInfoBefore), identifier: 'worldInfoBefore'}, |         { role: 'system', content: formatWorldInfo(worldInfoBefore), identifier: 'worldInfoBefore' }, | ||||||
|         {role: 'system', content: formatWorldInfo(worldInfoAfter), identifier: 'worldInfoAfter'}, |         { role: 'system', content: formatWorldInfo(worldInfoAfter), identifier: 'worldInfoAfter' }, | ||||||
|         {role: 'system', content: charDescription, identifier: 'charDescription'}, |         { role: 'system', content: charDescription, identifier: 'charDescription' }, | ||||||
|         {role: 'system', content: charPersonalityText, identifier: 'charPersonality'}, |         { role: 'system', content: charPersonalityText, identifier: 'charPersonality' }, | ||||||
|         {role: 'system', content: scenarioText, identifier: 'scenario'}, |         { role: 'system', content: scenarioText, identifier: 'scenario' }, | ||||||
|         // Unordered prompts without marker |         // Unordered prompts without marker | ||||||
|         {role: 'system', content: oai_settings.nsfw_avoidance_prompt, identifier: 'nsfwAvoidance'}, |         { role: 'system', content: oai_settings.nsfw_avoidance_prompt, identifier: 'nsfwAvoidance' }, | ||||||
|         {role: 'system', content: oai_settings.impersonation_prompt, identifier: 'impersonate'}, |         { role: 'system', content: oai_settings.impersonation_prompt, identifier: 'impersonate' }, | ||||||
|         {role: 'system', content: quietPrompt, identifier: 'quietPrompt'}, |         { role: 'system', content: quietPrompt, identifier: 'quietPrompt' }, | ||||||
|         {role: 'system', content: bias, identifier: 'bias'} |         { role: 'system', content: bias, identifier: 'bias' } | ||||||
|     ]; |     ]; | ||||||
|  |  | ||||||
|     // Tavern Extras - Summary |     // Tavern Extras - Summary | ||||||
| @@ -708,7 +708,7 @@ function preparePromptsForChatCompletion(Scenario, charPersonality, name2, world | |||||||
|  |  | ||||||
|     // Persona Description |     // Persona Description | ||||||
|     if (power_user.persona_description) { |     if (power_user.persona_description) { | ||||||
|         systemPrompts.push({role: 'system', content: power_user.persona_description, identifier: 'personaDescription'}); |         systemPrompts.push({ role: 'system', content: power_user.persona_description, identifier: 'personaDescription' }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // This is the prompt order defined by the user |     // This is the prompt order defined by the user | ||||||
| @@ -789,7 +789,7 @@ function prepareOpenAIMessages({ | |||||||
|     quietPrompt, |     quietPrompt, | ||||||
|     extensionPrompts, |     extensionPrompts, | ||||||
|     cyclePrompt |     cyclePrompt | ||||||
|                                      } = {}, dryRun) { | } = {}, dryRun) { | ||||||
|     // Without a character selected, there is no way to accurately calculate tokens |     // Without a character selected, there is no way to accurately calculate tokens | ||||||
|     if (!promptManager.activeCharacter && dryRun) return [null, false]; |     if (!promptManager.activeCharacter && dryRun) return [null, false]; | ||||||
|  |  | ||||||
| @@ -804,7 +804,7 @@ function prepareOpenAIMessages({ | |||||||
|         const prompts = preparePromptsForChatCompletion(Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts); |         const prompts = preparePromptsForChatCompletion(Scenario, charPersonality, name2, worldInfoBefore, worldInfoAfter, charDescription, quietPrompt, bias, extensionPrompts); | ||||||
|  |  | ||||||
|         // Fill the chat completion with as much context as the budget allows |         // Fill the chat completion with as much context as the budget allows | ||||||
|         populateChatCompletion(prompts, chatCompletion, {bias, quietPrompt, type, cyclePrompt}); |         populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, type, cyclePrompt }); | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|         if (error instanceof TokenBudgetExceededError) { |         if (error instanceof TokenBudgetExceededError) { | ||||||
|             toastr.error('An error occurred while counting tokens: Token budget exceeded.') |             toastr.error('An error occurred while counting tokens: Token budget exceeded.') | ||||||
| @@ -1267,7 +1267,7 @@ class TokenHandler { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     resetCounts() { |     resetCounts() { | ||||||
|         Object.keys(this.counts).forEach((key) => this.counts[key] = 0 ); |         Object.keys(this.counts).forEach((key) => this.counts[key] = 0); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     setCounts(counts) { |     setCounts(counts) { | ||||||
| @@ -1397,7 +1397,7 @@ class Message { | |||||||
|         this.content = content; |         this.content = content; | ||||||
|  |  | ||||||
|         if (this.content) { |         if (this.content) { | ||||||
|             this.tokens = tokenHandler.count({role: this.role, content: this.content}) |             this.tokens = tokenHandler.count({ role: this.role, content: this.content }) | ||||||
|         } else { |         } else { | ||||||
|             this.tokens = 0; |             this.tokens = 0; | ||||||
|         } |         } | ||||||
| @@ -1421,7 +1421,7 @@ class Message { | |||||||
|      * Returns the number of tokens in the message. |      * Returns the number of tokens in the message. | ||||||
|      * @returns {number} Number of tokens in the message. |      * @returns {number} Number of tokens in the message. | ||||||
|      */ |      */ | ||||||
|     getTokens() {return this.tokens}; |     getTokens() { return this.tokens }; | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -1439,8 +1439,8 @@ class MessageCollection  { | |||||||
|      * @param {...Object} items - An array of Message or MessageCollection instances to be added to the collection. |      * @param {...Object} items - An array of Message or MessageCollection instances to be added to the collection. | ||||||
|      */ |      */ | ||||||
|     constructor(identifier, ...items) { |     constructor(identifier, ...items) { | ||||||
|         for(let item of items) { |         for (let item of items) { | ||||||
|             if(!(item instanceof Message || item instanceof MessageCollection)) { |             if (!(item instanceof Message || item instanceof MessageCollection)) { | ||||||
|                 throw new Error('Only Message and MessageCollection instances can be added to MessageCollection'); |                 throw new Error('Only Message and MessageCollection instances can be added to MessageCollection'); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -1456,7 +1456,7 @@ class MessageCollection  { | |||||||
|     getChat() { |     getChat() { | ||||||
|         return this.collection.reduce((acc, message) => { |         return this.collection.reduce((acc, message) => { | ||||||
|             const name = message.name; |             const name = message.name; | ||||||
|             if (message.content) acc.push({role: message.role, ...(name && { name }), content: message.content}); |             if (message.content) acc.push({ role: message.role, ...(name && { name }), content: message.content }); | ||||||
|             return acc; |             return acc; | ||||||
|         }, []); |         }, []); | ||||||
|     } |     } | ||||||
| @@ -2450,7 +2450,7 @@ function onSettingsPresetChange() { | |||||||
|         settingsToUpdate: settingsToUpdate, |         settingsToUpdate: settingsToUpdate, | ||||||
|         settings: oai_settings, |         settings: oai_settings, | ||||||
|         savePreset: saveOpenAIPreset |         savePreset: saveOpenAIPreset | ||||||
|     }).finally(r =>{ |     }).finally(r => { | ||||||
|         for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) { |         for (const [key, [selector, setting, isCheckbox]] of Object.entries(settingsToUpdate)) { | ||||||
|             if (preset[key] !== undefined) { |             if (preset[key] !== undefined) { | ||||||
|                 if (isCheckbox) { |                 if (isCheckbox) { | ||||||
| @@ -2799,8 +2799,8 @@ function onProxyPasswordShowClick() { | |||||||
|     $(this).toggleClass('fa-eye-slash fa-eye'); |     $(this).toggleClass('fa-eye-slash fa-eye'); | ||||||
| } | } | ||||||
|  |  | ||||||
| $(document).ready(function () { | $(document).ready(async function () { | ||||||
|     loadTokenCache(); |     await loadTokenCache(); | ||||||
|  |  | ||||||
|     $('#test_api_button').on('click', testApiConnection); |     $('#test_api_button').on('click', testApiConnection); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -370,99 +370,6 @@ export function splitRecursive(input, length, delimitiers = ['\n\n', '\n', ' ', | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| export class IndexedDBStore { |  | ||||||
|     constructor(dbName, storeName) { |  | ||||||
|         this.dbName = dbName; |  | ||||||
|         this.storeName = storeName; |  | ||||||
|         this.db = null; |  | ||||||
|         this.version = Date.now(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async open() { |  | ||||||
|         return new Promise((resolve, reject) => { |  | ||||||
|             const request = indexedDB.open(this.dbName, this.version); |  | ||||||
|  |  | ||||||
|             request.onupgradeneeded = (event) => { |  | ||||||
|                 const db = event.target.result; |  | ||||||
|                 if (!db.objectStoreNames.contains(this.storeName)) { |  | ||||||
|                     db.createObjectStore(this.storeName, { keyPath: null, autoIncrement: false }); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             request.onsuccess = (event) => { |  | ||||||
|                 console.debug(`IndexedDBStore.open(${this.dbName})`); |  | ||||||
|                 this.db = event.target.result; |  | ||||||
|                 resolve(this.db); |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             request.onerror = (event) => { |  | ||||||
|                 console.error(`IndexedDBStore.open(${this.dbName})`); |  | ||||||
|                 reject(event.target.error); |  | ||||||
|             }; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async get(key) { |  | ||||||
|         if (!this.db) await this.open(); |  | ||||||
|  |  | ||||||
|         return new Promise((resolve, reject) => { |  | ||||||
|             const transaction = this.db.transaction(this.storeName, "readonly"); |  | ||||||
|             const objectStore = transaction.objectStore(this.storeName); |  | ||||||
|             const request = objectStore.get(key); |  | ||||||
|  |  | ||||||
|             request.onsuccess = (event) => { |  | ||||||
|                 console.debug(`IndexedDBStore.get(${key})`); |  | ||||||
|                 resolve(event.target.result); |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             request.onerror = (event) => { |  | ||||||
|                 console.error(`IndexedDBStore.get(${key})`); |  | ||||||
|                 reject(event.target.error); |  | ||||||
|             }; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async put(key, object) { |  | ||||||
|         if (!this.db) await this.open(); |  | ||||||
|  |  | ||||||
|         return new Promise((resolve, reject) => { |  | ||||||
|             const transaction = this.db.transaction(this.storeName, "readwrite"); |  | ||||||
|             const objectStore = transaction.objectStore(this.storeName); |  | ||||||
|             const request = objectStore.put(object, key); |  | ||||||
|  |  | ||||||
|             request.onsuccess = (event) => { |  | ||||||
|                 console.debug(`IndexedDBStore.put(${key})`); |  | ||||||
|                 resolve(event.target.result); |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             request.onerror = (event) => { |  | ||||||
|                 console.error(`IndexedDBStore.put(${key})`); |  | ||||||
|                 reject(event.target.error); |  | ||||||
|             }; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async delete(key) { |  | ||||||
|         if (!this.db) await this.open(); |  | ||||||
|  |  | ||||||
|         return new Promise((resolve, reject) => { |  | ||||||
|             const transaction = this.db.transaction(this.storeName, "readwrite"); |  | ||||||
|             const objectStore = transaction.objectStore(this.storeName); |  | ||||||
|             const request = objectStore.delete(key); |  | ||||||
|  |  | ||||||
|             request.onsuccess = (event) => { |  | ||||||
|                 console.debug(`IndexedDBStore.delete(${key})`); |  | ||||||
|                 resolve(event.target.result); |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             request.onerror = (event) => { |  | ||||||
|                 console.error(`IndexedDBStore.delete(${key})`); |  | ||||||
|                 reject(event.target.error); |  | ||||||
|             }; |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export function isDataURL(str) { | export function isDataURL(str) { | ||||||
|     const regex = /^data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)*;?)?(base64)?,([a-z0-9!$&',()*+;=\-_%.~:@\/?#]+)?$/i; |     const regex = /^data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)*;?)?(base64)?,([a-z0-9!$&',()*+;=\-_%.~:@\/?#]+)?$/i; | ||||||
|     return regex.test(str); |     return regex.test(str); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user