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

prorage

v1.1.1

Published

Storage used like object, Based on ES6 API Proxy and @vue/reactivity.

Downloads

16

Readme

Prorage

中文 | English

Storage used like object, Based on ES6 API Proxy and @vue/reactivity.

Prorage = Proxy + Storage

[Playground]: Stackblitz

Contents

Features

  • Storage used like object
  • Customizable, most customization can be achieved by Plugins.
  • No side effects, supports Tree Shaking.
  • Based on @vue/reactivity, it can be used well with Vue.
  • Better for Vue projects that without Vuex/Pinia, but can also be used without Vue.

Quick Start

Installation

npm install @vue/reactivity
npm install prorage

If you have already installed Vue, you do not need to install @vue/reactivity.

Usage

import { createStorage } from 'prorage'

const storage = createStorage()


storage.foo = 'foo'
delete foo

storage.bar = []
storage.bar.push('hello')

createStorage

Options

import { createStorage, expiresPlugin } from 'prorage'

const storage = createStorage({
  storage: localStorage,
  stringify: JSON.stringify,
  parse: JSON.parse,
  saveFlush: 'async',
  plugins: [expiresPlugin()],

  prefix: 'prefix#',
})

| Parameter | Type | Default | Description | | :-: | :-: | :-: | :-: | | storage | StorageLike | localStorage | Storage Object | | stringify | StringifyLike | JSON.stringify | Method to convert to JSON | | parse | ParseLike | JSON.parse | Method to parse JSON | | saveFlush | "sync" | "async" | "async" | Timing of save execution | | plugins | ProragePlugin[] | [] | Plugins | | prefix | string | | Prefix of storage key name |

  • StorageLike, such as localStorage and sessionStorage, should have methods: getItem, setItem and removeItem. getItem must be a synchronous method.
  • When saveFlush is set to async, multiple operations will be merged into one save, sync will save immediately after each operation.
  • For information on plugins, please refer to the Built-in Plugin and Plugin Development.

API

storage.clear

clear data.

storage.clear() // Clear all data (data that matches the prefix)

storage.clear('foo') // Clear data under the 'foo' namespace

storage.reload

reload data.

storage.reload('foo')

It should be noted that old data will be out of control (overwritten). For example:

storage.test = { a: 1 }
const temp = storage.test
temp.a = 2 // it works

storage.reload('test')
temp.a = 3 // it not works
temp === storage.test // false

在调用 reloadstorage.test 上的引用被替换为重新读取的数据, 原先储存的 temp 不再是 storage.test 的数据. 故再对 temp 对象进行修改, 不会再影响 storage.test.

After run reload, the reference to storage.test is replaced with the reloaded data, and temp isn't the data of storage.test. So, modifying the temp, can't affect storage.test.

with storage event
addEventListener('storage', ({ key }) => storage.reload(key))

storage.save

Save data.

storage.save('foo')

watch

Similar to the watch function in Vue, but with some limitations. This API is mainly aimed at projects that without Vue.

watch(
  () => storage,
  () => {
    console.log('change')
  }, {
    deep: true,
    flush: 'async'
  }
)
  • The first parameter source, only supports function.
  • The second parameter callback, no provide oldValue and newValue.
  • The third parameter options, deep and immediate are same as Vue. flush only supports sync and async, sync is same as Vue, async is executed asynchronously.

Built-in Plugin

extraPlugin

Add extra properties to the data. As a basic Plugin, it does not need to be declared for use.

import { createStorage, useExtra } from 'prorage'
const storage = createStorage()

storage.foo = useExtra('bar', {
  test: 'hello world'
})

getExtra(storage, 'foo') // { test: 'hello world' }

API

useExtra

create a data with extra properties.

function useExtra<T>(value: T, extra: Record<string, unknown>): T
getExtra

get the extra properties of key on target.

function getExtra(target: object, key: string | symbol): Record<string, unknown>

expiresPlugin

add expires to the data.

import { createStorage, expiresPlugin, useExpires } from 'prorage'
const storage = createStorage({
  plugins: [
    expiresPlugin({
      checkInterval: 1000,
    })
  ]
})

storage.foo = useExpires('bar', { days: 7 })

Options

| Parameter | Type | Default | Description | | :-: | :-: | :-: | :-: | | checkInterval | 'none' | 'raf' | number | "none" | Interval of checking expires |

  • Expired data is not immediately deleted, but will be deleted when be getted.
  • If checkInterval is "raf" or a number, the data will be periodically checked for expires using requestAnimationFrame/setTimeout. Only the data that has been getted will be added to the check queue.
  • When using setTimeout, there is a compensation mechanism. When expires - Date.now() is greater than checkInterval, the next execution delay will be expires - Date.now().

