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

hstorage

v0.1.0

Published

A storage lib.

Downloads

5

Readme

hstorage

A storage lib.

TOC

Introduction

hstorage is a storage lib which has a custom type system and is rather lightweight(<10KB after minification WITHOUT zipping). With your store declared, type checking and conflict detecting will be available out of the box.

Usage

npm

  1. Use npm to install it as a dependency:

    npm install hstorage
  2. Import the exports of this lib:

    import { /* ... */ } from "hstorage";
    // or
    const { /* ... */ } = require("hstorage");
  3. Use them in your code.

CDN

  1. Include one of the following script tags in your HTML file:

    via jsdelivr:

    <script type="text/javascript" crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/hstorage@latest/dist/hstorage.umd.min.js"></script>

    or via unpkg:

    <script type="text/javascript" crossorigin="anonymous" src="https://unpkg.com/hstorage@latest/dist/hstorage.umd.min.js"></script>
  2. Access the APIs via the HS global.

    const { /* ... */ } = HS;

If you want a specified version, just replace latest with that in the url. By the way, it is recommended to use a specified version in production.

For more information about these two CDN sites, visit www.jsdelivr.com and unpkg.com.

API Reference

(The API reference is written in TypeScript.)

/**
 * @desc The type of storage-like objects.
 */
interface StorageLike {

    /**
     * @desc Get the item value by giving the key to it.
     */
    getItem(key: string): string | null;

    /**
     * @desc Set the item value by giving the key to it.
     */
    setItem(key: string, value: string): void;

}

/**
 * @desc The type of store options. (These are all
 * partial properties on store instances, so you can
 * refer to the property details for more information.)
 */
interface StoreOptions<T> {
    defaultValue?: T | null;
    type?: Type<T> | null;
    delay?: number;
    storage?: StorageLike;
    lazyLoad?: boolean;
    strictLoad?: boolean;
    secure?: boolean;
    autoFix?: boolean;
    onInvalid?: StoreInvalidCallback<T> | null;
    onConflict?: StoreConflictCallback<T> | null;
    pathSeparator?: string;
}

/**
 * @desc The class for store instances.
 */
class Store<T = unknown> {

    /**
     * @desc The defaults of store options.
     */
    static defaults: StoreOptions<any>;

    /**
     * @desc The constructor which accepts a required name and optional options.
     */
    constructor(name: string, options?: StoreOptions<T>);

    /**
     * @desc The name which is used as the key to the store.
     */
    name: string;

    /**
     * @desc The default value of the store. If this is omitted but the `type` property
     * is given, `type.defaultValue` will be adopted.
     *
     */
    defaultValue: T | null;

    /**
     * @desc The type of the store. (See the `Type` interface and built-in types below.)
     * If this is omitted but the `defaultValue` is given, an inferred type generated by
     * `inferType`(see below) will be adopted.
     */
    type: Type<T> | null;

    /**
     * @desc The delay of saving in milliseconds. (If zero, save synchronously.)
     */
    delay: number;

    /**
     * @desc The storage to use.
     * @default localStorage
     */
    storage: StorageLike;

    /**
     * @desc Whether not to load immediately when the store is being created.
     * @default false
     */
    readonly lazyLoad: boolean;

    /**
     * @desc Whether to do type checking while loading the store from storage.
     * @default true
     */
    strictLoad: boolean;

    /**
     * @desc Whether to check conflicts while saving.
     * @default true
     */
    secure: boolean;

    /**
     * @desc Whether to try to fix invalid value automatically.
     * @default true
     */
    autoFix: boolean;

    /**
     * @desc The invalidation callback. (If not given, errors will be thrown instead.)
     * @param paths The invalid property paths. (Path `[]` means the root.)
     */
    onInvalid: ((this: Store<T>, paths: string[][]) => void) | null;

