mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-02-03 04:37:40 +01:00
Replace object stores for Chroma and token cache
This commit is contained in:
parent
edcce96a6e
commit
3850e6b50a
@ -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
|
||||||
@ -778,18 +778,18 @@ function preparePromptsForChatCompletion(Scenario, charPersonality, name2, world
|
|||||||
* @returns {(*[]|boolean)[]} An array where the first element is the prepared chat and the second element is a boolean flag.
|
* @returns {(*[]|boolean)[]} An array where the first element is the prepared chat and the second element is a boolean flag.
|
||||||
*/
|
*/
|
||||||
function prepareOpenAIMessages({
|
function prepareOpenAIMessages({
|
||||||
name2,
|
name2,
|
||||||
charDescription,
|
charDescription,
|
||||||
charPersonality,
|
charPersonality,
|
||||||
Scenario,
|
Scenario,
|
||||||
worldInfoBefore,
|
worldInfoBefore,
|
||||||
worldInfoAfter,
|
worldInfoAfter,
|
||||||
bias,
|
bias,
|
||||||
type,
|
type,
|
||||||
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,13 +804,13 @@ 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.')
|
||||||
chatCompletion.log('Token budget exceeded.');
|
chatCompletion.log('Token budget exceeded.');
|
||||||
promptManager.error = 'Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.';
|
promptManager.error = 'Not enough free tokens for mandatory prompts. Raise your token Limit or disable custom prompts.';
|
||||||
} else if (error instanceof InvalidCharacterNameError) {
|
} else if (error instanceof InvalidCharacterNameError) {
|
||||||
toastr.warning('An error occurred while counting tokens: Invalid character name')
|
toastr.warning('An error occurred while counting tokens: Invalid character name')
|
||||||
chatCompletion.log('Invalid character name');
|
chatCompletion.log('Invalid character name');
|
||||||
promptManager.error = 'The name of at least one character contained whitespaces or special characters. Please check your user and character name.';
|
promptManager.error = 'The name of at least one character contained whitespaces or special characters. Please check your user and character name.';
|
||||||
@ -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 };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1429,7 +1429,7 @@ class Message {
|
|||||||
*
|
*
|
||||||
* @class MessageCollection
|
* @class MessageCollection
|
||||||
*/
|
*/
|
||||||
class MessageCollection {
|
class MessageCollection {
|
||||||
collection = [];
|
collection = [];
|
||||||
identifier;
|
identifier;
|
||||||
|
|
||||||
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user