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

electron-nano-store

v0.6.0

Published

A minimalistic, secure, type-safe data store for Electron

Downloads

26

Readme

Stand With Ukraine


Nano Electron store

A minimalistic, secure, type-safe data store for Electron. This package is flat wrapper around fs-nano-store and with a few strict checks to allow safely use it in renderer.

Note See the full fs-nano-store documentation for more information about how store works

Installation

npm install electron-nano-store

or

pnpm add electron-nano-store

or

yarn add electron-nano-store

Usage

Simple

Just expose in main world defineStore function and use it directly in your renderer anywhere.

// In Electron Preload Script
import { defineStore } from "electron-nano-store"
import { contextBridge } from 'electron'

contextBridge.exposeInMainWorld('defineStore', defineStore)
// In Renderer
const store = await defineStore('user')

store.set('role', 'admin')
console.log(store.get('role')) // -> 'admin'

Recommended

As an additional safety precaution, you can choose not to expose a defineStore function, but instead expose an already defined store. The examples below also show an example of use with TypeScript.

// contracts.ts
export type UserStore = {
	role: 'admin' | 'user'
}
// in Preload Script
import { defineStore } from "electron-nano-store"
import { contextBridge } from 'electron'
import type { UserStore } from 'contracts.ts'

const userStorePromise = defineStore<UserStore>('user')
contextBridge.exposeInMainWorld('userStorePromise', userStorePromise)
// In Renderer
import type { UserStore } from 'contracts.ts'
import type { defineStore } from 'electron-nano-store'

declare global {
	interface Window {
		userStorePromise: ReturnType<typeof defineStore<UserStore>>
	}
}
const store = await window.userStorePromise
store.set('role', 'admin')
console.log(store.get('role')) // -> 'admin'

store.set('role', 'wrong-role') // TS Error: Argument of type '"wrong-role"' is not assignable to parameter of type '"admin" | "user"'

Recommended with automatic type inference

The exchange of types between the preload and the renderer can be a little annoying and complicated. So you can use unplugin-auto-expose for automatic type inference

// in Preload Script
import { defineStore } from "electron-nano-store"

type UserStore = {
	role: 'admin' | 'user'
}
export const userStorePromise = defineStore<UserStore>('user')
// In Renderer
import { userStorePromise } from '#preload'

const store = await userStorePromise

In Main

This package was intentionally designed with many restrictions for use in preload. If you want to use it in main, or you need more control you should use fs-nano-store directly

// In Main
import { defineStore } from 'fs-nano-store'
import { resolveStoreFilepath } from 'electron-nano-store'
import { app } from 'electron'

const store = await defineStore(
	resolveStoreFilepath(
		'store-name',
		app.getPath('userData')
	)
)

Listen store changes in Renderer

fs-nano-store automatically tracks all changes to the store, and emit a changed event if the store has been changed out of context.

However, electron does not allow you to expose EventEmitter to the main world. This means that even though it defines and returns change property, you can't add listeners directly.

const { changes } = defineStore('store-name')
changes.addListener // undefined

To do this, you must define store in the preload context, add listeners there, and proxy all events to an existing EventTarget, such as a window

// in Preload Script
const storePromise = defineStore<Store>('user')
storePromise.then(({ changes }) => {
	changes.addListener(
		'changed',
		() => globalThis.dispatchEvent(new CustomEvent('user:changed')),
	)
})
// in Renderer
globalThis.addEventListener(
	'user:changed',
	() => { /* ... */
	}
)

Security limitation

  1. You can't somehow change where are storage files placed in filesystems. It always in electron.app.getPath('userData') directory defined by electron. Since the defineStore function may be exposed to a non-secure context, this is done to prevent malicious code from making uncontrolled writes anywhere on the file system.
    // 🚫 Harmful use
    defineStore('.privat-config', { customPath: '/somewhere/in/user/filesystem/' })

    Note If you need create store in some different location, you should make your own wrapper around fs-nano-store. Look how use In Main.

  2. For the same reasons, you cannot use any path fragments in the repository name
    // 🚫 Harmful use
    defineStore('../../somewhere/in/user/filesystem/privat-config')