From fbae1c429d5f10a7968d1f4b95e75f884d581734 Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Mon, 30 Dec 2024 18:17:47 -0500 Subject: [PATCH] Support Session Storage and refactor the storage APIs (WebSQL will be fixed soon) --- .../src/interceptors/storage/sharedStorage.ts | 3 - AeroSandbox/src/interceptors/storage/sql.ts | 12 ++-- .../src/interceptors/storage/storage.ts | 28 ++++++++- AeroSandbox/src/interceptors/util/storage.ts | 60 ++++++++++--------- .../util/storageAPIInterceptorsGeneric.ts | 58 ------------------ .../util/storageApiInterceptorsGeneric.ts | 59 ++++++++++++++++++ 6 files changed, 123 insertions(+), 97 deletions(-) delete mode 100644 AeroSandbox/src/interceptors/storage/sharedStorage.ts delete mode 100644 AeroSandbox/src/interceptors/util/storageAPIInterceptorsGeneric.ts create mode 100644 AeroSandbox/src/interceptors/util/storageApiInterceptorsGeneric.ts diff --git a/AeroSandbox/src/interceptors/storage/sharedStorage.ts b/AeroSandbox/src/interceptors/storage/sharedStorage.ts deleted file mode 100644 index 6eccc09..0000000 --- a/AeroSandbox/src/interceptors/storage/sharedStorage.ts +++ /dev/null @@ -1,3 +0,0 @@ -import genStorageAPIInterceptors from "$util/storageAPIInterceptorsGeneric"; - -export default genStorageAPIInterceptors("sharedStorage", true); diff --git a/AeroSandbox/src/interceptors/storage/sql.ts b/AeroSandbox/src/interceptors/storage/sql.ts index b6cb170..1274759 100644 --- a/AeroSandbox/src/interceptors/storage/sql.ts +++ b/AeroSandbox/src/interceptors/storage/sql.ts @@ -1,18 +1,13 @@ import { type APIInterceptor, SupportEnum } from "$types/apiInterceptors.d.ts"; +import { proxyLocation } from "$shared/proxyLocation"; /** - * The id to use for escaping the SQL storage key - * @param cookieStoreId - * @returns */ -const createHandler = (cookieStoreId?) => { +const createHandler = () => { return (target, that, args) => { const [key]: [string] = args; - let newKey = $aero.config.sandbox.storageKey + key; - if (cookieStoreId) { - newKey = `${cookieStoreId}_${newKey}`; - } + const newKey = `${proxyLocation().origin}_sql_${key}`; args[0] = newKey; @@ -30,6 +25,7 @@ const createHandler = (cookieStoreId?) => { }; }; +// FIXME: This is all deprecated way of doing thiigs export default [ { createStorageProxyHandlers: cookieStoreId => diff --git a/AeroSandbox/src/interceptors/storage/storage.ts b/AeroSandbox/src/interceptors/storage/storage.ts index faf3b6e..317016c 100644 --- a/AeroSandbox/src/interceptors/storage/storage.ts +++ b/AeroSandbox/src/interceptors/storage/storage.ts @@ -1,3 +1,29 @@ +import { type APIInterceptor, SupportEnum } from "$types/apiInterceptors"; + import genStorageAPIInterceptors from "$util/storageAPIInterceptorsGeneric"; +import { proxyLocation } from "$shared/proxyLocation"; -export default genStorageAPIInterceptors("storage"); +export default [ + ...genStorageAPIInterceptors("sharedStorage", $aero.sandbox.config.sharedStorageId), + ...genStorageAPIInterceptors("storage", $aero.sandbox.config.storageStoreId), + ...genStorageAPIInterceptors("sessionStorage", `${$aero.sandbox.config.sessionStoreId}_${$aero.clientId}`), + // This is needed for Session Storage only + { + init: () => { + // Remove the keys from the previous sessions + for (let i = 0; i < sessionStorage.length; i++) { + const realKey = sessionStorage.key(i); + if (realKey.startsWith(proxyLocation().origin)) { + const keyWithoutOriginEscape = realKey.replace(new RegExp(`^${proxyLocation().origin}_`), ""); + if ( + // Is key from a previous session? + keyWithoutOriginEscape.startsWith(`${$aero.sandbox.config.sessionStoreId}_`)) + sessionStorage.removeItem(realKey); + } + } + }, + globalProp: "sessionStorage", + forStorage: true, + supports: SupportEnum.widelyAvailable + } +] as APIInterceptor[] diff --git a/AeroSandbox/src/interceptors/util/storage.ts b/AeroSandbox/src/interceptors/util/storage.ts index 5911665..4a9ca20 100644 --- a/AeroSandbox/src/interceptors/util/storage.ts +++ b/AeroSandbox/src/interceptors/util/storage.ts @@ -1,38 +1,44 @@ -import { type GeneratorCtxTypeProxyHandler } from "$types/apiInterceptors"; +/** + * Note: The cookie store ID is so that we can store data from other storage types when the storage is exhausted from them. We need to know what is actually a cookie. + * @module + */ import { storagePrefix } from "$shared/storage"; -const storageNomenclatureHandlers: { [key: string]: GeneratorCtxTypeProxyHandler } = { - prefix: (ctx) => ({ - apply: (target, that, args) => { - const [key] = args; - args[0] = prefixKey(ctx.cookieStoreId, key); - return Reflect.apply(target, that, args); +function genStorageNomenclatureHandlers(storeId): { [key: string]: ProxyHandler } { + return { + prefix: { + apply: (target, that, args) => { + const [key] = args; + args[0] = prefixKey(storeId, key); + return Reflect.apply(target, that, args); + } + }, + unprefix: { + apply: (target, that, args) => { + const [key] = args; + args[0] = unprefixKey(storeId, key); + return Reflect.apply(target, that, args); + } } - } as ProxyHandler), - unprefix: (ctx) => ({ - apply: (target, that, args) => { - const [key] = args; - args[0] = unprefixKey(ctx.cookieStoreId, key); - return Reflect.apply(target, that, args); - } - } as ProxyHandler), -}; + } +} + -function prefixKey(cookieStoreId, key: string): string { +function prefixKey(prefix, key: string): string { let proxifiedKey = storagePrefix(key); - if (cookieStoreId) { - proxifiedKey = `${cookieStoreId}_${proxifiedKey}`; + if (prefix) { + proxifiedKey = `${prefix}_${proxifiedKey}`; } return proxifiedKey; } -function unprefixKey(cookieStoreId, key: string): string { - if (!key.startsWith(cookieStoreId)) - $aero.logger.fatalErr("Failed to unprefix the key (the key does not belong to the current cookie store)"); - const keyWithoutCookieStoreId = key.replace(new RegExp(`^${cookieStoreId}`), ""); - if (!keyWithoutCookieStoreId.startsWith(storagePrefix(""))) - $aero.logger.fatalErr("Failed to unprefix the key (the key does not have the storage prefix)"); - return keyWithoutCookieStoreId.replace(storagePrefix(""), ""); +/** Works for everything except Session Storage */ +function unprefixKey(storeId: string, key: string): string { + const storeIdKey = storagePrefix(storeId); + if (!key.startsWith(storeIdKey)) + $aero.logger.fatalErr(`Failed to unprefix the key (the key does not have the expected cookie store key prefix, "${storeIdKey}")!`); + const keyWithoutStoreIdKey = key.replace(new RegExp(`^${storeIdKey}`), ""); + return keyWithoutStoreIdKey; } -export { storageNomenclatureHandlers }; \ No newline at end of file +export { genStorageNomenclatureHandlers, prefixKey, unprefixKey }; \ No newline at end of file diff --git a/AeroSandbox/src/interceptors/util/storageAPIInterceptorsGeneric.ts b/AeroSandbox/src/interceptors/util/storageAPIInterceptorsGeneric.ts deleted file mode 100644 index 326bb5a..0000000 --- a/AeroSandbox/src/interceptors/util/storageAPIInterceptorsGeneric.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { type APIInterceptor, SupportEnum } from "$types/apiInterceptors.d.ts"; - -import { storageNomenclatureHandlers } from "./storage"; - -export default function genStorageAPIInterceptors(key: string, sharedStorage = false): APIInterceptor[] { - return storageAPIInterceptorsGeneric.map(apiInterceptor => { - apiInterceptor.globalProp = key + apiInterceptor.globalProp; - apiInterceptor.supports = sharedStorage ? SupportEnum.shippingChromium | SupportEnum.draft : SupportEnum.widelyAvailable; - return apiInterceptor; - }); -} - -const storageAPIInterceptorsGeneric = [ - { - proxyHandler: { - apply(_target, that, _args) { - for (let i = 0; i < that.length; i++) { - const key = that.key(i); - if (key.startsWith(that.cookieStoreId)) - that.removeItem(key); - } - } - } as ProxyHandler, - forStorage: true, - globalProp: ".clear" - }, - { - genProxyHandler: storageNomenclatureHandlers.unprefix, - forStorage: true, - globalProp: ".getItem" - }, - { - genProxyHandler: ctx => ({ - apply(_target, that, args) { - const [getIndex] = args; - const proxifiedKeys: string[] = []; - for (let i = 0; i < that.length; i++) { - const key = that.key(i); - if (key.startsWith(ctx.cookieStoreId)) - proxifiedKeys.push(that.unprefixKey(ctx.cookieStoreId, key)); - } - return proxifiedKeys[getIndex]; - } - }) as ProxyHandler, - forStorage: true, - globalProp: ".key" - }, - { - genProxyHandler: storageNomenclatureHandlers.prefix, - forStorage: true, - globalProp: ".setItem" - }, - { - genProxyHandler: storageNomenclatureHandlers.unprefix, - forStorage: true, - globalProp: ".removeItem" - }, -] as APIInterceptor[]; diff --git a/AeroSandbox/src/interceptors/util/storageApiInterceptorsGeneric.ts b/AeroSandbox/src/interceptors/util/storageApiInterceptorsGeneric.ts new file mode 100644 index 0000000..91143f5 --- /dev/null +++ b/AeroSandbox/src/interceptors/util/storageApiInterceptorsGeneric.ts @@ -0,0 +1,59 @@ +import { type APIInterceptor, SupportEnum } from "$types/apiInterceptors.d.ts"; + +import { genStorageNomenclatureHandlers, unprefixKey } from "./storage"; +import { storagePrefix } from "$shared/storage"; + +export default function genStorageApiInterceptors(key: string, storeId: string): APIInterceptor[] { + const storageNomenclatureHandlers = genStorageNomenclatureHandlers(storeId); + const storageAPIInterceptorsGeneric = [ + { + proxyHandler: { + apply(target, _that, _args) { + const storageApi = key === "sessionStorage" ? localStorage : target; + for (let i = 0; i < storageApi.length; i++) { + const realKey = storageApi.key(i); + const storeIdKey = storagePrefix(storeId); + if (realKey.startsWith(storeIdKey)) + storageApi.removeItem(realKey); + } + } + } as ProxyHandler, + globalProp: ".clear" + }, + { + proxyHandler: storageNomenclatureHandlers.unprefix, + globalProp: ".getItem" + }, + { + proxyHandler: { + apply(target, _that, args) { + const storageApi = key === "sessionStorage" ? localStorage : target; + const [getIndex] = args; + const proxifiedKeys: string[] = []; + for (let i = 0; i < storageApi.length; i++) { + const realKey = storageApi.key(i); + const storeIdKey = storagePrefix(storeId); + if (realKey.startsWith(storeIdKey)) + proxifiedKeys.push(unprefixKey(storeId, realKey)); + } + return proxifiedKeys[getIndex]; + } + } as ProxyHandler, + globalProp: ".key" + }, + { + proxyHandler: storageNomenclatureHandlers.prefix, + globalProp: ".setItem" + }, + { + proxyHandler: storageNomenclatureHandlers.unprefix, + globalProp: ".removeItem" + }, + ] as APIInterceptor[]; + return storageAPIInterceptorsGeneric.map(apiInterceptor => { + apiInterceptor.forStorage = true; + apiInterceptor.globalProp = key + apiInterceptor.globalProp; + apiInterceptor.supports = key === "sharedStorage" ? SupportEnum.shippingChromium | SupportEnum.draft : SupportEnum.widelyAvailable; + return apiInterceptor; + }); +} \ No newline at end of file