API

useExpires
function useExpires<T>(value: T, expires: ExpiresDate): T

type ExpiresDate = number | Date | ExpiresDateOptions
type ExpiresDateOptions = {
  milliseconds?: number
  seconds?: number
  minutes?: number
  hours?: number
  days?: number
  months?: number
  years?: number
}
  • If expires is number, timestamp, absolute time for expires.
  • If expires is Date, absolute time for expires.
  • If expires is ExpiresDateOptions, it represents the relative time for expiration (relative to now). months is calculated based on the natural month, and is not equal to 30 days.

translatePlugin

Translate data to a format that is better for storage.

import { createStorage, translatePlugin } from 'prorage'
const storage = createStorage({
  plugins: [translatePlugin()]
})

storage.foo = new Date()
storage.bar = 123n
storage.baz = /test/gi
storage.qux = Infinity

Default Supports Types: BigInt, NaN/Infinity/-Infinity, Date, RegExp.

Options

| Parameters | Type | Default | Description | | :-: | :-: | :-: | :-: | | dictionary | TranslateDictionary[] | [] | dictionary for translate |

dictionary
import { createStorage, translatePlugin } from 'prorage'
const storage = createStorage({
  plugins: [
    translatePlugin({
      dictionary: [
        {
          name: 'Symbol',
          test: (value) => typeof value === 'symbol',
          stringify: (value) => value.toString(),
          parse: (value) => {
            const _Symbol = (value) => {
              try {
                return new Function(`return ${value}`)()
              } catch (e) {
                return typeof value === 'symbol'
                  ? value
                  : Symbol.for(String(value))
              }
            }
            const _value = value.replace(/^Symbol\((.*)\)$/, '_Symbol("$1")')
            return new Function('_Symbol', `return ${_value}`)(_Symbol)
          },
        }
      ]
    })
  ]
})

storage.foo = Symbol.for('123')

| Parameters | Type | Description | | :-: | :-: | :-: | | name | string | Unique identifier | | test | (value: unknown) => boolean | Test the value, if return true, the value will be translated by the dictionary | | stringify | (value: any) => any | Translate to storage format | | parse | (value: any) => any | Restores data |

  • name 需要唯一, 内置的标识有: BigInt, Number, Date, RegExp.

  • 按数组顺序进行 test (内置的追加在数组末尾), 匹配后该数据将不再进行其他转换操作.

  • name is unique. The built-in name are: BigInt, Number, Date, RegExp.

  • The test is runed in the order of the array (built-ins are appended to array end), if return true, the data don't try other translate.

Other

Data Types Support

Keys support is same as object, but symbol will be ignored when JSON.stringify.

Values of support:

Primitives

| Type | Basic Support | With translatePlugin | | :-: | :-: | :-: | | undefined | ✔️ | ✔️ | | null | ✔️ | ✔️ | | String | ✔️ | ✔️ | | Boolean | ✔️ | ✔️ | | Number | ✔️ | ✔️ | | BigInt | ❌ | ✔️ | | Symbol | ❌ | can support Symbol.for (user configurable) |

Objects

| Type | Basic Support | With translatePlugin | Description | | :-: | :-: | :-: | :-: | | Basic Object | ✔️ | ✔️ | | | Array | ✔️ | ✔️ | | | Date | ❌ | ✔️ | | | RegExp | ❌ | ✔️ | | | Function | ❌ | Barely supported (user configurable) | scopes will lose | | Set | ❌ | ❌ | Code cost more than the benefits | | Map | ❌ | ❌ | Code cost more than the benefits | | WeakSet | ❌ | ❌ | No value | | WeakMap | ❌ | ❌ | No value |

Plugin Development

Plugin Development

Circular Object

You can use JSON library like flatted to solve the problem of circular object.

import { stringify, parse, } from 'flatted'
import { createStorage } from 'prorage'

const storage = createStorage({
  stringify,
  parse,
})

storage.test = {}
storage.test.circular = storage.test

With TypeScript

import { createStorage } from 'prorage'

type MyStorage = {
  foo: string
  bar: number
}
const storage = createStorage<MyStorage>()

With React

Like @vue/reactivity with React. Simple example: Prorage With React - StackBlitz.

Why can't trigger Vue update

If you use the vue.xxx.global.js or vue.xxx-browser.js version of Vue, it will cause dependency @vue/reactivity to not be the same code, resulting in independent trigger events between the two. Avoid using these two versions of Vue.