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

@universal-stores/persist

v1.2.1

Published

A collection of stores and storage mechanisms for persisting and retrieving data to and from different sources, synchronously or asynchronously.

Downloads

12

Readme

@universal-stores/persist

A collection of stores and storage mechanisms for persisting and retrieving data to and from different sources, synchronously or asynchronously.

This package is based on universal-stores, which are observable containers of values.

NPM Package

npm install @universal-stores/persist

Documentation

Highlights

Async and Sync implementation

This package provides two factory functions: makePersistentStore and makeAsyncPersistentStore. They can be used to create both sync and async stores.

For async stores, .set(...) and .update(...) return a promise that resolves once the data is successfully persisted to the underlying storage mechanism.

Built-ins for common use cases

When you create a persistent store, both factory functions need a storage object, that is an object that implements read, write and delete operations.

You can use one of the built-ins provided in this package, such as:

  • WebStorageAdapters.local, WebStorageAdapters.session
  • RESTStorageAdapters.textAsync, RESTStorageAdapters.jsonAsync
  • FileStorageAdapters.textSync, FileStorageAdapters.jsonSync, FileStorageAdapters.textAsync, FileStorageAdapters.jsonAsync

Or you can create your own by providing an object that implements ItemStorage or AsyncItemStorage, depending on whether you need a synchronous or asynchronous store.

Example use cases

Bind a store to a REST endpoint

An async store can be used to fetch data from and persist data to a REST endpoint. RESTStorageAdapters contains presets for JSON and plain text adapters, but you can easily configure the "raw" RESTStorageAdapters.async adapter to handle other data formats.

import {
	makeAsyncPersistentStore,
	RESTStorageAdapters,
} from '@universal-stores/persist';

const todos$ = makeAsyncPersistentStore<string[]>([], {
	storage: RESTStorageAdapters.jsonAsync('http://localhost:3000/todos.json'),
});
todos$.fetch().catch((err) => console.warn("couldn't fetch TODOs")); // fetch content from the API if available.

todos$.subscribe(console.log); // will print [] immediately, as it is the initial value of the store,
// followed by the parsed content of "todos.json" requested by the `.fetch()` call.

For more examples on how to use the REST adapter, check out the tests here: rest.test.ts.

Bind a store to a custom async source

Async stores are quite flexible when it comes to storage adapters. You can provide your own by simply passing an object that implements set, get and clear.

Here is an example that uses tRPC with a query-able only resource:

import {makeAsyncPersistentStore} from '@universal-stores/persist';

const todos$ = makeAsyncPersistentStore<string[]>([], {
	storage: {
		get(options) {
			return trpc.todos.query({signal: options?.signal});
		},
		set: () => Promise.reject(new Error('not supported')),
		clear: () => Promise.reject(new Error('not supported')),
	},
});
todos$.fetch().catch((err) => console.warn("couldn't fetch TODOs")); // fetch content from the API if available.

todos$.subscribe(console.log); // will print [] immediately, as it is the initial value of the store,
// followed by the parsed content returned by the tRPC query.

Persisting client-side data in the local storage

A client-side "TODO list" could be represented in its simplest form as an array of strings.

An implementation could use a persistent store that saves data to the localStorage, as you can see in the following example:

import {
	makePersistentStore,
	WebStorageAdapters,
} from '@universal-stores/persist';

const todos$ = makePersistentStore<string[]>([], {
	storage: WebStorageAdapters.local('todos'),
});
todos$.fetch(); // fetch content from local storage if available.

console.log(todos$.content()); // will print [] the first time, and ['drink coffee', 'read the newsletter'] after reloading the page, due to the `.set()` call below.

todos$.set(['drink coffee', 'read the newsletter']); // will persist the array to the local storage.

The first parameter of makePersistentStore is the default value of the store, while the second parameter is a configuration object. The default value is used as the store value until .fetch() or .set(...)/.update(...) are called. This key is the only parameter we passed to the WebStorageAdapters.local function. This function creates an adapter that manages I/O operations on the localStorage of the browser.

The set method that would normally update the value of the store and propagate the information to all its subscribers, will also call localStorage.setItem('todos', JSON.stringify(<current value>)) under the hood to persist the data.

The configuration object also accepts custom serialization/deserialization functions to let you persist complex types that may not have a 1:1 representation in JSON (e.g. Date).

Persisting data to a file

import {
	makePersistentStore,
	FileStorageAdapters,
} from '@universal-stores/persist';

const todos$ = makePersistentStore<string[]>([], {
	storage: FileStorageAdapters.jsonSync('todos.txt'),
});
todos$.fetch(); // fetch content from todos.txt if available.

console.log(todos$.content()); // will print [] the first time, and ['drink coffee', 'read the newsletter'] after relaunching the script, due to the `.set()` call below.

todos$.set(['drink coffee', 'read the newsletter']); // will persist the array to todos.txt

Custom serialization/deserialization

import {
	makePersistentStore,
	FileStorageAdapters,
} from '@universal-stores/persist';

const data$ = makePersistentStore<Uint8Array>(new Uint8Array(), {
	storage: FileStorageAdapters.sync('data.bin', {
		serde: {
			serialize: (content) => Buffer.from(content),
			deserialize: (buffer) => new Uint8Array(buffer),
		},
	}),
});
data$.fetch();

console.log(data$.content()); // will print `Uint8Array(0) []` the first time, and `Uint8Array(3) [ 1, 2, 3 ]` after relaunching the script, due to the `.set()` call below.

data$.set(new Uint8Array([1, 2, 3]));