    /**
     * @desc The conflict callback. (If not given, errors will be thrown instead.)
     * @param newSource The current source in the storage.
     * @param oldSource The copy of the old source in the storage.
     * @example
     * ```js
     * onConflict(newSource, oldSource) {
     *     if (youWantTheNewSource) {
     *         this.load(newSource);
     *     } else {
     *         this.storage.setItem(this.name, oldSource);
     *         this.save();
     *     }
     * }
     * ```
     */
    onConflict: ((this: Store<T>, newSource: string | null, oldSource: string | null) => void) | null;

    /**
     * @desc The path separator to use.
     * @default '.'
     */
    pathSeparator: string;

    /**
     * @desc Manually save the store. (synchronously)
     * @returns Whether the saving is successful. (`false` if conflict occurs
     * or no `storage` given.)
     */
    save(): boolean;

    /**
     * @desc Reset the specific value.
     * @param selector A path string or a path array.
     * @returns Whether the reset is successful. (`false` if the default value can't be found.)
     * @example
     * ```js
     *     store.reset('foo.bar');
     *     store.reset(['foo', 'bar']);
     * ```
     */
    reset(selector: string | string[]): boolean;

    /**
     * @desc Load the store from source.
     * @param source The source string. If omitted, read the source from `storage`.
     * @returns Whether the loading is successful. (`false` both on invalidation
     * if it can't be fixed and no storage presence.)
     */
    load(source?: string | null): boolean;

    /**
     * @desc Check whether the sources conflict.
     * @returns `true` if conflict; `false` otherwise.
     */
    checkConflict(): boolean;

    /**
     * @desc Get the specific store value by giving a property path string or
     * a path array. Invoke it without arguments to get the whole store value.
     */
    get(path?: string | string[]): unknown;

    /**
     * @desc Set the specific value by giving a path and a new value or
     * an updating callback which accepts the old value and returns a new one.
     * @returns Whether the operation is successful. (`false` on invalidation if it can't fixed.)
     */
    set(path: string | string[], patch: unknown | (this: Store<T>, oldValue: unknown) => unknown): boolean;

}

/**
 * @desc The type of validating results.
 */
interface ValidatingResult {

    /**
     * @desc Whether the value is valid.
     */
    valid: boolean;

    /**
     * @desc The path of invalid properties if there is any.
     */
    paths?: string[][];

}

/**
 * @desc The type of type class instances.
 */
interface Type<T> {

    /**
     * @desc The default value.
     */
    defaultValue: T;

    /**
     * @desc Validate the given result.
     */
    validate(value: unknown): ValidatingResult;

}

/**
 * @desc Turn an object into a map of corresponding type class instances.
 */
type Types<T extends {}> = {
    [K in Extract<keyof T, string>]: Type<T[K]>;
};

/**
 * @desc The type class of any types. (always valid)
 */
class Any implements Type<any> {
    constructor(defaultValue?: any);
    defaultValue: any;
    validate(): ValidatingResult;
}

/**
 * @desc Create an any type instance.
 */
function any(defaultValue?: any): Any;

/**
 * @desc The type class of booleans.
 */
class Boolean implements Type<boolean> {
    defaultValue: boolean;
    constructor(defaultValue?: boolean);
    validate(value: unknown): ValidatingResult;
}

/**
 * @desc Create a boolean type instance.
 */
function boolean(defaultValue?: boolean): Boolean;

/**
 * @desc The type of string options. (See property details.)
 */
interface StringOptions {
    defaultValue?: string;
    minLength?: number;
    maxLength?: number;
    pattern?: RegExp | null;
}

/**
 * @desc The type class of strings.
 */
class String implements Type<string> {

    static defaults: StringOptions;

    constructor(options?: StringOptions);

    defaultValue: string;

    /**
     * @desc The minimum string length.
     * @default 0
     */
    minLength: number;

    /**
     * @desc The maximum string length.
     * @default Infinity
     */
    maxLength: number;

    /**
     * @desc The string pattern. (`null` means any pattern.)
     */
    pattern: RegExp | null;

    validate(value: unknown): ValidatingResult;

}

/**
 * @desc Create a string type instance.
 */
function string(options?: StringOptions): String;

