Chiku (č“„) is a lightweight storage management library designed for handling simple storage operations with multiple storage types, such as IndexedDB, localStorage, and sessionStorage. It provides a consistent API for storing, retrieving, and deleting data across different storage mechanisms, with built-in support for key-value operations, batch operations, and data locking.
- Simple API: Provides functions like
get
,set
,del
,getMany
,setMany
, anddelMany
for seamless interaction with storage systems. - Multi-storage Support: Works with IndexedDB, localStorage, sessionStorage, or any custom storage you define.
- Data Locking: Includes
lock
andfree
methods for managing access to resources across different processes or tabs. - Batch Operations: Perform batch operations efficiently with
getMany
,setMany
, anddelMany
. - Customizable: Allows for easy customization and extension to other storage solutions.
č“„ ("chiku") in Japanese represents "accumulation" or "storage", reflecting the purpose of this library: to provide efficient, easy-to-use tools for data storage and management.
Perfect for both client-side and server-side environments.
Please note at this step, I think tree shaking is essential so different functions need to install other functions List:
get$(key: string) => MaybePromise<T>
Permissionread
valueset$(key: string, value: T) => MaybePromise<void>
Permissionwrite
valuedel$(key: string) => MaybePromise<void>
Permissiondelete
valuegetMany$(keys: string[]) => MaybePromise<T[]>
Function get multiple valuessetMany$(entries: [string, T][]) => MaybePromise<void>
Function set multiple valuesdelMany$(keys: string[]) => MaybePromise<void>
Function delete multiple valuesupdate$(fn: (value: unknown) => MaybePromise<T>) => MaybePromise<T>
Function lock storage
import { createStorage, createStore, get, assert } from "chiku"
import { get$, set$, del$, getMany$, setMany$, delMany$, update$ } from "chiku/drivers/indexeddb"
const storage = createStorage(
createStore<{
counter: boolean
name: string
}>(),
{ get$, set$, del$, getMany$, setMany$, delMany$, update$ }
)
List drivers:
chiku/drivers/indexeddb
- Driver for indexedDB storagechiku/drivers/local-storage
- Driver for localStorage storagechiku/drivers/memory
- Driver for memory storage
Function get
value from storage
const value = await get(storage, "counter")
// value is `number | undefined`
Function getMany
value from storage
const values = await getMany(storage, ["counter", "name"])
values.forEach(value => assert(value))
// values is `[number, string]`
Function set
value to storage
await set(storage, "counter", 10)
Function setMany
value to storage
await setMany(storage, [
["counter", 10],
["name", "Tachibana Shin"]
])
Function del
value from storage
await del(storage, "counter")
Function delMany
value from storage
await delMany(storage, ["counter", "name"])
Function update
value to storage
await update(storage, "counter", (value = 0) => value + 1)
Function check assert
value
const value = await get(storage, "counter")
// value is `number | undefined`
assert(value)
// value is `number`
const values = await get(storage, ["counter", "name"])
// values is `[number | undefined, string | undefined]`
values.forEach(value => assert(value))
// values is `[number, string]`
Function cache
value to storage
interface CacheOptions<T> {
/** @default 600_000 */
expires?: number
/** @default 0 */
stale?: number
get: T | (() => MaybeOrPromise<T>)
}
expires = 60s
The lifetime of this data, whether it needs to be refreshed or notstale = 0
By default when this function is called if it has not expired the data will be updated in the background. Ifstale
is still valid it will skip background data updatesget: T | (() => MaybeOrPromise<T>)
The value or function result value for update cache
By default get
and getMany
will return their type. However, if it is not set then its return type is T | undefined
. but because this function has the ability to get its own data, the return type of this function is always T
without assert
const value = await cache(storage, "name", async () => "Tachibana Shin")
// value is string
The remember
function is used to retrieve a value from a given storage system. If the value is not present in the storage, the function will generate the value using a provided function fn
, store the newly generated value, and then return it.
This method is particularly useful when caching or memoizing values that are expensive to compute, ensuring that the value is generated once and stored for future retrieval.
-
storage: RS
The storage object that conforms to theRStorage
interface. This object is responsible for holding key-value pairs (kv
), where the values can be retrieved and stored using theget
andset
methods. -
key: Key
The key used to store and retrieve the value from the storage. It must be a valid key in thekv
map of the provided storage. -
fn: () => MaybeOrPromise<RS["kv"][Key]>
A function that generates the value if it does not already exist in the storage. The function can either return the value directly (synchronously) or return a promise that resolves to the value (asynchronously).
Promise<RS["kv"][Key]>
The retrieved or newly generated value from the storage. If the value did not exist, it is generated, stored, and then returned.- By default
get
andgetMany
will return their type. However, if it is not set then its return type isT | undefined
. but because this function has the ability to get its own data, the return type of this function is alwaysT
withoutassert
const value = await remember(storage, "counter", async () => {
// Generate the value if it does not exist
return await fetchUserSettingsFromAPI()
})
// value is number
- The function attempts to retrieve a value associated with the provided
key
from the storage using theget
method. - If the value does not exist (i.e.,
undefined
), the providedfn
function is called to generate the value. - The newly generated value is stored using the
set
method with the samekey
. - The function finally returns the retrieved or generated value.
This function is useful in scenarios where you want to avoid recalculating a value or re-fetching data unnecessarily if it has already been stored previously.
Here is the documentation for the watch
function:
The watch
function allows you to observe changes to a specific key in the storage system. When the value associated with the key is updated, a provided callback function (fn
) is executed. This is useful for syncing storage data changes across multiple components, tabs, or windows.
This function has great power, it can track data even if changes occur in Worker
, ServiceWorker
or even IFrame
and other tab
-
storage: RS
The storage object that conforms to theRStorage
interface. The storage must have broadcasting and listener capabilities to handle change events for keys. -
key: Key
The key in thekv
map of the storage object that you want to watch for changes. -
fn: (newValue: () => RS["kv"][Key] | undefined) => void
A callback function that gets executed whenever the value of the watched key changes. ThenewValue
passed to the function is either the updated value orundefined
if the value was removed or did not exist. -
options?: WatchOptions
An optional object to customize the behavior of the watcher.local?: boolean
(default:false
)
Iftrue
, the watcher will also respond to local changes (within the same tab or window). Iffalse
, the watcher only responds to broadcasted changes (e.g., from other tabs or windows).
() => void
A cleanup function that can be called to stop watching for changes. This removes the associated event listener and ensures no further callbacks are invoked.
const stopWatching = watch(storage, "counter", await (newValue) => {
console.log("Updated user preferences:", async newValue())
})
// Later on, you can stop watching by calling the returned function
stopWatching()
-
Event Handler Setup:
An event handler is created to listen for messages related to changes to the specifiedkey
. When a change is detected, the callback functionfn
is triggered with the new value of the key orundefined
if the value was removed. -
Storage Event Binding:
Depending on the storage system, the event handler can listen to changes either locally (within the same browser tab) or across different tabs (through the BroadcastChannel API). Iflocal
istrue
, it listens to local changes as well. -
Event Broadcasting:
The storage object manages a list of broadcast channels and handlers. When a value changes, the change event is broadcasted, and the corresponding handlers are invoked to execute the callback. -
Cleanup:
The function returns a cleanup function that allows you to stop watching for changes. This removes the handler from the storage's list of listeners.
This function is useful when you need to synchronize data changes between multiple views, components, or browser contexts. For example, when multiple tabs need to stay updated with the latest user preferences or other shared state.