UNPKG

accounts

Version:

Tempo Accounts SDK

100 lines 3.92 kB
import * as z from 'zod/mini'; import { persist } from 'zustand/middleware'; import { subscribeWithSelector } from 'zustand/middleware'; import { createStore } from 'zustand/vanilla'; import * as Storage from './Storage.js'; /** * Creates a Zustand vanilla store with `subscribeWithSelector` and `persist` middleware. */ export function create(options) { const { chainId, maxAccounts, persistCredentials = true, schema, storage = typeof window !== 'undefined' ? Storage.idb({ key: 'tempo' }) : Storage.memory({ key: 'tempo' }), } = options; return createStore(subscribeWithSelector(persist(() => ({ accessKeys: [], accounts: [], activeAccount: 0, chainId, requestQueue: [], }), { merge: (persisted, current) => hydrate(persisted, current, { schema }), name: 'store', partialize: (state) => serialize(state, { maxAccounts, persistCredentials }), storage, version: 0, }))); } /** Converts runtime provider state into the persisted refresh snapshot. */ export function serialize(state, options = {}) { const { maxAccounts, persistCredentials = true } = options; const accounts = maxAccounts && state.accounts.length > maxAccounts ? state.accounts.slice(0, maxAccounts) : state.accounts; return { accounts, activeAccount: state.activeAccount, ...(persistCredentials ? { accessKeys: state.accessKeys } : {}), ...(state.auth ? { auth: state.auth } : {}), chainId: state.chainId, }; } /** Restores runtime provider state from a persisted refresh snapshot. */ export function hydrate(persisted, current, options = {}) { const state = persisted && typeof persisted === 'object' ? persisted : {}; const accounts_persisted = Array.isArray(state.accounts) ? state.accounts.filter(isStoredAccount) : undefined; const accounts = accounts_persisted?.map((persisted) => { const account = current.accounts.find((a) => a.address.toLowerCase() === persisted.address.toLowerCase()); return account ?? persisted; }) ?? current.accounts; const accounts_valid = options.schema ? accounts.filter((account) => z.safeParse(options.schema, account).success) : accounts; return { ...state, ...current, accounts: accounts_valid, activeAccount: accounts_valid.length === 0 ? 0 : Math.min(state.activeAccount ?? current.activeAccount, accounts_valid.length - 1), accessKeys: normalizeAccessKeys(state.accessKeys) ?? current.accessKeys, chainId: state.chainId ?? current.chainId, }; } function normalizeAccessKeys(accessKeys) { if (!accessKeys) return undefined; return accessKeys.filter((key) => { if (!key || typeof key !== 'object') return false; const value = key; return (typeof value.access === 'string' && typeof value.address === 'string' && typeof value.chainId === 'number' && (value.keyType === 'secp256k1' || value.keyType === 'p256' || value.keyType === 'webAuthn' || value.keyType === 'webCrypto')); }); } function isStoredAccount(account) { if (!account || typeof account !== 'object') return false; return typeof account.address === 'string'; } /** * Waits for the store to finish hydrating from storage. * * Returns immediately if the store has already hydrated. Otherwise, waits * for the `onFinishHydration` callback with a 100ms safety timeout fallback. */ export async function waitForHydration(store) { if (store.persist.hasHydrated()) return; await new Promise((resolve) => { store.persist.onFinishHydration(() => resolve()); setTimeout(() => resolve(), 100); }); } //# sourceMappingURL=Store.js.map