@sovgut/state v2.1.13
@sovgut/state
ð Features
- ðŊ Simple & Intuitive API - Get started in minutes with a familiar localStorage-like API
- ðū Multiple Storage Strategies - Choose between localStorage, sessionStorage, cookies, or in-memory storage
- ðĶ TypeScript First - Full TypeScript support with intelligent type inference
- ð Reactive Updates - Built-in observer pattern for real-time state synchronization
- ðĄïļ Type Safety - Automatic type casting and validation with custom error handling
- ðŠķ Lightweight - Minimal bundle size (10kb)
- ð§ Flexible Configuration - Fallback values, strict mode, and custom casting options
- ðŠ Advanced Cookie Support - Full cookie options including SameSite, Secure, and expiration
ðĶ Installation
npm install @sovgut/state
yarn add @sovgut/state
pnpm add @sovgut/state
ðŊ Quick Start
import { LocalState, SessionState, MemoryState, CookieState } from "@sovgut/state";
// Store a value
LocalState.set("user", { name: "John", age: 30 });
// Retrieve a value
const user = LocalState.get("user");
// Listen for changes
LocalState.on("user", (event) => {
console.log("User updated:", event);
});
ð Table of Contents
- Storage Strategies
- Basic Usage
- Type Safety & Casting
- Observer Pattern
- React Integration
- API Reference
- Error Handling
ðū Storage Strategies
LocalState
Persists data in localStorage
- survives browser restarts.
import { LocalState } from "@sovgut/state";
LocalState.set("theme", "dark");
const theme = LocalState.get("theme"); // "dark"
SessionState
Stores data in sessionStorage
- cleared when tab closes.
import { SessionState } from "@sovgut/state";
SessionState.set("tempData", { expires: Date.now() + 3600000 });
const data = SessionState.get("tempData");
MemoryState
In-memory storage - cleared on page reload.
import { MemoryState } from "@sovgut/state";
MemoryState.set("cache", new Map([["key", "value"]]));
const cache = MemoryState.get("cache");
CookieState
HTTP cookie storage with advanced options.
import { CookieState } from "@sovgut/state";
CookieState.set("sessionId", "abc123", {
expires: 7, // days
secure: true,
sameSite: "strict"
});
ð§ Basic Usage
Setting Values
All storage types support any serializable JavaScript value:
// Primitives
LocalState.set("count", 42);
LocalState.set("name", "Alice");
LocalState.set("isActive", true);
LocalState.set("bigNumber", 9007199254740991n);
// Objects and Arrays
LocalState.set("user", { id: 1, name: "Bob" });
LocalState.set("tags", ["javascript", "typescript"]);
// Complex structures
LocalState.set("settings", {
theme: "dark",
notifications: {
email: true,
push: false
},
favorites: ["dashboard", "profile"]
});
Getting Values
Basic Retrieval
const count = LocalState.get("count"); // 42
const user = LocalState.get("user"); // { id: 1, name: "Bob" }
const missing = LocalState.get("nonexistent"); // undefined
With Fallback Values
// Returns fallback if key doesn't exist
const theme = LocalState.get("theme", { fallback: "light" }); // "light"
// Fallback also determines the return type
const score = LocalState.get("score", { fallback: 0 }); // number
const tags = LocalState.get("tags", { fallback: [] as string[] }); // string[]
Strict Mode
// Throws StateDoesNotExist error if key is missing
try {
const required = LocalState.get("required", { strict: true });
} catch (error) {
console.error("Key does not exist:", error.message);
}
Removing Values
// Remove single item
LocalState.remove("user");
// Clear all items
LocalState.clear();
// Check existence
if (LocalState.has("user")) {
LocalState.remove("user");
}
ð Type Safety & Casting
Automatic Type Inference
// TypeScript infers types from fallback values
const count = LocalState.get("count", { fallback: 0 }); // number
const name = LocalState.get("name", { fallback: "" }); // string
const items = LocalState.get("items", { fallback: [] as Item[] }); // Item[]
Explicit Type Casting
// Cast stored values to specific types
LocalState.set("stringNumber", "42");
const asString = LocalState.get("stringNumber", { cast: "string" }); // "42"
const asNumber = LocalState.get("stringNumber", { cast: "number" }); // 42
const asBoolean = LocalState.get("stringNumber", { cast: "boolean" }); // true
const asBigInt = LocalState.get("stringNumber", { cast: "bigint" }); // 42n
Generic Type Parameters
interface User {
id: number;
name: string;
email: string;
}
// Explicitly type the return value
const user = LocalState.get<User>("currentUser");
// With strict mode
const user = LocalState.get<User>("currentUser", { strict: true });
ðïļ Observer Pattern
Event Listeners
import { type IStorageEventData } from "@sovgut/state";
// Listen for all changes to a key
LocalState.on("user", (event: IStorageEventData) => {
console.log("User changed:", event);
});
// Listen once
LocalState.once("notification", (event) => {
console.log("Notification received:", event);
});
// Remove specific listener
const handler = (event: IStorageEventData) => console.log(event);
LocalState.on("data", handler);
LocalState.off("data", handler);
// Remove all listeners
LocalState.removeAllListeners();
Cross-Component State Sync
// Component A
LocalState.set("sharedState", { count: 1 });
// Component B - automatically receives updates
LocalState.on("sharedState", (event) => {
console.log("State updated in Component A:", event);
});
âïļ React Integration
Custom Hook Example
import { useEffect, useState, useCallback } from "react";
import { LocalState, type IStorageEventData } from "@sovgut/state";
function useLocalState<T>(key: string, initialValue: T) {
const [value, setValue] = useState<T>(() =>
LocalState.get(key, { fallback: initialValue })
);
useEffect(() => {
const handler = (event: IStorageEventData<T>) => {
setValue(event ?? initialValue);
};
LocalState.on(key, handler);
return () => LocalState.off(key, handler);
}, [key, initialValue]);
const updateValue = useCallback((newValue: T) => {
LocalState.set(key, newValue);
}, [key]);
return [value, updateValue] as const;
}
// Usage
function App() {
const [theme, setTheme] = useLocalState("theme", "light");
return (
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Current theme: {theme}
</button>
);
}
Context Provider Pattern
import React, { createContext, useContext, useEffect, useState } from "react";
import { LocalState, type IStorageEventData } from "@sovgut/state";
interface AppState {
user: User | null;
settings: Settings;
}
const StateContext = createContext<{
state: AppState;
updateUser: (user: User | null) => void;
updateSettings: (settings: Settings) => void;
}>({} as any);
export function StateProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState<AppState>({
user: LocalState.get("user", { fallback: null }),
settings: LocalState.get("settings", { fallback: defaultSettings })
});
useEffect(() => {
const handleUserChange = (event: IStorageEventData<User>) => {
setState(prev => ({ ...prev, user: event }));
};
const handleSettingsChange = (event: IStorageEventData<Settings>) => {
setState(prev => ({ ...prev, settings: event ?? defaultSettings }));
};
LocalState.on("user", handleUserChange);
LocalState.on("settings", handleSettingsChange);
return () => {
LocalState.off("user", handleUserChange);
LocalState.off("settings", handleSettingsChange);
};
}, []);
const updateUser = (user: User | null) => LocalState.set("user", user);
const updateSettings = (settings: Settings) => LocalState.set("settings", settings);
return (
<StateContext.Provider value={{ state, updateUser, updateSettings }}>
{children}
</StateContext.Provider>
);
}
export const useAppState = () => useContext(StateContext);
ð API Reference
Common Methods
All storage classes (LocalState
, SessionState
, MemoryState
, CookieState
) share these methods:
get<T>(key: string, options?: GetOptions<T>): T | undefined
Retrieves a value from storage.
Options:
fallback?: T
- Default value if key doesn't existstrict?: boolean
- Throw error if key doesn't existcast?: 'string' | 'number' | 'boolean' | 'bigint'
- Type casting
set<T>(key: string, value: T, options?: SetOptions): void
Stores a value in storage.
Cookie-specific options:
expires?: Date | number
- Expiration date or days from nowmaxAge?: number
- Maximum age in secondsdomain?: string
- Cookie domainpath?: string
- Cookie pathsecure?: boolean
- HTTPS onlysameSite?: 'strict' | 'lax' | 'none'
- CSRF protection
remove(key: string): void
Removes a value from storage.
clear(): void
Removes all values from storage.
has(key: string): boolean
Checks if a key exists in storage.
Observer Methods
on<T>(event: string, callback: (event: IStorageEventData<T>) => void): void
Adds an event listener.
once<T>(event: string, callback: (event: IStorageEventData<T>) => void): void
Adds a one-time event listener.
off<T>(event: string, callback: (event: IStorageEventData<T>) => void): void
Removes an event listener.
removeAllListeners(): void
Removes all event listeners.
ðĄïļ Error Handling
Built-in Error Types
import { StateDoesNotExist, StateInvalidCast } from "@sovgut/state";
// Handle missing keys
try {
const data = LocalState.get("required", { strict: true });
} catch (error) {
if (error instanceof StateDoesNotExist) {
console.error(`Key "${error.key}" not found in ${error.storage}`);
}
}
// Handle invalid casts
try {
LocalState.set("invalid", "not-a-number");
const num = LocalState.get("invalid", { cast: "number", strict: true });
} catch (error) {
if (error instanceof StateInvalidCast) {
console.error(`Cannot cast "${error.value}" to ${error.type} for key "${error.key}" in ${error.storage}`);
}
}
ðĪ Contributing
If you find any issues or have suggestions for improvements, feel free to open an issue or submit a pull request on GitHub.
ð License
This project is licensed under the MIT License - see the LICENSE file for details.
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago