Protect local storage calls (#3043)
This PR provides some safe wrappers for local storage calls. Local storage is not available in all environments (for example, a React Native web view). The PR also adds an eslint rule preventing direct calls to local / session storage. ### Change Type - [x] `patch` — Bug fix ### Release Notes - Fixes a bug that could cause crashes in React Native webviews.
This commit is contained in:
parent
3161e5cb4f
commit
2f28d7c6f8
18 changed files with 625 additions and 52 deletions
|
@ -14,6 +14,7 @@ module.exports = {
|
||||||
'@next/next',
|
'@next/next',
|
||||||
'react-hooks',
|
'react-hooks',
|
||||||
'deprecation',
|
'deprecation',
|
||||||
|
'no-storage',
|
||||||
],
|
],
|
||||||
settings: {
|
settings: {
|
||||||
next: {
|
next: {
|
||||||
|
@ -21,6 +22,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
'no-storage/no-browser-storage': 2,
|
||||||
'deprecation/deprecation': 'error',
|
'deprecation/deprecation': 'error',
|
||||||
'@next/next/no-html-link-for-pages': 'off',
|
'@next/next/no-html-link-for-pages': 'off',
|
||||||
'react/jsx-key': 'off',
|
'react/jsx-key': 'off',
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
/**
|
// What is going on in this file?
|
||||||
* What is going on in this file?
|
// We had some bad early assumptions about how we would store documents.
|
||||||
*
|
// Which ended up with us generating random persistenceKey strings for the
|
||||||
* We had some bad early assumptions about how we would store documents.
|
// 'scratch' document for each user (i.e. each browser context), and storing it in localStorage.
|
||||||
* Which ended up with us generating random persistenceKey strings for the
|
// Many users still have that random string in their localStorage so we need to load it. But for new
|
||||||
* 'scratch' document for each user (i.e. each browser context), and storing it in localStorage.
|
// users it does not need to be unique and we can just use a constant.
|
||||||
*
|
|
||||||
* Many users still have that random string in their localStorage so we need to load it. But for new
|
import { getFromLocalStorage, setInLocalStorage } from 'tldraw'
|
||||||
* users it does not need to be unique and we can just use a constant.
|
|
||||||
*/
|
|
||||||
// DO NOT CHANGE THESE WITHOUT ADDING MIGRATION LOGIC. DOING SO WOULD WIPE ALL EXISTING LOCAL DATA.
|
// DO NOT CHANGE THESE WITHOUT ADDING MIGRATION LOGIC. DOING SO WOULD WIPE ALL EXISTING LOCAL DATA.
|
||||||
const defaultDocumentKey = 'TLDRAW_DEFAULT_DOCUMENT_NAME_v2'
|
const defaultDocumentKey = 'TLDRAW_DEFAULT_DOCUMENT_NAME_v2'
|
||||||
const w = typeof window === 'undefined' ? undefined : window
|
|
||||||
|
|
||||||
export const SCRATCH_PERSISTENCE_KEY =
|
export const SCRATCH_PERSISTENCE_KEY =
|
||||||
(w?.localStorage.getItem(defaultDocumentKey) as any) ?? 'tldraw_document_v3'
|
(getFromLocalStorage(defaultDocumentKey) as any) ?? 'tldraw_document_v3'
|
||||||
w?.localStorage.setItem(defaultDocumentKey, SCRATCH_PERSISTENCE_KEY)
|
setInLocalStorage(defaultDocumentKey, SCRATCH_PERSISTENCE_KEY)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { T, atom } from 'tldraw'
|
import { T, atom, getFromLocalStorage, setInLocalStorage } from 'tldraw'
|
||||||
|
|
||||||
const channel =
|
const channel =
|
||||||
typeof BroadcastChannel !== 'undefined' ? new BroadcastChannel('tldrawUserPreferences') : null
|
typeof BroadcastChannel !== 'undefined' ? new BroadcastChannel('tldrawUserPreferences') : null
|
||||||
|
@ -37,9 +37,7 @@ function createPreference<Type>(key: string, validator: T.Validator<Type>, defau
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadItemFromStorage<Type>(key: string, validator: T.Validator<Type>): Type | null {
|
function loadItemFromStorage<Type>(key: string, validator: T.Validator<Type>): Type | null {
|
||||||
if (typeof localStorage === 'undefined' || !localStorage) return null
|
const item = getFromLocalStorage(`tldrawUserPreferences.${key}`, null)
|
||||||
|
|
||||||
const item = localStorage.getItem(`tldrawUserPreferences.${key}`)
|
|
||||||
if (item == null) return null
|
if (item == null) return null
|
||||||
try {
|
try {
|
||||||
return validator.validate(JSON.parse(item))
|
return validator.validate(JSON.parse(item))
|
||||||
|
@ -49,11 +47,5 @@ function loadItemFromStorage<Type>(key: string, validator: T.Validator<Type>): T
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveItemToStorage(key: string, value: unknown): void {
|
function saveItemToStorage(key: string, value: unknown): void {
|
||||||
if (typeof localStorage === 'undefined' || !localStorage) return
|
setInLocalStorage(`tldrawUserPreferences.${key}`, JSON.stringify(value))
|
||||||
|
|
||||||
try {
|
|
||||||
localStorage.setItem(`tldrawUserPreferences.${key}`, JSON.stringify(value))
|
|
||||||
} catch (e) {
|
|
||||||
// not a big deal
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import { useLayoutEffect, useState } from 'react'
|
import { useLayoutEffect, useState } from 'react'
|
||||||
import { Tldraw, createTLStore, defaultShapeUtils, throttle } from 'tldraw'
|
import {
|
||||||
|
Tldraw,
|
||||||
|
createTLStore,
|
||||||
|
defaultShapeUtils,
|
||||||
|
getFromLocalStorage,
|
||||||
|
setInLocalStorage,
|
||||||
|
throttle,
|
||||||
|
} from 'tldraw'
|
||||||
import 'tldraw/tldraw.css'
|
import 'tldraw/tldraw.css'
|
||||||
|
|
||||||
// There's a guide at the bottom of this file!
|
// There's a guide at the bottom of this file!
|
||||||
|
@ -20,7 +27,7 @@ export default function PersistenceExample() {
|
||||||
setLoadingState({ status: 'loading' })
|
setLoadingState({ status: 'loading' })
|
||||||
|
|
||||||
// Get persisted data from local storage
|
// Get persisted data from local storage
|
||||||
const persistedSnapshot = localStorage.getItem(PERSISTENCE_KEY)
|
const persistedSnapshot = getFromLocalStorage(PERSISTENCE_KEY)
|
||||||
|
|
||||||
if (persistedSnapshot) {
|
if (persistedSnapshot) {
|
||||||
try {
|
try {
|
||||||
|
@ -38,7 +45,7 @@ export default function PersistenceExample() {
|
||||||
const cleanupFn = store.listen(
|
const cleanupFn = store.listen(
|
||||||
throttle(() => {
|
throttle(() => {
|
||||||
const snapshot = store.getSnapshot()
|
const snapshot = store.getSnapshot()
|
||||||
localStorage.setItem(PERSISTENCE_KEY, JSON.stringify(snapshot))
|
setInLocalStorage(PERSISTENCE_KEY, JSON.stringify(snapshot))
|
||||||
}, 500)
|
}, 500)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,7 @@
|
||||||
"@sentry/cli": "^2.25.0",
|
"@sentry/cli": "^2.25.0",
|
||||||
"@yarnpkg/types": "^4.0.0",
|
"@yarnpkg/types": "^4.0.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
|
"eslint-plugin-no-storage": "^1.0.2",
|
||||||
"purgecss": "^5.0.0",
|
"purgecss": "^5.0.0",
|
||||||
"svgo": "^3.0.2"
|
"svgo": "^3.0.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,12 @@ import {
|
||||||
pageIdValidator,
|
pageIdValidator,
|
||||||
shapeIdValidator,
|
shapeIdValidator,
|
||||||
} from '@tldraw/tlschema'
|
} from '@tldraw/tlschema'
|
||||||
import { objectMapFromEntries } from '@tldraw/utils'
|
import {
|
||||||
|
deleteFromSessionStorage,
|
||||||
|
getFromSessionStorage,
|
||||||
|
objectMapFromEntries,
|
||||||
|
setInSessionStorage,
|
||||||
|
} from '@tldraw/utils'
|
||||||
import { T } from '@tldraw/validate'
|
import { T } from '@tldraw/validate'
|
||||||
import { uniqueId } from '../utils/uniqueId'
|
import { uniqueId } from '../utils/uniqueId'
|
||||||
|
|
||||||
|
@ -26,7 +31,9 @@ const tabIdKey = 'TLDRAW_TAB_ID_v2' as const
|
||||||
const window = globalThis.window as
|
const window = globalThis.window as
|
||||||
| {
|
| {
|
||||||
navigator: Window['navigator']
|
navigator: Window['navigator']
|
||||||
|
// eslint-disable-next-line no-storage/no-browser-storage
|
||||||
localStorage: Window['localStorage']
|
localStorage: Window['localStorage']
|
||||||
|
// eslint-disable-next-line no-storage/no-browser-storage
|
||||||
sessionStorage: Window['sessionStorage']
|
sessionStorage: Window['sessionStorage']
|
||||||
addEventListener: Window['addEventListener']
|
addEventListener: Window['addEventListener']
|
||||||
TLDRAW_TAB_ID_v2?: string
|
TLDRAW_TAB_ID_v2?: string
|
||||||
|
@ -51,7 +58,7 @@ function iOS() {
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export const TAB_ID: string = window
|
export const TAB_ID: string = window
|
||||||
? window[tabIdKey] ?? window.sessionStorage[tabIdKey] ?? `TLDRAW_INSTANCE_STATE_V1_` + uniqueId()
|
? window[tabIdKey] ?? getFromSessionStorage(tabIdKey) ?? `TLDRAW_INSTANCE_STATE_V1_` + uniqueId()
|
||||||
: '<error>'
|
: '<error>'
|
||||||
if (window) {
|
if (window) {
|
||||||
window[tabIdKey] = TAB_ID
|
window[tabIdKey] = TAB_ID
|
||||||
|
@ -62,14 +69,14 @@ if (window) {
|
||||||
// in which case they'll have two tabs with the same UI state.
|
// in which case they'll have two tabs with the same UI state.
|
||||||
// It's not a big deal, but it's not ideal.
|
// It's not a big deal, but it's not ideal.
|
||||||
// And anyway I can't see a way to duplicate a tab in iOS Safari.
|
// And anyway I can't see a way to duplicate a tab in iOS Safari.
|
||||||
window.sessionStorage[tabIdKey] = TAB_ID
|
setInSessionStorage(tabIdKey, TAB_ID)
|
||||||
} else {
|
} else {
|
||||||
delete window.sessionStorage[tabIdKey]
|
deleteFromSessionStorage(tabIdKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window?.addEventListener('beforeunload', () => {
|
window?.addEventListener('beforeunload', () => {
|
||||||
window.sessionStorage[tabIdKey] = TAB_ID
|
setInSessionStorage(tabIdKey, TAB_ID)
|
||||||
})
|
})
|
||||||
|
|
||||||
const Versions = {
|
const Versions = {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { atom } from '@tldraw/state'
|
import { atom } from '@tldraw/state'
|
||||||
import { defineMigrations, migrate } from '@tldraw/store'
|
import { defineMigrations, migrate } from '@tldraw/store'
|
||||||
import { getDefaultTranslationLocale } from '@tldraw/tlschema'
|
import { getDefaultTranslationLocale } from '@tldraw/tlschema'
|
||||||
|
import { getFromLocalStorage, setInLocalStorage } from '@tldraw/utils'
|
||||||
import { T } from '@tldraw/validate'
|
import { T } from '@tldraw/validate'
|
||||||
import { uniqueId } from '../utils/uniqueId'
|
import { uniqueId } from '../utils/uniqueId'
|
||||||
|
|
||||||
|
@ -203,7 +204,7 @@ function loadUserPreferences(): TLUserPreferences {
|
||||||
const userData =
|
const userData =
|
||||||
typeof window === 'undefined'
|
typeof window === 'undefined'
|
||||||
? null
|
? null
|
||||||
: ((JSON.parse(window?.localStorage?.getItem(USER_DATA_KEY) || 'null') ??
|
: ((JSON.parse(getFromLocalStorage(USER_DATA_KEY) || 'null') ??
|
||||||
null) as null | UserDataSnapshot)
|
null) as null | UserDataSnapshot)
|
||||||
|
|
||||||
return migrateUserPreferences(userData)
|
return migrateUserPreferences(userData)
|
||||||
|
@ -212,15 +213,13 @@ function loadUserPreferences(): TLUserPreferences {
|
||||||
const globalUserPreferences = atom<TLUserPreferences | null>('globalUserData', null)
|
const globalUserPreferences = atom<TLUserPreferences | null>('globalUserData', null)
|
||||||
|
|
||||||
function storeUserPreferences() {
|
function storeUserPreferences() {
|
||||||
if (typeof window !== 'undefined' && window.localStorage) {
|
setInLocalStorage(
|
||||||
window.localStorage.setItem(
|
USER_DATA_KEY,
|
||||||
USER_DATA_KEY,
|
JSON.stringify({
|
||||||
JSON.stringify({
|
version: userMigrations.currentVersion,
|
||||||
version: userMigrations.currentVersion,
|
user: globalUserPreferences.get(),
|
||||||
user: globalUserPreferences.get(),
|
})
|
||||||
})
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Atom, atom, react } from '@tldraw/state'
|
import { Atom, atom, react } from '@tldraw/state'
|
||||||
|
import { deleteFromSessionStorage, getFromSessionStorage, setInSessionStorage } from '@tldraw/utils'
|
||||||
|
|
||||||
// --- 1. DEFINE ---
|
// --- 1. DEFINE ---
|
||||||
//
|
//
|
||||||
|
@ -128,9 +129,9 @@ function createDebugValueBase<T>(def: DebugFlagDef<T>): DebugFlag<T> {
|
||||||
const currentValue = valueAtom.get()
|
const currentValue = valueAtom.get()
|
||||||
try {
|
try {
|
||||||
if (currentValue === defaultValue) {
|
if (currentValue === defaultValue) {
|
||||||
window.sessionStorage.removeItem(`tldraw_debug:${def.name}`)
|
deleteFromSessionStorage(`tldraw_debug:${def.name}`)
|
||||||
} else {
|
} else {
|
||||||
window.sessionStorage.setItem(`tldraw_debug:${def.name}`, JSON.stringify(currentValue))
|
setInSessionStorage(`tldraw_debug:${def.name}`, JSON.stringify(currentValue))
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// not a big deal
|
// not a big deal
|
||||||
|
@ -154,7 +155,7 @@ function createDebugValueBase<T>(def: DebugFlagDef<T>): DebugFlag<T> {
|
||||||
|
|
||||||
function getStoredInitialValue(name: string) {
|
function getStoredInitialValue(name: string) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(window?.sessionStorage.getItem(`tldraw_debug:${name}`) ?? 'null')
|
return JSON.parse(getFromSessionStorage(`tldraw_debug:${name}`) ?? 'null')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { clearLocalStorage } from '@tldraw/utils'
|
||||||
import { deleteDB } from 'idb'
|
import { deleteDB } from 'idb'
|
||||||
import { getAllIndexDbNames } from './indexedDb'
|
import { getAllIndexDbNames } from './indexedDb'
|
||||||
|
|
||||||
|
@ -6,13 +7,15 @@ import { getAllIndexDbNames } from './indexedDb'
|
||||||
*
|
*
|
||||||
* @public */
|
* @public */
|
||||||
export async function hardReset({ shouldReload = true } = {}) {
|
export async function hardReset({ shouldReload = true } = {}) {
|
||||||
sessionStorage.clear()
|
clearLocalStorage()
|
||||||
|
|
||||||
await Promise.all(getAllIndexDbNames().map((db) => deleteDB(db)))
|
await Promise.all(getAllIndexDbNames().map((db) => deleteDB(db)))
|
||||||
|
|
||||||
localStorage.clear()
|
clearLocalStorage()
|
||||||
if (shouldReload) {
|
if (shouldReload) {
|
||||||
window.location.reload()
|
if (typeof window !== 'undefined') {
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
const clearAll = async () => {
|
const clearAll = async () => {
|
||||||
const dbs = (indexedDB as any)._databases as Map<any, any>
|
const dbs = (indexedDB as any)._databases as Map<any, any>
|
||||||
dbs.clear()
|
dbs.clear()
|
||||||
|
// eslint-disable-next-line no-storage/no-browser-storage
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { RecordsDiff, SerializedSchema, SerializedStore } from '@tldraw/store'
|
import { RecordsDiff, SerializedSchema, SerializedStore } from '@tldraw/store'
|
||||||
import { TLRecord, TLStoreSchema } from '@tldraw/tlschema'
|
import { TLRecord, TLStoreSchema } from '@tldraw/tlschema'
|
||||||
|
import { getFromLocalStorage, setInLocalStorage } from '@tldraw/utils'
|
||||||
import { IDBPDatabase, openDB } from 'idb'
|
import { IDBPDatabase, openDB } from 'idb'
|
||||||
import { TLSessionStateSnapshot } from '../../config/TLSessionStateSnapshot'
|
import { TLSessionStateSnapshot } from '../../config/TLSessionStateSnapshot'
|
||||||
|
|
||||||
|
@ -221,7 +222,7 @@ async function pruneSessionState({
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export function getAllIndexDbNames(): string[] {
|
export function getAllIndexDbNames(): string[] {
|
||||||
const result = JSON.parse(window?.localStorage.getItem(dbNameIndexKey) || '[]') ?? []
|
const result = JSON.parse(getFromLocalStorage(dbNameIndexKey) || '[]') ?? []
|
||||||
if (!Array.isArray(result)) {
|
if (!Array.isArray(result)) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -231,5 +232,5 @@ export function getAllIndexDbNames(): string[] {
|
||||||
function addDbName(name: string) {
|
function addDbName(name: string) {
|
||||||
const all = new Set(getAllIndexDbNames())
|
const all = new Set(getAllIndexDbNames())
|
||||||
all.add(name)
|
all.add(name)
|
||||||
window?.localStorage.setItem(dbNameIndexKey, JSON.stringify([...all]))
|
setInLocalStorage(dbNameIndexKey, JSON.stringify([...all]))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { getFromLocalStorage, setInLocalStorage } from '@tldraw/editor'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
|
@ -5,7 +6,7 @@ export function useLocalStorageState<T = any>(key: string, defaultValue: T) {
|
||||||
const [state, setState] = React.useState(defaultValue)
|
const [state, setState] = React.useState(defaultValue)
|
||||||
|
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => {
|
||||||
const value = localStorage.getItem(key)
|
const value = getFromLocalStorage(key)
|
||||||
if (value) {
|
if (value) {
|
||||||
try {
|
try {
|
||||||
setState(JSON.parse(value))
|
setState(JSON.parse(value))
|
||||||
|
@ -19,7 +20,7 @@ export function useLocalStorageState<T = any>(key: string, defaultValue: T) {
|
||||||
(setter: T | ((value: T) => T)) => {
|
(setter: T | ((value: T) => T)) => {
|
||||||
setState((s) => {
|
setState((s) => {
|
||||||
const value = typeof setter === 'function' ? (setter as any)(s) : setter
|
const value = typeof setter === 'function' ? (setter as any)(s) : setter
|
||||||
localStorage.setItem(key, JSON.stringify(value))
|
setInLocalStorage(key, JSON.stringify(value))
|
||||||
return value
|
return value
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,6 +19,12 @@ export const assert: (value: unknown, message?: string) => asserts value;
|
||||||
// @internal (undocumented)
|
// @internal (undocumented)
|
||||||
export const assertExists: <T>(value: T, message?: string | undefined) => NonNullable<T>;
|
export const assertExists: <T>(value: T, message?: string | undefined) => NonNullable<T>;
|
||||||
|
|
||||||
|
// @public
|
||||||
|
export function clearLocalStorage(): void;
|
||||||
|
|
||||||
|
// @public
|
||||||
|
export function clearSessionStorage(): void;
|
||||||
|
|
||||||
// @internal (undocumented)
|
// @internal (undocumented)
|
||||||
export function compact<T>(arr: T[]): NonNullable<T>[];
|
export function compact<T>(arr: T[]): NonNullable<T>[];
|
||||||
|
|
||||||
|
@ -34,6 +40,12 @@ export function dedupe<T>(input: T[], equals?: (a: any, b: any) => boolean): T[]
|
||||||
// @public
|
// @public
|
||||||
export function deepCopy<T = unknown>(obj: T): T;
|
export function deepCopy<T = unknown>(obj: T): T;
|
||||||
|
|
||||||
|
// @public
|
||||||
|
export function deleteFromLocalStorage(key: string): void;
|
||||||
|
|
||||||
|
// @public
|
||||||
|
export function deleteFromSessionStorage(key: string): void;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export type ErrorResult<E> = {
|
export type ErrorResult<E> = {
|
||||||
readonly ok: false;
|
readonly ok: false;
|
||||||
|
@ -68,6 +80,12 @@ export function getErrorAnnotations(error: Error): ErrorAnnotations;
|
||||||
// @public
|
// @public
|
||||||
export function getFirstFromIterable<T = unknown>(set: Map<any, T> | Set<T>): T;
|
export function getFirstFromIterable<T = unknown>(set: Map<any, T> | Set<T>): T;
|
||||||
|
|
||||||
|
// @public
|
||||||
|
export function getFromLocalStorage(key: string, defaultValue?: null): any;
|
||||||
|
|
||||||
|
// @public
|
||||||
|
export function getFromSessionStorage(key: string, defaultValue?: null): any;
|
||||||
|
|
||||||
// @public
|
// @public
|
||||||
export function getHashForBuffer(buffer: ArrayBuffer): string;
|
export function getHashForBuffer(buffer: ArrayBuffer): string;
|
||||||
|
|
||||||
|
@ -273,6 +291,12 @@ export function rng(seed?: string): () => number;
|
||||||
// @public
|
// @public
|
||||||
export function rotateArray<T>(arr: T[], offset: number): T[];
|
export function rotateArray<T>(arr: T[], offset: number): T[];
|
||||||
|
|
||||||
|
// @public
|
||||||
|
export function setInLocalStorage(key: string, value: any): void;
|
||||||
|
|
||||||
|
// @public
|
||||||
|
export function setInSessionStorage(key: string, value: any): void;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export function sortById<T extends {
|
export function sortById<T extends {
|
||||||
id: any;
|
id: any;
|
||||||
|
|
|
@ -172,6 +172,62 @@
|
||||||
"name": "",
|
"name": "",
|
||||||
"preserveMemberOrder": false,
|
"preserveMemberOrder": false,
|
||||||
"members": [
|
"members": [
|
||||||
|
{
|
||||||
|
"kind": "Function",
|
||||||
|
"canonicalReference": "@tldraw/utils!clearLocalStorage:function(1)",
|
||||||
|
"docComment": "/**\n * Clear all values from local storage. Will not throw an error if localStorage is not available.\n *\n * @public\n */\n",
|
||||||
|
"excerptTokens": [
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "export declare function clearLocalStorage(): "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "void"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ";"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileUrlPath": "packages/utils/src/lib/local-storage.ts",
|
||||||
|
"returnTypeTokenRange": {
|
||||||
|
"startIndex": 1,
|
||||||
|
"endIndex": 2
|
||||||
|
},
|
||||||
|
"releaseTag": "Public",
|
||||||
|
"overloadIndex": 1,
|
||||||
|
"parameters": [],
|
||||||
|
"name": "clearLocalStorage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Function",
|
||||||
|
"canonicalReference": "@tldraw/utils!clearSessionStorage:function(1)",
|
||||||
|
"docComment": "/**\n * Clear all values from session storage. Will not throw an error if sessionStorage is not available.\n *\n * @public\n */\n",
|
||||||
|
"excerptTokens": [
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "export declare function clearSessionStorage(): "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "void"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ";"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileUrlPath": "packages/utils/src/lib/session-storage.ts",
|
||||||
|
"returnTypeTokenRange": {
|
||||||
|
"startIndex": 1,
|
||||||
|
"endIndex": 2
|
||||||
|
},
|
||||||
|
"releaseTag": "Public",
|
||||||
|
"overloadIndex": 1,
|
||||||
|
"parameters": [],
|
||||||
|
"name": "clearSessionStorage"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "Function",
|
"kind": "Function",
|
||||||
"canonicalReference": "@tldraw/utils!debounce:function(1)",
|
"canonicalReference": "@tldraw/utils!debounce:function(1)",
|
||||||
|
@ -423,6 +479,96 @@
|
||||||
],
|
],
|
||||||
"name": "deepCopy"
|
"name": "deepCopy"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "Function",
|
||||||
|
"canonicalReference": "@tldraw/utils!deleteFromLocalStorage:function(1)",
|
||||||
|
"docComment": "/**\n * Remove a value from local storage. Will not throw an error if localStorage is not available.\n *\n * @param key - The key to set.\n *\n * @public\n */\n",
|
||||||
|
"excerptTokens": [
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "export declare function deleteFromLocalStorage(key: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "): "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "void"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ";"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileUrlPath": "packages/utils/src/lib/local-storage.ts",
|
||||||
|
"returnTypeTokenRange": {
|
||||||
|
"startIndex": 3,
|
||||||
|
"endIndex": 4
|
||||||
|
},
|
||||||
|
"releaseTag": "Public",
|
||||||
|
"overloadIndex": 1,
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"parameterName": "key",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 1,
|
||||||
|
"endIndex": 2
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "deleteFromLocalStorage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Function",
|
||||||
|
"canonicalReference": "@tldraw/utils!deleteFromSessionStorage:function(1)",
|
||||||
|
"docComment": "/**\n * Remove a value from session storage. Will not throw an error if sessionStorage is not available.\n *\n * @param key - The key to set.\n *\n * @public\n */\n",
|
||||||
|
"excerptTokens": [
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "export declare function deleteFromSessionStorage(key: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "): "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "void"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ";"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileUrlPath": "packages/utils/src/lib/session-storage.ts",
|
||||||
|
"returnTypeTokenRange": {
|
||||||
|
"startIndex": 3,
|
||||||
|
"endIndex": 4
|
||||||
|
},
|
||||||
|
"releaseTag": "Public",
|
||||||
|
"overloadIndex": 1,
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"parameterName": "key",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 1,
|
||||||
|
"endIndex": 2
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "deleteFromSessionStorage"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "TypeAlias",
|
"kind": "TypeAlias",
|
||||||
"canonicalReference": "@tldraw/utils!ErrorResult:type",
|
"canonicalReference": "@tldraw/utils!ErrorResult:type",
|
||||||
|
@ -654,6 +800,128 @@
|
||||||
],
|
],
|
||||||
"name": "getFirstFromIterable"
|
"name": "getFirstFromIterable"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "Function",
|
||||||
|
"canonicalReference": "@tldraw/utils!getFromLocalStorage:function(1)",
|
||||||
|
"docComment": "/**\n * Get a value from local storage.\n *\n * @param key - The key to get.\n *\n * @param defaultValue - The default value to return if the key is not found or if local storage is not available.\n *\n * @public\n */\n",
|
||||||
|
"excerptTokens": [
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "export declare function getFromLocalStorage(key: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ", defaultValue?: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "): "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ";"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileUrlPath": "packages/utils/src/lib/local-storage.ts",
|
||||||
|
"returnTypeTokenRange": {
|
||||||
|
"startIndex": 5,
|
||||||
|
"endIndex": 6
|
||||||
|
},
|
||||||
|
"releaseTag": "Public",
|
||||||
|
"overloadIndex": 1,
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"parameterName": "key",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 1,
|
||||||
|
"endIndex": 2
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameterName": "defaultValue",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 3,
|
||||||
|
"endIndex": 4
|
||||||
|
},
|
||||||
|
"isOptional": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "getFromLocalStorage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Function",
|
||||||
|
"canonicalReference": "@tldraw/utils!getFromSessionStorage:function(1)",
|
||||||
|
"docComment": "/**\n * Get a value from session storage.\n *\n * @param key - The key to get.\n *\n * @param defaultValue - The default value to return if the key is not found or if session storage is not available.\n *\n * @public\n */\n",
|
||||||
|
"excerptTokens": [
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "export declare function getFromSessionStorage(key: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ", defaultValue?: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "): "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ";"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileUrlPath": "packages/utils/src/lib/session-storage.ts",
|
||||||
|
"returnTypeTokenRange": {
|
||||||
|
"startIndex": 5,
|
||||||
|
"endIndex": 6
|
||||||
|
},
|
||||||
|
"releaseTag": "Public",
|
||||||
|
"overloadIndex": 1,
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"parameterName": "key",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 1,
|
||||||
|
"endIndex": 2
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameterName": "defaultValue",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 3,
|
||||||
|
"endIndex": 4
|
||||||
|
},
|
||||||
|
"isOptional": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "getFromSessionStorage"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "Function",
|
"kind": "Function",
|
||||||
"canonicalReference": "@tldraw/utils!getHashForBuffer:function(1)",
|
"canonicalReference": "@tldraw/utils!getHashForBuffer:function(1)",
|
||||||
|
@ -3061,6 +3329,128 @@
|
||||||
],
|
],
|
||||||
"name": "rotateArray"
|
"name": "rotateArray"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "Function",
|
||||||
|
"canonicalReference": "@tldraw/utils!setInLocalStorage:function(1)",
|
||||||
|
"docComment": "/**\n * Set a value in local storage. Will not throw an error if localStorage is not available.\n *\n * @param key - The key to set.\n *\n * @param value - The value to set.\n *\n * @public\n */\n",
|
||||||
|
"excerptTokens": [
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "export declare function setInLocalStorage(key: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ", value: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "): "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "void"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ";"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileUrlPath": "packages/utils/src/lib/local-storage.ts",
|
||||||
|
"returnTypeTokenRange": {
|
||||||
|
"startIndex": 5,
|
||||||
|
"endIndex": 6
|
||||||
|
},
|
||||||
|
"releaseTag": "Public",
|
||||||
|
"overloadIndex": 1,
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"parameterName": "key",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 1,
|
||||||
|
"endIndex": 2
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameterName": "value",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 3,
|
||||||
|
"endIndex": 4
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "setInLocalStorage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Function",
|
||||||
|
"canonicalReference": "@tldraw/utils!setInSessionStorage:function(1)",
|
||||||
|
"docComment": "/**\n * Set a value in session storage. Will not throw an error if sessionStorage is not available.\n *\n * @param key - The key to set.\n *\n * @param value - The value to set.\n *\n * @public\n */\n",
|
||||||
|
"excerptTokens": [
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "export declare function setInSessionStorage(key: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ", value: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "): "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "void"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ";"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileUrlPath": "packages/utils/src/lib/session-storage.ts",
|
||||||
|
"returnTypeTokenRange": {
|
||||||
|
"startIndex": 5,
|
||||||
|
"endIndex": 6
|
||||||
|
},
|
||||||
|
"releaseTag": "Public",
|
||||||
|
"overloadIndex": 1,
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"parameterName": "key",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 1,
|
||||||
|
"endIndex": 2
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameterName": "value",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 3,
|
||||||
|
"endIndex": 4
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "setInSessionStorage"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "Function",
|
"kind": "Function",
|
||||||
"canonicalReference": "@tldraw/utils!sortById:function(1)",
|
"canonicalReference": "@tldraw/utils!sortById:function(1)",
|
||||||
|
|
|
@ -23,6 +23,12 @@ export { noop, omitFromStackTrace, throttle } from './lib/function'
|
||||||
export { getHashForBuffer, getHashForObject, getHashForString, lns } from './lib/hash'
|
export { getHashForBuffer, getHashForObject, getHashForString, lns } from './lib/hash'
|
||||||
export { getFirstFromIterable } from './lib/iterable'
|
export { getFirstFromIterable } from './lib/iterable'
|
||||||
export type { JsonArray, JsonObject, JsonPrimitive, JsonValue } from './lib/json-value'
|
export type { JsonArray, JsonObject, JsonPrimitive, JsonValue } from './lib/json-value'
|
||||||
|
export {
|
||||||
|
clearLocalStorage,
|
||||||
|
deleteFromLocalStorage,
|
||||||
|
getFromLocalStorage,
|
||||||
|
setInLocalStorage,
|
||||||
|
} from './lib/local-storage'
|
||||||
export { MediaHelpers } from './lib/media'
|
export { MediaHelpers } from './lib/media'
|
||||||
export { invLerp, lerp, modulate, rng } from './lib/number'
|
export { invLerp, lerp, modulate, rng } from './lib/number'
|
||||||
export {
|
export {
|
||||||
|
@ -52,6 +58,12 @@ export {
|
||||||
sortByIndex,
|
sortByIndex,
|
||||||
validateIndexKey,
|
validateIndexKey,
|
||||||
} from './lib/reordering/reordering'
|
} from './lib/reordering/reordering'
|
||||||
|
export {
|
||||||
|
clearSessionStorage,
|
||||||
|
deleteFromSessionStorage,
|
||||||
|
getFromSessionStorage,
|
||||||
|
setInSessionStorage,
|
||||||
|
} from './lib/session-storage'
|
||||||
export { sortById } from './lib/sort'
|
export { sortById } from './lib/sort'
|
||||||
export type { Expand, RecursivePartial, Required } from './lib/types'
|
export type { Expand, RecursivePartial, Required } from './lib/types'
|
||||||
export { isDefined, isNonNull, isNonNullish, structuredClone } from './lib/value'
|
export { isDefined, isNonNull, isNonNullish, structuredClone } from './lib/value'
|
||||||
|
|
63
packages/utils/src/lib/local-storage.ts
Normal file
63
packages/utils/src/lib/local-storage.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/* eslint-disable no-storage/no-browser-storage */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a value from local storage.
|
||||||
|
*
|
||||||
|
* @param key - The key to get.
|
||||||
|
* @param defaultValue - The default value to return if the key is not found or if local storage is not available.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getFromLocalStorage(key: string, defaultValue = null) {
|
||||||
|
try {
|
||||||
|
const value = localStorage.getItem(key)
|
||||||
|
if (value === null) return defaultValue
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value in local storage. Will not throw an error if localStorage is not available.
|
||||||
|
*
|
||||||
|
* @param key - The key to set.
|
||||||
|
* @param value - The value to set.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function setInLocalStorage(key: string, value: any) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(key, JSON.stringify(value))
|
||||||
|
} catch {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a value from local storage. Will not throw an error if localStorage is not available.
|
||||||
|
*
|
||||||
|
* @param key - The key to set.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function deleteFromLocalStorage(key: string) {
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(key)
|
||||||
|
} catch {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all values from local storage. Will not throw an error if localStorage is not available.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function clearLocalStorage() {
|
||||||
|
try {
|
||||||
|
localStorage.clear()
|
||||||
|
} catch {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
63
packages/utils/src/lib/session-storage.ts
Normal file
63
packages/utils/src/lib/session-storage.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/* eslint-disable no-storage/no-browser-storage */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a value from session storage.
|
||||||
|
*
|
||||||
|
* @param key - The key to get.
|
||||||
|
* @param defaultValue - The default value to return if the key is not found or if session storage is not available.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getFromSessionStorage(key: string, defaultValue = null) {
|
||||||
|
try {
|
||||||
|
const value = sessionStorage.getItem(key)
|
||||||
|
if (value === null) return defaultValue
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value in session storage. Will not throw an error if sessionStorage is not available.
|
||||||
|
*
|
||||||
|
* @param key - The key to set.
|
||||||
|
* @param value - The value to set.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function setInSessionStorage(key: string, value: any) {
|
||||||
|
try {
|
||||||
|
sessionStorage.setItem(key, JSON.stringify(value))
|
||||||
|
} catch {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a value from session storage. Will not throw an error if sessionStorage is not available.
|
||||||
|
*
|
||||||
|
* @param key - The key to set.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function deleteFromSessionStorage(key: string) {
|
||||||
|
try {
|
||||||
|
sessionStorage.removeItem(key)
|
||||||
|
} catch {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all values from session storage. Will not throw an error if sessionStorage is not available.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function clearSessionStorage() {
|
||||||
|
try {
|
||||||
|
sessionStorage.clear()
|
||||||
|
} catch {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
|
@ -7299,6 +7299,7 @@ __metadata:
|
||||||
eslint-plugin-import: "npm:^2.27.5"
|
eslint-plugin-import: "npm:^2.27.5"
|
||||||
eslint-plugin-local: "npm:^1.0.0"
|
eslint-plugin-local: "npm:^1.0.0"
|
||||||
eslint-plugin-no-only-tests: "npm:^3.1.0"
|
eslint-plugin-no-only-tests: "npm:^3.1.0"
|
||||||
|
eslint-plugin-no-storage: "npm:^1.0.2"
|
||||||
eslint-plugin-react: "npm:^7.32.2"
|
eslint-plugin-react: "npm:^7.32.2"
|
||||||
eslint-plugin-react-hooks: "npm:^4.6.0"
|
eslint-plugin-react-hooks: "npm:^4.6.0"
|
||||||
fs-extra: "npm:^11.1.0"
|
fs-extra: "npm:^11.1.0"
|
||||||
|
@ -12902,6 +12903,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"eslint-plugin-no-storage@npm:^1.0.2":
|
||||||
|
version: 1.0.2
|
||||||
|
resolution: "eslint-plugin-no-storage@npm:1.0.2"
|
||||||
|
checksum: 971323b852846a3eb1558ff68aa51e36ff6671cf99f261410768a3c4c2baebc1702decee3afbc16128d12d965ea00b574adf2ee35fc4b68b58d74810e6bcb37b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"eslint-plugin-react-hooks@npm:^4.5.0, eslint-plugin-react-hooks@npm:^4.6.0":
|
"eslint-plugin-react-hooks@npm:^4.5.0, eslint-plugin-react-hooks@npm:^4.6.0":
|
||||||
version: 4.6.0
|
version: 4.6.0
|
||||||
resolution: "eslint-plugin-react-hooks@npm:4.6.0"
|
resolution: "eslint-plugin-react-hooks@npm:4.6.0"
|
||||||
|
|
Loading…
Reference in a new issue