2.1.13 â€Ē Published 1 month ago

@sovgut/state v2.1.13

Weekly downloads
-
License
MIT
Repository
github
Last release
1 month ago

@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

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 exist
  • strict?: boolean - Throw error if key doesn't exist
  • cast?: '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 now
  • maxAge?: number - Maximum age in seconds
  • domain?: string - Cookie domain
  • path?: string - Cookie path
  • secure?: boolean - HTTPS only
  • sameSite?: '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.


2.1.9

1 month ago

2.1.12

1 month ago

2.1.13

1 month ago

2.1.10

1 month ago

2.1.11

1 month ago

2.1.2

1 month ago

2.1.1

1 month ago

2.1.4

1 month ago

2.1.5

1 month ago

2.1.8

1 month ago

2.1.7

1 month ago

2.0.37

11 months ago

2.0.36

11 months ago

2.0.35

11 months ago

2.0.33

11 months ago

2.0.32

11 months ago

2.0.30

11 months ago

2.0.9

11 months ago

2.0.7

11 months ago

2.0.6

11 months ago

2.0.5

11 months ago

2.0.4

11 months ago

2.0.3

11 months ago

2.0.1

12 months ago

2.0.0

12 months ago

1.1.1

1 year ago

1.1.0

1 year ago

1.0.44

1 year ago

1.0.43

1 year ago

1.0.42

1 year ago

1.0.41

1 year ago

1.0.40

1 year ago

1.0.34

1 year ago

1.0.33

1 year ago

1.0.32

1 year ago

1.0.31

1 year ago

1.0.30

1 year ago

1.0.29

1 year ago

1.0.28

1 year ago

1.0.26

1 year ago

1.0.25

1 year ago

1.0.24

1 year ago

1.0.23

1 year ago

1.0.22

1 year ago

1.0.21

1 year ago

1.0.20

1 year ago

1.0.18

1 year ago

1.0.17

1 year ago

1.0.16

1 year ago

1.0.15

1 year ago

1.0.14

1 year ago

1.0.13

1 year ago

1.0.12

1 year ago

1.0.11

1 year ago

1.0.10

1 year ago

1.0.9

1 year ago

1.0.8

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago