npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@figliolia/typed-storage

v1.0.1

Published

A type safe wrapper for browser storage API's

Downloads

19

Readme

Typed Storage

A type-safe wrapper around the browser's LocalStorage and SessionStorage API's. The motivation behind this package is two fold:

  1. When working with large teams, ensuring that key-names are consistent and follow certain conventions can be combersome
  2. Ensuring that only values of a certain type are stored for each key can require can be difficult because the API's require string-values when storing data

The TypedStorage API allows you define a schema for your data and restricts storing data to only that which matches your key's corresponding value type.

Installation

npm i @figliolia/typed-storage
# or 
yarn add @figliolia/typed-storage

Basic Usage

Defining Your API

import { TypedStorage } from "@figliolia/typed-storage";

export interface Schema {
  JWT: string;
  userId: number;
  shoppingCart: { item: string, price: number }[];
}

const LocalStorage = new TypedStorage<Schema>(localStorage);
// or 
const SessionStorage = new TypedStorage<Schema>(sessionStorage);

Using Your API

When using your TypedStorage instances, your data-type is preserved when setting and getting:

import { LocalStorage } from "./path/to/myLocalStorage";

LocalStorage.setItem("shoppingCart", [
  {item: "Bananas", price: 3.00 },
  {item: "Apples", price: 2.50 },
  // Stringified under the hood
]);

const cart = LocalStorage.getItem("shoppingCart"); // parsed under the hood
// { item: string, price: number }[] | null


const cart = LocalStorage.getItem("cart"); // mispelled key
// Fails typescript validation!

Supported Data Types

The TypedStorage API supports storing any data-type that is JSON-valid. For keys with values of type object or array, JSON.parse() will be used to deserialize your data upon retreival.

For keys with values of type string or number, best effort parsing is used to determine the correct type on retrieval. For example if the value being retrieved can be safely converted to an integer, float, or BigInt, it will be. Otherwise the value will be returned as a string.

For instances where your key-value pair requires a customized serialization or deserialization technique, you can instantiate your TypedStorage along with your deserializer mapped to your key:

import { TypedStorage } from "@figliolia/typed-storage";

interface Schema {
  meyKey: Map<string, number>; // (invalid JSON)
}

const MyStorage = new TypedStorage<Schema>(localStorage, {
  meyKey: {
    // Convert map to object and stringify it for storage
    serialize: (map) => {
      const obj: Record<string, number> = {};
      for(const [key, value] of map) {
        obj[key] = value;
      }
      return JSON.stringify(map);
    },
    // Convert stored object back into a Map for runtime usage
    deserialize: (map) => {
      const obj = JSON.parse(map);
      const result = new Map();
      for(const key in obj) {
        result.set(key, obj[key]);
      }
      return result;
    } 
  }
});

Serializers can also be used in instances where you wish to handle numeric values as strings

import { TypedStorage } from "@figliolia/typed-storage";

interface Schema {
  floatValue: string; // Handle float as strings
}

const MyStorage = new TypedStorage<Schema>(localStorage, {
  floatValue: {
    // Prevent float-like string from being returned as a number
    deserialize: (floatValue) => floatValue;
  }
})

Because TypedStorage parses using a best-effort interpretation of the value, if your string contains only numeric characters, it'll be parsed as a number regardless of your schema definition.

If you run into a case such as this and you wish to preserve string-types for numeric values, provide a serialize method for that key that simply returns the value as is.

Advanced Usage

This library also provides an enhancement to the TypedStorage API called LiveStorage. It works identically to TypedStorage with the exception that LiveStorage allows you to synchronize your application logic with the data you store:

import { LiveStorage } from "@figliolia/typed-storage";

export interface Schema {
  JWT: string;
  userId: number;
  shoppingCart: { item: string, price: number }[];
}

const LocalStorage = new TypedStorage<Schema>(localStorage);
// or 
const SessionStorage = new TypedStorage<Schema>(sessionStorage);

// Let's update our UI whenever our `shoppingCart` changes

const currency = new Intl.NumberFormat('en-us', {
  style: 'currency',
  currency: 'USD'
});

const checkoutUI = document.getElementById("checkout");

LocalStorage.on("shoppingCart", cart => {
  if(cart === null) {
    // the key was deleted
    checkoutUI.textContent = currency.format(0.00);
    return;
  }

  // Update your checkout UI's total cost $
  const newPrice = cart.reduce((acc, next) => {
    acc += next.price;
    return acc;
  }, 0);
  checkoutUI.textContent = currency.format(newPrice);
});

In the example above, we'll update our checkout UI whenever the user's shopping cart is updated.

When is LiveStorage the better idea?

Use LiveStorage in areas where your business logic depends heavily on reads/writes to storage. If your application is performing read/writes in logic that spans multiple features or modules, you may find the that LiveStorage API allows you to write more centralized logic in otherwise complex scenarios.