/**
 * @desc The type of number options. (See property details.)
 */
interface NumberOptions {
    defaultValue?: number;
    min?: number;
    max?: number;
    integer?: boolean;
}

/**
 * @desc The type class of numbers.
 */
class Number implements Type<number> {

    static defaults: NumberOptions;

    constructor(options?: NumberOptions);

    defaultValue: number;

    /**
     * @desc The minimum limit.
     * @default -Infinity
     */
    min: number;

    /**
     * @desc The maximum limit.
     * @default Infinity
     */
    max: number;

    /**
     * @desc Whether the number must be an integer.
     * @default false
     */
    integer: boolean;

    validate(value: unknown): ValidatingResult;

}

/**
 * @desc Create a number type instance.
 */
function number(options?: NumberOptions | undefined): Number;

/**
 * @desc The type of nullable options. (See property details.)
 */
interface NullableOptions<T extends null | undefined> {
    defaultValue?: T;
    null?: boolean;
    undefined?: boolean;
}

/**
 * @desc The type class of nullable value.
 */
class Nullable<T extends null | undefined = null | undefined> implements Type<T> {

    static defaults: NullableOptions<null | undefined>;

    constructor(options?: NullableOptions<T>);

    defaultValue: T;

    /**
     * @desc Whether `null` is acceptable.
     * @default true
     */
    null: boolean;

    /**
     * @desc Whether `undefined` is acceptable.
     * @default true
     */
    undefined: boolean;

    validate(value: unknown): ValidatingResult;

}

/**
 * @desc Create a nullable type instance.
 */
function nullable: <T extends null | undefined = null | undefined>
    (options?: NullableOptions<T>): Nullable<T>;

/**
 * @desc The type class of dictionaries.
 */
class Dictionary<T extends {} = any> implements Type<T> {

    /**
     * @desc The property types. (`null` or `undefined` means no limit.)
     * @example
     * ```js
     * dictionary.types = {
     *     foo: HS.string(),
     *     bar: HS.number(),
     * };
     * ```
     */
    readonly types?: Types<T> | null;

    constructor(types?: Types<T> | null);

    /**
     * @desc The default value. (If not provided, one will be created from `types`.)
     */
    defaultValue: T;

    validate(value: unknown): ValidatingResult;

}

/**
 * @desc Create a dictionary type instance.
 */
function dictionary<T extends {} = any>(types?: Types<T> | null): Dictionary<T>;

/**
 * @desc The type of list options. (See property details.)
 */
interface ListOptions<T> {
    defaultValue?: T[];
    type: Type<T>;
}

/**
 * @desc The type class of lists.
 */
class List<T = unknown> implements Type<T[]> {

    static defaults: ListOptions<unknown>;

    constructor(options?: ListOptions<T>);

    defaultValue: T[];

    /**
     * @desc The type of list elements.
     * @default Any
     */
    type: Type<T>;

    validate(value: unknown): ValidatingResult;

}

/**
 * @desc Create a list type instance.
 */
function list<T = unknown>(options?: ListOptions<T>): List<T>;

/**
 * @desc The type of union options. (See property details.)
 */
interface UnionOptions<T> {
    defaultValue?: T;
    types: Type<T>[];
}

/**
 * @desc The type class of union.
 */
class Union<T = unknown> implements Type<T> {

    constructor(options?: UnionOptions<T>);

    /**
     * @desc The default value. (If not provided, one will be created from `types`.)
     */
    defaultValue: T;

    /**
     * @desc The types included in the union.
     */
    types: Type<T>[];

    validate(value: unknown): ValidatingResult;

}

/**
 * @desc Create a union type instance.
 */
function union<T = unknown>(options?: UnionOptions<T>): Union<T>;

/**
 * @desc Get the type nested in the given type by giving the path to it.
 * (Its ancestors must be dictionary types.)
 */
function getTypeByPath(type: Type<unknown>, path: string[]): Type<unknown>;

/**
 * @desc Infer type from the given value.
 */
function inferType(value: unknown): Type<unknown>;

Links