Skip to content

Repository files navigation

@giftlion/typed-storage

A lightweight, type-safe localStorage wrapper with a SQL-like query API for TypeScript and React applications.

Features

  • 🎯 Fully Type-Safe - Powered by Zod schemas with complete TypeScript inference
  • 🔗 Chainable Query API - Intuitive query().where().select() syntax
  • Reactive React Hooks - Auto-sync your components with localStorage changes
  • 🪶 Lightweight - Minimal bundle size with zero dependencies (except Zod)
  • 🔄 Cross-Tab Sync - Automatically syncs data across browser tabs
  • 🎨 Familiar API - Inspired by Drizzle ORM and tRPC

Installation

npm install @giftlion/typed-storage zod

Quick Start

1. Define Your Schema

import { createClient } from "@giftlion/type-storage";
import { z } from "zod";

const schema = z.object({
  users: z.object({
    id: z.number(),
    name: z.string(),
    email: z.string(),
  }),
  posts: z.object({
    id: z.number(),
    title: z.string(),
    authorId: z.number(),
  }),
});

const db = createClient("my-app", { schema });

2. Basic CRUD Operations

// Insert
db.users.insert({ id: 1, name: "Alice", email: "alice@example.com" });

// Query all
const { data: users } = db.users.query();

// Query with filter
const { data: activeUsers } = db.users
  .query()
  .where((user) => user.id > 10);

// Query with select
const { data: emails } = db.users
  .query()
  .select({ email: true });

// Chain where and select
const { data: filtered } = db.users
  .query()
  .where((user) => user.name.includes("Alice"))
  .select({ name: true, email: true });

// Update
db.users.update().where((user) => user.id === 1, { name: "Alice Updated" });

// Delete
db.users.delete().where((user) => user.id === 1);

React Integration

Real-time Data Sync

Use the useLiveStorage hook to automatically re-render components when data changes:

import { useLiveStorage } from "@giftlion/typed-storage/react";
import { db } from "./db";

function UserList() {
  const { data: users } = useLiveStorage(() => db.users.query());

  return (
    <ul>
      {users?.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

function AddUser() {
  const handleAdd = () => {
    db.users.insert({
      id: Date.now(),
      name: "New User",
      email: "new@example.com",
    });
    // UserList component automatically updates!
  };

  return <button onClick={handleAdd}>Add User</button>;
}

API Reference

Query Methods

query()

Start a new query on a table.

db.users.query()

.where(predicate)

Filter results based on a condition.

db.users.query().where((user) => user.age > 18)

.select(fields)

Select specific fields from results.

db.users.query().select({ name: true, email: true })

Mutation Methods

insert(item)

Insert a single item.

const { data, error } = db.users.insert({ id: 1, name: "Alice", email: "a@example.com" });

insertMany(items)

Insert multiple items.

db.users.insertMany([
  { id: 1, name: "Alice", email: "a@example.com" },
  { id: 2, name: "Bob", email: "b@example.com" },
]);

update().where(predicate, updates)

Update items matching a condition.

db.users.update().where(
  (user) => user.id === 1,
  { name: "Updated Name" }
);

delete().where(predicate)

Delete items matching a condition.

db.users.delete().where((user) => user.id === 1);

deleteMany().where(predicate)

Delete multiple items.

db.users.deleteMany().where((user) => user.age < 18);

Helper Functions

import { equal, contains, notEqual } from "@giftlion/typed-storage";

// Exact match
db.users.query().where(equal({ id: 1 }))

// Contains substring
db.users.query().where(contains({ name: "Alice" }))

// Not equal
db.users.query().where(notEqual({ status: "inactive" }))

Advanced Usage

Chaining Queries

const { data } = db.users
  .query()
  .where((u) => u.age > 18)
  .select({ name: true, email: true })
  .where((u) => u.email.includes("@gmail.com"));

Working with Nested Data

const schema = z.object({
  users: z.object({
    id: z.number(),
    name: z.string(),
    profile: z.object({
      bio: z.string(),
      avatar: z.string(),
    }),
  }),
});

const db = createClient("app", { schema });

db.users.insert({
  id: 1,
  name: "Alice",
  profile: { bio: "Developer", avatar: "avatar.jpg" },
});

const { data } = db.users
  .query()
  .where((u) => u.profile.bio.includes("Developer"));

TypeScript Tips

The library provides full type inference:

const { data } = db.users.query().select({ name: true, email: true });
// data is typed as: { name: string; email: string }[]

const { data: user } = db.users.insert({ id: 1, name: "Alice", email: "..." });
// user is typed as: { id: number; name: string; email: string }

License

MIT

Contributing

Contributions are welcome! Please open an issue or PR on GitHub.


Built with ❤️ using TypeScript and Zod

About

No description, website, or topics provided.

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages