Skip to content

A type-safe wrapper around localStorage and sessionStorage with TypeScript support, namespace isolation, and automatic serialization

License

Notifications You must be signed in to change notification settings

Papaskas/strict-store

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Strict Store

npm version license Bundle Size

A type-safe wrapper around localStorage and sessionStorage with TypeScript support, namespace isolation, and automatic serialization.

✨ Features

  • πŸ›‘ Full Type Safety β€” Compile-time type checking for all operations
  • 🧠 Smart Serialization β€” Automatic handling of:
    • Primitive types
    • Complex types
    • TypedArray
  • πŸ—‚ Namespace Isolation β€” Prevent name collisions with hierarchical organization
  • ⚑ Dual Storage Support β€” Switch between localStorage (persistent) and sessionStorage (session-based)
  • πŸ—ƒ Batch Operations β€” Save, remove, or pick multiple keys at once
  • πŸ”„ Merge & Partial Update β€” Merge new values into stored objects
  • πŸ•΅οΈ Change Listeners β€” Subscribe to storage changes
  • πŸ” forEach & getByNamespace β€” Iterate and filter by namespace

πŸ—ƒοΈ Supported types:

  • string
  • number
  • boolean
  • null
  • union
  • object
  • array
  • enum
  • BigInt
  • Map
  • Set
  • Int8Array
  • Uint8Array
  • Uint8ClampedArray
  • Int16Array
  • Uint16Array
  • Int32Array
  • Uint32Array
  • Float32Array
  • Float64Array
  • BigInt64Array
  • BigUint64Array

πŸ“¦ Installation

npm install strict-store
# or
yarn add strict-store
# or
pnpm add strict-store

πŸ›‘οΈ Type Safety

The library enforces type safety at compile time:

const counterKey = createKey<number>('app', 'counter');

StrictStore.save(counterKey, 'string value'); // Error: Type 'string' is not assignable to type 'number'
StrictStore.save(counterKey, 42); // OK

πŸ—„οΈ Storage type selection

Choose between localStorage (persistent) and sessionStorage (tab-specific):

const localKey = createKey(..., 'local');
const sessionKey = createKey(..., 'session');

πŸš€ Quick start

import { createKey, StrictStore } from 'strict-store';

// Create keys for different namespaces and storage types
const themeKey = createKey<'light' | 'dark'>('app', 'theme', 'local');
const langKey = createKey<'en' | 'fr'>('app', 'lang', 'session');
const userKey = createKey<{ name: string; age: number; }>('app', 'user', 'local');

StrictStore.save(themeKey, 'dark'); // Save with type checking
StrictStore.saveBatch([ // Batch operations
  [themeKey, 'light'],
  [langKey, 'en']
]);

// Merge (partial update)
StrictStore.merge(userKey, { name: 'New Name' });

const themeValue: 'light' | 'dark' | null = StrictStore.get(themeKey); // Retrieve with correct type inference
const [theme, lang] = StrictStore.pick([themeKey, langKey]); // Retrieve batch of values

// Get all items or by namespace
const entries: { key, value }[] = StrictStore.entries();
const appEntries: { key, value }[] = StrictStore.entries(['app']);

// Get all keys or by namespace
const keys = StrictStore.keys();
const appKeys = StrictStore.keys(['app']);

// Remove item
StrictStore.remove([themeKey]);

// Check key
const hasKey: boolean = StrictStore.has(themeKey);
const hasKeys: boolean[] = StrictStore.has([themeKey, langKey]);

// Get the count of all StrictStore-managed items
const count: number = StrictStore.size();
const appCount: number = StrictStore.size(['app']);

// Clear all or by namespace
StrictStore.clear();
StrictStore.clear(['app']);

// Iterate over all items or by namespace
StrictStore.forEach((key, value) => {
  console.log(key, value);
}, ['app']);

// Listen for changes keys or ns
const unsubscribe = StrictStore.onChange((key, oldValue, newValue) => {
  // ...
}, [themeKey]); // keys or ns

// Unsubscribe from changes
unsubscribe();

πŸ“¦ API Reference

Below is a summary of the main methods.
See the Wiki for detailed usage, types, and advanced examples.

πŸ—οΈ createKey

  createKey<T>(
    namespace: string, // namespace for key
    name: string, // key name
    storeType?: 'local' | 'session' = 'local' // storage type: 'local' (default) or 'session'
  ): StoreKey<T>

πŸ› οΈ strictStore methods

strictStore
  .get<T>(key: StoreKey<T>): T | null; // Retrieve a value by key.
  .pick<T>(keys: StoreKey<T>[]): (T | null)[]; // Retrieve multiple values by keys.
  .entries<T>(namespace?: string): { key: string, value: T }[]; // Retrieve all items or by namespace
  .keys(ns?: string[]): keys[]; // Get all keys or by a namespace
  .save<T>(key: StoreKey<T>, value: T): void; // Save a value by key
  .saveBatch<T>(entries: [StoreKey<T>, T][]): void; // Save multiple key-value pairs at once
  .remove<T>(key: StoreKey<T>[]): void; // Remove one or more keys.
  .has<T>(key: StoreKey<T>): boolean; // Check if key exist
  .has<T>(key: StoreKey<T>[]): boolean[]; // Check if keys exist
  .size(ns?: string[]): number; // Get the number of items, optionally filtered by namespace
  .clear(namespace?: string[]): void; // Remove all items, optionally filtered by namespace
  .merge<T>(key: StoreKey<T>, partial: DeepPartial<T>): void; // Merge a partial object into an existing stored object
  .forEach<T>(callback: (key: string, value: T) => void, ns?: string[]): void; // Iterate over all key-value pairs, optionally filtered by namespace
  .onChange(
    callback: (key, oldValue, newValue) => void,
    target?: StoreKey<unknown>[] | string[],
  ): () => void; // Subscribe to storage changes. Returns an unsubscribe function.

🧩 Complex type examples

Arrays:

const tagsKey = createKey<string[]>(
  'app', 
  'tags',
);

strictStore.save(tagsKey, ['ts', 'storage', 'util']); // string[] preserved

Objects:

type User = {
  id: number;
  name: string;
  settings: {
    darkMode: boolean
  }
};

const userKey = createKey<User>(
  'app',
  'user',
);

strictStore.save(userKey, {
  id: 1,
  name: 'Alex',
  settings: { darkMode: true }
}); // Structure is type-checked

⚠️ Key Isolation

Strict Store only works with keys created via the createKey function.
Each key is automatically prefixed with a unique namespace, and the library only interacts with keys that have this prefix in localStorage or sessionStorage.

Keys created outside of Strict Store, or without the appropriate prefix, are not visible to the library and will not be processed.

This ensures data isolation and prevents accidental conflicts with other libraries or custom code that uses storage directly.

🚧 Limitations

  • Avoid using colons (':') in namespace or name values β€” this symbol is reserved as a namespace delimiter.
  • The undefined type is not supported β€” it will be converted to null during JSON serialization.
  • Lodash is used under the hood.

βš™οΈ Requirements

  • TypeScript >= 4.9.0

πŸ“š Full documentation

Full API documentation is available in the GitHub Wiki.

About

A type-safe wrapper around localStorage and sessionStorage with TypeScript support, namespace isolation, and automatic serialization

Topics

Resources

License

Stars

Watchers

Forks

Contributors 2

  •  
  •