A sessionStorage & localStorage utility with namespaced entries and extra Freshness.
A project which uses Sactchel+Bindel extensively can be found here: Signal-v-Noise | GitHub
- Installation
- What is this?
- How to use
- Choosing sessionStorage or localStorage
- Satchel Pockets & Namespaces
- Satchel Events
- Satchel Freshness
- Satchel Instance Methods
- Satchel Static Methods
- Optional imports in bindle.js
- Licence & Copyright
Installation ↑
Use npm
to install Satchel from github targeting a specific tag; drop everything after the hash if you just want the latest version.
npm i https://github.com/bnjmnrsh/satchel#v0.2.4
What is this? ↑
Satchel.js
is a library for managing browser sessionStorage
and localStorage
entries. It allows developers to set entry expiry times and test a given entry's freshness when retrieving values. It also allows for the namespacing of entries using shared key prefixes called 'pockets'. To help manage pockets, Satchel.js
ships with bindle.js
, a small suite of optional imports: emptyPocket()
, tidyPocket()
and getAllPocketKeys()
.
How to use ↑
import { Satchel } from '@bnjmnrsh/satchel'
const twoHours = 7200; // seconds
const taco = new Satchel('taco', { data: 'a tasty treat', expiry: Date.now() + twoHours }, false, 'pocket');
console.log(taco.isFresh());
// true
console.log(taco.get());
// returns { data: 'a tasty treat', expiry: null }
Satchel
can be passed the following parameters during instantiation:
Satchel(
key:string, // required
cargo:object { data: string|object, expiry: number },
localStore:boolean // defaults is false for sessionStorage
pocket:string // optional namespace, defaults to 'pocket'
)
At a minimum, Satchel
requires a key
to instantiate. During setup, if not provided any further values, Satchel
will create a sessionStorage
(default) or localStorage
entry with a string representation of a default cargo
object:
{ data: null, expiry:null, _creation: number }
Satchel
's cargo object contains three properties:
data
: can be a number, string or serialisable object †expiry
: an optional number, representing a future UNIX time in seconds_creation
: a UNIX creation timestamp in seconds (cannot be modified directly) ††
Both the data
and expiry
properties default to null
. You can update and retrieve these values using .set()
and .get()
††.
const taco = new Satchel('taco')
console.log(taco.get())
// {data: null, expiry: null}
taco.set({data: 'a tasty treat'})
console.log(taco.get())
// {data: 'a tasty treat', expiry: null}
†
⚠️ NOTE:localStorage
andsessionStorage
can only hold string values. Any data passed toSatchel.set()
is run thoroughJSON.stringify()
which has inherent limitations, effecting deeply nested objects, circular references, and very large numbers.
†† ☝️ NOTE: The
_creation
property holds a UNIX timestamp recording the moment a key was created –– it cannot be modified directly.
See also Satchel.setKey()
Choosing sessionStorage or localStorage ↑
Satchel
uses sessionStorage
by default. You can specify localStorage
by passing true
as a third parameter during instantiation:
const localTaco = new Satchel('localTaco', {data: 'a yummy treat'}, true);
Pockets & Namespaces ↑
Satchel
scopes all of its entries using a 'pocket' namespace, which can be modified as needed as the fourth parameter during instantiation:
// default 'pocket' namespace
const taco1 = new Satchel('taco', {data: 'a yummy treat'}, false);
console.log(taco1.getKey());
// stcl.pocket.taco <- default 'stcl.pocket' namespaces
const taco2 = new Satchel('taco', {data: 'a yummy treat'}, false, 'tacoTruck');
console.log(taco2.getKey());
// stcl.tacoTruck.taco <-- custom 'stcl.tacoTruck' pocket namespace
The stcl
prefix of the storage key may be customised prior to instantiation by setting the Satchel.stcl
property:
Satchel.stcl = 'restaurant'
const taco = new Satchel('taco', {data: 'a yummy treat'}, false);
console.log(taco.getKey()); // restaurant.pocket.taco <-- prefixed namespace
Satchel.stcl
is both a getter
and setter
, so it can be used to retrieve the value if required. This can be useful when working with the optional functions found in bindle.js.
Satchel Freshness ↑
Satchel provides several methods to manage key freshness. By default, .get()
will only return values that are fresh by testing freshness with .isFresh()
internally. This behaviour can be overridden by passing .get(true)
, which will return values from the store regardless of freshness.
The .age()
helper method returns an object of age-related values for a given store key. The returned object includes the properties age
(seconds), which is the age of the current store, and a creation
property, a UNIX timestamp of its creation date. It also includes a fresh
boolean property indicating the record's current freshness state.
See also the optional import tidyPocket()
will remove any stale records from a given 'pocket' in a store.
Satchel Events ↑
Satchels methods set()
, and bin()
methods emit Satchel
custom events which largely mirror the properties of the StorageEvent API, albeit nested within the e.detail
CustomEvent property. Unlike StorageEvent
, which emits across browser tabs, Satchel
events are limited to the current browser tab.
Basic Usage:
window.addEventListener('Satchel', fn);
const fn = function(e) {
if (e.type !== 'Satchel') return
console.log(e.detail);
}
Satchel.('taco', {data: 'a tasty treat'})
The event emitted by the above function could look something like this:
e.detail {
action: 'set'
key: 'stcl.pocket.taco',
newValue: {
data: 'a tasty treat',
expiry: null,
creation: 1666013618656
},
oldValue: null,
storageArea: 'SessionStorage',
url: 'http://localhost:8080', // window.location.href,
}
The action
property indicates the Satchel
method, which fired the event.
Additionally the optional imports tidyPocket()
and emptyPocket()
emit events with details related to these operations. See bindle.js for details.
Satchel instance methods ↑
To work with stored data Satchel
provides the following instance methods.
Returns an object of age-related values or null
if the key is not found in the store.
{
age: 704915, // number, seconds since the creation of key in store
creation: 1666013618656, // UNIX timestamp at point of creation
expiry: 1666013623656, // UNIX timestamp for when this key expires
fresh: false // boolean for if the current key is fresh
}
Usage: console.log(taco.age());
Removes the key associated with the current Satchel
instance from storage. Returns true
if the key was removed and null
if the key for the current instance could not be found in the store.
Usage: console.log(taco.bin()); // true
A successful bin()
operation emits the following event:
e.detail {
action: 'bin'
key: 'stcl.pocket.taco',
newValue: null,
oldValue: {
data: 'a tasty treat',
expiry: null,
creation: integer (seconds)
},
storageArea: 'SessionStorage',
url: String(window.location.href),
}
If the stored entry is 'fresh', .get()
returns the value of the stored key object containing the properties:
data
(object|string)expiry
(number in seconds) properties, returning false if not 'fresh'.
The method accepts an optional boolean .get(true)
, which will force it to return a stored entry regardless of whether the entry is 'fresh' or not.
If an entry cannot be found for the current key instance (for example, as the result of a previous .bin()
operation), .get()
returns null
.
Usage:
const taco = new Satchel('taco')
console.log(taco.get()); // { _creation: 1666884532131, data: null, expiry: null }`
Accepts a cargo
object with either of two optional values:
data
(number|string|object) Objects passed todata
will be passed throughJSON.stringify()
.†expiry
(number) represents a future UNIX date in seconds until the current entry expires.
As of version 0.2.3 cargo.data
and cargo.expiry
can be set independently.
Usage:
const _24h = 86400
const taco = new Satchel('taco',{data: 'a tasty treat'})
taco.set({expiry: Date.now() + _24h})
†
⚠️ NOTE:localStorage
andsessionStorage
can only hold string values. Any data passed toSatchel.set()
is run thoroughJSON.stringify()
which has inherent limitations, effecting deeply nested objects, circular references, and very large numbers.
e.detail {
action: 'set'
key: 'stcl.pocket.taco',
newValue: {
data: 'a great snack':
expiry: null,
creation: 1666013618656
},
oldValue: {
data: 'a tasty treat':
expiry: null,
creation: 1666013618656
},
storageArea: 'SessionStorage',
url: 'http://localhost:8080',
}
Returns a boolean
indicating the freshness of the current key in storage, null
if the key could not be found.
Usage:
console.log(taco.isFresh()); // true
Returns a dot-separated string representing the key of the current Satchel instance, including prefix and 'pocket' namespaces. Note that getKey()
returns the key value as stored on the current Satchel
instance, and is not proof that the key currently exists in storage.
The default instance namespace is stchl
, and the default "pocket" namespace is pocket
.
See notes on Pockets & Namespaces.
Usage:
const taco = new Satchel('taco);
console.log(taco.getKey()) // "stchl.pocket.taco"
Satchel Static Methods ↑
Satchel.getSatchel(key, local=false, pocket='pocket')
Returns a Satchel
instance if the key is found in storage or null
if not found. Accepts a string
for the 'key' (required), boolean
for the storage area, and string
for the 'pocket' namespace (optional). Default is false
for sessionStorage
, true
for localStorage
.
Usage: Satchel.getSatchel('taco', true, 'myPocket')
For when you want to use Satchel
for its side effects only and don't need an instance returned. Is a thin wrapper around new Satchel()
See How to use for a full list of options.
Useage: Satchel.setKey('taco')
Optional imports in bindle.js ↑
Satchel
comes with bindle.js
, which adds optional methods to help manage pocket namespaces.
import { getAllPocketKeys, tidyPocket, emptyPocket } from '@bnjmnrsh/satchel/dist/bindle'
getAllPocketKeys( local=false, pocket='pocket', stcl='stcl')
Returns an array of all 'pocket' namespace keys regardless of freshness. Accepts a boolean for the storage area, which defaults to false
for sessionStorage
, true
for localStorage
, a string for the 'pocket' to look for (default is 'pocket'), and an optional stcl
string if a namespace has been set on the Satchel.stcl
object.
Usage:
console.log(getAllPocketKeys(false)) // ['stcl.pocket.taco' ...]
// With a custom prefix:
Satchel.stcl = 'tacoTruck'
console.log(getAllPocketKeys(false, 'pocket', Satchel.stcl)) // ['tacoTruck.pocket.taco' ...]
tidyPocket(local = false, pocket='pocket', stcl='stcl')
Removes expired items from a pocket. Accepts a boolean
for the storage area (defaulting to false
for sessionStorage
, true
for localStorage
), a string for the 'pocket' namespace (default is 'pocket'), and an optional 'stcl
' string
if a namespace prefix has been set on the Satchel.stcl
object.
Returns an array containing the number of items remaining in the pocket and the total number of items remaining in the store regardless of namespace. If no items are found for a given 'pocket', null
is returned.
Usage:
tidyPocket(true, 'myPocket') // example return [2,5]
emptyPocket(local=false, pocket='pocket', stcl='stcl')
Completely empties a store of all keys within a given 'pocket' namespace, regardless of freshness. Accepts a boolean
for the storage area (defaulting to false
for sessionStorage
, true
for localStorage
), a string for the 'pocket' namespace (default is 'pocket'), and an optional 'stcl
' string
if a namespace prefix has been set on the Satchel.stcl
object.
Returns an array containing the number of items remaining in the pocket (which for emptyPocket()
should always be 0
) and the total number of items remaining in the store regardless of namespace. If no items are found for a given 'pocket', null
is returned.
Usage:
// Local Store
emptyPocket(true) // example return [0,5]
Copyright (c) 2022 Benjamin O. Rush (bnjmnrsh).