commit c5914a4d189980941ab56d0886424d9967f244b2 Author: behnamrhp Date: Mon Mar 11 19:55:01 2024 +0300 Built mode with cache time diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a2c2649 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Paul Henschel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/context.d.ts b/context.d.ts new file mode 100644 index 0000000..d16f009 --- /dev/null +++ b/context.d.ts @@ -0,0 +1,25 @@ +import ReactExports from 'react'; +import type { ReactNode } from 'react'; +import type { StoreApi } from 'zustand'; +type UseContextStore> = { + (): ExtractState; + (selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +}; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type WithoutCallSignature = { + [K in keyof T]: T[K]; +}; +/** + * @deprecated Use `createStore` and `useStore` for context usage + */ +declare function createContext>(): { + Provider: ({ createStore, children, }: { + createStore: () => S; + children: ReactNode; + }) => ReactExports.FunctionComponentElement>; + useStore: UseContextStore; + useStoreApi: () => WithoutCallSignature; +}; +export default createContext; diff --git a/context.js b/context.js new file mode 100644 index 0000000..7ff2b53 --- /dev/null +++ b/context.js @@ -0,0 +1,65 @@ +'use strict'; + +var ReactExports = require('react'); +var traditional = require('zustand/traditional'); + +function _extends() { + _extends = Object.assign ? Object.assign.bind() : function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + return target; + }; + return _extends.apply(this, arguments); +} + +var createElement = ReactExports.createElement, + reactCreateContext = ReactExports.createContext, + useContext = ReactExports.useContext, + useMemo = ReactExports.useMemo, + useRef = ReactExports.useRef; +function createContext() { + if (process.env.NODE_ENV !== 'production') { + console.warn("[DEPRECATED] `context` will be removed in a future version. Instead use `import { createStore, useStore } from 'zustand'`. See: https://github.com/pmndrs/zustand/discussions/1180."); + } + var ZustandContext = reactCreateContext(undefined); + var Provider = function Provider(_ref) { + var createStore = _ref.createStore, + children = _ref.children; + var storeRef = useRef(); + if (!storeRef.current) { + storeRef.current = createStore(); + } + return createElement(ZustandContext.Provider, { + value: storeRef.current + }, children); + }; + var useContextStore = function useContextStore(selector, equalityFn) { + var store = useContext(ZustandContext); + if (!store) { + throw new Error('Seems like you have not used zustand provider as an ancestor.'); + } + return traditional.useStoreWithEqualityFn(store, selector, equalityFn); + }; + var useStoreApi = function useStoreApi() { + var store = useContext(ZustandContext); + if (!store) { + throw new Error('Seems like you have not used zustand provider as an ancestor.'); + } + return useMemo(function () { + return _extends({}, store); + }, [store]); + }; + return { + Provider: Provider, + useStore: useContextStore, + useStoreApi: useStoreApi + }; +} + +module.exports = createContext; diff --git a/esm/context.d.mts b/esm/context.d.mts new file mode 100644 index 0000000..d16f009 --- /dev/null +++ b/esm/context.d.mts @@ -0,0 +1,25 @@ +import ReactExports from 'react'; +import type { ReactNode } from 'react'; +import type { StoreApi } from 'zustand'; +type UseContextStore> = { + (): ExtractState; + (selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +}; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type WithoutCallSignature = { + [K in keyof T]: T[K]; +}; +/** + * @deprecated Use `createStore` and `useStore` for context usage + */ +declare function createContext>(): { + Provider: ({ createStore, children, }: { + createStore: () => S; + children: ReactNode; + }) => ReactExports.FunctionComponentElement>; + useStore: UseContextStore; + useStoreApi: () => WithoutCallSignature; +}; +export default createContext; diff --git a/esm/context.d.ts b/esm/context.d.ts new file mode 100644 index 0000000..d16f009 --- /dev/null +++ b/esm/context.d.ts @@ -0,0 +1,25 @@ +import ReactExports from 'react'; +import type { ReactNode } from 'react'; +import type { StoreApi } from 'zustand'; +type UseContextStore> = { + (): ExtractState; + (selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +}; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type WithoutCallSignature = { + [K in keyof T]: T[K]; +}; +/** + * @deprecated Use `createStore` and `useStore` for context usage + */ +declare function createContext>(): { + Provider: ({ createStore, children, }: { + createStore: () => S; + children: ReactNode; + }) => ReactExports.FunctionComponentElement>; + useStore: UseContextStore; + useStoreApi: () => WithoutCallSignature; +}; +export default createContext; diff --git a/esm/context.js b/esm/context.js new file mode 100644 index 0000000..5f05131 --- /dev/null +++ b/esm/context.js @@ -0,0 +1,61 @@ +import ReactExports from 'react'; +import { useStoreWithEqualityFn } from 'zustand/traditional'; + +const { + createElement, + createContext: reactCreateContext, + useContext, + useMemo, + useRef +} = ReactExports; +function createContext() { + if (process.env.NODE_ENV !== "production") { + console.warn( + "[DEPRECATED] `context` will be removed in a future version. Instead use `import { createStore, useStore } from 'zustand'`. See: https://github.com/pmndrs/zustand/discussions/1180." + ); + } + const ZustandContext = reactCreateContext(void 0); + const Provider = ({ + createStore, + children + }) => { + const storeRef = useRef(); + if (!storeRef.current) { + storeRef.current = createStore(); + } + return createElement( + ZustandContext.Provider, + { value: storeRef.current }, + children + ); + }; + const useContextStore = (selector, equalityFn) => { + const store = useContext(ZustandContext); + if (!store) { + throw new Error( + "Seems like you have not used zustand provider as an ancestor." + ); + } + return useStoreWithEqualityFn( + store, + selector, + equalityFn + ); + }; + const useStoreApi = () => { + const store = useContext(ZustandContext); + if (!store) { + throw new Error( + "Seems like you have not used zustand provider as an ancestor." + ); + } + return useMemo(() => ({ ...store }), [store]); + }; + return { + Provider, + useStore: useContextStore, + useStoreApi + }; +} + +export { createContext as default }; diff --git a/esm/context.mjs b/esm/context.mjs new file mode 100644 index 0000000..1d2f4ba --- /dev/null +++ b/esm/context.mjs @@ -0,0 +1,61 @@ +import ReactExports from 'react'; +import { useStoreWithEqualityFn } from 'zustand/traditional'; + +const { + createElement, + createContext: reactCreateContext, + useContext, + useMemo, + useRef +} = ReactExports; +function createContext() { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + console.warn( + "[DEPRECATED] `context` will be removed in a future version. Instead use `import { createStore, useStore } from 'zustand'`. See: https://github.com/pmndrs/zustand/discussions/1180." + ); + } + const ZustandContext = reactCreateContext(void 0); + const Provider = ({ + createStore, + children + }) => { + const storeRef = useRef(); + if (!storeRef.current) { + storeRef.current = createStore(); + } + return createElement( + ZustandContext.Provider, + { value: storeRef.current }, + children + ); + }; + const useContextStore = (selector, equalityFn) => { + const store = useContext(ZustandContext); + if (!store) { + throw new Error( + "Seems like you have not used zustand provider as an ancestor." + ); + } + return useStoreWithEqualityFn( + store, + selector, + equalityFn + ); + }; + const useStoreApi = () => { + const store = useContext(ZustandContext); + if (!store) { + throw new Error( + "Seems like you have not used zustand provider as an ancestor." + ); + } + return useMemo(() => ({ ...store }), [store]); + }; + return { + Provider, + useStore: useContextStore, + useStoreApi + }; +} + +export { createContext as default }; diff --git a/esm/index.d.mts b/esm/index.d.mts new file mode 100644 index 0000000..425cbbe --- /dev/null +++ b/esm/index.d.mts @@ -0,0 +1,3 @@ +export * from './vanilla.mjs'; +export * from './react.mjs'; +export { default } from './react.mjs'; diff --git a/esm/index.d.ts b/esm/index.d.ts new file mode 100644 index 0000000..7e9c8b6 --- /dev/null +++ b/esm/index.d.ts @@ -0,0 +1,3 @@ +export * from './vanilla'; +export * from './react'; +export { default } from './react'; diff --git a/esm/index.js b/esm/index.js new file mode 100644 index 0000000..e3f5f9a --- /dev/null +++ b/esm/index.js @@ -0,0 +1,48 @@ +import { createStore } from 'zustand/vanilla'; +export * from 'zustand/vanilla'; +import ReactExports from 'react'; +import useSyncExternalStoreExports from 'use-sync-external-store/shim/with-selector.js'; + +const { useDebugValue } = ReactExports; +const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports; +let didWarnAboutEqualityFn = false; +const identity = (arg) => arg; +function useStore(api, selector = identity, equalityFn) { + if (process.env.NODE_ENV !== "production" && equalityFn && !didWarnAboutEqualityFn) { + console.warn( + "[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937" + ); + didWarnAboutEqualityFn = true; + } + const slice = useSyncExternalStoreWithSelector( + api.subscribe, + api.getState, + api.getServerState || api.getInitialState, + selector, + equalityFn + ); + useDebugValue(slice); + return slice; +} +const createImpl = (createState) => { + if (process.env.NODE_ENV !== "production" && typeof createState !== "function") { + console.warn( + "[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`." + ); + } + const api = typeof createState === "function" ? createStore(createState) : createState; + const useBoundStore = (selector, equalityFn) => useStore(api, selector, equalityFn); + Object.assign(useBoundStore, api); + return useBoundStore; +}; +const create = (createState) => createState ? createImpl(createState) : createImpl; +var react = (createState) => { + if (process.env.NODE_ENV !== "production") { + console.warn( + "[DEPRECATED] Default export is deprecated. Instead use `import { create } from 'zustand'`." + ); + } + return create(createState); +}; + +export { create, react as default, useStore }; diff --git a/esm/index.mjs b/esm/index.mjs new file mode 100644 index 0000000..8b60c00 --- /dev/null +++ b/esm/index.mjs @@ -0,0 +1,48 @@ +import { createStore } from 'zustand/vanilla'; +export * from 'zustand/vanilla'; +import ReactExports from 'react'; +import useSyncExternalStoreExports from 'use-sync-external-store/shim/with-selector.js'; + +const { useDebugValue } = ReactExports; +const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports; +let didWarnAboutEqualityFn = false; +const identity = (arg) => arg; +function useStore(api, selector = identity, equalityFn) { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && equalityFn && !didWarnAboutEqualityFn) { + console.warn( + "[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937" + ); + didWarnAboutEqualityFn = true; + } + const slice = useSyncExternalStoreWithSelector( + api.subscribe, + api.getState, + api.getServerState || api.getInitialState, + selector, + equalityFn + ); + useDebugValue(slice); + return slice; +} +const createImpl = (createState) => { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && typeof createState !== "function") { + console.warn( + "[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`." + ); + } + const api = typeof createState === "function" ? createStore(createState) : createState; + const useBoundStore = (selector, equalityFn) => useStore(api, selector, equalityFn); + Object.assign(useBoundStore, api); + return useBoundStore; +}; +const create = (createState) => createState ? createImpl(createState) : createImpl; +var react = (createState) => { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + console.warn( + "[DEPRECATED] Default export is deprecated. Instead use `import { create } from 'zustand'`." + ); + } + return create(createState); +}; + +export { create, react as default, useStore }; diff --git a/esm/middleware.d.mts b/esm/middleware.d.mts new file mode 100644 index 0000000..e765879 --- /dev/null +++ b/esm/middleware.d.mts @@ -0,0 +1,5 @@ +export * from './middleware/redux.mjs'; +export * from './middleware/devtools.mjs'; +export * from './middleware/subscribeWithSelector.mjs'; +export * from './middleware/combine.mjs'; +export * from './middleware/persist.mjs'; diff --git a/esm/middleware.d.ts b/esm/middleware.d.ts new file mode 100644 index 0000000..1806630 --- /dev/null +++ b/esm/middleware.d.ts @@ -0,0 +1,5 @@ +export * from './middleware/redux'; +export * from './middleware/devtools'; +export * from './middleware/subscribeWithSelector'; +export * from './middleware/combine'; +export * from './middleware/persist'; diff --git a/esm/middleware.js b/esm/middleware.js new file mode 100644 index 0000000..c2c91bc --- /dev/null +++ b/esm/middleware.js @@ -0,0 +1,584 @@ +const reduxImpl = (reducer, initial) => (set, _get, api) => { + api.dispatch = (action) => { + set((state) => reducer(state, action), false, action); + return action; + }; + api.dispatchFromDevtools = true; + return { dispatch: (...a) => api.dispatch(...a), ...initial }; +}; +const redux = reduxImpl; + +const trackedConnections = /* @__PURE__ */ new Map(); +const getTrackedConnectionState = (name) => { + const api = trackedConnections.get(name); + if (!api) + return {}; + return Object.fromEntries( + Object.entries(api.stores).map(([key, api2]) => [key, api2.getState()]) + ); +}; +const extractConnectionInformation = (store, extensionConnector, options) => { + if (store === void 0) { + return { + type: "untracked", + connection: extensionConnector.connect(options) + }; + } + const existingConnection = trackedConnections.get(options.name); + if (existingConnection) { + return { type: "tracked", store, ...existingConnection }; + } + const newConnection = { + connection: extensionConnector.connect(options), + stores: {} + }; + trackedConnections.set(options.name, newConnection); + return { type: "tracked", store, ...newConnection }; +}; +const devtoolsImpl = (fn, devtoolsOptions = {}) => (set, get, api) => { + const { enabled, anonymousActionType, store, ...options } = devtoolsOptions; + let extensionConnector; + try { + extensionConnector = (enabled != null ? enabled : process.env.NODE_ENV !== "production") && window.__REDUX_DEVTOOLS_EXTENSION__; + } catch (e) { + } + if (!extensionConnector) { + if (process.env.NODE_ENV !== "production" && enabled) { + console.warn( + "[zustand devtools middleware] Please install/enable Redux devtools extension" + ); + } + return fn(set, get, api); + } + const { connection, ...connectionInformation } = extractConnectionInformation(store, extensionConnector, options); + let isRecording = true; + api.setState = (state, replace, nameOrAction) => { + const r = set(state, replace); + if (!isRecording) + return r; + const action = nameOrAction === void 0 ? { type: anonymousActionType || "anonymous" } : typeof nameOrAction === "string" ? { type: nameOrAction } : nameOrAction; + if (store === void 0) { + connection == null ? void 0 : connection.send(action, get()); + return r; + } + connection == null ? void 0 : connection.send( + { + ...action, + type: `${store}/${action.type}` + }, + { + ...getTrackedConnectionState(options.name), + [store]: api.getState() + } + ); + return r; + }; + const setStateFromDevtools = (...a) => { + const originalIsRecording = isRecording; + isRecording = false; + set(...a); + isRecording = originalIsRecording; + }; + const initialState = fn(api.setState, get, api); + if (connectionInformation.type === "untracked") { + connection == null ? void 0 : connection.init(initialState); + } else { + connectionInformation.stores[connectionInformation.store] = api; + connection == null ? void 0 : connection.init( + Object.fromEntries( + Object.entries(connectionInformation.stores).map(([key, store2]) => [ + key, + key === connectionInformation.store ? initialState : store2.getState() + ]) + ) + ); + } + if (api.dispatchFromDevtools && typeof api.dispatch === "function") { + let didWarnAboutReservedActionType = false; + const originalDispatch = api.dispatch; + api.dispatch = (...a) => { + if (process.env.NODE_ENV !== "production" && a[0].type === "__setState" && !didWarnAboutReservedActionType) { + console.warn( + '[zustand devtools middleware] "__setState" action type is reserved to set state from the devtools. Avoid using it.' + ); + didWarnAboutReservedActionType = true; + } + originalDispatch(...a); + }; + } + connection.subscribe((message) => { + var _a; + switch (message.type) { + case "ACTION": + if (typeof message.payload !== "string") { + console.error( + "[zustand devtools middleware] Unsupported action format" + ); + return; + } + return parseJsonThen( + message.payload, + (action) => { + if (action.type === "__setState") { + if (store === void 0) { + setStateFromDevtools(action.state); + return; + } + if (Object.keys(action.state).length !== 1) { + console.error( + ` + [zustand devtools middleware] Unsupported __setState action format. + When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(), + and value of this only key should be a state object. Example: { "type": "__setState", "state": { "abc123Store": { "foo": "bar" } } } + ` + ); + } + const stateFromDevtools = action.state[store]; + if (stateFromDevtools === void 0 || stateFromDevtools === null) { + return; + } + if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) { + setStateFromDevtools(stateFromDevtools); + } + return; + } + if (!api.dispatchFromDevtools) + return; + if (typeof api.dispatch !== "function") + return; + api.dispatch(action); + } + ); + case "DISPATCH": + switch (message.payload.type) { + case "RESET": + setStateFromDevtools(initialState); + if (store === void 0) { + return connection == null ? void 0 : connection.init(api.getState()); + } + return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + case "COMMIT": + if (store === void 0) { + connection == null ? void 0 : connection.init(api.getState()); + return; + } + return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + case "ROLLBACK": + return parseJsonThen(message.state, (state) => { + if (store === void 0) { + setStateFromDevtools(state); + connection == null ? void 0 : connection.init(api.getState()); + return; + } + setStateFromDevtools(state[store]); + connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + }); + case "JUMP_TO_STATE": + case "JUMP_TO_ACTION": + return parseJsonThen(message.state, (state) => { + if (store === void 0) { + setStateFromDevtools(state); + return; + } + if (JSON.stringify(api.getState()) !== JSON.stringify(state[store])) { + setStateFromDevtools(state[store]); + } + }); + case "IMPORT_STATE": { + const { nextLiftedState } = message.payload; + const lastComputedState = (_a = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _a.state; + if (!lastComputedState) + return; + if (store === void 0) { + setStateFromDevtools(lastComputedState); + } else { + setStateFromDevtools(lastComputedState[store]); + } + connection == null ? void 0 : connection.send( + null, + // FIXME no-any + nextLiftedState + ); + return; + } + case "PAUSE_RECORDING": + return isRecording = !isRecording; + } + return; + } + }); + return initialState; +}; +const devtools = devtoolsImpl; +const parseJsonThen = (stringified, f) => { + let parsed; + try { + parsed = JSON.parse(stringified); + } catch (e) { + console.error( + "[zustand devtools middleware] Could not parse the received json", + e + ); + } + if (parsed !== void 0) + f(parsed); +}; + +const subscribeWithSelectorImpl = (fn) => (set, get, api) => { + const origSubscribe = api.subscribe; + api.subscribe = (selector, optListener, options) => { + let listener = selector; + if (optListener) { + const equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is; + let currentSlice = selector(api.getState()); + listener = (state) => { + const nextSlice = selector(state); + if (!equalityFn(currentSlice, nextSlice)) { + const previousSlice = currentSlice; + optListener(currentSlice = nextSlice, previousSlice); + } + }; + if (options == null ? void 0 : options.fireImmediately) { + optListener(currentSlice, currentSlice); + } + } + return origSubscribe(listener); + }; + const initialState = fn(set, get, api); + return initialState; +}; +const subscribeWithSelector = subscribeWithSelectorImpl; + +const combine = (initialState, create) => (...a) => Object.assign({}, initialState, create(...a)); + +function createJSONStorage(getStorage, options) { + let storage; + try { + storage = getStorage(); + } catch (e) { + return; + } + const persistStorage = { + getItem: (name) => { + var _a; + const parse = (str2) => { + if (str2 === null) { + return null; + } + return JSON.parse(str2, options == null ? void 0 : options.reviver); + }; + const str = (_a = storage.getItem(name)) != null ? _a : null; + if (str instanceof Promise) { + return str.then(parse); + } + return parse(str); + }, + setItem: (name, newValue) => storage.setItem( + name, + JSON.stringify(newValue, options == null ? void 0 : options.replacer) + ), + removeItem: (name) => storage.removeItem(name) + }; + return persistStorage; +} +const toThenable = (fn) => (input) => { + try { + const result = fn(input); + if (result instanceof Promise) { + return result; + } + return { + then(onFulfilled) { + return toThenable(onFulfilled)(result); + }, + catch(_onRejected) { + return this; + } + }; + } catch (e) { + return { + then(_onFulfilled) { + return this; + }, + catch(onRejected) { + return toThenable(onRejected)(e); + } + }; + } +}; +const oldImpl = (config, baseOptions) => (set, get, api) => { + let options = { + getStorage: () => localStorage, + serialize: JSON.stringify, + deserialize: JSON.parse, + partialize: (state) => state, + version: 0, + merge: (persistedState, currentState) => ({ + ...currentState, + ...persistedState + }), + ...baseOptions + }; + let hasHydrated = false; + const hydrationListeners = /* @__PURE__ */ new Set(); + const finishHydrationListeners = /* @__PURE__ */ new Set(); + let storage; + try { + storage = options.getStorage(); + } catch (e) { + } + if (!storage) { + return config( + (...args) => { + console.warn( + `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.` + ); + set(...args); + }, + get, + api + ); + } + const thenableSerialize = toThenable(options.serialize); + const setItem = () => { + const state = options.partialize({ ...get() }); + let errorInSync; + const thenable = thenableSerialize({ state, version: options.version }).then( + (serializedValue) => storage.setItem(options.name, serializedValue) + ).catch((e) => { + errorInSync = e; + }); + if (errorInSync) { + throw errorInSync; + } + return thenable; + }; + const savedSetState = api.setState; + api.setState = (state, replace) => { + savedSetState(state, replace); + void setItem(); + }; + const configResult = config( + (...args) => { + set(...args); + void setItem(); + }, + get, + api + ); + let stateFromStorage; + const hydrate = () => { + var _a; + if (!storage) + return; + hasHydrated = false; + hydrationListeners.forEach((cb) => cb(get())); + const postRehydrationCallback = ((_a = options.onRehydrateStorage) == null ? void 0 : _a.call(options, get())) || void 0; + return toThenable(storage.getItem.bind(storage))(options.name).then((storageValue) => { + if (storageValue) { + return options.deserialize(storageValue); + } + }).then((deserializedStorageValue) => { + if (deserializedStorageValue) { + if (typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== options.version) { + if (options.migrate) { + return options.migrate( + deserializedStorageValue.state, + deserializedStorageValue.version + ); + } + console.error( + `State loaded from storage couldn't be migrated since no migrate function was provided` + ); + } else { + return deserializedStorageValue.state; + } + } + }).then((migratedState) => { + var _a2; + stateFromStorage = options.merge( + migratedState, + (_a2 = get()) != null ? _a2 : configResult + ); + set(stateFromStorage, true); + return setItem(); + }).then(() => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0); + hasHydrated = true; + finishHydrationListeners.forEach((cb) => cb(stateFromStorage)); + }).catch((e) => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e); + }); + }; + api.persist = { + setOptions: (newOptions) => { + options = { + ...options, + ...newOptions + }; + if (newOptions.getStorage) { + storage = newOptions.getStorage(); + } + }, + clearStorage: () => { + storage == null ? void 0 : storage.removeItem(options.name); + }, + getOptions: () => options, + rehydrate: () => hydrate(), + hasHydrated: () => hasHydrated, + onHydrate: (cb) => { + hydrationListeners.add(cb); + return () => { + hydrationListeners.delete(cb); + }; + }, + onFinishHydration: (cb) => { + finishHydrationListeners.add(cb); + return () => { + finishHydrationListeners.delete(cb); + }; + } + }; + hydrate(); + return stateFromStorage || configResult; +}; +const newImpl = (config, baseOptions) => (set, get, api) => { + let options = { + storage: createJSONStorage(() => localStorage), + partialize: (state) => state, + version: 0, + merge: (persistedState, currentState) => ({ + ...currentState, + ...persistedState + }), + ...baseOptions + }; + let hasHydrated = false; + const hydrationListeners = /* @__PURE__ */ new Set(); + const finishHydrationListeners = /* @__PURE__ */ new Set(); + let storage = options.storage; + if (!storage) { + return config( + (...args) => { + console.warn( + `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.` + ); + set(...args); + }, + get, + api + ); + } + const setItem = () => { + const state = options.partialize({ ...get() }); + return storage.setItem(options.name, { + state, + version: options.version + }); + }; + const savedSetState = api.setState; + api.setState = (state, replace) => { + savedSetState(state, replace); + void setItem(); + }; + const configResult = config( + (...args) => { + set(...args); + void setItem(); + }, + get, + api + ); + api.getInitialState = () => configResult; + let stateFromStorage; + const hydrate = () => { + var _a, _b; + if (!storage) + return; + hasHydrated = false; + hydrationListeners.forEach((cb) => { + var _a2; + return cb((_a2 = get()) != null ? _a2 : configResult); + }); + const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0; + return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => { + if (deserializedStorageValue) { + if (typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== options.version) { + if (options.migrate) { + return options.migrate( + deserializedStorageValue.state, + deserializedStorageValue.version + ); + } + console.error( + `State loaded from storage couldn't be migrated since no migrate function was provided` + ); + } else { + return deserializedStorageValue.state; + } + } + }).then((migratedState) => { + var _a2; + stateFromStorage = options.merge( + migratedState, + (_a2 = get()) != null ? _a2 : configResult + ); + set(stateFromStorage, true); + return setItem(); + }).then(() => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0); + stateFromStorage = get(); + hasHydrated = true; + finishHydrationListeners.forEach((cb) => cb(stateFromStorage)); + }).catch((e) => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e); + }); + }; + api.persist = { + setOptions: (newOptions) => { + options = { + ...options, + ...newOptions + }; + if (newOptions.storage) { + storage = newOptions.storage; + } + }, + clearStorage: () => { + storage == null ? void 0 : storage.removeItem(options.name); + }, + getOptions: () => options, + rehydrate: () => hydrate(), + hasHydrated: () => hasHydrated, + onHydrate: (cb) => { + hydrationListeners.add(cb); + return () => { + hydrationListeners.delete(cb); + }; + }, + onFinishHydration: (cb) => { + finishHydrationListeners.add(cb); + return () => { + finishHydrationListeners.delete(cb); + }; + } + }; + if (!options.skipHydration) { + hydrate(); + } + return stateFromStorage || configResult; +}; +const persistImpl = (config, baseOptions) => { + if ("getStorage" in baseOptions || "serialize" in baseOptions || "deserialize" in baseOptions) { + if (process.env.NODE_ENV !== "production") { + console.warn( + "[DEPRECATED] `getStorage`, `serialize` and `deserialize` options are deprecated. Use `storage` option instead." + ); + } + return oldImpl(config, baseOptions); + } + return newImpl(config, baseOptions); +}; +const persist = persistImpl; + +export { combine, createJSONStorage, devtools, persist, redux, subscribeWithSelector }; diff --git a/esm/middleware.mjs b/esm/middleware.mjs new file mode 100644 index 0000000..ad4efa9 --- /dev/null +++ b/esm/middleware.mjs @@ -0,0 +1,584 @@ +const reduxImpl = (reducer, initial) => (set, _get, api) => { + api.dispatch = (action) => { + set((state) => reducer(state, action), false, action); + return action; + }; + api.dispatchFromDevtools = true; + return { dispatch: (...a) => api.dispatch(...a), ...initial }; +}; +const redux = reduxImpl; + +const trackedConnections = /* @__PURE__ */ new Map(); +const getTrackedConnectionState = (name) => { + const api = trackedConnections.get(name); + if (!api) + return {}; + return Object.fromEntries( + Object.entries(api.stores).map(([key, api2]) => [key, api2.getState()]) + ); +}; +const extractConnectionInformation = (store, extensionConnector, options) => { + if (store === void 0) { + return { + type: "untracked", + connection: extensionConnector.connect(options) + }; + } + const existingConnection = trackedConnections.get(options.name); + if (existingConnection) { + return { type: "tracked", store, ...existingConnection }; + } + const newConnection = { + connection: extensionConnector.connect(options), + stores: {} + }; + trackedConnections.set(options.name, newConnection); + return { type: "tracked", store, ...newConnection }; +}; +const devtoolsImpl = (fn, devtoolsOptions = {}) => (set, get, api) => { + const { enabled, anonymousActionType, store, ...options } = devtoolsOptions; + let extensionConnector; + try { + extensionConnector = (enabled != null ? enabled : (import.meta.env ? import.meta.env.MODE : void 0) !== "production") && window.__REDUX_DEVTOOLS_EXTENSION__; + } catch (e) { + } + if (!extensionConnector) { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && enabled) { + console.warn( + "[zustand devtools middleware] Please install/enable Redux devtools extension" + ); + } + return fn(set, get, api); + } + const { connection, ...connectionInformation } = extractConnectionInformation(store, extensionConnector, options); + let isRecording = true; + api.setState = (state, replace, nameOrAction) => { + const r = set(state, replace); + if (!isRecording) + return r; + const action = nameOrAction === void 0 ? { type: anonymousActionType || "anonymous" } : typeof nameOrAction === "string" ? { type: nameOrAction } : nameOrAction; + if (store === void 0) { + connection == null ? void 0 : connection.send(action, get()); + return r; + } + connection == null ? void 0 : connection.send( + { + ...action, + type: `${store}/${action.type}` + }, + { + ...getTrackedConnectionState(options.name), + [store]: api.getState() + } + ); + return r; + }; + const setStateFromDevtools = (...a) => { + const originalIsRecording = isRecording; + isRecording = false; + set(...a); + isRecording = originalIsRecording; + }; + const initialState = fn(api.setState, get, api); + if (connectionInformation.type === "untracked") { + connection == null ? void 0 : connection.init(initialState); + } else { + connectionInformation.stores[connectionInformation.store] = api; + connection == null ? void 0 : connection.init( + Object.fromEntries( + Object.entries(connectionInformation.stores).map(([key, store2]) => [ + key, + key === connectionInformation.store ? initialState : store2.getState() + ]) + ) + ); + } + if (api.dispatchFromDevtools && typeof api.dispatch === "function") { + let didWarnAboutReservedActionType = false; + const originalDispatch = api.dispatch; + api.dispatch = (...a) => { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && a[0].type === "__setState" && !didWarnAboutReservedActionType) { + console.warn( + '[zustand devtools middleware] "__setState" action type is reserved to set state from the devtools. Avoid using it.' + ); + didWarnAboutReservedActionType = true; + } + originalDispatch(...a); + }; + } + connection.subscribe((message) => { + var _a; + switch (message.type) { + case "ACTION": + if (typeof message.payload !== "string") { + console.error( + "[zustand devtools middleware] Unsupported action format" + ); + return; + } + return parseJsonThen( + message.payload, + (action) => { + if (action.type === "__setState") { + if (store === void 0) { + setStateFromDevtools(action.state); + return; + } + if (Object.keys(action.state).length !== 1) { + console.error( + ` + [zustand devtools middleware] Unsupported __setState action format. + When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(), + and value of this only key should be a state object. Example: { "type": "__setState", "state": { "abc123Store": { "foo": "bar" } } } + ` + ); + } + const stateFromDevtools = action.state[store]; + if (stateFromDevtools === void 0 || stateFromDevtools === null) { + return; + } + if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) { + setStateFromDevtools(stateFromDevtools); + } + return; + } + if (!api.dispatchFromDevtools) + return; + if (typeof api.dispatch !== "function") + return; + api.dispatch(action); + } + ); + case "DISPATCH": + switch (message.payload.type) { + case "RESET": + setStateFromDevtools(initialState); + if (store === void 0) { + return connection == null ? void 0 : connection.init(api.getState()); + } + return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + case "COMMIT": + if (store === void 0) { + connection == null ? void 0 : connection.init(api.getState()); + return; + } + return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + case "ROLLBACK": + return parseJsonThen(message.state, (state) => { + if (store === void 0) { + setStateFromDevtools(state); + connection == null ? void 0 : connection.init(api.getState()); + return; + } + setStateFromDevtools(state[store]); + connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + }); + case "JUMP_TO_STATE": + case "JUMP_TO_ACTION": + return parseJsonThen(message.state, (state) => { + if (store === void 0) { + setStateFromDevtools(state); + return; + } + if (JSON.stringify(api.getState()) !== JSON.stringify(state[store])) { + setStateFromDevtools(state[store]); + } + }); + case "IMPORT_STATE": { + const { nextLiftedState } = message.payload; + const lastComputedState = (_a = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _a.state; + if (!lastComputedState) + return; + if (store === void 0) { + setStateFromDevtools(lastComputedState); + } else { + setStateFromDevtools(lastComputedState[store]); + } + connection == null ? void 0 : connection.send( + null, + // FIXME no-any + nextLiftedState + ); + return; + } + case "PAUSE_RECORDING": + return isRecording = !isRecording; + } + return; + } + }); + return initialState; +}; +const devtools = devtoolsImpl; +const parseJsonThen = (stringified, f) => { + let parsed; + try { + parsed = JSON.parse(stringified); + } catch (e) { + console.error( + "[zustand devtools middleware] Could not parse the received json", + e + ); + } + if (parsed !== void 0) + f(parsed); +}; + +const subscribeWithSelectorImpl = (fn) => (set, get, api) => { + const origSubscribe = api.subscribe; + api.subscribe = (selector, optListener, options) => { + let listener = selector; + if (optListener) { + const equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is; + let currentSlice = selector(api.getState()); + listener = (state) => { + const nextSlice = selector(state); + if (!equalityFn(currentSlice, nextSlice)) { + const previousSlice = currentSlice; + optListener(currentSlice = nextSlice, previousSlice); + } + }; + if (options == null ? void 0 : options.fireImmediately) { + optListener(currentSlice, currentSlice); + } + } + return origSubscribe(listener); + }; + const initialState = fn(set, get, api); + return initialState; +}; +const subscribeWithSelector = subscribeWithSelectorImpl; + +const combine = (initialState, create) => (...a) => Object.assign({}, initialState, create(...a)); + +function createJSONStorage(getStorage, options) { + let storage; + try { + storage = getStorage(); + } catch (e) { + return; + } + const persistStorage = { + getItem: (name) => { + var _a; + const parse = (str2) => { + if (str2 === null) { + return null; + } + return JSON.parse(str2, options == null ? void 0 : options.reviver); + }; + const str = (_a = storage.getItem(name)) != null ? _a : null; + if (str instanceof Promise) { + return str.then(parse); + } + return parse(str); + }, + setItem: (name, newValue) => storage.setItem( + name, + JSON.stringify(newValue, options == null ? void 0 : options.replacer) + ), + removeItem: (name) => storage.removeItem(name) + }; + return persistStorage; +} +const toThenable = (fn) => (input) => { + try { + const result = fn(input); + if (result instanceof Promise) { + return result; + } + return { + then(onFulfilled) { + return toThenable(onFulfilled)(result); + }, + catch(_onRejected) { + return this; + } + }; + } catch (e) { + return { + then(_onFulfilled) { + return this; + }, + catch(onRejected) { + return toThenable(onRejected)(e); + } + }; + } +}; +const oldImpl = (config, baseOptions) => (set, get, api) => { + let options = { + getStorage: () => localStorage, + serialize: JSON.stringify, + deserialize: JSON.parse, + partialize: (state) => state, + version: 0, + merge: (persistedState, currentState) => ({ + ...currentState, + ...persistedState + }), + ...baseOptions + }; + let hasHydrated = false; + const hydrationListeners = /* @__PURE__ */ new Set(); + const finishHydrationListeners = /* @__PURE__ */ new Set(); + let storage; + try { + storage = options.getStorage(); + } catch (e) { + } + if (!storage) { + return config( + (...args) => { + console.warn( + `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.` + ); + set(...args); + }, + get, + api + ); + } + const thenableSerialize = toThenable(options.serialize); + const setItem = () => { + const state = options.partialize({ ...get() }); + let errorInSync; + const thenable = thenableSerialize({ state, version: options.version }).then( + (serializedValue) => storage.setItem(options.name, serializedValue) + ).catch((e) => { + errorInSync = e; + }); + if (errorInSync) { + throw errorInSync; + } + return thenable; + }; + const savedSetState = api.setState; + api.setState = (state, replace) => { + savedSetState(state, replace); + void setItem(); + }; + const configResult = config( + (...args) => { + set(...args); + void setItem(); + }, + get, + api + ); + let stateFromStorage; + const hydrate = () => { + var _a; + if (!storage) + return; + hasHydrated = false; + hydrationListeners.forEach((cb) => cb(get())); + const postRehydrationCallback = ((_a = options.onRehydrateStorage) == null ? void 0 : _a.call(options, get())) || void 0; + return toThenable(storage.getItem.bind(storage))(options.name).then((storageValue) => { + if (storageValue) { + return options.deserialize(storageValue); + } + }).then((deserializedStorageValue) => { + if (deserializedStorageValue) { + if (typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== options.version) { + if (options.migrate) { + return options.migrate( + deserializedStorageValue.state, + deserializedStorageValue.version + ); + } + console.error( + `State loaded from storage couldn't be migrated since no migrate function was provided` + ); + } else { + return deserializedStorageValue.state; + } + } + }).then((migratedState) => { + var _a2; + stateFromStorage = options.merge( + migratedState, + (_a2 = get()) != null ? _a2 : configResult + ); + set(stateFromStorage, true); + return setItem(); + }).then(() => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0); + hasHydrated = true; + finishHydrationListeners.forEach((cb) => cb(stateFromStorage)); + }).catch((e) => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e); + }); + }; + api.persist = { + setOptions: (newOptions) => { + options = { + ...options, + ...newOptions + }; + if (newOptions.getStorage) { + storage = newOptions.getStorage(); + } + }, + clearStorage: () => { + storage == null ? void 0 : storage.removeItem(options.name); + }, + getOptions: () => options, + rehydrate: () => hydrate(), + hasHydrated: () => hasHydrated, + onHydrate: (cb) => { + hydrationListeners.add(cb); + return () => { + hydrationListeners.delete(cb); + }; + }, + onFinishHydration: (cb) => { + finishHydrationListeners.add(cb); + return () => { + finishHydrationListeners.delete(cb); + }; + } + }; + hydrate(); + return stateFromStorage || configResult; +}; +const newImpl = (config, baseOptions) => (set, get, api) => { + let options = { + storage: createJSONStorage(() => localStorage), + partialize: (state) => state, + version: 0, + merge: (persistedState, currentState) => ({ + ...currentState, + ...persistedState + }), + ...baseOptions + }; + let hasHydrated = false; + const hydrationListeners = /* @__PURE__ */ new Set(); + const finishHydrationListeners = /* @__PURE__ */ new Set(); + let storage = options.storage; + if (!storage) { + return config( + (...args) => { + console.warn( + `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.` + ); + set(...args); + }, + get, + api + ); + } + const setItem = () => { + const state = options.partialize({ ...get() }); + return storage.setItem(options.name, { + state, + version: options.version + }); + }; + const savedSetState = api.setState; + api.setState = (state, replace) => { + savedSetState(state, replace); + void setItem(); + }; + const configResult = config( + (...args) => { + set(...args); + void setItem(); + }, + get, + api + ); + api.getInitialState = () => configResult; + let stateFromStorage; + const hydrate = () => { + var _a, _b; + if (!storage) + return; + hasHydrated = false; + hydrationListeners.forEach((cb) => { + var _a2; + return cb((_a2 = get()) != null ? _a2 : configResult); + }); + const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0; + return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => { + if (deserializedStorageValue) { + if (typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== options.version) { + if (options.migrate) { + return options.migrate( + deserializedStorageValue.state, + deserializedStorageValue.version + ); + } + console.error( + `State loaded from storage couldn't be migrated since no migrate function was provided` + ); + } else { + return deserializedStorageValue.state; + } + } + }).then((migratedState) => { + var _a2; + stateFromStorage = options.merge( + migratedState, + (_a2 = get()) != null ? _a2 : configResult + ); + set(stateFromStorage, true); + return setItem(); + }).then(() => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0); + stateFromStorage = get(); + hasHydrated = true; + finishHydrationListeners.forEach((cb) => cb(stateFromStorage)); + }).catch((e) => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e); + }); + }; + api.persist = { + setOptions: (newOptions) => { + options = { + ...options, + ...newOptions + }; + if (newOptions.storage) { + storage = newOptions.storage; + } + }, + clearStorage: () => { + storage == null ? void 0 : storage.removeItem(options.name); + }, + getOptions: () => options, + rehydrate: () => hydrate(), + hasHydrated: () => hasHydrated, + onHydrate: (cb) => { + hydrationListeners.add(cb); + return () => { + hydrationListeners.delete(cb); + }; + }, + onFinishHydration: (cb) => { + finishHydrationListeners.add(cb); + return () => { + finishHydrationListeners.delete(cb); + }; + } + }; + if (!options.skipHydration) { + hydrate(); + } + return stateFromStorage || configResult; +}; +const persistImpl = (config, baseOptions) => { + if ("getStorage" in baseOptions || "serialize" in baseOptions || "deserialize" in baseOptions) { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + console.warn( + "[DEPRECATED] `getStorage`, `serialize` and `deserialize` options are deprecated. Use `storage` option instead." + ); + } + return oldImpl(config, baseOptions); + } + return newImpl(config, baseOptions); +}; +const persist = persistImpl; + +export { combine, createJSONStorage, devtools, persist, redux, subscribeWithSelector }; diff --git a/esm/middleware/combine.d.mts b/esm/middleware/combine.d.mts new file mode 100644 index 0000000..d6ebb81 --- /dev/null +++ b/esm/middleware/combine.d.mts @@ -0,0 +1,5 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla.mjs'; +type Write = Omit & U; +type Combine = (initialState: T, additionalStateCreator: StateCreator) => StateCreator, Mps, Mcs>; +export declare const combine: Combine; +export {}; diff --git a/esm/middleware/combine.d.ts b/esm/middleware/combine.d.ts new file mode 100644 index 0000000..6e9d2d1 --- /dev/null +++ b/esm/middleware/combine.d.ts @@ -0,0 +1,5 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Write = Omit & U; +type Combine = (initialState: T, additionalStateCreator: StateCreator) => StateCreator, Mps, Mcs>; +export declare const combine: Combine; +export {}; diff --git a/esm/middleware/devtools.d.mts b/esm/middleware/devtools.d.mts new file mode 100644 index 0000000..b7624c0 --- /dev/null +++ b/esm/middleware/devtools.d.mts @@ -0,0 +1,49 @@ +import type { StateCreator, StoreApi, StoreMutatorIdentifier } from '../vanilla.mjs'; +type Config = Parameters<(Window extends { + __REDUX_DEVTOOLS_EXTENSION__?: infer T; +} ? T : { + connect: (param: any) => any; +})['connect']>[0]; +declare module '../vanilla.mjs' { + interface StoreMutators { + 'zustand/devtools': WithDevtools; + } +} +type Cast = T extends U ? T : U; +type Write = Omit & U; +type TakeTwo = T extends { + length: 0; +} ? [undefined, undefined] : T extends { + length: 1; +} ? [...a0: Cast, a1: undefined] : T extends { + length: 0 | 1; +} ? [...a0: Cast, a1: undefined] : T extends { + length: 2; +} ? T : T extends { + length: 1 | 2; +} ? T : T extends { + length: 0 | 1 | 2; +} ? T : T extends [infer A0, infer A1, ...unknown[]] ? [A0, A1] : T extends [infer A0, (infer A1)?, ...unknown[]] ? [A0, A1?] : T extends [(infer A0)?, (infer A1)?, ...unknown[]] ? [A0?, A1?] : never; +type WithDevtools = Write>; +type StoreDevtools = S extends { + setState: (...a: infer Sa) => infer Sr; +} ? { + setState(...a: [...a: TakeTwo, action?: A]): Sr; +} : never; +export interface DevtoolsOptions extends Config { + name?: string; + enabled?: boolean; + anonymousActionType?: string; + store?: string; +} +type Devtools = (initializer: StateCreator, devtoolsOptions?: DevtoolsOptions) => StateCreator; +declare module '../vanilla.mjs' { + interface StoreMutators { + 'zustand/devtools': WithDevtools; + } +} +export type NamedSet = WithDevtools>['setState']; +export declare const devtools: Devtools; +export {}; diff --git a/esm/middleware/devtools.d.ts b/esm/middleware/devtools.d.ts new file mode 100644 index 0000000..34489bd --- /dev/null +++ b/esm/middleware/devtools.d.ts @@ -0,0 +1,49 @@ +import type { StateCreator, StoreApi, StoreMutatorIdentifier } from '../vanilla'; +type Config = Parameters<(Window extends { + __REDUX_DEVTOOLS_EXTENSION__?: infer T; +} ? T : { + connect: (param: any) => any; +})['connect']>[0]; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/devtools': WithDevtools; + } +} +type Cast = T extends U ? T : U; +type Write = Omit & U; +type TakeTwo = T extends { + length: 0; +} ? [undefined, undefined] : T extends { + length: 1; +} ? [...a0: Cast, a1: undefined] : T extends { + length: 0 | 1; +} ? [...a0: Cast, a1: undefined] : T extends { + length: 2; +} ? T : T extends { + length: 1 | 2; +} ? T : T extends { + length: 0 | 1 | 2; +} ? T : T extends [infer A0, infer A1, ...unknown[]] ? [A0, A1] : T extends [infer A0, (infer A1)?, ...unknown[]] ? [A0, A1?] : T extends [(infer A0)?, (infer A1)?, ...unknown[]] ? [A0?, A1?] : never; +type WithDevtools = Write>; +type StoreDevtools = S extends { + setState: (...a: infer Sa) => infer Sr; +} ? { + setState(...a: [...a: TakeTwo, action?: A]): Sr; +} : never; +export interface DevtoolsOptions extends Config { + name?: string; + enabled?: boolean; + anonymousActionType?: string; + store?: string; +} +type Devtools = (initializer: StateCreator, devtoolsOptions?: DevtoolsOptions) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/devtools': WithDevtools; + } +} +export type NamedSet = WithDevtools>['setState']; +export declare const devtools: Devtools; +export {}; diff --git a/esm/middleware/immer.d.mts b/esm/middleware/immer.d.mts new file mode 100644 index 0000000..27bc45f --- /dev/null +++ b/esm/middleware/immer.d.mts @@ -0,0 +1,25 @@ +import type { Draft } from 'immer'; +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla.mjs'; +type Immer = (initializer: StateCreator) => StateCreator; +declare module '../vanilla.mjs' { + interface StoreMutators { + ['zustand/immer']: WithImmer; + } +} +type Write = Omit & U; +type SkipTwo = T extends { + length: 0; +} ? [] : T extends { + length: 1; +} ? [] : T extends { + length: 0 | 1; +} ? [] : T extends [unknown, unknown, ...infer A] ? A : T extends [unknown, unknown?, ...infer A] ? A : T extends [unknown?, unknown?, ...infer A] ? A : never; +type WithImmer = Write>; +type StoreImmer = S extends { + getState: () => infer T; + setState: infer SetState; +} ? SetState extends (...a: infer A) => infer Sr ? { + setState(nextStateOrUpdater: T | Partial | ((state: Draft) => void), shouldReplace?: boolean | undefined, ...a: SkipTwo): Sr; +} : never : never; +export declare const immer: Immer; +export {}; diff --git a/esm/middleware/immer.d.ts b/esm/middleware/immer.d.ts new file mode 100644 index 0000000..e770ab3 --- /dev/null +++ b/esm/middleware/immer.d.ts @@ -0,0 +1,25 @@ +import type { Draft } from 'immer'; +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Immer = (initializer: StateCreator) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + ['zustand/immer']: WithImmer; + } +} +type Write = Omit & U; +type SkipTwo = T extends { + length: 0; +} ? [] : T extends { + length: 1; +} ? [] : T extends { + length: 0 | 1; +} ? [] : T extends [unknown, unknown, ...infer A] ? A : T extends [unknown, unknown?, ...infer A] ? A : T extends [unknown?, unknown?, ...infer A] ? A : never; +type WithImmer = Write>; +type StoreImmer = S extends { + getState: () => infer T; + setState: infer SetState; +} ? SetState extends (...a: infer A) => infer Sr ? { + setState(nextStateOrUpdater: T | Partial | ((state: Draft) => void), shouldReplace?: boolean | undefined, ...a: SkipTwo): Sr; +} : never : never; +export declare const immer: Immer; +export {}; diff --git a/esm/middleware/immer.js b/esm/middleware/immer.js new file mode 100644 index 0000000..4c12d2a --- /dev/null +++ b/esm/middleware/immer.js @@ -0,0 +1,12 @@ +import { produce } from 'immer'; + +const immerImpl = (initializer) => (set, get, store) => { + store.setState = (updater, replace, ...a) => { + const nextState = typeof updater === "function" ? produce(updater) : updater; + return set(nextState, replace, ...a); + }; + return initializer(store.setState, get, store); +}; +const immer = immerImpl; + +export { immer }; diff --git a/esm/middleware/immer.mjs b/esm/middleware/immer.mjs new file mode 100644 index 0000000..4c12d2a --- /dev/null +++ b/esm/middleware/immer.mjs @@ -0,0 +1,12 @@ +import { produce } from 'immer'; + +const immerImpl = (initializer) => (set, get, store) => { + store.setState = (updater, replace, ...a) => { + const nextState = typeof updater === "function" ? produce(updater) : updater; + return set(nextState, replace, ...a); + }; + return initializer(store.setState, get, store); +}; +const immer = immerImpl; + +export { immer }; diff --git a/esm/middleware/persist.d.mts b/esm/middleware/persist.d.mts new file mode 100644 index 0000000..8c00627 --- /dev/null +++ b/esm/middleware/persist.d.mts @@ -0,0 +1,119 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla.mjs'; +export interface StateStorage { + getItem: (name: string) => string | null | Promise; + setItem: (name: string, value: string) => unknown | Promise; + removeItem: (name: string) => unknown | Promise; +} +export type StorageValue = { + state: S; + version?: number; +}; +export interface PersistStorage { + getItem: (name: string) => StorageValue | null | Promise | null>; + setItem: (name: string, value: StorageValue) => unknown | Promise; + removeItem: (name: string) => unknown | Promise; +} +type JsonStorageOptions = { + reviver?: (key: string, value: unknown) => unknown; + replacer?: (key: string, value: unknown) => unknown; +}; +export declare function createJSONStorage(getStorage: () => StateStorage, options?: JsonStorageOptions): PersistStorage | undefined; +export interface PersistOptions { + /** Name of the storage (must be unique) */ + name: string; + /** + * @deprecated Use `storage` instead. + * A function returning a storage. + * The storage must fit `window.localStorage`'s api (or an async version of it). + * For example the storage could be `AsyncStorage` from React Native. + * + * @default () => localStorage + */ + getStorage?: () => StateStorage; + /** + * @deprecated Use `storage` instead. + * Use a custom serializer. + * The returned string will be stored in the storage. + * + * @default JSON.stringify + */ + serialize?: (state: StorageValue) => string | Promise; + /** + * @deprecated Use `storage` instead. + * Use a custom deserializer. + * Must return an object matching StorageValue + * + * @param str The storage's current value. + * @default JSON.parse + */ + deserialize?: (str: string) => StorageValue | Promise>; + /** + * Use a custom persist storage. + * + * Combining `createJSONStorage` helps creating a persist storage + * with JSON.parse and JSON.stringify. + * + * @default createJSONStorage(() => localStorage) + */ + storage?: PersistStorage | undefined; + /** + * Filter the persisted value. + * + * @params state The state's value + */ + partialize?: (state: S) => PersistedState; + /** + * A function returning another (optional) function. + * The main function will be called before the state rehydration. + * The returned function will be called after the state rehydration or when an error occurred. + */ + onRehydrateStorage?: (state: S) => ((state?: S, error?: unknown) => void) | void; + /** + * If the stored state's version mismatch the one specified here, the storage will not be used. + * This is useful when adding a breaking change to your store. + */ + version?: number; + /** + * A function to perform persisted state migration. + * This function will be called when persisted state versions mismatch with the one specified here. + */ + migrate?: (persistedState: unknown, version: number) => PersistedState | Promise; + /** + * A function to perform custom hydration merges when combining the stored state with the current one. + * By default, this function does a shallow merge. + */ + merge?: (persistedState: unknown, currentState: S) => S; + /** + * An optional boolean that will prevent the persist middleware from triggering hydration on initialization, + * This allows you to call `rehydrate()` at a specific point in your apps rendering life-cycle. + * + * This is useful in SSR application. + * + * @default false + */ + skipHydration?: boolean; +} +type PersistListener = (state: S) => void; +type StorePersist = { + persist: { + setOptions: (options: Partial>) => void; + clearStorage: () => void; + rehydrate: () => Promise | void; + hasHydrated: () => boolean; + onHydrate: (fn: PersistListener) => () => void; + onFinishHydration: (fn: PersistListener) => () => void; + getOptions: () => Partial>; + }; +}; +type Persist = (initializer: StateCreator, options: PersistOptions) => StateCreator; +declare module '../vanilla.mjs' { + interface StoreMutators { + 'zustand/persist': WithPersist; + } +} +type Write = Omit & U; +type WithPersist = S extends { + getState: () => infer T; +} ? Write> : never; +export declare const persist: Persist; +export {}; diff --git a/esm/middleware/persist.d.ts b/esm/middleware/persist.d.ts new file mode 100644 index 0000000..3cb38bd --- /dev/null +++ b/esm/middleware/persist.d.ts @@ -0,0 +1,119 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +export interface StateStorage { + getItem: (name: string) => string | null | Promise; + setItem: (name: string, value: string) => unknown | Promise; + removeItem: (name: string) => unknown | Promise; +} +export type StorageValue = { + state: S; + version?: number; +}; +export interface PersistStorage { + getItem: (name: string) => StorageValue | null | Promise | null>; + setItem: (name: string, value: StorageValue) => unknown | Promise; + removeItem: (name: string) => unknown | Promise; +} +type JsonStorageOptions = { + reviver?: (key: string, value: unknown) => unknown; + replacer?: (key: string, value: unknown) => unknown; +}; +export declare function createJSONStorage(getStorage: () => StateStorage, options?: JsonStorageOptions): PersistStorage | undefined; +export interface PersistOptions { + /** Name of the storage (must be unique) */ + name: string; + /** + * @deprecated Use `storage` instead. + * A function returning a storage. + * The storage must fit `window.localStorage`'s api (or an async version of it). + * For example the storage could be `AsyncStorage` from React Native. + * + * @default () => localStorage + */ + getStorage?: () => StateStorage; + /** + * @deprecated Use `storage` instead. + * Use a custom serializer. + * The returned string will be stored in the storage. + * + * @default JSON.stringify + */ + serialize?: (state: StorageValue) => string | Promise; + /** + * @deprecated Use `storage` instead. + * Use a custom deserializer. + * Must return an object matching StorageValue + * + * @param str The storage's current value. + * @default JSON.parse + */ + deserialize?: (str: string) => StorageValue | Promise>; + /** + * Use a custom persist storage. + * + * Combining `createJSONStorage` helps creating a persist storage + * with JSON.parse and JSON.stringify. + * + * @default createJSONStorage(() => localStorage) + */ + storage?: PersistStorage | undefined; + /** + * Filter the persisted value. + * + * @params state The state's value + */ + partialize?: (state: S) => PersistedState; + /** + * A function returning another (optional) function. + * The main function will be called before the state rehydration. + * The returned function will be called after the state rehydration or when an error occurred. + */ + onRehydrateStorage?: (state: S) => ((state?: S, error?: unknown) => void) | void; + /** + * If the stored state's version mismatch the one specified here, the storage will not be used. + * This is useful when adding a breaking change to your store. + */ + version?: number; + /** + * A function to perform persisted state migration. + * This function will be called when persisted state versions mismatch with the one specified here. + */ + migrate?: (persistedState: unknown, version: number) => PersistedState | Promise; + /** + * A function to perform custom hydration merges when combining the stored state with the current one. + * By default, this function does a shallow merge. + */ + merge?: (persistedState: unknown, currentState: S) => S; + /** + * An optional boolean that will prevent the persist middleware from triggering hydration on initialization, + * This allows you to call `rehydrate()` at a specific point in your apps rendering life-cycle. + * + * This is useful in SSR application. + * + * @default false + */ + skipHydration?: boolean; +} +type PersistListener = (state: S) => void; +type StorePersist = { + persist: { + setOptions: (options: Partial>) => void; + clearStorage: () => void; + rehydrate: () => Promise | void; + hasHydrated: () => boolean; + onHydrate: (fn: PersistListener) => () => void; + onFinishHydration: (fn: PersistListener) => () => void; + getOptions: () => Partial>; + }; +}; +type Persist = (initializer: StateCreator, options: PersistOptions) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/persist': WithPersist; + } +} +type Write = Omit & U; +type WithPersist = S extends { + getState: () => infer T; +} ? Write> : never; +export declare const persist: Persist; +export {}; diff --git a/esm/middleware/redux.d.mts b/esm/middleware/redux.d.mts new file mode 100644 index 0000000..567c3b8 --- /dev/null +++ b/esm/middleware/redux.d.mts @@ -0,0 +1,21 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla.mjs'; +type Write = Omit & U; +type Action = { + type: string; +}; +type StoreRedux = { + dispatch: (a: A) => A; + dispatchFromDevtools: true; +}; +type ReduxState = { + dispatch: StoreRedux['dispatch']; +}; +type WithRedux = Write>; +type Redux = (reducer: (state: T, action: A) => T, initialState: T) => StateCreator>, Cms, [['zustand/redux', A]]>; +declare module '../vanilla.mjs' { + interface StoreMutators { + 'zustand/redux': WithRedux; + } +} +export declare const redux: Redux; +export {}; diff --git a/esm/middleware/redux.d.ts b/esm/middleware/redux.d.ts new file mode 100644 index 0000000..41a61e1 --- /dev/null +++ b/esm/middleware/redux.d.ts @@ -0,0 +1,21 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Write = Omit & U; +type Action = { + type: string; +}; +type StoreRedux = { + dispatch: (a: A) => A; + dispatchFromDevtools: true; +}; +type ReduxState = { + dispatch: StoreRedux['dispatch']; +}; +type WithRedux = Write>; +type Redux = (reducer: (state: T, action: A) => T, initialState: T) => StateCreator>, Cms, [['zustand/redux', A]]>; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/redux': WithRedux; + } +} +export declare const redux: Redux; +export {}; diff --git a/esm/middleware/subscribeWithSelector.d.mts b/esm/middleware/subscribeWithSelector.d.mts new file mode 100644 index 0000000..306e853 --- /dev/null +++ b/esm/middleware/subscribeWithSelector.d.mts @@ -0,0 +1,25 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla.mjs'; +type SubscribeWithSelector = (initializer: StateCreator) => StateCreator; +type Write = Omit & U; +type WithSelectorSubscribe = S extends { + getState: () => infer T; +} ? Write> : never; +declare module '../vanilla.mjs' { + interface StoreMutators { + ['zustand/subscribeWithSelector']: WithSelectorSubscribe; + } +} +type StoreSubscribeWithSelector = { + subscribe: { + (listener: (selectedState: T, previousSelectedState: T) => void): () => void; + (selector: (state: T) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: { + equalityFn?: (a: U, b: U) => boolean; + fireImmediately?: boolean; + }): () => void; + }; +}; +export declare const subscribeWithSelector: SubscribeWithSelector; +export {}; diff --git a/esm/middleware/subscribeWithSelector.d.ts b/esm/middleware/subscribeWithSelector.d.ts new file mode 100644 index 0000000..3243f26 --- /dev/null +++ b/esm/middleware/subscribeWithSelector.d.ts @@ -0,0 +1,25 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type SubscribeWithSelector = (initializer: StateCreator) => StateCreator; +type Write = Omit & U; +type WithSelectorSubscribe = S extends { + getState: () => infer T; +} ? Write> : never; +declare module '../vanilla' { + interface StoreMutators { + ['zustand/subscribeWithSelector']: WithSelectorSubscribe; + } +} +type StoreSubscribeWithSelector = { + subscribe: { + (listener: (selectedState: T, previousSelectedState: T) => void): () => void; + (selector: (state: T) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: { + equalityFn?: (a: U, b: U) => boolean; + fireImmediately?: boolean; + }): () => void; + }; +}; +export declare const subscribeWithSelector: SubscribeWithSelector; +export {}; diff --git a/esm/react.d.mts b/esm/react.d.mts new file mode 100644 index 0000000..42607e5 --- /dev/null +++ b/esm/react.d.mts @@ -0,0 +1,38 @@ +import type { Mutate, StateCreator, StoreApi, StoreMutatorIdentifier } from './vanilla.mjs'; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type ReadonlyStoreApi = Pick, 'getState' | 'subscribe'>; +type WithReact> = S & { + /** @deprecated please use api.getInitialState() */ + getServerState?: () => ExtractState; +}; +export declare function useStore>>(api: S): ExtractState; +export declare function useStore>, U>(api: S, selector: (state: ExtractState) => U): U; +/** + * @deprecated The usage with three arguments is deprecated. Use `useStoreWithEqualityFn` from 'zustand/traditional'. The usage with one or two arguments is not deprecated. + * https://github.com/pmndrs/zustand/discussions/1937 + */ +export declare function useStore>, U>(api: S, selector: (state: ExtractState) => U, equalityFn: ((a: U, b: U) => boolean) | undefined): U; +export type UseBoundStore>> = { + (): ExtractState; + (selector: (state: ExtractState) => U): U; + /** + * @deprecated Use `createWithEqualityFn` from 'zustand/traditional' + */ + (selector: (state: ExtractState) => U, equalityFn: (a: U, b: U) => boolean): U; +} & S; +type Create = { + (initializer: StateCreator): UseBoundStore, Mos>>; + (): (initializer: StateCreator) => UseBoundStore, Mos>>; + /** + * @deprecated Use `useStore` hook to bind store + */ + >(store: S): UseBoundStore; +}; +export declare const create: Create; +/** + * @deprecated Use `import { create } from 'zustand'` + */ +declare const _default: Create; +export default _default; diff --git a/esm/react.d.ts b/esm/react.d.ts new file mode 100644 index 0000000..21608cc --- /dev/null +++ b/esm/react.d.ts @@ -0,0 +1,38 @@ +import type { Mutate, StateCreator, StoreApi, StoreMutatorIdentifier } from './vanilla'; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type ReadonlyStoreApi = Pick, 'getState' | 'subscribe'>; +type WithReact> = S & { + /** @deprecated please use api.getInitialState() */ + getServerState?: () => ExtractState; +}; +export declare function useStore>>(api: S): ExtractState; +export declare function useStore>, U>(api: S, selector: (state: ExtractState) => U): U; +/** + * @deprecated The usage with three arguments is deprecated. Use `useStoreWithEqualityFn` from 'zustand/traditional'. The usage with one or two arguments is not deprecated. + * https://github.com/pmndrs/zustand/discussions/1937 + */ +export declare function useStore>, U>(api: S, selector: (state: ExtractState) => U, equalityFn: ((a: U, b: U) => boolean) | undefined): U; +export type UseBoundStore>> = { + (): ExtractState; + (selector: (state: ExtractState) => U): U; + /** + * @deprecated Use `createWithEqualityFn` from 'zustand/traditional' + */ + (selector: (state: ExtractState) => U, equalityFn: (a: U, b: U) => boolean): U; +} & S; +type Create = { + (initializer: StateCreator): UseBoundStore, Mos>>; + (): (initializer: StateCreator) => UseBoundStore, Mos>>; + /** + * @deprecated Use `useStore` hook to bind store + */ + >(store: S): UseBoundStore; +}; +export declare const create: Create; +/** + * @deprecated Use `import { create } from 'zustand'` + */ +declare const _default: Create; +export default _default; diff --git a/esm/react/shallow.d.mts b/esm/react/shallow.d.mts new file mode 100644 index 0000000..9068d46 --- /dev/null +++ b/esm/react/shallow.d.mts @@ -0,0 +1 @@ +export declare function useShallow(selector: (state: S) => U): (state: S) => U; diff --git a/esm/react/shallow.d.ts b/esm/react/shallow.d.ts new file mode 100644 index 0000000..9068d46 --- /dev/null +++ b/esm/react/shallow.d.ts @@ -0,0 +1 @@ +export declare function useShallow(selector: (state: S) => U): (state: S) => U; diff --git a/esm/react/shallow.js b/esm/react/shallow.js new file mode 100644 index 0000000..fa743e4 --- /dev/null +++ b/esm/react/shallow.js @@ -0,0 +1,51 @@ +import ReactExports from 'react'; + +function shallow(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) + return false; + for (const [key, value] of objA) { + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) + return false; + for (const value of objA) { + if (!objB.has(value)) { + return false; + } + } + return true; + } + const keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (const keyA of keysA) { + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; +} + +const { useRef } = ReactExports; +function useShallow(selector) { + const prev = useRef(); + return (state) => { + const next = selector(state); + return shallow(prev.current, next) ? prev.current : prev.current = next; + }; +} + +export { useShallow }; diff --git a/esm/react/shallow.mjs b/esm/react/shallow.mjs new file mode 100644 index 0000000..fa743e4 --- /dev/null +++ b/esm/react/shallow.mjs @@ -0,0 +1,51 @@ +import ReactExports from 'react'; + +function shallow(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) + return false; + for (const [key, value] of objA) { + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) + return false; + for (const value of objA) { + if (!objB.has(value)) { + return false; + } + } + return true; + } + const keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (const keyA of keysA) { + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; +} + +const { useRef } = ReactExports; +function useShallow(selector) { + const prev = useRef(); + return (state) => { + const next = selector(state); + return shallow(prev.current, next) ? prev.current : prev.current = next; + }; +} + +export { useShallow }; diff --git a/esm/shallow.d.mts b/esm/shallow.d.mts new file mode 100644 index 0000000..61c347b --- /dev/null +++ b/esm/shallow.d.mts @@ -0,0 +1,7 @@ +import { shallow } from './vanilla/shallow.mjs'; +/** + * @deprecated Use `import { shallow } from 'zustand/shallow'` + */ +declare const _default: typeof shallow; +export default _default; +export { shallow }; diff --git a/esm/shallow.d.ts b/esm/shallow.d.ts new file mode 100644 index 0000000..0914a7c --- /dev/null +++ b/esm/shallow.d.ts @@ -0,0 +1,7 @@ +import { shallow } from './vanilla/shallow'; +/** + * @deprecated Use `import { shallow } from 'zustand/shallow'` + */ +declare const _default: typeof shallow; +export default _default; +export { shallow }; diff --git a/esm/shallow.js b/esm/shallow.js new file mode 100644 index 0000000..fdfbe55 --- /dev/null +++ b/esm/shallow.js @@ -0,0 +1,49 @@ +function shallow$1(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) + return false; + for (const [key, value] of objA) { + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) + return false; + for (const value of objA) { + if (!objB.has(value)) { + return false; + } + } + return true; + } + const keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (const keyA of keysA) { + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; +} + +var shallow = (objA, objB) => { + if (process.env.NODE_ENV !== "production") { + console.warn( + "[DEPRECATED] Default export is deprecated. Instead use `import { shallow } from 'zustand/shallow'`." + ); + } + return shallow$1(objA, objB); +}; + +export { shallow as default, shallow$1 as shallow }; diff --git a/esm/shallow.mjs b/esm/shallow.mjs new file mode 100644 index 0000000..7cf3b40 --- /dev/null +++ b/esm/shallow.mjs @@ -0,0 +1,49 @@ +function shallow$1(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) + return false; + for (const [key, value] of objA) { + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) + return false; + for (const value of objA) { + if (!objB.has(value)) { + return false; + } + } + return true; + } + const keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (const keyA of keysA) { + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; +} + +var shallow = (objA, objB) => { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + console.warn( + "[DEPRECATED] Default export is deprecated. Instead use `import { shallow } from 'zustand/shallow'`." + ); + } + return shallow$1(objA, objB); +}; + +export { shallow as default, shallow$1 as shallow }; diff --git a/esm/traditional.d.mts b/esm/traditional.d.mts new file mode 100644 index 0000000..8942b71 --- /dev/null +++ b/esm/traditional.d.mts @@ -0,0 +1,21 @@ +import type { Mutate, StateCreator, StoreApi, StoreMutatorIdentifier } from './vanilla.mjs'; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type ReadonlyStoreApi = Pick, 'getState' | 'subscribe'>; +type WithReact> = S & { + /** @deprecated please use api.getInitialState() */ + getServerState?: () => ExtractState; +}; +export declare function useStoreWithEqualityFn>>(api: S): ExtractState; +export declare function useStoreWithEqualityFn>, U>(api: S, selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +export type UseBoundStoreWithEqualityFn>> = { + (): ExtractState; + (selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +} & S; +type CreateWithEqualityFn = { + (initializer: StateCreator, defaultEqualityFn?: (a: U, b: U) => boolean): UseBoundStoreWithEqualityFn, Mos>>; + (): (initializer: StateCreator, defaultEqualityFn?: (a: U, b: U) => boolean) => UseBoundStoreWithEqualityFn, Mos>>; +}; +export declare const createWithEqualityFn: CreateWithEqualityFn; +export {}; diff --git a/esm/traditional.d.ts b/esm/traditional.d.ts new file mode 100644 index 0000000..9d14d0f --- /dev/null +++ b/esm/traditional.d.ts @@ -0,0 +1,21 @@ +import type { Mutate, StateCreator, StoreApi, StoreMutatorIdentifier } from './vanilla'; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type ReadonlyStoreApi = Pick, 'getState' | 'subscribe'>; +type WithReact> = S & { + /** @deprecated please use api.getInitialState() */ + getServerState?: () => ExtractState; +}; +export declare function useStoreWithEqualityFn>>(api: S): ExtractState; +export declare function useStoreWithEqualityFn>, U>(api: S, selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +export type UseBoundStoreWithEqualityFn>> = { + (): ExtractState; + (selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +} & S; +type CreateWithEqualityFn = { + (initializer: StateCreator, defaultEqualityFn?: (a: U, b: U) => boolean): UseBoundStoreWithEqualityFn, Mos>>; + (): (initializer: StateCreator, defaultEqualityFn?: (a: U, b: U) => boolean) => UseBoundStoreWithEqualityFn, Mos>>; +}; +export declare const createWithEqualityFn: CreateWithEqualityFn; +export {}; diff --git a/esm/traditional.js b/esm/traditional.js new file mode 100644 index 0000000..dd7417a --- /dev/null +++ b/esm/traditional.js @@ -0,0 +1,27 @@ +import ReactExports from 'react'; +import useSyncExternalStoreExports from 'use-sync-external-store/shim/with-selector.js'; +import { createStore } from 'zustand/vanilla'; + +const { useDebugValue } = ReactExports; +const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports; +const identity = (arg) => arg; +function useStoreWithEqualityFn(api, selector = identity, equalityFn) { + const slice = useSyncExternalStoreWithSelector( + api.subscribe, + api.getState, + api.getServerState || api.getInitialState, + selector, + equalityFn + ); + useDebugValue(slice); + return slice; +} +const createWithEqualityFnImpl = (createState, defaultEqualityFn) => { + const api = createStore(createState); + const useBoundStoreWithEqualityFn = (selector, equalityFn = defaultEqualityFn) => useStoreWithEqualityFn(api, selector, equalityFn); + Object.assign(useBoundStoreWithEqualityFn, api); + return useBoundStoreWithEqualityFn; +}; +const createWithEqualityFn = (createState, defaultEqualityFn) => createState ? createWithEqualityFnImpl(createState, defaultEqualityFn) : createWithEqualityFnImpl; + +export { createWithEqualityFn, useStoreWithEqualityFn }; diff --git a/esm/traditional.mjs b/esm/traditional.mjs new file mode 100644 index 0000000..dd7417a --- /dev/null +++ b/esm/traditional.mjs @@ -0,0 +1,27 @@ +import ReactExports from 'react'; +import useSyncExternalStoreExports from 'use-sync-external-store/shim/with-selector.js'; +import { createStore } from 'zustand/vanilla'; + +const { useDebugValue } = ReactExports; +const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports; +const identity = (arg) => arg; +function useStoreWithEqualityFn(api, selector = identity, equalityFn) { + const slice = useSyncExternalStoreWithSelector( + api.subscribe, + api.getState, + api.getServerState || api.getInitialState, + selector, + equalityFn + ); + useDebugValue(slice); + return slice; +} +const createWithEqualityFnImpl = (createState, defaultEqualityFn) => { + const api = createStore(createState); + const useBoundStoreWithEqualityFn = (selector, equalityFn = defaultEqualityFn) => useStoreWithEqualityFn(api, selector, equalityFn); + Object.assign(useBoundStoreWithEqualityFn, api); + return useBoundStoreWithEqualityFn; +}; +const createWithEqualityFn = (createState, defaultEqualityFn) => createState ? createWithEqualityFnImpl(createState, defaultEqualityFn) : createWithEqualityFnImpl; + +export { createWithEqualityFn, useStoreWithEqualityFn }; diff --git a/esm/vanilla.d.mts b/esm/vanilla.d.mts new file mode 100644 index 0000000..e177a48 --- /dev/null +++ b/esm/vanilla.d.mts @@ -0,0 +1,85 @@ +type SetStateInternal = { + _(partial: T | Partial | { + _(state: T): T | Partial; + }['_'], replace?: boolean | undefined): void; +}['_']; +export interface StoreApi { + setState: SetStateInternal; + getState: () => T; + getInitialState: () => T; + subscribe: (listener: (state: T, prevState: T) => void) => () => void; + /** + * @deprecated Use `unsubscribe` returned by `subscribe` + */ + destroy: () => void; +} +type Get = K extends keyof T ? T[K] : F; +export type Mutate = number extends Ms['length' & keyof Ms] ? S : Ms extends [] ? S : Ms extends [[infer Mi, infer Ma], ...infer Mrs] ? Mutate[Mi & StoreMutatorIdentifier], Mrs> : never; +export type StateCreator = ((setState: Get, Mis>, 'setState', never>, getState: Get, Mis>, 'getState', never>, store: Mutate, Mis>) => U) & { + $$storeMutators?: Mos; +}; +export interface StoreMutators { +} +export type StoreMutatorIdentifier = keyof StoreMutators; +type CreateStore = { + (initializer: StateCreator): Mutate, Mos>; + (): (initializer: StateCreator) => Mutate, Mos>; +}; +type Configs = { + cacheTime?: number; +}; +type CreateStoreImpl = (initializer: StateCreator, configs?: Configs) => Mutate, Mos>; +export declare const createStore: CreateStoreImpl; +/** + * @deprecated Use `import { createStore } from 'zustand/vanilla'` + */ +declare const _default: CreateStore; +export default _default; +/** + * @deprecated Use `unknown` instead of `State` + */ +export type State = unknown; +/** + * @deprecated Use `Partial | ((s: T) => Partial)` instead of `PartialState` + */ +export type PartialState = Partial | ((state: T) => Partial); +/** + * @deprecated Use `(s: T) => U` instead of `StateSelector` + */ +export type StateSelector = (state: T) => U; +/** + * @deprecated Use `(a: T, b: T) => boolean` instead of `EqualityChecker` + */ +export type EqualityChecker = (state: T, newState: T) => boolean; +/** + * @deprecated Use `(state: T, previousState: T) => void` instead of `StateListener` + */ +export type StateListener = (state: T, previousState: T) => void; +/** + * @deprecated Use `(slice: T, previousSlice: T) => void` instead of `StateSliceListener`. + */ +export type StateSliceListener = (slice: T, previousSlice: T) => void; +/** + * @deprecated Use `(listener: (state: T) => void) => void` instead of `Subscribe`. + */ +export type Subscribe = { + (listener: (state: T, previousState: T) => void): () => void; +}; +/** + * @deprecated You might be looking for `StateCreator`, if not then + * use `StoreApi['setState']` instead of `SetState`. + */ +export type SetState = { + _(partial: T | Partial | { + _(state: T): T | Partial; + }['_'], replace?: boolean | undefined): void; +}['_']; +/** + * @deprecated You might be looking for `StateCreator`, if not then + * use `StoreApi['getState']` instead of `GetState`. + */ +export type GetState = () => T; +/** + * @deprecated Use `StoreApi['destroy']` instead of `Destroy`. + */ +export type Destroy = () => void; diff --git a/esm/vanilla.d.ts b/esm/vanilla.d.ts new file mode 100644 index 0000000..e177a48 --- /dev/null +++ b/esm/vanilla.d.ts @@ -0,0 +1,85 @@ +type SetStateInternal = { + _(partial: T | Partial | { + _(state: T): T | Partial; + }['_'], replace?: boolean | undefined): void; +}['_']; +export interface StoreApi { + setState: SetStateInternal; + getState: () => T; + getInitialState: () => T; + subscribe: (listener: (state: T, prevState: T) => void) => () => void; + /** + * @deprecated Use `unsubscribe` returned by `subscribe` + */ + destroy: () => void; +} +type Get = K extends keyof T ? T[K] : F; +export type Mutate = number extends Ms['length' & keyof Ms] ? S : Ms extends [] ? S : Ms extends [[infer Mi, infer Ma], ...infer Mrs] ? Mutate[Mi & StoreMutatorIdentifier], Mrs> : never; +export type StateCreator = ((setState: Get, Mis>, 'setState', never>, getState: Get, Mis>, 'getState', never>, store: Mutate, Mis>) => U) & { + $$storeMutators?: Mos; +}; +export interface StoreMutators { +} +export type StoreMutatorIdentifier = keyof StoreMutators; +type CreateStore = { + (initializer: StateCreator): Mutate, Mos>; + (): (initializer: StateCreator) => Mutate, Mos>; +}; +type Configs = { + cacheTime?: number; +}; +type CreateStoreImpl = (initializer: StateCreator, configs?: Configs) => Mutate, Mos>; +export declare const createStore: CreateStoreImpl; +/** + * @deprecated Use `import { createStore } from 'zustand/vanilla'` + */ +declare const _default: CreateStore; +export default _default; +/** + * @deprecated Use `unknown` instead of `State` + */ +export type State = unknown; +/** + * @deprecated Use `Partial | ((s: T) => Partial)` instead of `PartialState` + */ +export type PartialState = Partial | ((state: T) => Partial); +/** + * @deprecated Use `(s: T) => U` instead of `StateSelector` + */ +export type StateSelector = (state: T) => U; +/** + * @deprecated Use `(a: T, b: T) => boolean` instead of `EqualityChecker` + */ +export type EqualityChecker = (state: T, newState: T) => boolean; +/** + * @deprecated Use `(state: T, previousState: T) => void` instead of `StateListener` + */ +export type StateListener = (state: T, previousState: T) => void; +/** + * @deprecated Use `(slice: T, previousSlice: T) => void` instead of `StateSliceListener`. + */ +export type StateSliceListener = (slice: T, previousSlice: T) => void; +/** + * @deprecated Use `(listener: (state: T) => void) => void` instead of `Subscribe`. + */ +export type Subscribe = { + (listener: (state: T, previousState: T) => void): () => void; +}; +/** + * @deprecated You might be looking for `StateCreator`, if not then + * use `StoreApi['setState']` instead of `SetState`. + */ +export type SetState = { + _(partial: T | Partial | { + _(state: T): T | Partial; + }['_'], replace?: boolean | undefined): void; +}['_']; +/** + * @deprecated You might be looking for `StateCreator`, if not then + * use `StoreApi['getState']` instead of `GetState`. + */ +export type GetState = () => T; +/** + * @deprecated Use `StoreApi['destroy']` instead of `Destroy`. + */ +export type Destroy = () => void; diff --git a/esm/vanilla.js b/esm/vanilla.js new file mode 100644 index 0000000..37ea9b6 --- /dev/null +++ b/esm/vanilla.js @@ -0,0 +1,58 @@ +const createStoreImpl = (createState, configs) => { + let timeout; + let state; + const listeners = /* @__PURE__ */ new Set(); + const setState = (partial, replace) => { + const nextState = typeof partial === "function" ? partial(state) : partial; + if (!Object.is(nextState, state)) { + const previousState = state; + state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState); + listeners.forEach((listener) => listener(state, previousState)); + } + }; + const getState = () => state; + const getInitialState = () => initialState; + const resetBasedOnCachTime = () => { + if (!(configs == null ? void 0 : configs.cacheTime)) + return; + if (timeout) + clearTimeout(timeout); + timeout = setTimeout(() => { + state = createState(setState, getState, api); + }, configs == null ? void 0 : configs.cacheTime); + }; + const subscribe = (listener) => { + listeners.add(listener); + if (timeout) + clearTimeout(timeout); + return () => { + const responseDelete = listeners.delete(listener); + if (!listeners.size) { + resetBasedOnCachTime(); + } + return responseDelete; + }; + }; + const destroy = () => { + if (process.env.NODE_ENV !== "production") { + console.warn( + "[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected." + ); + } + listeners.clear(); + }; + const api = { setState, getState, getInitialState, subscribe, destroy }; + const initialState = state = createState(setState, getState, api); + return api; +}; +const createStore = createStoreImpl; +var vanilla = (createState) => { + if (process.env.NODE_ENV !== "production") { + console.warn( + "[DEPRECATED] Default export is deprecated. Instead use import { createStore } from 'zustand/vanilla'." + ); + } + return createStore(createState); +}; + +export { createStore, vanilla as default }; diff --git a/esm/vanilla.mjs b/esm/vanilla.mjs new file mode 100644 index 0000000..d7dc542 --- /dev/null +++ b/esm/vanilla.mjs @@ -0,0 +1,58 @@ +const createStoreImpl = (createState, configs) => { + let timeout; + let state; + const listeners = /* @__PURE__ */ new Set(); + const setState = (partial, replace) => { + const nextState = typeof partial === "function" ? partial(state) : partial; + if (!Object.is(nextState, state)) { + const previousState = state; + state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState); + listeners.forEach((listener) => listener(state, previousState)); + } + }; + const getState = () => state; + const getInitialState = () => initialState; + const resetBasedOnCachTime = () => { + if (!(configs == null ? void 0 : configs.cacheTime)) + return; + if (timeout) + clearTimeout(timeout); + timeout = setTimeout(() => { + state = createState(setState, getState, api); + }, configs == null ? void 0 : configs.cacheTime); + }; + const subscribe = (listener) => { + listeners.add(listener); + if (timeout) + clearTimeout(timeout); + return () => { + const responseDelete = listeners.delete(listener); + if (!listeners.size) { + resetBasedOnCachTime(); + } + return responseDelete; + }; + }; + const destroy = () => { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + console.warn( + "[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected." + ); + } + listeners.clear(); + }; + const api = { setState, getState, getInitialState, subscribe, destroy }; + const initialState = state = createState(setState, getState, api); + return api; +}; +const createStore = createStoreImpl; +var vanilla = (createState) => { + if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { + console.warn( + "[DEPRECATED] Default export is deprecated. Instead use import { createStore } from 'zustand/vanilla'." + ); + } + return createStore(createState); +}; + +export { createStore, vanilla as default }; diff --git a/esm/vanilla/shallow.d.mts b/esm/vanilla/shallow.d.mts new file mode 100644 index 0000000..a8d2354 --- /dev/null +++ b/esm/vanilla/shallow.d.mts @@ -0,0 +1 @@ +export declare function shallow(objA: T, objB: T): boolean; diff --git a/esm/vanilla/shallow.d.ts b/esm/vanilla/shallow.d.ts new file mode 100644 index 0000000..a8d2354 --- /dev/null +++ b/esm/vanilla/shallow.d.ts @@ -0,0 +1 @@ +export declare function shallow(objA: T, objB: T): boolean; diff --git a/esm/vanilla/shallow.js b/esm/vanilla/shallow.js new file mode 100644 index 0000000..13411eb --- /dev/null +++ b/esm/vanilla/shallow.js @@ -0,0 +1,40 @@ +function shallow(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) + return false; + for (const [key, value] of objA) { + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) + return false; + for (const value of objA) { + if (!objB.has(value)) { + return false; + } + } + return true; + } + const keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (const keyA of keysA) { + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; +} + +export { shallow }; diff --git a/esm/vanilla/shallow.mjs b/esm/vanilla/shallow.mjs new file mode 100644 index 0000000..13411eb --- /dev/null +++ b/esm/vanilla/shallow.mjs @@ -0,0 +1,40 @@ +function shallow(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) + return false; + for (const [key, value] of objA) { + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) + return false; + for (const value of objA) { + if (!objB.has(value)) { + return false; + } + } + return true; + } + const keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (const keyA of keysA) { + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; +} + +export { shallow }; diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..7e9c8b6 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,3 @@ +export * from './vanilla'; +export * from './react'; +export { default } from './react'; diff --git a/index.js b/index.js new file mode 100644 index 0000000..585d3b3 --- /dev/null +++ b/index.js @@ -0,0 +1,60 @@ +'use strict'; + +var vanilla = require('zustand/vanilla'); +var ReactExports = require('react'); +var useSyncExternalStoreExports = require('use-sync-external-store/shim/with-selector'); + +var useDebugValue = ReactExports.useDebugValue; +var useSyncExternalStoreWithSelector = useSyncExternalStoreExports.useSyncExternalStoreWithSelector; +var didWarnAboutEqualityFn = false; +var identity = function identity(arg) { + return arg; +}; +function useStore(api, selector, equalityFn) { + if (selector === void 0) { + selector = identity; + } + if (process.env.NODE_ENV !== 'production' && equalityFn && !didWarnAboutEqualityFn) { + console.warn("[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937"); + didWarnAboutEqualityFn = true; + } + var slice = useSyncExternalStoreWithSelector(api.subscribe, api.getState, api.getServerState || api.getInitialState, selector, equalityFn); + useDebugValue(slice); + return slice; +} +var createImpl = function createImpl(createState) { + if (process.env.NODE_ENV !== 'production' && typeof createState !== 'function') { + console.warn("[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`."); + } + var api = typeof createState === 'function' ? vanilla.createStore(createState) : createState; + var useBoundStore = function useBoundStore(selector, equalityFn) { + return useStore(api, selector, equalityFn); + }; + Object.assign(useBoundStore, api); + return useBoundStore; +}; +var create = function create(createState) { + return createState ? createImpl(createState) : createImpl; +}; +var react = (function (createState) { + if (process.env.NODE_ENV !== 'production') { + console.warn("[DEPRECATED] Default export is deprecated. Instead use `import { create } from 'zustand'`."); + } + return create(createState); +}); + +exports.create = create; +exports.default = react; +exports.useStore = useStore; +Object.keys(vanilla).forEach(function (k) { + if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, { + enumerable: true, + get: function () { return vanilla[k]; } + }); +}); + +module.exports = react; +module.exports.create = create; +module.exports.useStore = useStore; +module.exports.createStore = vanilla.createStore; +exports.default = module.exports; diff --git a/middleware.d.ts b/middleware.d.ts new file mode 100644 index 0000000..1806630 --- /dev/null +++ b/middleware.d.ts @@ -0,0 +1,5 @@ +export * from './middleware/redux'; +export * from './middleware/devtools'; +export * from './middleware/subscribeWithSelector'; +export * from './middleware/combine'; +export * from './middleware/persist'; diff --git a/middleware.js b/middleware.js new file mode 100644 index 0000000..7f8318a --- /dev/null +++ b/middleware.js @@ -0,0 +1,612 @@ +'use strict'; + +function _extends() { + _extends = Object.assign ? Object.assign.bind() : function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + return target; + }; + return _extends.apply(this, arguments); +} +function _objectWithoutPropertiesLoose(source, excluded) { + if (source == null) return {}; + var target = {}; + var sourceKeys = Object.keys(source); + var key, i; + for (i = 0; i < sourceKeys.length; i++) { + key = sourceKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + target[key] = source[key]; + } + return target; +} + +var reduxImpl = function reduxImpl(reducer, initial) { + return function (set, _get, api) { + api.dispatch = function (action) { + set(function (state) { + return reducer(state, action); + }, false, action); + return action; + }; + api.dispatchFromDevtools = true; + return _extends({ + dispatch: function dispatch() { + var _ref; + return (_ref = api).dispatch.apply(_ref, arguments); + } + }, initial); + }; +}; +var redux = reduxImpl; + +var _excluded = ["enabled", "anonymousActionType", "store"], + _excluded2 = ["connection"]; +var trackedConnections = new Map(); +var getTrackedConnectionState = function getTrackedConnectionState(name) { + var api = trackedConnections.get(name); + if (!api) return {}; + return Object.fromEntries(Object.entries(api.stores).map(function (_ref) { + var key = _ref[0], + api = _ref[1]; + return [key, api.getState()]; + })); +}; +var extractConnectionInformation = function extractConnectionInformation(store, extensionConnector, options) { + if (store === undefined) { + return { + type: 'untracked', + connection: extensionConnector.connect(options) + }; + } + var existingConnection = trackedConnections.get(options.name); + if (existingConnection) { + return _extends({ + type: 'tracked', + store: store + }, existingConnection); + } + var newConnection = { + connection: extensionConnector.connect(options), + stores: {} + }; + trackedConnections.set(options.name, newConnection); + return _extends({ + type: 'tracked', + store: store + }, newConnection); +}; +var devtoolsImpl = function devtoolsImpl(fn, devtoolsOptions) { + if (devtoolsOptions === void 0) { + devtoolsOptions = {}; + } + return function (set, get, api) { + var _devtoolsOptions = devtoolsOptions, + enabled = _devtoolsOptions.enabled, + anonymousActionType = _devtoolsOptions.anonymousActionType, + store = _devtoolsOptions.store, + options = _objectWithoutPropertiesLoose(_devtoolsOptions, _excluded); + var extensionConnector; + try { + extensionConnector = (enabled != null ? enabled : process.env.NODE_ENV !== 'production') && window.__REDUX_DEVTOOLS_EXTENSION__; + } catch (e) {} + if (!extensionConnector) { + if (process.env.NODE_ENV !== 'production' && enabled) { + console.warn('[zustand devtools middleware] Please install/enable Redux devtools extension'); + } + return fn(set, get, api); + } + var _extractConnectionInf = extractConnectionInformation(store, extensionConnector, options), + connection = _extractConnectionInf.connection, + connectionInformation = _objectWithoutPropertiesLoose(_extractConnectionInf, _excluded2); + var isRecording = true; + api.setState = function (state, replace, nameOrAction) { + var _extends2; + var r = set(state, replace); + if (!isRecording) return r; + var action = nameOrAction === undefined ? { + type: anonymousActionType || 'anonymous' + } : typeof nameOrAction === 'string' ? { + type: nameOrAction + } : nameOrAction; + if (store === undefined) { + connection == null || connection.send(action, get()); + return r; + } + connection == null || connection.send(_extends({}, action, { + type: store + "/" + action.type + }), _extends({}, getTrackedConnectionState(options.name), (_extends2 = {}, _extends2[store] = api.getState(), _extends2))); + return r; + }; + var setStateFromDevtools = function setStateFromDevtools() { + var originalIsRecording = isRecording; + isRecording = false; + set.apply(void 0, arguments); + isRecording = originalIsRecording; + }; + var initialState = fn(api.setState, get, api); + if (connectionInformation.type === 'untracked') { + connection == null || connection.init(initialState); + } else { + connectionInformation.stores[connectionInformation.store] = api; + connection == null || connection.init(Object.fromEntries(Object.entries(connectionInformation.stores).map(function (_ref2) { + var key = _ref2[0], + store = _ref2[1]; + return [key, key === connectionInformation.store ? initialState : store.getState()]; + }))); + } + if (api.dispatchFromDevtools && typeof api.dispatch === 'function') { + var didWarnAboutReservedActionType = false; + var originalDispatch = api.dispatch; + api.dispatch = function () { + for (var _len = arguments.length, a = new Array(_len), _key = 0; _key < _len; _key++) { + a[_key] = arguments[_key]; + } + if (process.env.NODE_ENV !== 'production' && a[0].type === '__setState' && !didWarnAboutReservedActionType) { + console.warn('[zustand devtools middleware] "__setState" action type is reserved ' + 'to set state from the devtools. Avoid using it.'); + didWarnAboutReservedActionType = true; + } + originalDispatch.apply(void 0, a); + }; + } + connection.subscribe(function (message) { + switch (message.type) { + case 'ACTION': + if (typeof message.payload !== 'string') { + console.error('[zustand devtools middleware] Unsupported action format'); + return; + } + return parseJsonThen(message.payload, function (action) { + if (action.type === '__setState') { + if (store === undefined) { + setStateFromDevtools(action.state); + return; + } + if (Object.keys(action.state).length !== 1) { + console.error("\n [zustand devtools middleware] Unsupported __setState action format. \n When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(),\n and value of this only key should be a state object. Example: { \"type\": \"__setState\", \"state\": { \"abc123Store\": { \"foo\": \"bar\" } } }\n "); + } + var stateFromDevtools = action.state[store]; + if (stateFromDevtools === undefined || stateFromDevtools === null) { + return; + } + if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) { + setStateFromDevtools(stateFromDevtools); + } + return; + } + if (!api.dispatchFromDevtools) return; + if (typeof api.dispatch !== 'function') return; + api.dispatch(action); + }); + case 'DISPATCH': + switch (message.payload.type) { + case 'RESET': + setStateFromDevtools(initialState); + if (store === undefined) { + return connection == null ? void 0 : connection.init(api.getState()); + } + return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + case 'COMMIT': + if (store === undefined) { + connection == null || connection.init(api.getState()); + return; + } + return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + case 'ROLLBACK': + return parseJsonThen(message.state, function (state) { + if (store === undefined) { + setStateFromDevtools(state); + connection == null || connection.init(api.getState()); + return; + } + setStateFromDevtools(state[store]); + connection == null || connection.init(getTrackedConnectionState(options.name)); + }); + case 'JUMP_TO_STATE': + case 'JUMP_TO_ACTION': + return parseJsonThen(message.state, function (state) { + if (store === undefined) { + setStateFromDevtools(state); + return; + } + if (JSON.stringify(api.getState()) !== JSON.stringify(state[store])) { + setStateFromDevtools(state[store]); + } + }); + case 'IMPORT_STATE': + { + var _nextLiftedState$comp; + var nextLiftedState = message.payload.nextLiftedState; + var lastComputedState = (_nextLiftedState$comp = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _nextLiftedState$comp.state; + if (!lastComputedState) return; + if (store === undefined) { + setStateFromDevtools(lastComputedState); + } else { + setStateFromDevtools(lastComputedState[store]); + } + connection == null || connection.send(null, nextLiftedState); + return; + } + case 'PAUSE_RECORDING': + return isRecording = !isRecording; + } + return; + } + }); + return initialState; + }; +}; +var devtools = devtoolsImpl; +var parseJsonThen = function parseJsonThen(stringified, f) { + var parsed; + try { + parsed = JSON.parse(stringified); + } catch (e) { + console.error('[zustand devtools middleware] Could not parse the received json', e); + } + if (parsed !== undefined) f(parsed); +}; + +var subscribeWithSelectorImpl = function subscribeWithSelectorImpl(fn) { + return function (set, get, api) { + var origSubscribe = api.subscribe; + api.subscribe = function (selector, optListener, options) { + var listener = selector; + if (optListener) { + var equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is; + var currentSlice = selector(api.getState()); + listener = function listener(state) { + var nextSlice = selector(state); + if (!equalityFn(currentSlice, nextSlice)) { + var previousSlice = currentSlice; + optListener(currentSlice = nextSlice, previousSlice); + } + }; + if (options != null && options.fireImmediately) { + optListener(currentSlice, currentSlice); + } + } + return origSubscribe(listener); + }; + var initialState = fn(set, get, api); + return initialState; + }; +}; +var subscribeWithSelector = subscribeWithSelectorImpl; + +var combine = function combine(initialState, create) { + return function () { + return Object.assign({}, initialState, create.apply(void 0, arguments)); + }; +}; + +function createJSONStorage(getStorage, options) { + var storage; + try { + storage = getStorage(); + } catch (e) { + return; + } + var persistStorage = { + getItem: function getItem(name) { + var _getItem; + var parse = function parse(str) { + if (str === null) { + return null; + } + return JSON.parse(str, options == null ? void 0 : options.reviver); + }; + var str = (_getItem = storage.getItem(name)) != null ? _getItem : null; + if (str instanceof Promise) { + return str.then(parse); + } + return parse(str); + }, + setItem: function setItem(name, newValue) { + return storage.setItem(name, JSON.stringify(newValue, options == null ? void 0 : options.replacer)); + }, + removeItem: function removeItem(name) { + return storage.removeItem(name); + } + }; + return persistStorage; +} +var toThenable = function toThenable(fn) { + return function (input) { + try { + var result = fn(input); + if (result instanceof Promise) { + return result; + } + return { + then: function then(onFulfilled) { + return toThenable(onFulfilled)(result); + }, + catch: function _catch(_onRejected) { + return this; + } + }; + } catch (e) { + return { + then: function then(_onFulfilled) { + return this; + }, + catch: function _catch(onRejected) { + return toThenable(onRejected)(e); + } + }; + } + }; +}; +var oldImpl = function oldImpl(config, baseOptions) { + return function (set, get, api) { + var options = _extends({ + getStorage: function getStorage() { + return localStorage; + }, + serialize: JSON.stringify, + deserialize: JSON.parse, + partialize: function partialize(state) { + return state; + }, + version: 0, + merge: function merge(persistedState, currentState) { + return _extends({}, currentState, persistedState); + } + }, baseOptions); + var _hasHydrated = false; + var hydrationListeners = new Set(); + var finishHydrationListeners = new Set(); + var storage; + try { + storage = options.getStorage(); + } catch (e) {} + if (!storage) { + return config(function () { + console.warn("[zustand persist middleware] Unable to update item '" + options.name + "', the given storage is currently unavailable."); + set.apply(void 0, arguments); + }, get, api); + } + var thenableSerialize = toThenable(options.serialize); + var setItem = function setItem() { + var state = options.partialize(_extends({}, get())); + var errorInSync; + var thenable = thenableSerialize({ + state: state, + version: options.version + }).then(function (serializedValue) { + return storage.setItem(options.name, serializedValue); + }).catch(function (e) { + errorInSync = e; + }); + if (errorInSync) { + throw errorInSync; + } + return thenable; + }; + var savedSetState = api.setState; + api.setState = function (state, replace) { + savedSetState(state, replace); + void setItem(); + }; + var configResult = config(function () { + set.apply(void 0, arguments); + void setItem(); + }, get, api); + var stateFromStorage; + var hydrate = function hydrate() { + if (!storage) return; + _hasHydrated = false; + hydrationListeners.forEach(function (cb) { + return cb(get()); + }); + var postRehydrationCallback = (options.onRehydrateStorage == null ? void 0 : options.onRehydrateStorage(get())) || undefined; + return toThenable(storage.getItem.bind(storage))(options.name).then(function (storageValue) { + if (storageValue) { + return options.deserialize(storageValue); + } + }).then(function (deserializedStorageValue) { + if (deserializedStorageValue) { + if (typeof deserializedStorageValue.version === 'number' && deserializedStorageValue.version !== options.version) { + if (options.migrate) { + return options.migrate(deserializedStorageValue.state, deserializedStorageValue.version); + } + console.error("State loaded from storage couldn't be migrated since no migrate function was provided"); + } else { + return deserializedStorageValue.state; + } + } + }).then(function (migratedState) { + var _get; + stateFromStorage = options.merge(migratedState, (_get = get()) != null ? _get : configResult); + set(stateFromStorage, true); + return setItem(); + }).then(function () { + postRehydrationCallback == null || postRehydrationCallback(stateFromStorage, undefined); + _hasHydrated = true; + finishHydrationListeners.forEach(function (cb) { + return cb(stateFromStorage); + }); + }).catch(function (e) { + postRehydrationCallback == null || postRehydrationCallback(undefined, e); + }); + }; + api.persist = { + setOptions: function setOptions(newOptions) { + options = _extends({}, options, newOptions); + if (newOptions.getStorage) { + storage = newOptions.getStorage(); + } + }, + clearStorage: function clearStorage() { + var _storage; + (_storage = storage) == null || _storage.removeItem(options.name); + }, + getOptions: function getOptions() { + return options; + }, + rehydrate: function rehydrate() { + return hydrate(); + }, + hasHydrated: function hasHydrated() { + return _hasHydrated; + }, + onHydrate: function onHydrate(cb) { + hydrationListeners.add(cb); + return function () { + hydrationListeners.delete(cb); + }; + }, + onFinishHydration: function onFinishHydration(cb) { + finishHydrationListeners.add(cb); + return function () { + finishHydrationListeners.delete(cb); + }; + } + }; + hydrate(); + return stateFromStorage || configResult; + }; +}; +var newImpl = function newImpl(config, baseOptions) { + return function (set, get, api) { + var options = _extends({ + storage: createJSONStorage(function () { + return localStorage; + }), + partialize: function partialize(state) { + return state; + }, + version: 0, + merge: function merge(persistedState, currentState) { + return _extends({}, currentState, persistedState); + } + }, baseOptions); + var _hasHydrated2 = false; + var hydrationListeners = new Set(); + var finishHydrationListeners = new Set(); + var storage = options.storage; + if (!storage) { + return config(function () { + console.warn("[zustand persist middleware] Unable to update item '" + options.name + "', the given storage is currently unavailable."); + set.apply(void 0, arguments); + }, get, api); + } + var setItem = function setItem() { + var state = options.partialize(_extends({}, get())); + return storage.setItem(options.name, { + state: state, + version: options.version + }); + }; + var savedSetState = api.setState; + api.setState = function (state, replace) { + savedSetState(state, replace); + void setItem(); + }; + var configResult = config(function () { + set.apply(void 0, arguments); + void setItem(); + }, get, api); + api.getInitialState = function () { + return configResult; + }; + var stateFromStorage; + var hydrate = function hydrate() { + var _get3; + if (!storage) return; + _hasHydrated2 = false; + hydrationListeners.forEach(function (cb) { + var _get2; + return cb((_get2 = get()) != null ? _get2 : configResult); + }); + var postRehydrationCallback = (options.onRehydrateStorage == null ? void 0 : options.onRehydrateStorage((_get3 = get()) != null ? _get3 : configResult)) || undefined; + return toThenable(storage.getItem.bind(storage))(options.name).then(function (deserializedStorageValue) { + if (deserializedStorageValue) { + if (typeof deserializedStorageValue.version === 'number' && deserializedStorageValue.version !== options.version) { + if (options.migrate) { + return options.migrate(deserializedStorageValue.state, deserializedStorageValue.version); + } + console.error("State loaded from storage couldn't be migrated since no migrate function was provided"); + } else { + return deserializedStorageValue.state; + } + } + }).then(function (migratedState) { + var _get4; + stateFromStorage = options.merge(migratedState, (_get4 = get()) != null ? _get4 : configResult); + set(stateFromStorage, true); + return setItem(); + }).then(function () { + postRehydrationCallback == null || postRehydrationCallback(stateFromStorage, undefined); + stateFromStorage = get(); + _hasHydrated2 = true; + finishHydrationListeners.forEach(function (cb) { + return cb(stateFromStorage); + }); + }).catch(function (e) { + postRehydrationCallback == null || postRehydrationCallback(undefined, e); + }); + }; + api.persist = { + setOptions: function setOptions(newOptions) { + options = _extends({}, options, newOptions); + if (newOptions.storage) { + storage = newOptions.storage; + } + }, + clearStorage: function clearStorage() { + var _storage2; + (_storage2 = storage) == null || _storage2.removeItem(options.name); + }, + getOptions: function getOptions() { + return options; + }, + rehydrate: function rehydrate() { + return hydrate(); + }, + hasHydrated: function hasHydrated() { + return _hasHydrated2; + }, + onHydrate: function onHydrate(cb) { + hydrationListeners.add(cb); + return function () { + hydrationListeners.delete(cb); + }; + }, + onFinishHydration: function onFinishHydration(cb) { + finishHydrationListeners.add(cb); + return function () { + finishHydrationListeners.delete(cb); + }; + } + }; + if (!options.skipHydration) { + hydrate(); + } + return stateFromStorage || configResult; + }; +}; +var persistImpl = function persistImpl(config, baseOptions) { + if ('getStorage' in baseOptions || 'serialize' in baseOptions || 'deserialize' in baseOptions) { + if (process.env.NODE_ENV !== 'production') { + console.warn('[DEPRECATED] `getStorage`, `serialize` and `deserialize` options are deprecated. Use `storage` option instead.'); + } + return oldImpl(config, baseOptions); + } + return newImpl(config, baseOptions); +}; +var persist = persistImpl; + +exports.combine = combine; +exports.createJSONStorage = createJSONStorage; +exports.devtools = devtools; +exports.persist = persist; +exports.redux = redux; +exports.subscribeWithSelector = subscribeWithSelector; diff --git a/middleware/combine.d.ts b/middleware/combine.d.ts new file mode 100644 index 0000000..6e9d2d1 --- /dev/null +++ b/middleware/combine.d.ts @@ -0,0 +1,5 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Write = Omit & U; +type Combine = (initialState: T, additionalStateCreator: StateCreator) => StateCreator, Mps, Mcs>; +export declare const combine: Combine; +export {}; diff --git a/middleware/devtools.d.ts b/middleware/devtools.d.ts new file mode 100644 index 0000000..34489bd --- /dev/null +++ b/middleware/devtools.d.ts @@ -0,0 +1,49 @@ +import type { StateCreator, StoreApi, StoreMutatorIdentifier } from '../vanilla'; +type Config = Parameters<(Window extends { + __REDUX_DEVTOOLS_EXTENSION__?: infer T; +} ? T : { + connect: (param: any) => any; +})['connect']>[0]; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/devtools': WithDevtools; + } +} +type Cast = T extends U ? T : U; +type Write = Omit & U; +type TakeTwo = T extends { + length: 0; +} ? [undefined, undefined] : T extends { + length: 1; +} ? [...a0: Cast, a1: undefined] : T extends { + length: 0 | 1; +} ? [...a0: Cast, a1: undefined] : T extends { + length: 2; +} ? T : T extends { + length: 1 | 2; +} ? T : T extends { + length: 0 | 1 | 2; +} ? T : T extends [infer A0, infer A1, ...unknown[]] ? [A0, A1] : T extends [infer A0, (infer A1)?, ...unknown[]] ? [A0, A1?] : T extends [(infer A0)?, (infer A1)?, ...unknown[]] ? [A0?, A1?] : never; +type WithDevtools = Write>; +type StoreDevtools = S extends { + setState: (...a: infer Sa) => infer Sr; +} ? { + setState(...a: [...a: TakeTwo, action?: A]): Sr; +} : never; +export interface DevtoolsOptions extends Config { + name?: string; + enabled?: boolean; + anonymousActionType?: string; + store?: string; +} +type Devtools = (initializer: StateCreator, devtoolsOptions?: DevtoolsOptions) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/devtools': WithDevtools; + } +} +export type NamedSet = WithDevtools>['setState']; +export declare const devtools: Devtools; +export {}; diff --git a/middleware/immer.d.ts b/middleware/immer.d.ts new file mode 100644 index 0000000..e770ab3 --- /dev/null +++ b/middleware/immer.d.ts @@ -0,0 +1,25 @@ +import type { Draft } from 'immer'; +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Immer = (initializer: StateCreator) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + ['zustand/immer']: WithImmer; + } +} +type Write = Omit & U; +type SkipTwo = T extends { + length: 0; +} ? [] : T extends { + length: 1; +} ? [] : T extends { + length: 0 | 1; +} ? [] : T extends [unknown, unknown, ...infer A] ? A : T extends [unknown, unknown?, ...infer A] ? A : T extends [unknown?, unknown?, ...infer A] ? A : never; +type WithImmer = Write>; +type StoreImmer = S extends { + getState: () => infer T; + setState: infer SetState; +} ? SetState extends (...a: infer A) => infer Sr ? { + setState(nextStateOrUpdater: T | Partial | ((state: Draft) => void), shouldReplace?: boolean | undefined, ...a: SkipTwo): Sr; +} : never : never; +export declare const immer: Immer; +export {}; diff --git a/middleware/immer.js b/middleware/immer.js new file mode 100644 index 0000000..d01990c --- /dev/null +++ b/middleware/immer.js @@ -0,0 +1,19 @@ +'use strict'; + +var immer$1 = require('immer'); + +var immerImpl = function immerImpl(initializer) { + return function (set, get, store) { + store.setState = function (updater, replace) { + var nextState = typeof updater === 'function' ? immer$1.produce(updater) : updater; + for (var _len = arguments.length, a = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + a[_key - 2] = arguments[_key]; + } + return set.apply(void 0, [nextState, replace].concat(a)); + }; + return initializer(store.setState, get, store); + }; +}; +var immer = immerImpl; + +exports.immer = immer; diff --git a/middleware/persist.d.ts b/middleware/persist.d.ts new file mode 100644 index 0000000..3cb38bd --- /dev/null +++ b/middleware/persist.d.ts @@ -0,0 +1,119 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +export interface StateStorage { + getItem: (name: string) => string | null | Promise; + setItem: (name: string, value: string) => unknown | Promise; + removeItem: (name: string) => unknown | Promise; +} +export type StorageValue = { + state: S; + version?: number; +}; +export interface PersistStorage { + getItem: (name: string) => StorageValue | null | Promise | null>; + setItem: (name: string, value: StorageValue) => unknown | Promise; + removeItem: (name: string) => unknown | Promise; +} +type JsonStorageOptions = { + reviver?: (key: string, value: unknown) => unknown; + replacer?: (key: string, value: unknown) => unknown; +}; +export declare function createJSONStorage(getStorage: () => StateStorage, options?: JsonStorageOptions): PersistStorage | undefined; +export interface PersistOptions { + /** Name of the storage (must be unique) */ + name: string; + /** + * @deprecated Use `storage` instead. + * A function returning a storage. + * The storage must fit `window.localStorage`'s api (or an async version of it). + * For example the storage could be `AsyncStorage` from React Native. + * + * @default () => localStorage + */ + getStorage?: () => StateStorage; + /** + * @deprecated Use `storage` instead. + * Use a custom serializer. + * The returned string will be stored in the storage. + * + * @default JSON.stringify + */ + serialize?: (state: StorageValue) => string | Promise; + /** + * @deprecated Use `storage` instead. + * Use a custom deserializer. + * Must return an object matching StorageValue + * + * @param str The storage's current value. + * @default JSON.parse + */ + deserialize?: (str: string) => StorageValue | Promise>; + /** + * Use a custom persist storage. + * + * Combining `createJSONStorage` helps creating a persist storage + * with JSON.parse and JSON.stringify. + * + * @default createJSONStorage(() => localStorage) + */ + storage?: PersistStorage | undefined; + /** + * Filter the persisted value. + * + * @params state The state's value + */ + partialize?: (state: S) => PersistedState; + /** + * A function returning another (optional) function. + * The main function will be called before the state rehydration. + * The returned function will be called after the state rehydration or when an error occurred. + */ + onRehydrateStorage?: (state: S) => ((state?: S, error?: unknown) => void) | void; + /** + * If the stored state's version mismatch the one specified here, the storage will not be used. + * This is useful when adding a breaking change to your store. + */ + version?: number; + /** + * A function to perform persisted state migration. + * This function will be called when persisted state versions mismatch with the one specified here. + */ + migrate?: (persistedState: unknown, version: number) => PersistedState | Promise; + /** + * A function to perform custom hydration merges when combining the stored state with the current one. + * By default, this function does a shallow merge. + */ + merge?: (persistedState: unknown, currentState: S) => S; + /** + * An optional boolean that will prevent the persist middleware from triggering hydration on initialization, + * This allows you to call `rehydrate()` at a specific point in your apps rendering life-cycle. + * + * This is useful in SSR application. + * + * @default false + */ + skipHydration?: boolean; +} +type PersistListener = (state: S) => void; +type StorePersist = { + persist: { + setOptions: (options: Partial>) => void; + clearStorage: () => void; + rehydrate: () => Promise | void; + hasHydrated: () => boolean; + onHydrate: (fn: PersistListener) => () => void; + onFinishHydration: (fn: PersistListener) => () => void; + getOptions: () => Partial>; + }; +}; +type Persist = (initializer: StateCreator, options: PersistOptions) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/persist': WithPersist; + } +} +type Write = Omit & U; +type WithPersist = S extends { + getState: () => infer T; +} ? Write> : never; +export declare const persist: Persist; +export {}; diff --git a/middleware/redux.d.ts b/middleware/redux.d.ts new file mode 100644 index 0000000..41a61e1 --- /dev/null +++ b/middleware/redux.d.ts @@ -0,0 +1,21 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Write = Omit & U; +type Action = { + type: string; +}; +type StoreRedux = { + dispatch: (a: A) => A; + dispatchFromDevtools: true; +}; +type ReduxState = { + dispatch: StoreRedux['dispatch']; +}; +type WithRedux = Write>; +type Redux = (reducer: (state: T, action: A) => T, initialState: T) => StateCreator>, Cms, [['zustand/redux', A]]>; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/redux': WithRedux; + } +} +export declare const redux: Redux; +export {}; diff --git a/middleware/subscribeWithSelector.d.ts b/middleware/subscribeWithSelector.d.ts new file mode 100644 index 0000000..3243f26 --- /dev/null +++ b/middleware/subscribeWithSelector.d.ts @@ -0,0 +1,25 @@ +import type { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type SubscribeWithSelector = (initializer: StateCreator) => StateCreator; +type Write = Omit & U; +type WithSelectorSubscribe = S extends { + getState: () => infer T; +} ? Write> : never; +declare module '../vanilla' { + interface StoreMutators { + ['zustand/subscribeWithSelector']: WithSelectorSubscribe; + } +} +type StoreSubscribeWithSelector = { + subscribe: { + (listener: (selectedState: T, previousSelectedState: T) => void): () => void; + (selector: (state: T) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: { + equalityFn?: (a: U, b: U) => boolean; + fireImmediately?: boolean; + }): () => void; + }; +}; +export declare const subscribeWithSelector: SubscribeWithSelector; +export {}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..fde7ad3 --- /dev/null +++ b/package.json @@ -0,0 +1,196 @@ +{ + "name": "zustand", + "private": false, + "version": "4.5.2", + "description": "🐻 Bear necessities for state management in React", + "main": "./index.js", + "types": "./index.d.ts", + "typesVersions": { + "<4.0": { + "esm/*": [ + "ts3.4/*" + ], + "*": [ + "ts3.4/*" + ] + } + }, + "files": [ + "**" + ], + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "types": "./esm/index.d.mts", + "default": "./esm/index.mjs" + }, + "module": { + "types": "./esm/index.d.ts", + "default": "./esm/index.js" + }, + "default": { + "types": "./index.d.ts", + "default": "./index.js" + } + }, + "./vanilla": { + "import": { + "types": "./esm/vanilla.d.mts", + "default": "./esm/vanilla.mjs" + }, + "module": { + "types": "./esm/vanilla.d.ts", + "default": "./esm/vanilla.js" + }, + "default": { + "types": "./vanilla.d.ts", + "default": "./vanilla.js" + } + }, + "./middleware": { + "import": { + "types": "./esm/middleware.d.mts", + "default": "./esm/middleware.mjs" + }, + "module": { + "types": "./esm/middleware.d.ts", + "default": "./esm/middleware.js" + }, + "default": { + "types": "./middleware.d.ts", + "default": "./middleware.js" + } + }, + "./middleware/immer": { + "import": { + "types": "./esm/middleware/immer.d.mts", + "default": "./esm/middleware/immer.mjs" + }, + "module": { + "types": "./esm/middleware/immer.d.ts", + "default": "./esm/middleware/immer.js" + }, + "default": { + "types": "./middleware/immer.d.ts", + "default": "./middleware/immer.js" + } + }, + "./shallow": { + "import": { + "types": "./esm/shallow.d.mts", + "default": "./esm/shallow.mjs" + }, + "module": { + "types": "./esm/shallow.d.ts", + "default": "./esm/shallow.js" + }, + "default": { + "types": "./shallow.d.ts", + "default": "./shallow.js" + } + }, + "./vanilla/shallow": { + "import": { + "types": "./esm/vanilla/shallow.d.mts", + "default": "./esm/vanilla/shallow.mjs" + }, + "module": { + "types": "./esm/vanilla/shallow.d.ts", + "default": "./esm/vanilla/shallow.js" + }, + "default": { + "types": "./vanilla/shallow.d.ts", + "default": "./vanilla/shallow.js" + } + }, + "./react/shallow": { + "import": { + "types": "./esm/react/shallow.d.mts", + "default": "./esm/react/shallow.mjs" + }, + "module": { + "types": "./esm/react/shallow.d.ts", + "default": "./esm/react/shallow.js" + }, + "default": { + "types": "./react/shallow.d.ts", + "default": "./react/shallow.js" + } + }, + "./traditional": { + "import": { + "types": "./esm/traditional.d.mts", + "default": "./esm/traditional.mjs" + }, + "module": { + "types": "./esm/traditional.d.ts", + "default": "./esm/traditional.js" + }, + "default": { + "types": "./traditional.d.ts", + "default": "./traditional.js" + } + }, + "./context": { + "import": { + "types": "./esm/context.d.mts", + "default": "./esm/context.mjs" + }, + "module": { + "types": "./esm/context.d.ts", + "default": "./esm/context.js" + }, + "default": { + "types": "./context.d.ts", + "default": "./context.js" + } + } + }, + "sideEffects": false, + "engines": { + "node": ">=12.7.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/pmndrs/zustand.git" + }, + "keywords": [ + "react", + "state", + "manager", + "management", + "redux", + "store" + ], + "author": "Paul Henschel", + "contributors": [ + "Jeremy Holcomb (https://github.com/JeremyRH)", + "Daishi Kato (https://github.com/dai-shi)" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/pmndrs/zustand/issues" + }, + "homepage": "https://github.com/pmndrs/zustand", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + }, + "packageManager": "yarn@1.22.21+sha256.dbed5b7e10c552ba0e1a545c948d5473bc6c5a28ce22a8fd27e493e3e5eb6370" +} diff --git a/react.d.ts b/react.d.ts new file mode 100644 index 0000000..21608cc --- /dev/null +++ b/react.d.ts @@ -0,0 +1,38 @@ +import type { Mutate, StateCreator, StoreApi, StoreMutatorIdentifier } from './vanilla'; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type ReadonlyStoreApi = Pick, 'getState' | 'subscribe'>; +type WithReact> = S & { + /** @deprecated please use api.getInitialState() */ + getServerState?: () => ExtractState; +}; +export declare function useStore>>(api: S): ExtractState; +export declare function useStore>, U>(api: S, selector: (state: ExtractState) => U): U; +/** + * @deprecated The usage with three arguments is deprecated. Use `useStoreWithEqualityFn` from 'zustand/traditional'. The usage with one or two arguments is not deprecated. + * https://github.com/pmndrs/zustand/discussions/1937 + */ +export declare function useStore>, U>(api: S, selector: (state: ExtractState) => U, equalityFn: ((a: U, b: U) => boolean) | undefined): U; +export type UseBoundStore>> = { + (): ExtractState; + (selector: (state: ExtractState) => U): U; + /** + * @deprecated Use `createWithEqualityFn` from 'zustand/traditional' + */ + (selector: (state: ExtractState) => U, equalityFn: (a: U, b: U) => boolean): U; +} & S; +type Create = { + (initializer: StateCreator): UseBoundStore, Mos>>; + (): (initializer: StateCreator) => UseBoundStore, Mos>>; + /** + * @deprecated Use `useStore` hook to bind store + */ + >(store: S): UseBoundStore; +}; +export declare const create: Create; +/** + * @deprecated Use `import { create } from 'zustand'` + */ +declare const _default: Create; +export default _default; diff --git a/react/shallow.d.ts b/react/shallow.d.ts new file mode 100644 index 0000000..9068d46 --- /dev/null +++ b/react/shallow.d.ts @@ -0,0 +1 @@ +export declare function useShallow(selector: (state: S) => U): (state: S) => U; diff --git a/react/shallow.js b/react/shallow.js new file mode 100644 index 0000000..7d4224f --- /dev/null +++ b/react/shallow.js @@ -0,0 +1,88 @@ +'use strict'; + +var ReactExports = require('react'); + +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); +} +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + return arr2; +} +function _createForOfIteratorHelperLoose(o, allowArrayLike) { + var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; + if (it) return (it = it.call(o)).next.bind(it); + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }; + } + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} + +function shallow(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) return false; + for (var _iterator = _createForOfIteratorHelperLoose(objA), _step; !(_step = _iterator()).done;) { + var _step$value = _step.value, + key = _step$value[0], + value = _step$value[1]; + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) return false; + for (var _iterator2 = _createForOfIteratorHelperLoose(objA), _step2; !(_step2 = _iterator2()).done;) { + var _value = _step2.value; + if (!objB.has(_value)) { + return false; + } + } + return true; + } + var keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (var _i = 0, _keysA = keysA; _i < _keysA.length; _i++) { + var keyA = _keysA[_i]; + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; +} + +var useRef = ReactExports.useRef; +function useShallow(selector) { + var prev = useRef(); + return function (state) { + var next = selector(state); + return shallow(prev.current, next) ? prev.current : prev.current = next; + }; +} + +exports.useShallow = useShallow; diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..3ee0ee3 --- /dev/null +++ b/readme.md @@ -0,0 +1,505 @@ +

+ +

+ +[![Build Status](https://img.shields.io/github/actions/workflow/status/pmndrs/zustand/lint-and-type.yml?branch=main&style=flat&colorA=000000&colorB=000000)](https://github.com/pmndrs/zustand/actions?query=workflow%3ALint) +[![Build Size](https://img.shields.io/bundlephobia/minzip/zustand?label=bundle%20size&style=flat&colorA=000000&colorB=000000)](https://bundlephobia.com/result?p=zustand) +[![Version](https://img.shields.io/npm/v/zustand?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/zustand) +[![Downloads](https://img.shields.io/npm/dt/zustand.svg?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/zustand) +[![Discord Shield](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=ffffff)](https://discord.gg/poimandres) + +A small, fast and scalable bearbones state-management solution using simplified flux principles. Has a comfy API based on hooks, isn't boilerplatey or opinionated. + +Don't disregard it because it's cute. It has quite the claws, lots of time was spent dealing with common pitfalls, like the dreaded [zombie child problem](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children), [react concurrency](https://github.com/bvaughn/rfcs/blob/useMutableSource/text/0000-use-mutable-source.md), and [context loss](https://github.com/facebook/react/issues/13332) between mixed renderers. It may be the one state-manager in the React space that gets all of these right. + +You can try a live demo [here](https://githubbox.com/pmndrs/zustand/tree/main/examples/demo). + +```bash +npm install zustand # or yarn add zustand or pnpm add zustand +``` + +:warning: This readme is written for JavaScript users. If you are a TypeScript user, be sure to check out our [TypeScript Usage section](#typescript-usage). + +## First create a store + +Your store is a hook! You can put anything in it: primitives, objects, functions. State has to be updated immutably and the `set` function [merges state](./docs/guides/immutable-state-and-merging.md) to help it. + +```jsx +import { create } from 'zustand' + +const useBearStore = create((set) => ({ + bears: 0, + increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), + removeAllBears: () => set({ bears: 0 }), +})) +``` + +## Then bind your components, and that's it! + +Use the hook anywhere, no providers are needed. Select your state and the component will re-render on changes. + +```jsx +function BearCounter() { + const bears = useBearStore((state) => state.bears) + return

{bears} around here ...

+} + +function Controls() { + const increasePopulation = useBearStore((state) => state.increasePopulation) + return +} +``` + +### Why zustand over redux? + +- Simple and un-opinionated +- Makes hooks the primary means of consuming state +- Doesn't wrap your app in context providers +- [Can inform components transiently (without causing render)](#transient-updates-for-often-occurring-state-changes) + +### Why zustand over context? + +- Less boilerplate +- Renders components only on changes +- Centralized, action-based state management + +--- + +# Recipes + +## Fetching everything + +You can, but bear in mind that it will cause the component to update on every state change! + +```jsx +const state = useBearStore() +``` + +## Selecting multiple state slices + +It detects changes with strict-equality (old === new) by default, this is efficient for atomic state picks. + +```jsx +const nuts = useBearStore((state) => state.nuts) +const honey = useBearStore((state) => state.honey) +``` + +If you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can use [useShallow](./docs/guides/prevent-rerenders-with-use-shallow.md) to prevent unnecessary rerenders when the selector output does not change according to shallow equal. + +```jsx +import { create } from 'zustand' +import { useShallow } from 'zustand/react/shallow' + +const useBearStore = create((set) => ({ + bears: 0, + increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), + removeAllBears: () => set({ bears: 0 }), +})) + +// Object pick, re-renders the component when either state.nuts or state.honey change +const { nuts, honey } = useBearStore( + useShallow((state) => ({ nuts: state.nuts, honey: state.honey })), +) + +// Array pick, re-renders the component when either state.nuts or state.honey change +const [nuts, honey] = useBearStore( + useShallow((state) => [state.nuts, state.honey]), +) + +// Mapped picks, re-renders the component when state.treats changes in order, count or keys +const treats = useBearStore(useShallow((state) => Object.keys(state.treats))) +``` + +For more control over re-rendering, you may provide any custom equality function. + +```jsx +const treats = useBearStore( + (state) => state.treats, + (oldTreats, newTreats) => compare(oldTreats, newTreats), +) +``` + +## Overwriting state + +The `set` function has a second argument, `false` by default. Instead of merging, it will replace the state model. Be careful not to wipe out parts you rely on, like actions. + +```jsx +import omit from 'lodash-es/omit' + +const useFishStore = create((set) => ({ + salmon: 1, + tuna: 2, + deleteEverything: () => set({}, true), // clears the entire store, actions included + deleteTuna: () => set((state) => omit(state, ['tuna']), true), +})) +``` + +## Async actions + +Just call `set` when you're ready, zustand doesn't care if your actions are async or not. + +```jsx +const useFishStore = create((set) => ({ + fishies: {}, + fetch: async (pond) => { + const response = await fetch(pond) + set({ fishies: await response.json() }) + }, +})) +``` + +## Read from state in actions + +`set` allows fn-updates `set(state => result)`, but you still have access to state outside of it through `get`. + +```jsx +const useSoundStore = create((set, get) => ({ + sound: 'grunt', + action: () => { + const sound = get().sound + ... +``` + +## Reading/writing state and reacting to changes outside of components + +Sometimes you need to access state in a non-reactive way or act upon the store. For these cases, the resulting hook has utility functions attached to its prototype. + +:warning: This technique is not recommended for adding state in [React Server Components](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md) (typically in Next.js 13 and above). It can lead to unexpected bugs and privacy issues for your users. For more details, see [#2200](https://github.com/pmndrs/zustand/discussions/2200). + +```jsx +const useDogStore = create(() => ({ paw: true, snout: true, fur: true })) + +// Getting non-reactive fresh state +const paw = useDogStore.getState().paw +// Listening to all changes, fires synchronously on every change +const unsub1 = useDogStore.subscribe(console.log) +// Updating state, will trigger listeners +useDogStore.setState({ paw: false }) +// Unsubscribe listeners +unsub1() + +// You can of course use the hook as you always would +function Component() { + const paw = useDogStore((state) => state.paw) + ... +``` + +### Using subscribe with selector + +If you need to subscribe with a selector, +`subscribeWithSelector` middleware will help. + +With this middleware `subscribe` accepts an additional signature: + +```ts +subscribe(selector, callback, options?: { equalityFn, fireImmediately }): Unsubscribe +``` + +```js +import { subscribeWithSelector } from 'zustand/middleware' +const useDogStore = create( + subscribeWithSelector(() => ({ paw: true, snout: true, fur: true })), +) + +// Listening to selected changes, in this case when "paw" changes +const unsub2 = useDogStore.subscribe((state) => state.paw, console.log) +// Subscribe also exposes the previous value +const unsub3 = useDogStore.subscribe( + (state) => state.paw, + (paw, previousPaw) => console.log(paw, previousPaw), +) +// Subscribe also supports an optional equality function +const unsub4 = useDogStore.subscribe( + (state) => [state.paw, state.fur], + console.log, + { equalityFn: shallow }, +) +// Subscribe and fire immediately +const unsub5 = useDogStore.subscribe((state) => state.paw, console.log, { + fireImmediately: true, +}) +``` + +## Using zustand without React + +Zustand core can be imported and used without the React dependency. The only difference is that the create function does not return a hook, but the API utilities. + +```jsx +import { createStore } from 'zustand/vanilla' + +const store = createStore((set) => ...) +const { getState, setState, subscribe, getInitialState } = store + +export default store +``` + +You can use a vanilla store with `useStore` hook available since v4. + +```jsx +import { useStore } from 'zustand' +import { vanillaStore } from './vanillaStore' + +const useBoundStore = (selector) => useStore(vanillaStore, selector) +``` + +:warning: Note that middlewares that modify `set` or `get` are not applied to `getState` and `setState`. + +## Transient updates (for often occurring state-changes) + +The subscribe function allows components to bind to a state-portion without forcing re-render on changes. Best combine it with useEffect for automatic unsubscribe on unmount. This can make a [drastic](https://codesandbox.io/s/peaceful-johnson-txtws) performance impact when you are allowed to mutate the view directly. + +```jsx +const useScratchStore = create((set) => ({ scratches: 0, ... })) + +const Component = () => { + // Fetch initial state + const scratchRef = useRef(useScratchStore.getState().scratches) + // Connect to the store on mount, disconnect on unmount, catch state-changes in a reference + useEffect(() => useScratchStore.subscribe( + state => (scratchRef.current = state.scratches) + ), []) + ... +``` + +## Sick of reducers and changing nested states? Use Immer! + +Reducing nested structures is tiresome. Have you tried [immer](https://github.com/mweststrate/immer)? + +```jsx +import { produce } from 'immer' + +const useLushStore = create((set) => ({ + lush: { forest: { contains: { a: 'bear' } } }, + clearForest: () => + set( + produce((state) => { + state.lush.forest.contains = null + }), + ), +})) + +const clearForest = useLushStore((state) => state.clearForest) +clearForest() +``` + +[Alternatively, there are some other solutions.](./docs/guides/updating-state.md#with-immer) + +## Persist middleware + +You can persist your store's data using any kind of storage. + +```jsx +import { create } from 'zustand' +import { persist, createJSONStorage } from 'zustand/middleware' + +const useFishStore = create( + persist( + (set, get) => ({ + fishes: 0, + addAFish: () => set({ fishes: get().fishes + 1 }), + }), + { + name: 'food-storage', // name of the item in the storage (must be unique) + storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used + }, + ), +) +``` + +[See the full documentation for this middleware.](./docs/integrations/persisting-store-data.md) + +## Immer middleware + +Immer is available as middleware too. + +```jsx +import { create } from 'zustand' +import { immer } from 'zustand/middleware/immer' + +const useBeeStore = create( + immer((set) => ({ + bees: 0, + addBees: (by) => + set((state) => { + state.bees += by + }), + })), +) +``` + +## Can't live without redux-like reducers and action types? + +```jsx +const types = { increase: 'INCREASE', decrease: 'DECREASE' } + +const reducer = (state, { type, by = 1 }) => { + switch (type) { + case types.increase: + return { grumpiness: state.grumpiness + by } + case types.decrease: + return { grumpiness: state.grumpiness - by } + } +} + +const useGrumpyStore = create((set) => ({ + grumpiness: 0, + dispatch: (args) => set((state) => reducer(state, args)), +})) + +const dispatch = useGrumpyStore((state) => state.dispatch) +dispatch({ type: types.increase, by: 2 }) +``` + +Or, just use our redux-middleware. It wires up your main-reducer, sets the initial state, and adds a dispatch function to the state itself and the vanilla API. + +```jsx +import { redux } from 'zustand/middleware' + +const useGrumpyStore = create(redux(reducer, initialState)) +``` + +## Redux devtools + +```jsx +import { devtools } from 'zustand/middleware' + +// Usage with a plain action store, it will log actions as "setState" +const usePlainStore = create(devtools((set) => ...)) +// Usage with a redux store, it will log full action types +const useReduxStore = create(devtools(redux(reducer, initialState))) +``` + +One redux devtools connection for multiple stores + +```jsx +import { devtools } from 'zustand/middleware' + +// Usage with a plain action store, it will log actions as "setState" +const usePlainStore1 = create(devtools((set) => ..., { name, store: storeName1 })) +const usePlainStore2 = create(devtools((set) => ..., { name, store: storeName2 })) +// Usage with a redux store, it will log full action types +const useReduxStore = create(devtools(redux(reducer, initialState)), , { name, store: storeName3 }) +const useReduxStore = create(devtools(redux(reducer, initialState)), , { name, store: storeName4 }) +``` + +Assigning different connection names will separate stores in redux devtools. This also helps group different stores into separate redux devtools connections. + +devtools takes the store function as its first argument, optionally you can name the store or configure [serialize](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#serialize) options with a second argument. + +Name store: `devtools(..., {name: "MyStore"})`, which will create a separate instance named "MyStore" in the devtools. + +Serialize options: `devtools(..., { serialize: { options: true } })`. + +#### Logging Actions + +devtools will only log actions from each separated store unlike in a typical _combined reducers_ redux store. See an approach to combining stores https://github.com/pmndrs/zustand/issues/163 + +You can log a specific action type for each `set` function by passing a third parameter: + +```jsx +const useBearStore = create(devtools((set) => ({ + ... + eatFish: () => set( + (prev) => ({ fishes: prev.fishes > 1 ? prev.fishes - 1 : 0 }), + false, + 'bear/eatFish' + ), + ... +``` + +You can also log the action's type along with its payload: + +```jsx + ... + addFishes: (count) => set( + (prev) => ({ fishes: prev.fishes + count }), + false, + { type: 'bear/addFishes', count, } + ), + ... +``` + +If an action type is not provided, it is defaulted to "anonymous". You can customize this default value by providing an `anonymousActionType` parameter: + +```jsx +devtools(..., { anonymousActionType: 'unknown', ... }) +``` + +If you wish to disable devtools (on production for instance). You can customize this setting by providing the `enabled` parameter: + +```jsx +devtools(..., { enabled: false, ... }) +``` + +## React context + +The store created with `create` doesn't require context providers. In some cases, you may want to use contexts for dependency injection or if you want to initialize your store with props from a component. Because the normal store is a hook, passing it as a normal context value may violate the rules of hooks. + +The recommended method available since v4 is to use the vanilla store. + +```jsx +import { createContext, useContext } from 'react' +import { createStore, useStore } from 'zustand' + +const store = createStore(...) // vanilla store without hooks + +const StoreContext = createContext() + +const App = () => ( + + ... + +) + +const Component = () => { + const store = useContext(StoreContext) + const slice = useStore(store, selector) + ... +``` + +## TypeScript Usage + +Basic typescript usage doesn't require anything special except for writing `create()(...)` instead of `create(...)`... + +```ts +import { create } from 'zustand' +import { devtools, persist } from 'zustand/middleware' +import type {} from '@redux-devtools/extension' // required for devtools typing + +interface BearState { + bears: number + increase: (by: number) => void +} + +const useBearStore = create()( + devtools( + persist( + (set) => ({ + bears: 0, + increase: (by) => set((state) => ({ bears: state.bears + by })), + }), + { + name: 'bear-storage', + }, + ), + ), +) +``` + +A more complete TypeScript guide is [here](docs/guides/typescript.md). + +## Best practices + +- You may wonder how to organize your code for better maintenance: [Splitting the store into separate slices](./docs/guides/slices-pattern.md). +- Recommended usage for this unopinionated library: [Flux inspired practice](./docs/guides/flux-inspired-practice.md). +- [Calling actions outside a React event handler in pre-React 18](./docs/guides/event-handler-in-pre-react-18.md). +- [Testing](./docs/guides/testing.md) +- For more, have a look [in the docs folder](./docs/) + +## Third-Party Libraries + +Some users may want to extend Zustand's feature set which can be done using third-party libraries made by the community. For information regarding third-party libraries with Zustand, visit [the doc](./docs/integrations/third-party-libraries.md). + +## Comparison with other libraries + +- [Difference between zustand and other state management libraries for React](https://docs.pmnd.rs/zustand/getting-started/comparison) diff --git a/shallow.d.ts b/shallow.d.ts new file mode 100644 index 0000000..0914a7c --- /dev/null +++ b/shallow.d.ts @@ -0,0 +1,7 @@ +import { shallow } from './vanilla/shallow'; +/** + * @deprecated Use `import { shallow } from 'zustand/shallow'` + */ +declare const _default: typeof shallow; +export default _default; +export { shallow }; diff --git a/shallow.js b/shallow.js new file mode 100644 index 0000000..220b4dc --- /dev/null +++ b/shallow.js @@ -0,0 +1,89 @@ +'use strict'; + +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); +} +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + return arr2; +} +function _createForOfIteratorHelperLoose(o, allowArrayLike) { + var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; + if (it) return (it = it.call(o)).next.bind(it); + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }; + } + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} + +function shallow$1(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) return false; + for (var _iterator = _createForOfIteratorHelperLoose(objA), _step; !(_step = _iterator()).done;) { + var _step$value = _step.value, + key = _step$value[0], + value = _step$value[1]; + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) return false; + for (var _iterator2 = _createForOfIteratorHelperLoose(objA), _step2; !(_step2 = _iterator2()).done;) { + var _value = _step2.value; + if (!objB.has(_value)) { + return false; + } + } + return true; + } + var keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (var _i = 0, _keysA = keysA; _i < _keysA.length; _i++) { + var keyA = _keysA[_i]; + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; +} + +var shallow = (function (objA, objB) { + if (process.env.NODE_ENV !== 'production') { + console.warn("[DEPRECATED] Default export is deprecated. Instead use `import { shallow } from 'zustand/shallow'`."); + } + return shallow$1(objA, objB); +}); + +exports.default = shallow; +exports.shallow = shallow$1; + +module.exports = shallow; +module.exports.shallow = shallow$1; +exports.default = module.exports; diff --git a/system/context.development.js b/system/context.development.js new file mode 100644 index 0000000..c29fb68 --- /dev/null +++ b/system/context.development.js @@ -0,0 +1,73 @@ +System.register(['react', 'zustand/traditional'], (function (exports) { + 'use strict'; + var ReactExports, useStoreWithEqualityFn; + return { + setters: [function (module) { + ReactExports = module.default; + }, function (module) { + useStoreWithEqualityFn = module.useStoreWithEqualityFn; + }], + execute: (function () { + + exports("default", createContext); + + const { + createElement, + createContext: reactCreateContext, + useContext, + useMemo, + useRef + } = ReactExports; + function createContext() { + { + console.warn( + "[DEPRECATED] `context` will be removed in a future version. Instead use `import { createStore, useStore } from 'zustand'`. See: https://github.com/pmndrs/zustand/discussions/1180." + ); + } + const ZustandContext = reactCreateContext(void 0); + const Provider = ({ + createStore, + children + }) => { + const storeRef = useRef(); + if (!storeRef.current) { + storeRef.current = createStore(); + } + return createElement( + ZustandContext.Provider, + { value: storeRef.current }, + children + ); + }; + const useContextStore = (selector, equalityFn) => { + const store = useContext(ZustandContext); + if (!store) { + throw new Error( + "Seems like you have not used zustand provider as an ancestor." + ); + } + return useStoreWithEqualityFn( + store, + selector, + equalityFn + ); + }; + const useStoreApi = () => { + const store = useContext(ZustandContext); + if (!store) { + throw new Error( + "Seems like you have not used zustand provider as an ancestor." + ); + } + return useMemo(() => ({ ...store }), [store]); + }; + return { + Provider, + useStore: useContextStore, + useStoreApi + }; + } + + }) + }; +})); diff --git a/system/context.production.js b/system/context.production.js new file mode 100644 index 0000000..63a6c08 --- /dev/null +++ b/system/context.production.js @@ -0,0 +1 @@ +System.register(["react","zustand/traditional"],function(a){"use strict";var o,s;return{setters:[function(r){o=r.default},function(r){s=r.useStoreWithEqualityFn}],execute:function(){a("default",l);const{createElement:r,createContext:c,useContext:i,useMemo:d,useRef:f}=o;function l(){const n=c(void 0);return{Provider:({createStore:e,children:u})=>{const t=f();return t.current||(t.current=e()),r(n.Provider,{value:t.current},u)},useStore:(e,u)=>{const t=i(n);if(!t)throw new Error("Seems like you have not used zustand provider as an ancestor.");return s(t,e,u)},useStoreApi:()=>{const e=i(n);if(!e)throw new Error("Seems like you have not used zustand provider as an ancestor.");return d(()=>({...e}),[e])}}}}}}); diff --git a/system/index.development.js b/system/index.development.js new file mode 100644 index 0000000..24e4a49 --- /dev/null +++ b/system/index.development.js @@ -0,0 +1,71 @@ +System.register(['zustand/vanilla', 'react', 'use-sync-external-store/shim/with-selector'], (function (exports) { + 'use strict'; + var _starExcludes = { + __proto__: null, + create: 1, + default: 1, + useStore: 1 + }; + var createStore, ReactExports, useSyncExternalStoreExports; + return { + setters: [function (module) { + createStore = module.createStore; + var setter = { __proto__: null }; + for (var name in module) { + if (!_starExcludes[name]) setter[name] = module[name]; + } + exports(setter); + }, function (module) { + ReactExports = module.default; + }, function (module) { + useSyncExternalStoreExports = module.default; + }], + execute: (function () { + + exports("useStore", useStore); + + const { useDebugValue } = ReactExports; + const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports; + let didWarnAboutEqualityFn = false; + const identity = (arg) => arg; + function useStore(api, selector = identity, equalityFn) { + if (equalityFn && !didWarnAboutEqualityFn) { + console.warn( + "[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937" + ); + didWarnAboutEqualityFn = true; + } + const slice = useSyncExternalStoreWithSelector( + api.subscribe, + api.getState, + api.getServerState || api.getInitialState, + selector, + equalityFn + ); + useDebugValue(slice); + return slice; + } + const createImpl = (createState) => { + if (typeof createState !== "function") { + console.warn( + "[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`." + ); + } + const api = typeof createState === "function" ? createStore(createState) : createState; + const useBoundStore = (selector, equalityFn) => useStore(api, selector, equalityFn); + Object.assign(useBoundStore, api); + return useBoundStore; + }; + const create = exports("create", (createState) => createState ? createImpl(createState) : createImpl); + var react = exports("default", (createState) => { + { + console.warn( + "[DEPRECATED] Default export is deprecated. Instead use `import { create } from 'zustand'`." + ); + } + return create(createState); + }); + + }) + }; +})); diff --git a/system/index.production.js b/system/index.production.js new file mode 100644 index 0000000..43dd7e7 --- /dev/null +++ b/system/index.production.js @@ -0,0 +1 @@ +System.register(["zustand/vanilla","react","use-sync-external-store/shim/with-selector"],function(n){"use strict";var _={__proto__:null,create:1,default:1,useStore:1},o,i,l;return{setters:[function(t){o=t.createStore;var c={__proto__:null};for(var r in t)_[r]||(c[r]=t[r]);n(c)},function(t){i=t.default},function(t){l=t.default}],execute:function(){n("useStore",f);const{useDebugValue:t}=i,{useSyncExternalStoreWithSelector:c}=l,r=e=>e;function f(e,s=r,u){const a=c(e.subscribe,e.getState,e.getServerState||e.getInitialState,s,u);return t(a),a}const S=e=>{const s=typeof e=="function"?o(e):e,u=(a,g)=>f(s,a,g);return Object.assign(u,s),u},v=n("create",e=>e?S(e):S);var d=n("default",e=>v(e))}}}); diff --git a/system/middleware.development.js b/system/middleware.development.js new file mode 100644 index 0000000..d53a440 --- /dev/null +++ b/system/middleware.development.js @@ -0,0 +1,593 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + exports("createJSONStorage", createJSONStorage); + + const reduxImpl = (reducer, initial) => (set, _get, api) => { + api.dispatch = (action) => { + set((state) => reducer(state, action), false, action); + return action; + }; + api.dispatchFromDevtools = true; + return { dispatch: (...a) => api.dispatch(...a), ...initial }; + }; + const redux = exports("redux", reduxImpl); + + const trackedConnections = /* @__PURE__ */ new Map(); + const getTrackedConnectionState = (name) => { + const api = trackedConnections.get(name); + if (!api) + return {}; + return Object.fromEntries( + Object.entries(api.stores).map(([key, api2]) => [key, api2.getState()]) + ); + }; + const extractConnectionInformation = (store, extensionConnector, options) => { + if (store === void 0) { + return { + type: "untracked", + connection: extensionConnector.connect(options) + }; + } + const existingConnection = trackedConnections.get(options.name); + if (existingConnection) { + return { type: "tracked", store, ...existingConnection }; + } + const newConnection = { + connection: extensionConnector.connect(options), + stores: {} + }; + trackedConnections.set(options.name, newConnection); + return { type: "tracked", store, ...newConnection }; + }; + const devtoolsImpl = (fn, devtoolsOptions = {}) => (set, get, api) => { + const { enabled, anonymousActionType, store, ...options } = devtoolsOptions; + let extensionConnector; + try { + extensionConnector = (enabled != null ? enabled : true) && window.__REDUX_DEVTOOLS_EXTENSION__; + } catch (e) { + } + if (!extensionConnector) { + if (enabled) { + console.warn( + "[zustand devtools middleware] Please install/enable Redux devtools extension" + ); + } + return fn(set, get, api); + } + const { connection, ...connectionInformation } = extractConnectionInformation(store, extensionConnector, options); + let isRecording = true; + api.setState = (state, replace, nameOrAction) => { + const r = set(state, replace); + if (!isRecording) + return r; + const action = nameOrAction === void 0 ? { type: anonymousActionType || "anonymous" } : typeof nameOrAction === "string" ? { type: nameOrAction } : nameOrAction; + if (store === void 0) { + connection == null ? void 0 : connection.send(action, get()); + return r; + } + connection == null ? void 0 : connection.send( + { + ...action, + type: `${store}/${action.type}` + }, + { + ...getTrackedConnectionState(options.name), + [store]: api.getState() + } + ); + return r; + }; + const setStateFromDevtools = (...a) => { + const originalIsRecording = isRecording; + isRecording = false; + set(...a); + isRecording = originalIsRecording; + }; + const initialState = fn(api.setState, get, api); + if (connectionInformation.type === "untracked") { + connection == null ? void 0 : connection.init(initialState); + } else { + connectionInformation.stores[connectionInformation.store] = api; + connection == null ? void 0 : connection.init( + Object.fromEntries( + Object.entries(connectionInformation.stores).map(([key, store2]) => [ + key, + key === connectionInformation.store ? initialState : store2.getState() + ]) + ) + ); + } + if (api.dispatchFromDevtools && typeof api.dispatch === "function") { + let didWarnAboutReservedActionType = false; + const originalDispatch = api.dispatch; + api.dispatch = (...a) => { + if (a[0].type === "__setState" && !didWarnAboutReservedActionType) { + console.warn( + '[zustand devtools middleware] "__setState" action type is reserved to set state from the devtools. Avoid using it.' + ); + didWarnAboutReservedActionType = true; + } + originalDispatch(...a); + }; + } + connection.subscribe((message) => { + var _a; + switch (message.type) { + case "ACTION": + if (typeof message.payload !== "string") { + console.error( + "[zustand devtools middleware] Unsupported action format" + ); + return; + } + return parseJsonThen( + message.payload, + (action) => { + if (action.type === "__setState") { + if (store === void 0) { + setStateFromDevtools(action.state); + return; + } + if (Object.keys(action.state).length !== 1) { + console.error( + ` + [zustand devtools middleware] Unsupported __setState action format. + When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(), + and value of this only key should be a state object. Example: { "type": "__setState", "state": { "abc123Store": { "foo": "bar" } } } + ` + ); + } + const stateFromDevtools = action.state[store]; + if (stateFromDevtools === void 0 || stateFromDevtools === null) { + return; + } + if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) { + setStateFromDevtools(stateFromDevtools); + } + return; + } + if (!api.dispatchFromDevtools) + return; + if (typeof api.dispatch !== "function") + return; + api.dispatch(action); + } + ); + case "DISPATCH": + switch (message.payload.type) { + case "RESET": + setStateFromDevtools(initialState); + if (store === void 0) { + return connection == null ? void 0 : connection.init(api.getState()); + } + return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + case "COMMIT": + if (store === void 0) { + connection == null ? void 0 : connection.init(api.getState()); + return; + } + return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + case "ROLLBACK": + return parseJsonThen(message.state, (state) => { + if (store === void 0) { + setStateFromDevtools(state); + connection == null ? void 0 : connection.init(api.getState()); + return; + } + setStateFromDevtools(state[store]); + connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + }); + case "JUMP_TO_STATE": + case "JUMP_TO_ACTION": + return parseJsonThen(message.state, (state) => { + if (store === void 0) { + setStateFromDevtools(state); + return; + } + if (JSON.stringify(api.getState()) !== JSON.stringify(state[store])) { + setStateFromDevtools(state[store]); + } + }); + case "IMPORT_STATE": { + const { nextLiftedState } = message.payload; + const lastComputedState = (_a = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _a.state; + if (!lastComputedState) + return; + if (store === void 0) { + setStateFromDevtools(lastComputedState); + } else { + setStateFromDevtools(lastComputedState[store]); + } + connection == null ? void 0 : connection.send( + null, + // FIXME no-any + nextLiftedState + ); + return; + } + case "PAUSE_RECORDING": + return isRecording = !isRecording; + } + return; + } + }); + return initialState; + }; + const devtools = exports("devtools", devtoolsImpl); + const parseJsonThen = (stringified, f) => { + let parsed; + try { + parsed = JSON.parse(stringified); + } catch (e) { + console.error( + "[zustand devtools middleware] Could not parse the received json", + e + ); + } + if (parsed !== void 0) + f(parsed); + }; + + const subscribeWithSelectorImpl = (fn) => (set, get, api) => { + const origSubscribe = api.subscribe; + api.subscribe = (selector, optListener, options) => { + let listener = selector; + if (optListener) { + const equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is; + let currentSlice = selector(api.getState()); + listener = (state) => { + const nextSlice = selector(state); + if (!equalityFn(currentSlice, nextSlice)) { + const previousSlice = currentSlice; + optListener(currentSlice = nextSlice, previousSlice); + } + }; + if (options == null ? void 0 : options.fireImmediately) { + optListener(currentSlice, currentSlice); + } + } + return origSubscribe(listener); + }; + const initialState = fn(set, get, api); + return initialState; + }; + const subscribeWithSelector = exports("subscribeWithSelector", subscribeWithSelectorImpl); + + const combine = exports("combine", (initialState, create) => (...a) => Object.assign({}, initialState, create(...a))); + + function createJSONStorage(getStorage, options) { + let storage; + try { + storage = getStorage(); + } catch (e) { + return; + } + const persistStorage = { + getItem: (name) => { + var _a; + const parse = (str2) => { + if (str2 === null) { + return null; + } + return JSON.parse(str2, options == null ? void 0 : options.reviver); + }; + const str = (_a = storage.getItem(name)) != null ? _a : null; + if (str instanceof Promise) { + return str.then(parse); + } + return parse(str); + }, + setItem: (name, newValue) => storage.setItem( + name, + JSON.stringify(newValue, options == null ? void 0 : options.replacer) + ), + removeItem: (name) => storage.removeItem(name) + }; + return persistStorage; + } + const toThenable = (fn) => (input) => { + try { + const result = fn(input); + if (result instanceof Promise) { + return result; + } + return { + then(onFulfilled) { + return toThenable(onFulfilled)(result); + }, + catch(_onRejected) { + return this; + } + }; + } catch (e) { + return { + then(_onFulfilled) { + return this; + }, + catch(onRejected) { + return toThenable(onRejected)(e); + } + }; + } + }; + const oldImpl = (config, baseOptions) => (set, get, api) => { + let options = { + getStorage: () => localStorage, + serialize: JSON.stringify, + deserialize: JSON.parse, + partialize: (state) => state, + version: 0, + merge: (persistedState, currentState) => ({ + ...currentState, + ...persistedState + }), + ...baseOptions + }; + let hasHydrated = false; + const hydrationListeners = /* @__PURE__ */ new Set(); + const finishHydrationListeners = /* @__PURE__ */ new Set(); + let storage; + try { + storage = options.getStorage(); + } catch (e) { + } + if (!storage) { + return config( + (...args) => { + console.warn( + `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.` + ); + set(...args); + }, + get, + api + ); + } + const thenableSerialize = toThenable(options.serialize); + const setItem = () => { + const state = options.partialize({ ...get() }); + let errorInSync; + const thenable = thenableSerialize({ state, version: options.version }).then( + (serializedValue) => storage.setItem(options.name, serializedValue) + ).catch((e) => { + errorInSync = e; + }); + if (errorInSync) { + throw errorInSync; + } + return thenable; + }; + const savedSetState = api.setState; + api.setState = (state, replace) => { + savedSetState(state, replace); + void setItem(); + }; + const configResult = config( + (...args) => { + set(...args); + void setItem(); + }, + get, + api + ); + let stateFromStorage; + const hydrate = () => { + var _a; + if (!storage) + return; + hasHydrated = false; + hydrationListeners.forEach((cb) => cb(get())); + const postRehydrationCallback = ((_a = options.onRehydrateStorage) == null ? void 0 : _a.call(options, get())) || void 0; + return toThenable(storage.getItem.bind(storage))(options.name).then((storageValue) => { + if (storageValue) { + return options.deserialize(storageValue); + } + }).then((deserializedStorageValue) => { + if (deserializedStorageValue) { + if (typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== options.version) { + if (options.migrate) { + return options.migrate( + deserializedStorageValue.state, + deserializedStorageValue.version + ); + } + console.error( + `State loaded from storage couldn't be migrated since no migrate function was provided` + ); + } else { + return deserializedStorageValue.state; + } + } + }).then((migratedState) => { + var _a2; + stateFromStorage = options.merge( + migratedState, + (_a2 = get()) != null ? _a2 : configResult + ); + set(stateFromStorage, true); + return setItem(); + }).then(() => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0); + hasHydrated = true; + finishHydrationListeners.forEach((cb) => cb(stateFromStorage)); + }).catch((e) => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e); + }); + }; + api.persist = { + setOptions: (newOptions) => { + options = { + ...options, + ...newOptions + }; + if (newOptions.getStorage) { + storage = newOptions.getStorage(); + } + }, + clearStorage: () => { + storage == null ? void 0 : storage.removeItem(options.name); + }, + getOptions: () => options, + rehydrate: () => hydrate(), + hasHydrated: () => hasHydrated, + onHydrate: (cb) => { + hydrationListeners.add(cb); + return () => { + hydrationListeners.delete(cb); + }; + }, + onFinishHydration: (cb) => { + finishHydrationListeners.add(cb); + return () => { + finishHydrationListeners.delete(cb); + }; + } + }; + hydrate(); + return stateFromStorage || configResult; + }; + const newImpl = (config, baseOptions) => (set, get, api) => { + let options = { + storage: createJSONStorage(() => localStorage), + partialize: (state) => state, + version: 0, + merge: (persistedState, currentState) => ({ + ...currentState, + ...persistedState + }), + ...baseOptions + }; + let hasHydrated = false; + const hydrationListeners = /* @__PURE__ */ new Set(); + const finishHydrationListeners = /* @__PURE__ */ new Set(); + let storage = options.storage; + if (!storage) { + return config( + (...args) => { + console.warn( + `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.` + ); + set(...args); + }, + get, + api + ); + } + const setItem = () => { + const state = options.partialize({ ...get() }); + return storage.setItem(options.name, { + state, + version: options.version + }); + }; + const savedSetState = api.setState; + api.setState = (state, replace) => { + savedSetState(state, replace); + void setItem(); + }; + const configResult = config( + (...args) => { + set(...args); + void setItem(); + }, + get, + api + ); + api.getInitialState = () => configResult; + let stateFromStorage; + const hydrate = () => { + var _a, _b; + if (!storage) + return; + hasHydrated = false; + hydrationListeners.forEach((cb) => { + var _a2; + return cb((_a2 = get()) != null ? _a2 : configResult); + }); + const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0; + return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => { + if (deserializedStorageValue) { + if (typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== options.version) { + if (options.migrate) { + return options.migrate( + deserializedStorageValue.state, + deserializedStorageValue.version + ); + } + console.error( + `State loaded from storage couldn't be migrated since no migrate function was provided` + ); + } else { + return deserializedStorageValue.state; + } + } + }).then((migratedState) => { + var _a2; + stateFromStorage = options.merge( + migratedState, + (_a2 = get()) != null ? _a2 : configResult + ); + set(stateFromStorage, true); + return setItem(); + }).then(() => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0); + stateFromStorage = get(); + hasHydrated = true; + finishHydrationListeners.forEach((cb) => cb(stateFromStorage)); + }).catch((e) => { + postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e); + }); + }; + api.persist = { + setOptions: (newOptions) => { + options = { + ...options, + ...newOptions + }; + if (newOptions.storage) { + storage = newOptions.storage; + } + }, + clearStorage: () => { + storage == null ? void 0 : storage.removeItem(options.name); + }, + getOptions: () => options, + rehydrate: () => hydrate(), + hasHydrated: () => hasHydrated, + onHydrate: (cb) => { + hydrationListeners.add(cb); + return () => { + hydrationListeners.delete(cb); + }; + }, + onFinishHydration: (cb) => { + finishHydrationListeners.add(cb); + return () => { + finishHydrationListeners.delete(cb); + }; + } + }; + if (!options.skipHydration) { + hydrate(); + } + return stateFromStorage || configResult; + }; + const persistImpl = (config, baseOptions) => { + if ("getStorage" in baseOptions || "serialize" in baseOptions || "deserialize" in baseOptions) { + { + console.warn( + "[DEPRECATED] `getStorage`, `serialize` and `deserialize` options are deprecated. Use `storage` option instead." + ); + } + return oldImpl(config, baseOptions); + } + return newImpl(config, baseOptions); + }; + const persist = exports("persist", persistImpl); + + }) + }; +})); diff --git a/system/middleware.production.js b/system/middleware.production.js new file mode 100644 index 0000000..96ad5e9 --- /dev/null +++ b/system/middleware.production.js @@ -0,0 +1,5 @@ +System.register([],function(b){"use strict";return{execute:function(){b("createJSONStorage",T);const A=b("redux",(d,c)=>(s,a,n)=>(n.dispatch=e=>(s(g=>d(g,e),!1,e),e),n.dispatchFromDevtools=!0,{dispatch:(...e)=>n.dispatch(...e),...c})),I=new Map,w=d=>{const c=I.get(d);return c?Object.fromEntries(Object.entries(c.stores).map(([s,a])=>[s,a.getState()])):{}},E=(d,c,s)=>{if(d===void 0)return{type:"untracked",connection:c.connect(s)};const a=I.get(s.name);if(a)return{type:"tracked",store:d,...a};const n={connection:c.connect(s),stores:{}};return I.set(s.name,n),{type:"tracked",store:d,...n}},P=b("devtools",(d,c={})=>(s,a,n)=>{const{enabled:e,anonymousActionType:g,store:u,...p}=c;let m;try{m=(e!=null?e:!1)&&window.__REDUX_DEVTOOLS_EXTENSION__}catch(r){}if(!m)return d(s,a,n);const{connection:l,...S}=E(u,m,p);let h=!0;n.setState=(r,o,i)=>{const t=s(r,o);if(!h)return t;const f=i===void 0?{type:g||"anonymous"}:typeof i=="string"?{type:i}:i;return u===void 0?(l==null||l.send(f,a()),t):(l==null||l.send({...f,type:`${u}/${f.type}`},{...w(p.name),[u]:n.getState()}),t)};const v=(...r)=>{const o=h;h=!1,s(...r),h=o},y=d(n.setState,a,n);if(S.type==="untracked"?l==null||l.init(y):(S.stores[S.store]=n,l==null||l.init(Object.fromEntries(Object.entries(S.stores).map(([r,o])=>[r,r===S.store?y:o.getState()])))),n.dispatchFromDevtools&&typeof n.dispatch=="function"){const r=n.dispatch;n.dispatch=(...o)=>{r(...o)}}return l.subscribe(r=>{var o;switch(r.type){case"ACTION":if(typeof r.payload!="string"){console.error("[zustand devtools middleware] Unsupported action format");return}return _(r.payload,i=>{if(i.type==="__setState"){if(u===void 0){v(i.state);return}Object.keys(i.state).length!==1&&console.error(` + [zustand devtools middleware] Unsupported __setState action format. + When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(), + and value of this only key should be a state object. Example: { "type": "__setState", "state": { "abc123Store": { "foo": "bar" } } } + `);const t=i.state[u];if(t==null)return;JSON.stringify(n.getState())!==JSON.stringify(t)&&v(t);return}n.dispatchFromDevtools&&typeof n.dispatch=="function"&&n.dispatch(i)});case"DISPATCH":switch(r.payload.type){case"RESET":return v(y),u===void 0?l==null?void 0:l.init(n.getState()):l==null?void 0:l.init(w(p.name));case"COMMIT":if(u===void 0){l==null||l.init(n.getState());return}return l==null?void 0:l.init(w(p.name));case"ROLLBACK":return _(r.state,i=>{if(u===void 0){v(i),l==null||l.init(n.getState());return}v(i[u]),l==null||l.init(w(p.name))});case"JUMP_TO_STATE":case"JUMP_TO_ACTION":return _(r.state,i=>{if(u===void 0){v(i);return}JSON.stringify(n.getState())!==JSON.stringify(i[u])&&v(i[u])});case"IMPORT_STATE":{const{nextLiftedState:i}=r.payload,t=(o=i.computedStates.slice(-1)[0])==null?void 0:o.state;if(!t)return;v(u===void 0?t:t[u]),l==null||l.send(null,i);return}case"PAUSE_RECORDING":return h=!h}return}}),y}),_=(d,c)=>{let s;try{s=JSON.parse(d)}catch(a){console.error("[zustand devtools middleware] Could not parse the received json",a)}s!==void 0&&c(s)},H=b("subscribeWithSelector",d=>(c,s,a)=>{const n=a.subscribe;return a.subscribe=(e,g,u)=>{let p=e;if(g){const m=(u==null?void 0:u.equalityFn)||Object.is;let l=e(a.getState());p=S=>{const h=e(S);if(!m(l,h)){const v=l;g(l=h,v)}},u!=null&&u.fireImmediately&&g(l,l)}return n(p)},d(c,s,a)}),R=b("combine",(d,c)=>(...s)=>Object.assign({},d,c(...s)));function T(d,c){let s;try{s=d()}catch(a){return}return{getItem:a=>{var n;const e=u=>u===null?null:JSON.parse(u,c==null?void 0:c.reviver),g=(n=s.getItem(a))!=null?n:null;return g instanceof Promise?g.then(e):e(g)},setItem:(a,n)=>s.setItem(a,JSON.stringify(n,c==null?void 0:c.replacer)),removeItem:a=>s.removeItem(a)}}const O=d=>c=>{try{const s=d(c);return s instanceof Promise?s:{then(a){return O(a)(s)},catch(a){return this}}}catch(s){return{then(a){return this},catch(a){return O(a)(s)}}}},N=(d,c)=>(s,a,n)=>{let e={getStorage:()=>localStorage,serialize:JSON.stringify,deserialize:JSON.parse,partialize:o=>o,version:0,merge:(o,i)=>({...i,...o}),...c},g=!1;const u=new Set,p=new Set;let m;try{m=e.getStorage()}catch(o){}if(!m)return d((...o)=>{console.warn(`[zustand persist middleware] Unable to update item '${e.name}', the given storage is currently unavailable.`),s(...o)},a,n);const l=O(e.serialize),S=()=>{const o=e.partialize({...a()});let i;const t=l({state:o,version:e.version}).then(f=>m.setItem(e.name,f)).catch(f=>{i=f});if(i)throw i;return t},h=n.setState;n.setState=(o,i)=>{h(o,i),S()};const v=d((...o)=>{s(...o),S()},a,n);let y;const r=()=>{var o;if(!m)return;g=!1,u.forEach(t=>t(a()));const i=((o=e.onRehydrateStorage)==null?void 0:o.call(e,a()))||void 0;return O(m.getItem.bind(m))(e.name).then(t=>{if(t)return e.deserialize(t)}).then(t=>{if(t)if(typeof t.version=="number"&&t.version!==e.version){if(e.migrate)return e.migrate(t.state,t.version);console.error("State loaded from storage couldn't be migrated since no migrate function was provided")}else return t.state}).then(t=>{var f;return y=e.merge(t,(f=a())!=null?f:v),s(y,!0),S()}).then(()=>{i==null||i(y,void 0),g=!0,p.forEach(t=>t(y))}).catch(t=>{i==null||i(void 0,t)})};return n.persist={setOptions:o=>{e={...e,...o},o.getStorage&&(m=o.getStorage())},clearStorage:()=>{m==null||m.removeItem(e.name)},getOptions:()=>e,rehydrate:()=>r(),hasHydrated:()=>g,onHydrate:o=>(u.add(o),()=>{u.delete(o)}),onFinishHydration:o=>(p.add(o),()=>{p.delete(o)})},r(),y||v},z=(d,c)=>(s,a,n)=>{let e={storage:T(()=>localStorage),partialize:r=>r,version:0,merge:(r,o)=>({...o,...r}),...c},g=!1;const u=new Set,p=new Set;let m=e.storage;if(!m)return d((...r)=>{console.warn(`[zustand persist middleware] Unable to update item '${e.name}', the given storage is currently unavailable.`),s(...r)},a,n);const l=()=>{const r=e.partialize({...a()});return m.setItem(e.name,{state:r,version:e.version})},S=n.setState;n.setState=(r,o)=>{S(r,o),l()};const h=d((...r)=>{s(...r),l()},a,n);n.getInitialState=()=>h;let v;const y=()=>{var r,o;if(!m)return;g=!1,u.forEach(t=>{var f;return t((f=a())!=null?f:h)});const i=((o=e.onRehydrateStorage)==null?void 0:o.call(e,(r=a())!=null?r:h))||void 0;return O(m.getItem.bind(m))(e.name).then(t=>{if(t)if(typeof t.version=="number"&&t.version!==e.version){if(e.migrate)return e.migrate(t.state,t.version);console.error("State loaded from storage couldn't be migrated since no migrate function was provided")}else return t.state}).then(t=>{var f;return v=e.merge(t,(f=a())!=null?f:h),s(v,!0),l()}).then(()=>{i==null||i(v,void 0),v=a(),g=!0,p.forEach(t=>t(v))}).catch(t=>{i==null||i(void 0,t)})};return n.persist={setOptions:r=>{e={...e,...r},r.storage&&(m=r.storage)},clearStorage:()=>{m==null||m.removeItem(e.name)},getOptions:()=>e,rehydrate:()=>y(),hasHydrated:()=>g,onHydrate:r=>(u.add(r),()=>{u.delete(r)}),onFinishHydration:r=>(p.add(r),()=>{p.delete(r)})},e.skipHydration||y(),v||h},$=b("persist",(d,c)=>"getStorage"in c||"serialize"in c||"deserialize"in c?N(d,c):z(d,c))}}}); diff --git a/system/middleware/immer.development.js b/system/middleware/immer.development.js new file mode 100644 index 0000000..d8d4fd6 --- /dev/null +++ b/system/middleware/immer.development.js @@ -0,0 +1,21 @@ +System.register(['immer'], (function (exports) { + 'use strict'; + var produce; + return { + setters: [function (module) { + produce = module.produce; + }], + execute: (function () { + + const immerImpl = (initializer) => (set, get, store) => { + store.setState = (updater, replace, ...a) => { + const nextState = typeof updater === "function" ? produce(updater) : updater; + return set(nextState, replace, ...a); + }; + return initializer(store.setState, get, store); + }; + const immer = exports("immer", immerImpl); + + }) + }; +})); diff --git a/system/middleware/immer.production.js b/system/middleware/immer.production.js new file mode 100644 index 0000000..f194c03 --- /dev/null +++ b/system/middleware/immer.production.js @@ -0,0 +1 @@ +System.register(["immer"],function(c){"use strict";var r;return{setters:[function(n){r=n.produce}],execute:function(){const S=c("immer",s=>(o,u,t)=>(t.setState=(e,i,...m)=>{const f=typeof e=="function"?r(e):e;return o(f,i,...m)},s(t.setState,u,t)))}}}); diff --git a/system/react/shallow.development.js b/system/react/shallow.development.js new file mode 100644 index 0000000..5d32732 --- /dev/null +++ b/system/react/shallow.development.js @@ -0,0 +1,62 @@ +System.register(['react'], (function (exports) { + 'use strict'; + var ReactExports; + return { + setters: [function (module) { + ReactExports = module.default; + }], + execute: (function () { + + exports("useShallow", useShallow); + + function shallow(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) + return false; + for (const [key, value] of objA) { + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) + return false; + for (const value of objA) { + if (!objB.has(value)) { + return false; + } + } + return true; + } + const keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (const keyA of keysA) { + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; + } + + const { useRef } = ReactExports; + function useShallow(selector) { + const prev = useRef(); + return (state) => { + const next = selector(state); + return shallow(prev.current, next) ? prev.current : prev.current = next; + }; + } + + }) + }; +})); diff --git a/system/react/shallow.production.js b/system/react/shallow.production.js new file mode 100644 index 0000000..9a3c5dd --- /dev/null +++ b/system/react/shallow.production.js @@ -0,0 +1 @@ +System.register(["react"],function(o){"use strict";var c;return{setters:[function(s){c=s.default}],execute:function(){o("useShallow",u);function s(r,t){if(Object.is(r,t))return!0;if(typeof r!="object"||r===null||typeof t!="object"||t===null)return!1;if(r instanceof Map&&t instanceof Map){if(r.size!==t.size)return!1;for(const[e,i]of r)if(!Object.is(i,t.get(e)))return!1;return!0}if(r instanceof Set&&t instanceof Set){if(r.size!==t.size)return!1;for(const e of r)if(!t.has(e))return!1;return!0}const n=Object.keys(r);if(n.length!==Object.keys(t).length)return!1;for(const e of n)if(!Object.prototype.hasOwnProperty.call(t,e)||!Object.is(r[e],t[e]))return!1;return!0}const{useRef:f}=c;function u(r){const t=f();return n=>{const e=r(n);return s(t.current,e)?t.current:t.current=e}}}}}); diff --git a/system/shallow.development.js b/system/shallow.development.js new file mode 100644 index 0000000..2334f58 --- /dev/null +++ b/system/shallow.development.js @@ -0,0 +1,58 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + exports("shallow", shallow$1); + + function shallow$1(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) + return false; + for (const [key, value] of objA) { + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) + return false; + for (const value of objA) { + if (!objB.has(value)) { + return false; + } + } + return true; + } + const keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (const keyA of keysA) { + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; + } + + var shallow = exports("default", (objA, objB) => { + { + console.warn( + "[DEPRECATED] Default export is deprecated. Instead use `import { shallow } from 'zustand/shallow'`." + ); + } + return shallow$1(objA, objB); + }); + + }) + }; +})); diff --git a/system/shallow.production.js b/system/shallow.production.js new file mode 100644 index 0000000..a3cd625 --- /dev/null +++ b/system/shallow.production.js @@ -0,0 +1 @@ +System.register([],function(n){"use strict";return{execute:function(){n("shallow",f);function f(t,r){if(Object.is(t,r))return!0;if(typeof t!="object"||t===null||typeof r!="object"||r===null)return!1;if(t instanceof Map&&r instanceof Map){if(t.size!==r.size)return!1;for(const[e,o]of t)if(!Object.is(o,r.get(e)))return!1;return!0}if(t instanceof Set&&r instanceof Set){if(t.size!==r.size)return!1;for(const e of t)if(!r.has(e))return!1;return!0}const i=Object.keys(t);if(i.length!==Object.keys(r).length)return!1;for(const e of i)if(!Object.prototype.hasOwnProperty.call(r,e)||!Object.is(t[e],r[e]))return!1;return!0}var s=n("default",(t,r)=>f(t,r))}}}); diff --git a/system/traditional.development.js b/system/traditional.development.js new file mode 100644 index 0000000..bfdcaf8 --- /dev/null +++ b/system/traditional.development.js @@ -0,0 +1,40 @@ +System.register(['react', 'use-sync-external-store/shim/with-selector', 'zustand/vanilla'], (function (exports) { + 'use strict'; + var ReactExports, useSyncExternalStoreExports, createStore; + return { + setters: [function (module) { + ReactExports = module.default; + }, function (module) { + useSyncExternalStoreExports = module.default; + }, function (module) { + createStore = module.createStore; + }], + execute: (function () { + + exports("useStoreWithEqualityFn", useStoreWithEqualityFn); + + const { useDebugValue } = ReactExports; + const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports; + const identity = (arg) => arg; + function useStoreWithEqualityFn(api, selector = identity, equalityFn) { + const slice = useSyncExternalStoreWithSelector( + api.subscribe, + api.getState, + api.getServerState || api.getInitialState, + selector, + equalityFn + ); + useDebugValue(slice); + return slice; + } + const createWithEqualityFnImpl = (createState, defaultEqualityFn) => { + const api = createStore(createState); + const useBoundStoreWithEqualityFn = (selector, equalityFn = defaultEqualityFn) => useStoreWithEqualityFn(api, selector, equalityFn); + Object.assign(useBoundStoreWithEqualityFn, api); + return useBoundStoreWithEqualityFn; + }; + const createWithEqualityFn = exports("createWithEqualityFn", (createState, defaultEqualityFn) => createState ? createWithEqualityFnImpl(createState, defaultEqualityFn) : createWithEqualityFnImpl); + + }) + }; +})); diff --git a/system/traditional.production.js b/system/traditional.production.js new file mode 100644 index 0000000..de131dc --- /dev/null +++ b/system/traditional.production.js @@ -0,0 +1 @@ +System.register(["react","use-sync-external-store/shim/with-selector","zustand/vanilla"],function(a){"use strict";var c,i,s;return{setters:[function(e){c=e.default},function(e){i=e.default},function(e){s=e.createStore}],execute:function(){a("useStoreWithEqualityFn",o);const{useDebugValue:e}=c,{useSyncExternalStoreWithSelector:S}=i,f=t=>t;function o(t,n=f,u){const r=S(t.subscribe,t.getState,t.getServerState||t.getInitialState,n,u);return e(r),r}const l=(t,n)=>{const u=s(t),r=(y,h=n)=>o(u,y,h);return Object.assign(r,u),r},g=a("createWithEqualityFn",(t,n)=>t?l(t,n):l)}}}); diff --git a/system/vanilla.development.js b/system/vanilla.development.js new file mode 100644 index 0000000..b2710ef --- /dev/null +++ b/system/vanilla.development.js @@ -0,0 +1,65 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + const createStoreImpl = (createState, configs) => { + let timeout; + let state; + const listeners = /* @__PURE__ */ new Set(); + const setState = (partial, replace) => { + const nextState = typeof partial === "function" ? partial(state) : partial; + if (!Object.is(nextState, state)) { + const previousState = state; + state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState); + listeners.forEach((listener) => listener(state, previousState)); + } + }; + const getState = () => state; + const getInitialState = () => initialState; + const resetBasedOnCachTime = () => { + if (!(configs == null ? void 0 : configs.cacheTime)) + return; + if (timeout) + clearTimeout(timeout); + timeout = setTimeout(() => { + state = createState(setState, getState, api); + }, configs == null ? void 0 : configs.cacheTime); + }; + const subscribe = (listener) => { + listeners.add(listener); + if (timeout) + clearTimeout(timeout); + return () => { + const responseDelete = listeners.delete(listener); + if (!listeners.size) { + resetBasedOnCachTime(); + } + return responseDelete; + }; + }; + const destroy = () => { + { + console.warn( + "[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected." + ); + } + listeners.clear(); + }; + const api = { setState, getState, getInitialState, subscribe, destroy }; + const initialState = state = createState(setState, getState, api); + return api; + }; + const createStore = exports("createStore", createStoreImpl); + var vanilla = exports("default", (createState) => { + { + console.warn( + "[DEPRECATED] Default export is deprecated. Instead use import { createStore } from 'zustand/vanilla'." + ); + } + return createStore(createState); + }); + + }) + }; +})); diff --git a/system/vanilla.production.js b/system/vanilla.production.js new file mode 100644 index 0000000..c58f358 --- /dev/null +++ b/system/vanilla.production.js @@ -0,0 +1 @@ +System.register([],function(f){"use strict";return{execute:function(){const S=f("createStore",(a,i)=>{let c,t;const n=new Set,u=(e,l)=>{const o=typeof e=="function"?e(t):e;if(!Object.is(o,t)){const T=t;t=(l!=null?l:typeof o!="object"||o===null)?o:Object.assign({},t,o),n.forEach(b=>b(t,T))}},s=()=>t,d=()=>y,m=()=>{i!=null&&i.cacheTime&&(c&&clearTimeout(c),c=setTimeout(()=>{t=a(u,s,r)},i==null?void 0:i.cacheTime))},r={setState:u,getState:s,getInitialState:d,subscribe:e=>(n.add(e),c&&clearTimeout(c),()=>{const l=n.delete(e);return n.size||m(),l}),destroy:()=>{n.clear()}},y=t=a(u,s,r);return r});var h=f("default",a=>S(a))}}}); diff --git a/system/vanilla/shallow.development.js b/system/vanilla/shallow.development.js new file mode 100644 index 0000000..1f096bc --- /dev/null +++ b/system/vanilla/shallow.development.js @@ -0,0 +1,49 @@ +System.register([], (function (exports) { + 'use strict'; + return { + execute: (function () { + + exports("shallow", shallow); + + function shallow(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) + return false; + for (const [key, value] of objA) { + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) + return false; + for (const value of objA) { + if (!objB.has(value)) { + return false; + } + } + return true; + } + const keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (const keyA of keysA) { + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; + } + + }) + }; +})); diff --git a/system/vanilla/shallow.production.js b/system/vanilla/shallow.production.js new file mode 100644 index 0000000..89898d6 --- /dev/null +++ b/system/vanilla/shallow.production.js @@ -0,0 +1 @@ +System.register([],function(f){"use strict";return{execute:function(){f("shallow",i);function i(t,r){if(Object.is(t,r))return!0;if(typeof t!="object"||t===null||typeof r!="object"||r===null)return!1;if(t instanceof Map&&r instanceof Map){if(t.size!==r.size)return!1;for(const[e,o]of t)if(!Object.is(o,r.get(e)))return!1;return!0}if(t instanceof Set&&r instanceof Set){if(t.size!==r.size)return!1;for(const e of t)if(!r.has(e))return!1;return!0}const n=Object.keys(t);if(n.length!==Object.keys(r).length)return!1;for(const e of n)if(!Object.prototype.hasOwnProperty.call(r,e)||!Object.is(t[e],r[e]))return!1;return!0}}}}); diff --git a/traditional.d.ts b/traditional.d.ts new file mode 100644 index 0000000..9d14d0f --- /dev/null +++ b/traditional.d.ts @@ -0,0 +1,21 @@ +import type { Mutate, StateCreator, StoreApi, StoreMutatorIdentifier } from './vanilla'; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type ReadonlyStoreApi = Pick, 'getState' | 'subscribe'>; +type WithReact> = S & { + /** @deprecated please use api.getInitialState() */ + getServerState?: () => ExtractState; +}; +export declare function useStoreWithEqualityFn>>(api: S): ExtractState; +export declare function useStoreWithEqualityFn>, U>(api: S, selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +export type UseBoundStoreWithEqualityFn>> = { + (): ExtractState; + (selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +} & S; +type CreateWithEqualityFn = { + (initializer: StateCreator, defaultEqualityFn?: (a: U, b: U) => boolean): UseBoundStoreWithEqualityFn, Mos>>; + (): (initializer: StateCreator, defaultEqualityFn?: (a: U, b: U) => boolean) => UseBoundStoreWithEqualityFn, Mos>>; +}; +export declare const createWithEqualityFn: CreateWithEqualityFn; +export {}; diff --git a/traditional.js b/traditional.js new file mode 100644 index 0000000..7776367 --- /dev/null +++ b/traditional.js @@ -0,0 +1,36 @@ +'use strict'; + +var ReactExports = require('react'); +var useSyncExternalStoreExports = require('use-sync-external-store/shim/with-selector'); +var vanilla = require('zustand/vanilla'); + +var useDebugValue = ReactExports.useDebugValue; +var useSyncExternalStoreWithSelector = useSyncExternalStoreExports.useSyncExternalStoreWithSelector; +var identity = function identity(arg) { + return arg; +}; +function useStoreWithEqualityFn(api, selector, equalityFn) { + if (selector === void 0) { + selector = identity; + } + var slice = useSyncExternalStoreWithSelector(api.subscribe, api.getState, api.getServerState || api.getInitialState, selector, equalityFn); + useDebugValue(slice); + return slice; +} +var createWithEqualityFnImpl = function createWithEqualityFnImpl(createState, defaultEqualityFn) { + var api = vanilla.createStore(createState); + var useBoundStoreWithEqualityFn = function useBoundStoreWithEqualityFn(selector, equalityFn) { + if (equalityFn === void 0) { + equalityFn = defaultEqualityFn; + } + return useStoreWithEqualityFn(api, selector, equalityFn); + }; + Object.assign(useBoundStoreWithEqualityFn, api); + return useBoundStoreWithEqualityFn; +}; +var createWithEqualityFn = function createWithEqualityFn(createState, defaultEqualityFn) { + return createState ? createWithEqualityFnImpl(createState, defaultEqualityFn) : createWithEqualityFnImpl; +}; + +exports.createWithEqualityFn = createWithEqualityFn; +exports.useStoreWithEqualityFn = useStoreWithEqualityFn; diff --git a/ts3.4/context.d.ts b/ts3.4/context.d.ts new file mode 100644 index 0000000..4be2213 --- /dev/null +++ b/ts3.4/context.d.ts @@ -0,0 +1,25 @@ +import ReactExports from 'react'; +import { ReactNode } from 'react'; +import { StoreApi } from 'zustand'; +type UseContextStore> = { + (): ExtractState; + (selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +}; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type WithoutCallSignature = { + [K in keyof T]: T[K]; +}; +/** + * @deprecated Use `createStore` and `useStore` for context usage + */ +declare function createContext>(): { + Provider: ({ createStore, children, }: { + createStore: () => S; + children: ReactNode; + }) => ReactExports.FunctionComponentElement>; + useStore: UseContextStore; + useStoreApi: () => WithoutCallSignature; +}; +export default createContext; diff --git a/ts3.4/esm/context.d.ts b/ts3.4/esm/context.d.ts new file mode 100644 index 0000000..4be2213 --- /dev/null +++ b/ts3.4/esm/context.d.ts @@ -0,0 +1,25 @@ +import ReactExports from 'react'; +import { ReactNode } from 'react'; +import { StoreApi } from 'zustand'; +type UseContextStore> = { + (): ExtractState; + (selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +}; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type WithoutCallSignature = { + [K in keyof T]: T[K]; +}; +/** + * @deprecated Use `createStore` and `useStore` for context usage + */ +declare function createContext>(): { + Provider: ({ createStore, children, }: { + createStore: () => S; + children: ReactNode; + }) => ReactExports.FunctionComponentElement>; + useStore: UseContextStore; + useStoreApi: () => WithoutCallSignature; +}; +export default createContext; diff --git a/ts3.4/esm/index.d.ts b/ts3.4/esm/index.d.ts new file mode 100644 index 0000000..7e9c8b6 --- /dev/null +++ b/ts3.4/esm/index.d.ts @@ -0,0 +1,3 @@ +export * from './vanilla'; +export * from './react'; +export { default } from './react'; diff --git a/ts3.4/esm/middleware.d.ts b/ts3.4/esm/middleware.d.ts new file mode 100644 index 0000000..1806630 --- /dev/null +++ b/ts3.4/esm/middleware.d.ts @@ -0,0 +1,5 @@ +export * from './middleware/redux'; +export * from './middleware/devtools'; +export * from './middleware/subscribeWithSelector'; +export * from './middleware/combine'; +export * from './middleware/persist'; diff --git a/ts3.4/esm/middleware/combine.d.ts b/ts3.4/esm/middleware/combine.d.ts new file mode 100644 index 0000000..0701780 --- /dev/null +++ b/ts3.4/esm/middleware/combine.d.ts @@ -0,0 +1,13 @@ +import { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Write = Pick> & U; +type Combine = (initialState: T, additionalStateCreator: StateCreator) => StateCreator, Mps, Mcs>; +export declare const combine: Combine; +export {}; diff --git a/ts3.4/esm/middleware/devtools.d.ts b/ts3.4/esm/middleware/devtools.d.ts new file mode 100644 index 0000000..afad734 --- /dev/null +++ b/ts3.4/esm/middleware/devtools.d.ts @@ -0,0 +1,102 @@ +import { StateCreator, StoreApi, StoreMutatorIdentifier } from '../vanilla'; +type Config = Parameters<(Window extends { + __REDUX_DEVTOOLS_EXTENSION__?: infer T; +} ? T : { + connect: (param: any) => any; +})['connect']>[0]; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/devtools': WithDevtools; + } +} +type Cast = T extends U ? T : U; +type Write = Pick> & U; +type TakeTwo = T extends { + length: 0; +} ? [ + undefined, + undefined +] : T extends { + length: 1; +} ? [ + /*a0*/ ...Cast, + /*a1*/ undefined +] : T extends { + length: 0 | 1; +} ? [ + /*a0*/ ...Cast, + /*a1*/ undefined +] : T extends { + length: 2; +} ? T : T extends { + length: 1 | 2; +} ? T : T extends { + length: 0 | 1 | 2; +} ? T : T extends [ + infer A0, + infer A1, + ...unknown[] +] ? [ + A0, + A1 +] : T extends [ + infer A0, + (infer A1)?, + ...unknown[] +] ? [ + A0, + A1? +] : T extends [ + (infer A0)?, + (infer A1)?, + ...unknown[] +] ? [ + A0?, + A1? +] : never; +type WithDevtools = Write>; +type StoreDevtools = S extends { + setState: (...a: infer Sa) => infer Sr; +} ? { + setState
(...a: [ + /*a*/ ...TakeTwo, + /*action*/ A + ]): Sr; +} : never; +export interface DevtoolsOptions extends Config { + name?: string; + enabled?: boolean; + anonymousActionType?: string; + store?: string; +} +type Devtools = (initializer: StateCreator, devtoolsOptions?: DevtoolsOptions) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/devtools': WithDevtools; + } +} +export type NamedSet = WithDevtools>['setState']; +export declare const devtools: Devtools; +export {}; diff --git a/ts3.4/esm/middleware/immer.d.ts b/ts3.4/esm/middleware/immer.d.ts new file mode 100644 index 0000000..3f06c55 --- /dev/null +++ b/ts3.4/esm/middleware/immer.d.ts @@ -0,0 +1,60 @@ +import { Draft } from 'immer'; +import { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Immer = (initializer: StateCreator) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + ['zustand/immer']: WithImmer; + } +} +type Write = Pick> & U; +type SkipTwo = T extends { + length: 0; +} ? [ +] : T extends { + length: 1; +} ? [ +] : T extends { + length: 0 | 1; +} ? [ +] : T extends [ + unknown, + unknown, + ...infer A +] ? A : T extends [ + unknown, + unknown?, + ...infer A +] ? A : T extends [ + unknown?, + unknown?, + ...infer A +] ? A : never; +type WithImmer = Write>; +type StoreImmer = S extends { + getState: () => infer T; + setState: infer SetState; +} ? SetState extends (...a: infer A) => infer Sr ? { + setState(nextStateOrUpdater: T | Partial | ((state: Draft) => void), shouldReplace?: boolean | undefined, ...a: SkipTwo): Sr; +} : never : never; +export declare const immer: Immer; +export {}; diff --git a/ts3.4/esm/middleware/persist.d.ts b/ts3.4/esm/middleware/persist.d.ts new file mode 100644 index 0000000..991dd16 --- /dev/null +++ b/ts3.4/esm/middleware/persist.d.ts @@ -0,0 +1,139 @@ +import { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +export interface StateStorage { + getItem: (name: string) => string | null | Promise; + setItem: (name: string, value: string) => unknown | Promise; + removeItem: (name: string) => unknown | Promise; +} +export type StorageValue = { + state: S; + version?: number; +}; +export interface PersistStorage { + getItem: (name: string) => StorageValue | null | Promise | null>; + setItem: (name: string, value: StorageValue) => unknown | Promise; + removeItem: (name: string) => unknown | Promise; +} +type JsonStorageOptions = { + reviver?: (key: string, value: unknown) => unknown; + replacer?: (key: string, value: unknown) => unknown; +}; +export declare function createJSONStorage(getStorage: () => StateStorage, options?: JsonStorageOptions): PersistStorage | undefined; +export interface PersistOptions { + /** Name of the storage (must be unique) */ + name: string; + /** + * @deprecated Use `storage` instead. + * A function returning a storage. + * The storage must fit `window.localStorage`'s api (or an async version of it). + * For example the storage could be `AsyncStorage` from React Native. + * + * @default () => localStorage + */ + getStorage?: () => StateStorage; + /** + * @deprecated Use `storage` instead. + * Use a custom serializer. + * The returned string will be stored in the storage. + * + * @default JSON.stringify + */ + serialize?: (state: StorageValue) => string | Promise; + /** + * @deprecated Use `storage` instead. + * Use a custom deserializer. + * Must return an object matching StorageValue + * + * @param str The storage's current value. + * @default JSON.parse + */ + deserialize?: (str: string) => StorageValue | Promise>; + /** + * Use a custom persist storage. + * + * Combining `createJSONStorage` helps creating a persist storage + * with JSON.parse and JSON.stringify. + * + * @default createJSONStorage(() => localStorage) + */ + storage?: PersistStorage | undefined; + /** + * Filter the persisted value. + * + * @params state The state's value + */ + partialize?: (state: S) => PersistedState; + /** + * A function returning another (optional) function. + * The main function will be called before the state rehydration. + * The returned function will be called after the state rehydration or when an error occurred. + */ + onRehydrateStorage?: (state: S) => ((state?: S, error?: unknown) => void) | void; + /** + * If the stored state's version mismatch the one specified here, the storage will not be used. + * This is useful when adding a breaking change to your store. + */ + version?: number; + /** + * A function to perform persisted state migration. + * This function will be called when persisted state versions mismatch with the one specified here. + */ + migrate?: (persistedState: unknown, version: number) => PersistedState | Promise; + /** + * A function to perform custom hydration merges when combining the stored state with the current one. + * By default, this function does a shallow merge. + */ + merge?: (persistedState: unknown, currentState: S) => S; + /** + * An optional boolean that will prevent the persist middleware from triggering hydration on initialization, + * This allows you to call `rehydrate()` at a specific point in your apps rendering life-cycle. + * + * This is useful in SSR application. + * + * @default false + */ + skipHydration?: boolean; +} +type PersistListener = (state: S) => void; +type StorePersist = { + persist: { + setOptions: (options: Partial>) => void; + clearStorage: () => void; + rehydrate: () => Promise | void; + hasHydrated: () => boolean; + onHydrate: (fn: PersistListener) => () => void; + onFinishHydration: (fn: PersistListener) => () => void; + getOptions: () => Partial>; + }; +}; +type Persist = (initializer: StateCreator, options: PersistOptions) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/persist': WithPersist; + } +} +type Write = Pick> & U; +type WithPersist = S extends { + getState: () => infer T; +} ? Write> : never; +export declare const persist: Persist; +export {}; diff --git a/ts3.4/esm/middleware/redux.d.ts b/ts3.4/esm/middleware/redux.d.ts new file mode 100644 index 0000000..24fe5c7 --- /dev/null +++ b/ts3.4/esm/middleware/redux.d.ts @@ -0,0 +1,30 @@ +import { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Write = Pick> & U; +type Action = { + type: string; +}; +type StoreRedux = { + dispatch: (a: A) => A; + dispatchFromDevtools: true; +}; +type ReduxState = { + dispatch: StoreRedux['dispatch']; +}; +type WithRedux = Write>; +type Redux = (reducer: (state: T, action: A) => T, initialState: T) => StateCreator>, Cms, [ + [ + 'zustand/redux', + A + ] +]>; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/redux': WithRedux; + } +} +export declare const redux: Redux; +export {}; diff --git a/ts3.4/esm/middleware/subscribeWithSelector.d.ts b/ts3.4/esm/middleware/subscribeWithSelector.d.ts new file mode 100644 index 0000000..98991f6 --- /dev/null +++ b/ts3.4/esm/middleware/subscribeWithSelector.d.ts @@ -0,0 +1,42 @@ +import { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type SubscribeWithSelector = (initializer: StateCreator) => StateCreator; +type Write = Pick> & U; +type WithSelectorSubscribe = S extends { + getState: () => infer T; +} ? Write> : never; +declare module '../vanilla' { + interface StoreMutators { + ['zustand/subscribeWithSelector']: WithSelectorSubscribe; + } +} +type StoreSubscribeWithSelector = { + subscribe: { + (listener: (selectedState: T, previousSelectedState: T) => void): () => void; + (selector: (state: T) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: { + equalityFn?: (a: U, b: U) => boolean; + fireImmediately?: boolean; + }): () => void; + }; +}; +export declare const subscribeWithSelector: SubscribeWithSelector; +export {}; diff --git a/ts3.4/esm/react.d.ts b/ts3.4/esm/react.d.ts new file mode 100644 index 0000000..3e61338 --- /dev/null +++ b/ts3.4/esm/react.d.ts @@ -0,0 +1,48 @@ +import { Mutate, StateCreator, StoreApi, StoreMutatorIdentifier } from './vanilla'; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type ReadonlyStoreApi = Pick, 'getState' | 'subscribe'>; +type WithReact> = S & { + /** @deprecated please use api.getInitialState() */ + getServerState?: () => ExtractState; +}; +export declare function useStore>>(api: S): ExtractState; +export declare function useStore>, U>(api: S, selector: (state: ExtractState) => U): U; +/** + * @deprecated The usage with three arguments is deprecated. Use `useStoreWithEqualityFn` from 'zustand/traditional'. The usage with one or two arguments is not deprecated. + * https://github.com/pmndrs/zustand/discussions/1937 + */ +export declare function useStore>, U>(api: S, selector: (state: ExtractState) => U, equalityFn: ((a: U, b: U) => boolean) | undefined): U; +export type UseBoundStore>> = { + (): ExtractState; + (selector: (state: ExtractState) => U): U; + /** + * @deprecated Use `createWithEqualityFn` from 'zustand/traditional' + */ + (selector: (state: ExtractState) => U, equalityFn: (a: U, b: U) => boolean): U; +} & S; +type Create = { + (initializer: StateCreator): UseBoundStore, Mos>>; + (): (initializer: StateCreator) => UseBoundStore, Mos>>; + /** + * @deprecated Use `useStore` hook to bind store + */ + >(store: S): UseBoundStore; +}; +export declare const create: Create; +/** + * @deprecated Use `import { create } from 'zustand'` + */ +declare const _default: Create; +export default _default; diff --git a/ts3.4/esm/react/shallow.d.ts b/ts3.4/esm/react/shallow.d.ts new file mode 100644 index 0000000..9068d46 --- /dev/null +++ b/ts3.4/esm/react/shallow.d.ts @@ -0,0 +1 @@ +export declare function useShallow(selector: (state: S) => U): (state: S) => U; diff --git a/ts3.4/esm/shallow.d.ts b/ts3.4/esm/shallow.d.ts new file mode 100644 index 0000000..0914a7c --- /dev/null +++ b/ts3.4/esm/shallow.d.ts @@ -0,0 +1,7 @@ +import { shallow } from './vanilla/shallow'; +/** + * @deprecated Use `import { shallow } from 'zustand/shallow'` + */ +declare const _default: typeof shallow; +export default _default; +export { shallow }; diff --git a/ts3.4/esm/traditional.d.ts b/ts3.4/esm/traditional.d.ts new file mode 100644 index 0000000..bf8b70c --- /dev/null +++ b/ts3.4/esm/traditional.d.ts @@ -0,0 +1,31 @@ +import { Mutate, StateCreator, StoreApi, StoreMutatorIdentifier } from './vanilla'; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type ReadonlyStoreApi = Pick, 'getState' | 'subscribe'>; +type WithReact> = S & { + /** @deprecated please use api.getInitialState() */ + getServerState?: () => ExtractState; +}; +export declare function useStoreWithEqualityFn>>(api: S): ExtractState; +export declare function useStoreWithEqualityFn>, U>(api: S, selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +export type UseBoundStoreWithEqualityFn>> = { + (): ExtractState; + (selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +} & S; +type CreateWithEqualityFn = { + (initializer: StateCreator, defaultEqualityFn?: (a: U, b: U) => boolean): UseBoundStoreWithEqualityFn, Mos>>; + (): (initializer: StateCreator, defaultEqualityFn?: (a: U, b: U) => boolean) => UseBoundStoreWithEqualityFn, Mos>>; +}; +export declare const createWithEqualityFn: CreateWithEqualityFn; +export {}; diff --git a/ts3.4/esm/vanilla.d.ts b/ts3.4/esm/vanilla.d.ts new file mode 100644 index 0000000..71c1961 --- /dev/null +++ b/ts3.4/esm/vanilla.d.ts @@ -0,0 +1,115 @@ +type SetStateInternal = { + _(partial: T | Partial | { + _(state: T): T | Partial; + }['_'], replace?: boolean | undefined): void; +}['_']; +export interface StoreApi { + setState: SetStateInternal; + getState: () => T; + getInitialState: () => T; + subscribe: (listener: (state: T, prevState: T) => void) => () => void; + /** + * @deprecated Use `unsubscribe` returned by `subscribe` + */ + destroy: () => void; +} +type Get = K extends keyof T ? T[K] : F; +export type Mutate = number extends Ms['length' & keyof Ms] ? S : Ms extends [ +] ? S : Ms extends [ + [ + infer Mi, + infer Ma + ], + ...infer Mrs +] ? Mutate[Mi & StoreMutatorIdentifier], Mrs> : never; +export type StateCreator = ((setState: Get, Mis>, 'setState', never>, getState: Get, Mis>, 'getState', never>, store: Mutate, Mis>) => U) & { + $$storeMutators?: Mos; +}; +export interface StoreMutators { +} +export type StoreMutatorIdentifier = keyof StoreMutators; +type CreateStore = { + (initializer: StateCreator): Mutate, Mos>; + (): (initializer: StateCreator) => Mutate, Mos>; +}; +type Configs = { + cacheTime?: number; +}; +type CreateStoreImpl = (initializer: StateCreator, configs?: Configs) => Mutate, Mos>; +export declare const createStore: CreateStoreImpl; +/** + * @deprecated Use `import { createStore } from 'zustand/vanilla'` + */ +declare const _default: CreateStore; +export default _default; +/** + * @deprecated Use `unknown` instead of `State` + */ +export type State = unknown; +/** + * @deprecated Use `Partial | ((s: T) => Partial)` instead of `PartialState` + */ +export type PartialState = Partial | ((state: T) => Partial); +/** + * @deprecated Use `(s: T) => U` instead of `StateSelector` + */ +export type StateSelector = (state: T) => U; +/** + * @deprecated Use `(a: T, b: T) => boolean` instead of `EqualityChecker` + */ +export type EqualityChecker = (state: T, newState: T) => boolean; +/** + * @deprecated Use `(state: T, previousState: T) => void` instead of `StateListener` + */ +export type StateListener = (state: T, previousState: T) => void; +/** + * @deprecated Use `(slice: T, previousSlice: T) => void` instead of `StateSliceListener`. + */ +export type StateSliceListener = (slice: T, previousSlice: T) => void; +/** + * @deprecated Use `(listener: (state: T) => void) => void` instead of `Subscribe`. + */ +export type Subscribe = { + (listener: (state: T, previousState: T) => void): () => void; +}; +/** + * @deprecated You might be looking for `StateCreator`, if not then + * use `StoreApi['setState']` instead of `SetState`. + */ +export type SetState = { + _(partial: T | Partial | { + _(state: T): T | Partial; + }['_'], replace?: boolean | undefined): void; +}['_']; +/** + * @deprecated You might be looking for `StateCreator`, if not then + * use `StoreApi['getState']` instead of `GetState`. + */ +export type GetState = () => T; +/** + * @deprecated Use `StoreApi['destroy']` instead of `Destroy`. + */ +export type Destroy = () => void; diff --git a/ts3.4/esm/vanilla/shallow.d.ts b/ts3.4/esm/vanilla/shallow.d.ts new file mode 100644 index 0000000..a8d2354 --- /dev/null +++ b/ts3.4/esm/vanilla/shallow.d.ts @@ -0,0 +1 @@ +export declare function shallow(objA: T, objB: T): boolean; diff --git a/ts3.4/index.d.ts b/ts3.4/index.d.ts new file mode 100644 index 0000000..7e9c8b6 --- /dev/null +++ b/ts3.4/index.d.ts @@ -0,0 +1,3 @@ +export * from './vanilla'; +export * from './react'; +export { default } from './react'; diff --git a/ts3.4/middleware.d.ts b/ts3.4/middleware.d.ts new file mode 100644 index 0000000..1806630 --- /dev/null +++ b/ts3.4/middleware.d.ts @@ -0,0 +1,5 @@ +export * from './middleware/redux'; +export * from './middleware/devtools'; +export * from './middleware/subscribeWithSelector'; +export * from './middleware/combine'; +export * from './middleware/persist'; diff --git a/ts3.4/middleware/combine.d.ts b/ts3.4/middleware/combine.d.ts new file mode 100644 index 0000000..0701780 --- /dev/null +++ b/ts3.4/middleware/combine.d.ts @@ -0,0 +1,13 @@ +import { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Write = Pick> & U; +type Combine = (initialState: T, additionalStateCreator: StateCreator) => StateCreator, Mps, Mcs>; +export declare const combine: Combine; +export {}; diff --git a/ts3.4/middleware/devtools.d.ts b/ts3.4/middleware/devtools.d.ts new file mode 100644 index 0000000..afad734 --- /dev/null +++ b/ts3.4/middleware/devtools.d.ts @@ -0,0 +1,102 @@ +import { StateCreator, StoreApi, StoreMutatorIdentifier } from '../vanilla'; +type Config = Parameters<(Window extends { + __REDUX_DEVTOOLS_EXTENSION__?: infer T; +} ? T : { + connect: (param: any) => any; +})['connect']>[0]; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/devtools': WithDevtools; + } +} +type Cast = T extends U ? T : U; +type Write = Pick> & U; +type TakeTwo = T extends { + length: 0; +} ? [ + undefined, + undefined +] : T extends { + length: 1; +} ? [ + /*a0*/ ...Cast, + /*a1*/ undefined +] : T extends { + length: 0 | 1; +} ? [ + /*a0*/ ...Cast, + /*a1*/ undefined +] : T extends { + length: 2; +} ? T : T extends { + length: 1 | 2; +} ? T : T extends { + length: 0 | 1 | 2; +} ? T : T extends [ + infer A0, + infer A1, + ...unknown[] +] ? [ + A0, + A1 +] : T extends [ + infer A0, + (infer A1)?, + ...unknown[] +] ? [ + A0, + A1? +] : T extends [ + (infer A0)?, + (infer A1)?, + ...unknown[] +] ? [ + A0?, + A1? +] : never; +type WithDevtools = Write>; +type StoreDevtools = S extends { + setState: (...a: infer Sa) => infer Sr; +} ? { + setState(...a: [ + /*a*/ ...TakeTwo, + /*action*/ A + ]): Sr; +} : never; +export interface DevtoolsOptions extends Config { + name?: string; + enabled?: boolean; + anonymousActionType?: string; + store?: string; +} +type Devtools = (initializer: StateCreator, devtoolsOptions?: DevtoolsOptions) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/devtools': WithDevtools; + } +} +export type NamedSet = WithDevtools>['setState']; +export declare const devtools: Devtools; +export {}; diff --git a/ts3.4/middleware/immer.d.ts b/ts3.4/middleware/immer.d.ts new file mode 100644 index 0000000..3f06c55 --- /dev/null +++ b/ts3.4/middleware/immer.d.ts @@ -0,0 +1,60 @@ +import { Draft } from 'immer'; +import { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Immer = (initializer: StateCreator) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + ['zustand/immer']: WithImmer; + } +} +type Write = Pick> & U; +type SkipTwo = T extends { + length: 0; +} ? [ +] : T extends { + length: 1; +} ? [ +] : T extends { + length: 0 | 1; +} ? [ +] : T extends [ + unknown, + unknown, + ...infer A +] ? A : T extends [ + unknown, + unknown?, + ...infer A +] ? A : T extends [ + unknown?, + unknown?, + ...infer A +] ? A : never; +type WithImmer = Write>; +type StoreImmer = S extends { + getState: () => infer T; + setState: infer SetState; +} ? SetState extends (...a: infer A) => infer Sr ? { + setState(nextStateOrUpdater: T | Partial | ((state: Draft) => void), shouldReplace?: boolean | undefined, ...a: SkipTwo): Sr; +} : never : never; +export declare const immer: Immer; +export {}; diff --git a/ts3.4/middleware/persist.d.ts b/ts3.4/middleware/persist.d.ts new file mode 100644 index 0000000..991dd16 --- /dev/null +++ b/ts3.4/middleware/persist.d.ts @@ -0,0 +1,139 @@ +import { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +export interface StateStorage { + getItem: (name: string) => string | null | Promise; + setItem: (name: string, value: string) => unknown | Promise; + removeItem: (name: string) => unknown | Promise; +} +export type StorageValue = { + state: S; + version?: number; +}; +export interface PersistStorage { + getItem: (name: string) => StorageValue | null | Promise | null>; + setItem: (name: string, value: StorageValue) => unknown | Promise; + removeItem: (name: string) => unknown | Promise; +} +type JsonStorageOptions = { + reviver?: (key: string, value: unknown) => unknown; + replacer?: (key: string, value: unknown) => unknown; +}; +export declare function createJSONStorage(getStorage: () => StateStorage, options?: JsonStorageOptions): PersistStorage | undefined; +export interface PersistOptions { + /** Name of the storage (must be unique) */ + name: string; + /** + * @deprecated Use `storage` instead. + * A function returning a storage. + * The storage must fit `window.localStorage`'s api (or an async version of it). + * For example the storage could be `AsyncStorage` from React Native. + * + * @default () => localStorage + */ + getStorage?: () => StateStorage; + /** + * @deprecated Use `storage` instead. + * Use a custom serializer. + * The returned string will be stored in the storage. + * + * @default JSON.stringify + */ + serialize?: (state: StorageValue) => string | Promise; + /** + * @deprecated Use `storage` instead. + * Use a custom deserializer. + * Must return an object matching StorageValue + * + * @param str The storage's current value. + * @default JSON.parse + */ + deserialize?: (str: string) => StorageValue | Promise>; + /** + * Use a custom persist storage. + * + * Combining `createJSONStorage` helps creating a persist storage + * with JSON.parse and JSON.stringify. + * + * @default createJSONStorage(() => localStorage) + */ + storage?: PersistStorage | undefined; + /** + * Filter the persisted value. + * + * @params state The state's value + */ + partialize?: (state: S) => PersistedState; + /** + * A function returning another (optional) function. + * The main function will be called before the state rehydration. + * The returned function will be called after the state rehydration or when an error occurred. + */ + onRehydrateStorage?: (state: S) => ((state?: S, error?: unknown) => void) | void; + /** + * If the stored state's version mismatch the one specified here, the storage will not be used. + * This is useful when adding a breaking change to your store. + */ + version?: number; + /** + * A function to perform persisted state migration. + * This function will be called when persisted state versions mismatch with the one specified here. + */ + migrate?: (persistedState: unknown, version: number) => PersistedState | Promise; + /** + * A function to perform custom hydration merges when combining the stored state with the current one. + * By default, this function does a shallow merge. + */ + merge?: (persistedState: unknown, currentState: S) => S; + /** + * An optional boolean that will prevent the persist middleware from triggering hydration on initialization, + * This allows you to call `rehydrate()` at a specific point in your apps rendering life-cycle. + * + * This is useful in SSR application. + * + * @default false + */ + skipHydration?: boolean; +} +type PersistListener = (state: S) => void; +type StorePersist = { + persist: { + setOptions: (options: Partial>) => void; + clearStorage: () => void; + rehydrate: () => Promise | void; + hasHydrated: () => boolean; + onHydrate: (fn: PersistListener) => () => void; + onFinishHydration: (fn: PersistListener) => () => void; + getOptions: () => Partial>; + }; +}; +type Persist = (initializer: StateCreator, options: PersistOptions) => StateCreator; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/persist': WithPersist; + } +} +type Write = Pick> & U; +type WithPersist = S extends { + getState: () => infer T; +} ? Write> : never; +export declare const persist: Persist; +export {}; diff --git a/ts3.4/middleware/redux.d.ts b/ts3.4/middleware/redux.d.ts new file mode 100644 index 0000000..24fe5c7 --- /dev/null +++ b/ts3.4/middleware/redux.d.ts @@ -0,0 +1,30 @@ +import { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type Write = Pick> & U; +type Action = { + type: string; +}; +type StoreRedux = { + dispatch: (a: A) => A; + dispatchFromDevtools: true; +}; +type ReduxState = { + dispatch: StoreRedux['dispatch']; +}; +type WithRedux = Write>; +type Redux = (reducer: (state: T, action: A) => T, initialState: T) => StateCreator>, Cms, [ + [ + 'zustand/redux', + A + ] +]>; +declare module '../vanilla' { + interface StoreMutators { + 'zustand/redux': WithRedux; + } +} +export declare const redux: Redux; +export {}; diff --git a/ts3.4/middleware/subscribeWithSelector.d.ts b/ts3.4/middleware/subscribeWithSelector.d.ts new file mode 100644 index 0000000..98991f6 --- /dev/null +++ b/ts3.4/middleware/subscribeWithSelector.d.ts @@ -0,0 +1,42 @@ +import { StateCreator, StoreMutatorIdentifier } from '../vanilla'; +type SubscribeWithSelector = (initializer: StateCreator) => StateCreator; +type Write = Pick> & U; +type WithSelectorSubscribe = S extends { + getState: () => infer T; +} ? Write> : never; +declare module '../vanilla' { + interface StoreMutators { + ['zustand/subscribeWithSelector']: WithSelectorSubscribe; + } +} +type StoreSubscribeWithSelector = { + subscribe: { + (listener: (selectedState: T, previousSelectedState: T) => void): () => void; + (selector: (state: T) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: { + equalityFn?: (a: U, b: U) => boolean; + fireImmediately?: boolean; + }): () => void; + }; +}; +export declare const subscribeWithSelector: SubscribeWithSelector; +export {}; diff --git a/ts3.4/react.d.ts b/ts3.4/react.d.ts new file mode 100644 index 0000000..3e61338 --- /dev/null +++ b/ts3.4/react.d.ts @@ -0,0 +1,48 @@ +import { Mutate, StateCreator, StoreApi, StoreMutatorIdentifier } from './vanilla'; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type ReadonlyStoreApi = Pick, 'getState' | 'subscribe'>; +type WithReact> = S & { + /** @deprecated please use api.getInitialState() */ + getServerState?: () => ExtractState; +}; +export declare function useStore>>(api: S): ExtractState; +export declare function useStore>, U>(api: S, selector: (state: ExtractState) => U): U; +/** + * @deprecated The usage with three arguments is deprecated. Use `useStoreWithEqualityFn` from 'zustand/traditional'. The usage with one or two arguments is not deprecated. + * https://github.com/pmndrs/zustand/discussions/1937 + */ +export declare function useStore>, U>(api: S, selector: (state: ExtractState) => U, equalityFn: ((a: U, b: U) => boolean) | undefined): U; +export type UseBoundStore>> = { + (): ExtractState; + (selector: (state: ExtractState) => U): U; + /** + * @deprecated Use `createWithEqualityFn` from 'zustand/traditional' + */ + (selector: (state: ExtractState) => U, equalityFn: (a: U, b: U) => boolean): U; +} & S; +type Create = { + (initializer: StateCreator): UseBoundStore, Mos>>; + (): (initializer: StateCreator) => UseBoundStore, Mos>>; + /** + * @deprecated Use `useStore` hook to bind store + */ + >(store: S): UseBoundStore; +}; +export declare const create: Create; +/** + * @deprecated Use `import { create } from 'zustand'` + */ +declare const _default: Create; +export default _default; diff --git a/ts3.4/react/shallow.d.ts b/ts3.4/react/shallow.d.ts new file mode 100644 index 0000000..9068d46 --- /dev/null +++ b/ts3.4/react/shallow.d.ts @@ -0,0 +1 @@ +export declare function useShallow(selector: (state: S) => U): (state: S) => U; diff --git a/ts3.4/shallow.d.ts b/ts3.4/shallow.d.ts new file mode 100644 index 0000000..0914a7c --- /dev/null +++ b/ts3.4/shallow.d.ts @@ -0,0 +1,7 @@ +import { shallow } from './vanilla/shallow'; +/** + * @deprecated Use `import { shallow } from 'zustand/shallow'` + */ +declare const _default: typeof shallow; +export default _default; +export { shallow }; diff --git a/ts3.4/traditional.d.ts b/ts3.4/traditional.d.ts new file mode 100644 index 0000000..bf8b70c --- /dev/null +++ b/ts3.4/traditional.d.ts @@ -0,0 +1,31 @@ +import { Mutate, StateCreator, StoreApi, StoreMutatorIdentifier } from './vanilla'; +type ExtractState = S extends { + getState: () => infer T; +} ? T : never; +type ReadonlyStoreApi = Pick, 'getState' | 'subscribe'>; +type WithReact> = S & { + /** @deprecated please use api.getInitialState() */ + getServerState?: () => ExtractState; +}; +export declare function useStoreWithEqualityFn>>(api: S): ExtractState; +export declare function useStoreWithEqualityFn>, U>(api: S, selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +export type UseBoundStoreWithEqualityFn>> = { + (): ExtractState; + (selector: (state: ExtractState) => U, equalityFn?: (a: U, b: U) => boolean): U; +} & S; +type CreateWithEqualityFn = { + (initializer: StateCreator, defaultEqualityFn?: (a: U, b: U) => boolean): UseBoundStoreWithEqualityFn, Mos>>; + (): (initializer: StateCreator, defaultEqualityFn?: (a: U, b: U) => boolean) => UseBoundStoreWithEqualityFn, Mos>>; +}; +export declare const createWithEqualityFn: CreateWithEqualityFn; +export {}; diff --git a/ts3.4/vanilla.d.ts b/ts3.4/vanilla.d.ts new file mode 100644 index 0000000..71c1961 --- /dev/null +++ b/ts3.4/vanilla.d.ts @@ -0,0 +1,115 @@ +type SetStateInternal = { + _(partial: T | Partial | { + _(state: T): T | Partial; + }['_'], replace?: boolean | undefined): void; +}['_']; +export interface StoreApi { + setState: SetStateInternal; + getState: () => T; + getInitialState: () => T; + subscribe: (listener: (state: T, prevState: T) => void) => () => void; + /** + * @deprecated Use `unsubscribe` returned by `subscribe` + */ + destroy: () => void; +} +type Get = K extends keyof T ? T[K] : F; +export type Mutate = number extends Ms['length' & keyof Ms] ? S : Ms extends [ +] ? S : Ms extends [ + [ + infer Mi, + infer Ma + ], + ...infer Mrs +] ? Mutate[Mi & StoreMutatorIdentifier], Mrs> : never; +export type StateCreator = ((setState: Get, Mis>, 'setState', never>, getState: Get, Mis>, 'getState', never>, store: Mutate, Mis>) => U) & { + $$storeMutators?: Mos; +}; +export interface StoreMutators { +} +export type StoreMutatorIdentifier = keyof StoreMutators; +type CreateStore = { + (initializer: StateCreator): Mutate, Mos>; + (): (initializer: StateCreator) => Mutate, Mos>; +}; +type Configs = { + cacheTime?: number; +}; +type CreateStoreImpl = (initializer: StateCreator, configs?: Configs) => Mutate, Mos>; +export declare const createStore: CreateStoreImpl; +/** + * @deprecated Use `import { createStore } from 'zustand/vanilla'` + */ +declare const _default: CreateStore; +export default _default; +/** + * @deprecated Use `unknown` instead of `State` + */ +export type State = unknown; +/** + * @deprecated Use `Partial | ((s: T) => Partial)` instead of `PartialState` + */ +export type PartialState = Partial | ((state: T) => Partial); +/** + * @deprecated Use `(s: T) => U` instead of `StateSelector` + */ +export type StateSelector = (state: T) => U; +/** + * @deprecated Use `(a: T, b: T) => boolean` instead of `EqualityChecker` + */ +export type EqualityChecker = (state: T, newState: T) => boolean; +/** + * @deprecated Use `(state: T, previousState: T) => void` instead of `StateListener` + */ +export type StateListener = (state: T, previousState: T) => void; +/** + * @deprecated Use `(slice: T, previousSlice: T) => void` instead of `StateSliceListener`. + */ +export type StateSliceListener = (slice: T, previousSlice: T) => void; +/** + * @deprecated Use `(listener: (state: T) => void) => void` instead of `Subscribe`. + */ +export type Subscribe = { + (listener: (state: T, previousState: T) => void): () => void; +}; +/** + * @deprecated You might be looking for `StateCreator`, if not then + * use `StoreApi['setState']` instead of `SetState`. + */ +export type SetState = { + _(partial: T | Partial | { + _(state: T): T | Partial; + }['_'], replace?: boolean | undefined): void; +}['_']; +/** + * @deprecated You might be looking for `StateCreator`, if not then + * use `StoreApi['getState']` instead of `GetState`. + */ +export type GetState = () => T; +/** + * @deprecated Use `StoreApi['destroy']` instead of `Destroy`. + */ +export type Destroy = () => void; diff --git a/ts3.4/vanilla/shallow.d.ts b/ts3.4/vanilla/shallow.d.ts new file mode 100644 index 0000000..a8d2354 --- /dev/null +++ b/ts3.4/vanilla/shallow.d.ts @@ -0,0 +1 @@ +export declare function shallow(objA: T, objB: T): boolean; diff --git a/umd/context.development.js b/umd/context.development.js new file mode 100644 index 0000000..440229c --- /dev/null +++ b/umd/context.development.js @@ -0,0 +1,68 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react'), require('zustand/traditional')) : + typeof define === 'function' && define.amd ? define(['react', 'zustand/traditional'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.zustandContext = factory(global.React, global.traditional)); +})(this, (function (ReactExports, traditional) { 'use strict'; + + function _extends() { + _extends = Object.assign ? Object.assign.bind() : function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + return target; + }; + return _extends.apply(this, arguments); + } + + var createElement = ReactExports.createElement, + reactCreateContext = ReactExports.createContext, + useContext = ReactExports.useContext, + useMemo = ReactExports.useMemo, + useRef = ReactExports.useRef; + function createContext() { + { + console.warn("[DEPRECATED] `context` will be removed in a future version. Instead use `import { createStore, useStore } from 'zustand'`. See: https://github.com/pmndrs/zustand/discussions/1180."); + } + var ZustandContext = reactCreateContext(undefined); + var Provider = function Provider(_ref) { + var createStore = _ref.createStore, + children = _ref.children; + var storeRef = useRef(); + if (!storeRef.current) { + storeRef.current = createStore(); + } + return createElement(ZustandContext.Provider, { + value: storeRef.current + }, children); + }; + var useContextStore = function useContextStore(selector, equalityFn) { + var store = useContext(ZustandContext); + if (!store) { + throw new Error('Seems like you have not used zustand provider as an ancestor.'); + } + return traditional.useStoreWithEqualityFn(store, selector, equalityFn); + }; + var useStoreApi = function useStoreApi() { + var store = useContext(ZustandContext); + if (!store) { + throw new Error('Seems like you have not used zustand provider as an ancestor.'); + } + return useMemo(function () { + return _extends({}, store); + }, [store]); + }; + return { + Provider: Provider, + useStore: useContextStore, + useStoreApi: useStoreApi + }; + } + + return createContext; + +})); diff --git a/umd/context.production.js b/umd/context.production.js new file mode 100644 index 0000000..f8f1ce1 --- /dev/null +++ b/umd/context.production.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("react"),require("zustand/traditional")):"function"==typeof define&&define.amd?define(["react","zustand/traditional"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).zustandContext=t(e.React,e.traditional)}(this,(function(e,t){"use strict";function r(){return r=Object.assign?Object.assign.bind():function(e){for(var t=1;t= 0) continue; + target[key] = source[key]; + } + return target; + } + + var reduxImpl = function reduxImpl(reducer, initial) { + return function (set, _get, api) { + api.dispatch = function (action) { + set(function (state) { + return reducer(state, action); + }, false, action); + return action; + }; + api.dispatchFromDevtools = true; + return _extends({ + dispatch: function dispatch() { + var _ref; + return (_ref = api).dispatch.apply(_ref, arguments); + } + }, initial); + }; + }; + var redux = reduxImpl; + + var _excluded = ["enabled", "anonymousActionType", "store"], + _excluded2 = ["connection"]; + var trackedConnections = new Map(); + var getTrackedConnectionState = function getTrackedConnectionState(name) { + var api = trackedConnections.get(name); + if (!api) return {}; + return Object.fromEntries(Object.entries(api.stores).map(function (_ref) { + var key = _ref[0], + api = _ref[1]; + return [key, api.getState()]; + })); + }; + var extractConnectionInformation = function extractConnectionInformation(store, extensionConnector, options) { + if (store === undefined) { + return { + type: 'untracked', + connection: extensionConnector.connect(options) + }; + } + var existingConnection = trackedConnections.get(options.name); + if (existingConnection) { + return _extends({ + type: 'tracked', + store: store + }, existingConnection); + } + var newConnection = { + connection: extensionConnector.connect(options), + stores: {} + }; + trackedConnections.set(options.name, newConnection); + return _extends({ + type: 'tracked', + store: store + }, newConnection); + }; + var devtoolsImpl = function devtoolsImpl(fn, devtoolsOptions) { + if (devtoolsOptions === void 0) { + devtoolsOptions = {}; + } + return function (set, get, api) { + var _devtoolsOptions = devtoolsOptions, + enabled = _devtoolsOptions.enabled, + anonymousActionType = _devtoolsOptions.anonymousActionType, + store = _devtoolsOptions.store, + options = _objectWithoutPropertiesLoose(_devtoolsOptions, _excluded); + var extensionConnector; + try { + extensionConnector = (enabled != null ? enabled : "development" !== 'production') && window.__REDUX_DEVTOOLS_EXTENSION__; + } catch (e) {} + if (!extensionConnector) { + if (enabled) { + console.warn('[zustand devtools middleware] Please install/enable Redux devtools extension'); + } + return fn(set, get, api); + } + var _extractConnectionInf = extractConnectionInformation(store, extensionConnector, options), + connection = _extractConnectionInf.connection, + connectionInformation = _objectWithoutPropertiesLoose(_extractConnectionInf, _excluded2); + var isRecording = true; + api.setState = function (state, replace, nameOrAction) { + var _extends2; + var r = set(state, replace); + if (!isRecording) return r; + var action = nameOrAction === undefined ? { + type: anonymousActionType || 'anonymous' + } : typeof nameOrAction === 'string' ? { + type: nameOrAction + } : nameOrAction; + if (store === undefined) { + connection == null || connection.send(action, get()); + return r; + } + connection == null || connection.send(_extends({}, action, { + type: store + "/" + action.type + }), _extends({}, getTrackedConnectionState(options.name), (_extends2 = {}, _extends2[store] = api.getState(), _extends2))); + return r; + }; + var setStateFromDevtools = function setStateFromDevtools() { + var originalIsRecording = isRecording; + isRecording = false; + set.apply(void 0, arguments); + isRecording = originalIsRecording; + }; + var initialState = fn(api.setState, get, api); + if (connectionInformation.type === 'untracked') { + connection == null || connection.init(initialState); + } else { + connectionInformation.stores[connectionInformation.store] = api; + connection == null || connection.init(Object.fromEntries(Object.entries(connectionInformation.stores).map(function (_ref2) { + var key = _ref2[0], + store = _ref2[1]; + return [key, key === connectionInformation.store ? initialState : store.getState()]; + }))); + } + if (api.dispatchFromDevtools && typeof api.dispatch === 'function') { + var didWarnAboutReservedActionType = false; + var originalDispatch = api.dispatch; + api.dispatch = function () { + for (var _len = arguments.length, a = new Array(_len), _key = 0; _key < _len; _key++) { + a[_key] = arguments[_key]; + } + if (a[0].type === '__setState' && !didWarnAboutReservedActionType) { + console.warn('[zustand devtools middleware] "__setState" action type is reserved ' + 'to set state from the devtools. Avoid using it.'); + didWarnAboutReservedActionType = true; + } + originalDispatch.apply(void 0, a); + }; + } + connection.subscribe(function (message) { + switch (message.type) { + case 'ACTION': + if (typeof message.payload !== 'string') { + console.error('[zustand devtools middleware] Unsupported action format'); + return; + } + return parseJsonThen(message.payload, function (action) { + if (action.type === '__setState') { + if (store === undefined) { + setStateFromDevtools(action.state); + return; + } + if (Object.keys(action.state).length !== 1) { + console.error("\n [zustand devtools middleware] Unsupported __setState action format. \n When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(),\n and value of this only key should be a state object. Example: { \"type\": \"__setState\", \"state\": { \"abc123Store\": { \"foo\": \"bar\" } } }\n "); + } + var stateFromDevtools = action.state[store]; + if (stateFromDevtools === undefined || stateFromDevtools === null) { + return; + } + if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) { + setStateFromDevtools(stateFromDevtools); + } + return; + } + if (!api.dispatchFromDevtools) return; + if (typeof api.dispatch !== 'function') return; + api.dispatch(action); + }); + case 'DISPATCH': + switch (message.payload.type) { + case 'RESET': + setStateFromDevtools(initialState); + if (store === undefined) { + return connection == null ? void 0 : connection.init(api.getState()); + } + return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + case 'COMMIT': + if (store === undefined) { + connection == null || connection.init(api.getState()); + return; + } + return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name)); + case 'ROLLBACK': + return parseJsonThen(message.state, function (state) { + if (store === undefined) { + setStateFromDevtools(state); + connection == null || connection.init(api.getState()); + return; + } + setStateFromDevtools(state[store]); + connection == null || connection.init(getTrackedConnectionState(options.name)); + }); + case 'JUMP_TO_STATE': + case 'JUMP_TO_ACTION': + return parseJsonThen(message.state, function (state) { + if (store === undefined) { + setStateFromDevtools(state); + return; + } + if (JSON.stringify(api.getState()) !== JSON.stringify(state[store])) { + setStateFromDevtools(state[store]); + } + }); + case 'IMPORT_STATE': + { + var _nextLiftedState$comp; + var nextLiftedState = message.payload.nextLiftedState; + var lastComputedState = (_nextLiftedState$comp = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _nextLiftedState$comp.state; + if (!lastComputedState) return; + if (store === undefined) { + setStateFromDevtools(lastComputedState); + } else { + setStateFromDevtools(lastComputedState[store]); + } + connection == null || connection.send(null, nextLiftedState); + return; + } + case 'PAUSE_RECORDING': + return isRecording = !isRecording; + } + return; + } + }); + return initialState; + }; + }; + var devtools = devtoolsImpl; + var parseJsonThen = function parseJsonThen(stringified, f) { + var parsed; + try { + parsed = JSON.parse(stringified); + } catch (e) { + console.error('[zustand devtools middleware] Could not parse the received json', e); + } + if (parsed !== undefined) f(parsed); + }; + + var subscribeWithSelectorImpl = function subscribeWithSelectorImpl(fn) { + return function (set, get, api) { + var origSubscribe = api.subscribe; + api.subscribe = function (selector, optListener, options) { + var listener = selector; + if (optListener) { + var equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is; + var currentSlice = selector(api.getState()); + listener = function listener(state) { + var nextSlice = selector(state); + if (!equalityFn(currentSlice, nextSlice)) { + var previousSlice = currentSlice; + optListener(currentSlice = nextSlice, previousSlice); + } + }; + if (options != null && options.fireImmediately) { + optListener(currentSlice, currentSlice); + } + } + return origSubscribe(listener); + }; + var initialState = fn(set, get, api); + return initialState; + }; + }; + var subscribeWithSelector = subscribeWithSelectorImpl; + + var combine = function combine(initialState, create) { + return function () { + return Object.assign({}, initialState, create.apply(void 0, arguments)); + }; + }; + + function createJSONStorage(getStorage, options) { + var storage; + try { + storage = getStorage(); + } catch (e) { + return; + } + var persistStorage = { + getItem: function getItem(name) { + var _getItem; + var parse = function parse(str) { + if (str === null) { + return null; + } + return JSON.parse(str, options == null ? void 0 : options.reviver); + }; + var str = (_getItem = storage.getItem(name)) != null ? _getItem : null; + if (str instanceof Promise) { + return str.then(parse); + } + return parse(str); + }, + setItem: function setItem(name, newValue) { + return storage.setItem(name, JSON.stringify(newValue, options == null ? void 0 : options.replacer)); + }, + removeItem: function removeItem(name) { + return storage.removeItem(name); + } + }; + return persistStorage; + } + var toThenable = function toThenable(fn) { + return function (input) { + try { + var result = fn(input); + if (result instanceof Promise) { + return result; + } + return { + then: function then(onFulfilled) { + return toThenable(onFulfilled)(result); + }, + catch: function _catch(_onRejected) { + return this; + } + }; + } catch (e) { + return { + then: function then(_onFulfilled) { + return this; + }, + catch: function _catch(onRejected) { + return toThenable(onRejected)(e); + } + }; + } + }; + }; + var oldImpl = function oldImpl(config, baseOptions) { + return function (set, get, api) { + var options = _extends({ + getStorage: function getStorage() { + return localStorage; + }, + serialize: JSON.stringify, + deserialize: JSON.parse, + partialize: function partialize(state) { + return state; + }, + version: 0, + merge: function merge(persistedState, currentState) { + return _extends({}, currentState, persistedState); + } + }, baseOptions); + var _hasHydrated = false; + var hydrationListeners = new Set(); + var finishHydrationListeners = new Set(); + var storage; + try { + storage = options.getStorage(); + } catch (e) {} + if (!storage) { + return config(function () { + console.warn("[zustand persist middleware] Unable to update item '" + options.name + "', the given storage is currently unavailable."); + set.apply(void 0, arguments); + }, get, api); + } + var thenableSerialize = toThenable(options.serialize); + var setItem = function setItem() { + var state = options.partialize(_extends({}, get())); + var errorInSync; + var thenable = thenableSerialize({ + state: state, + version: options.version + }).then(function (serializedValue) { + return storage.setItem(options.name, serializedValue); + }).catch(function (e) { + errorInSync = e; + }); + if (errorInSync) { + throw errorInSync; + } + return thenable; + }; + var savedSetState = api.setState; + api.setState = function (state, replace) { + savedSetState(state, replace); + void setItem(); + }; + var configResult = config(function () { + set.apply(void 0, arguments); + void setItem(); + }, get, api); + var stateFromStorage; + var hydrate = function hydrate() { + if (!storage) return; + _hasHydrated = false; + hydrationListeners.forEach(function (cb) { + return cb(get()); + }); + var postRehydrationCallback = (options.onRehydrateStorage == null ? void 0 : options.onRehydrateStorage(get())) || undefined; + return toThenable(storage.getItem.bind(storage))(options.name).then(function (storageValue) { + if (storageValue) { + return options.deserialize(storageValue); + } + }).then(function (deserializedStorageValue) { + if (deserializedStorageValue) { + if (typeof deserializedStorageValue.version === 'number' && deserializedStorageValue.version !== options.version) { + if (options.migrate) { + return options.migrate(deserializedStorageValue.state, deserializedStorageValue.version); + } + console.error("State loaded from storage couldn't be migrated since no migrate function was provided"); + } else { + return deserializedStorageValue.state; + } + } + }).then(function (migratedState) { + var _get; + stateFromStorage = options.merge(migratedState, (_get = get()) != null ? _get : configResult); + set(stateFromStorage, true); + return setItem(); + }).then(function () { + postRehydrationCallback == null || postRehydrationCallback(stateFromStorage, undefined); + _hasHydrated = true; + finishHydrationListeners.forEach(function (cb) { + return cb(stateFromStorage); + }); + }).catch(function (e) { + postRehydrationCallback == null || postRehydrationCallback(undefined, e); + }); + }; + api.persist = { + setOptions: function setOptions(newOptions) { + options = _extends({}, options, newOptions); + if (newOptions.getStorage) { + storage = newOptions.getStorage(); + } + }, + clearStorage: function clearStorage() { + var _storage; + (_storage = storage) == null || _storage.removeItem(options.name); + }, + getOptions: function getOptions() { + return options; + }, + rehydrate: function rehydrate() { + return hydrate(); + }, + hasHydrated: function hasHydrated() { + return _hasHydrated; + }, + onHydrate: function onHydrate(cb) { + hydrationListeners.add(cb); + return function () { + hydrationListeners.delete(cb); + }; + }, + onFinishHydration: function onFinishHydration(cb) { + finishHydrationListeners.add(cb); + return function () { + finishHydrationListeners.delete(cb); + }; + } + }; + hydrate(); + return stateFromStorage || configResult; + }; + }; + var newImpl = function newImpl(config, baseOptions) { + return function (set, get, api) { + var options = _extends({ + storage: createJSONStorage(function () { + return localStorage; + }), + partialize: function partialize(state) { + return state; + }, + version: 0, + merge: function merge(persistedState, currentState) { + return _extends({}, currentState, persistedState); + } + }, baseOptions); + var _hasHydrated2 = false; + var hydrationListeners = new Set(); + var finishHydrationListeners = new Set(); + var storage = options.storage; + if (!storage) { + return config(function () { + console.warn("[zustand persist middleware] Unable to update item '" + options.name + "', the given storage is currently unavailable."); + set.apply(void 0, arguments); + }, get, api); + } + var setItem = function setItem() { + var state = options.partialize(_extends({}, get())); + return storage.setItem(options.name, { + state: state, + version: options.version + }); + }; + var savedSetState = api.setState; + api.setState = function (state, replace) { + savedSetState(state, replace); + void setItem(); + }; + var configResult = config(function () { + set.apply(void 0, arguments); + void setItem(); + }, get, api); + api.getInitialState = function () { + return configResult; + }; + var stateFromStorage; + var hydrate = function hydrate() { + var _get3; + if (!storage) return; + _hasHydrated2 = false; + hydrationListeners.forEach(function (cb) { + var _get2; + return cb((_get2 = get()) != null ? _get2 : configResult); + }); + var postRehydrationCallback = (options.onRehydrateStorage == null ? void 0 : options.onRehydrateStorage((_get3 = get()) != null ? _get3 : configResult)) || undefined; + return toThenable(storage.getItem.bind(storage))(options.name).then(function (deserializedStorageValue) { + if (deserializedStorageValue) { + if (typeof deserializedStorageValue.version === 'number' && deserializedStorageValue.version !== options.version) { + if (options.migrate) { + return options.migrate(deserializedStorageValue.state, deserializedStorageValue.version); + } + console.error("State loaded from storage couldn't be migrated since no migrate function was provided"); + } else { + return deserializedStorageValue.state; + } + } + }).then(function (migratedState) { + var _get4; + stateFromStorage = options.merge(migratedState, (_get4 = get()) != null ? _get4 : configResult); + set(stateFromStorage, true); + return setItem(); + }).then(function () { + postRehydrationCallback == null || postRehydrationCallback(stateFromStorage, undefined); + stateFromStorage = get(); + _hasHydrated2 = true; + finishHydrationListeners.forEach(function (cb) { + return cb(stateFromStorage); + }); + }).catch(function (e) { + postRehydrationCallback == null || postRehydrationCallback(undefined, e); + }); + }; + api.persist = { + setOptions: function setOptions(newOptions) { + options = _extends({}, options, newOptions); + if (newOptions.storage) { + storage = newOptions.storage; + } + }, + clearStorage: function clearStorage() { + var _storage2; + (_storage2 = storage) == null || _storage2.removeItem(options.name); + }, + getOptions: function getOptions() { + return options; + }, + rehydrate: function rehydrate() { + return hydrate(); + }, + hasHydrated: function hasHydrated() { + return _hasHydrated2; + }, + onHydrate: function onHydrate(cb) { + hydrationListeners.add(cb); + return function () { + hydrationListeners.delete(cb); + }; + }, + onFinishHydration: function onFinishHydration(cb) { + finishHydrationListeners.add(cb); + return function () { + finishHydrationListeners.delete(cb); + }; + } + }; + if (!options.skipHydration) { + hydrate(); + } + return stateFromStorage || configResult; + }; + }; + var persistImpl = function persistImpl(config, baseOptions) { + if ('getStorage' in baseOptions || 'serialize' in baseOptions || 'deserialize' in baseOptions) { + { + console.warn('[DEPRECATED] `getStorage`, `serialize` and `deserialize` options are deprecated. Use `storage` option instead.'); + } + return oldImpl(config, baseOptions); + } + return newImpl(config, baseOptions); + }; + var persist = persistImpl; + + exports.combine = combine; + exports.createJSONStorage = createJSONStorage; + exports.devtools = devtools; + exports.persist = persist; + exports.redux = redux; + exports.subscribeWithSelector = subscribeWithSelector; + +})); diff --git a/umd/middleware.production.js b/umd/middleware.production.js new file mode 100644 index 0000000..9c87d01 --- /dev/null +++ b/umd/middleware.production.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).zustandMiddleware={})}(this,(function(t){"use strict";function e(){return e=Object.assign?Object.assign.bind():function(t){for(var e=1;e=0||(i[n]=t[n]);return i}var r=function(t,n){return function(r,i,o){return o.dispatch=function(e){return r((function(n){return t(n,e)}),!1,e),e},o.dispatchFromDevtools=!0,e({dispatch:function(){var t;return(t=o).dispatch.apply(t,arguments)}},n)}},i=["enabled","anonymousActionType","store"],o=["connection"],a=new Map,u=function(t){var e=a.get(t);return e?Object.fromEntries(Object.entries(e.stores).map((function(t){return[t[0],t[1].getState()]}))):{}},c=function(t,r){return void 0===r&&(r={}),function(c,f,l){var d,v=r,p=v.enabled,g=v.anonymousActionType,y=v.store,m=n(v,i);try{d=null!=p&&p&&window.__REDUX_DEVTOOLS_EXTENSION__}catch(t){}if(!d)return t(c,f,l);var h=function(t,n,r){if(void 0===t)return{type:"untracked",connection:n.connect(r)};var i=a.get(r.name);if(i)return e({type:"tracked",store:t},i);var o={connection:n.connect(r),stores:{}};return a.set(r.name,o),e({type:"tracked",store:t},o)}(y,d,m),S=h.connection,b=n(h,o),O=!0;l.setState=function(t,n,r){var i,o=c(t,n);if(!O)return o;var a=void 0===r?{type:g||"anonymous"}:"string"==typeof r?{type:r}:r;return void 0===y?(null==S||S.send(a,f()),o):(null==S||S.send(e({},a,{type:y+"/"+a.type}),e({},u(m.name),((i={})[y]=l.getState(),i))),o)};var w=function(){var t=O;O=!1,c.apply(void 0,arguments),O=t},I=t(l.setState,f,l);if("untracked"===b.type?null==S||S.init(I):(b.stores[b.store]=l,null==S||S.init(Object.fromEntries(Object.entries(b.stores).map((function(t){var e=t[0],n=t[1];return[e,e===b.store?I:n.getState()]}))))),l.dispatchFromDevtools&&"function"==typeof l.dispatch){var T=l.dispatch;l.dispatch=function(){for(var t=arguments.length,e=new Array(t),n=0;n 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + a[_key - 2] = arguments[_key]; + } + return set.apply(void 0, [nextState, replace].concat(a)); + }; + return initializer(store.setState, get, store); + }; + }; + var immer = immerImpl; + + exports.immer = immer; + +})); diff --git a/umd/middleware/immer.production.js b/umd/middleware/immer.production.js new file mode 100644 index 0000000..1405dfb --- /dev/null +++ b/umd/middleware/immer.production.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("immer")):"function"==typeof define&&define.amd?define(["exports","immer"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).zustandMiddlewareImmer={},e.immer)}(this,(function(e,t){"use strict";var n=function(e){return function(n,r,i){return i.setState=function(e,r){for(var i="function"==typeof e?t.produce(e):e,o=arguments.length,f=new Array(o>2?o-2:0),u=2;u arr.length) len = arr.length; + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + return arr2; + } + function _createForOfIteratorHelperLoose(o, allowArrayLike) { + var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; + if (it) return (it = it.call(o)).next.bind(it); + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }; + } + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + + function shallow(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) return false; + for (var _iterator = _createForOfIteratorHelperLoose(objA), _step; !(_step = _iterator()).done;) { + var _step$value = _step.value, + key = _step$value[0], + value = _step$value[1]; + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) return false; + for (var _iterator2 = _createForOfIteratorHelperLoose(objA), _step2; !(_step2 = _iterator2()).done;) { + var _value = _step2.value; + if (!objB.has(_value)) { + return false; + } + } + return true; + } + var keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (var _i = 0, _keysA = keysA; _i < _keysA.length; _i++) { + var keyA = _keysA[_i]; + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; + } + + var useRef = ReactExports.useRef; + function useShallow(selector) { + var prev = useRef(); + return function (state) { + var next = selector(state); + return shallow(prev.current, next) ? prev.current : prev.current = next; + }; + } + + exports.useShallow = useShallow; + +})); diff --git a/umd/react/shallow.production.js b/umd/react/shallow.production.js new file mode 100644 index 0000000..4dce901 --- /dev/null +++ b/umd/react/shallow.production.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).zustandReactShallow={},e.React)}(this,(function(e,t){"use strict";function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o=t.useRef;e.useShallow=function(e){var t=o();return function(r){var o=e(r);return function(e,t){if(Object.is(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;if(e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(var r,o=n(e);!(r=o()).done;){var i=r.value,a=i[0],u=i[1];if(!Object.is(u,t.get(a)))return!1}return!0}if(e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(var f,c=n(e);!(f=c()).done;){var l=f.value;if(!t.has(l))return!1}return!0}var s=Object.keys(e);if(s.length!==Object.keys(t).length)return!1;for(var y=0,p=s;y arr.length) len = arr.length; + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + return arr2; + } + function _createForOfIteratorHelperLoose(o, allowArrayLike) { + var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; + if (it) return (it = it.call(o)).next.bind(it); + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }; + } + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + + function shallow$1(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) return false; + for (var _iterator = _createForOfIteratorHelperLoose(objA), _step; !(_step = _iterator()).done;) { + var _step$value = _step.value, + key = _step$value[0], + value = _step$value[1]; + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) return false; + for (var _iterator2 = _createForOfIteratorHelperLoose(objA), _step2; !(_step2 = _iterator2()).done;) { + var _value = _step2.value; + if (!objB.has(_value)) { + return false; + } + } + return true; + } + var keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (var _i = 0, _keysA = keysA; _i < _keysA.length; _i++) { + var keyA = _keysA[_i]; + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; + } + + var shallow = (function (objA, objB) { + { + console.warn("[DEPRECATED] Default export is deprecated. Instead use `import { shallow } from 'zustand/shallow'`."); + } + return shallow$1(objA, objB); + }); + + exports.default = shallow; + exports.shallow = shallow$1; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/umd/shallow.production.js b/umd/shallow.production.js new file mode 100644 index 0000000..09b0f25 --- /dev/null +++ b/umd/shallow.production.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).zustandShallow={})}(this,(function(e){"use strict";function t(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function n(e,t){if(Object.is(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;if(e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(var n,o=r(e);!(n=o()).done;){var i=n.value,a=i[0],f=i[1];if(!Object.is(f,t.get(a)))return!1}return!0}if(e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(var u,l=r(e);!(u=l()).done;){var s=u.value;if(!t.has(s))return!1}return!0}var c=Object.keys(e);if(c.length!==Object.keys(t).length)return!1;for(var d=0,y=c;d arr.length) len = arr.length; + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + return arr2; + } + function _createForOfIteratorHelperLoose(o, allowArrayLike) { + var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; + if (it) return (it = it.call(o)).next.bind(it); + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }; + } + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } + + function shallow(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) return false; + for (var _iterator = _createForOfIteratorHelperLoose(objA), _step; !(_step = _iterator()).done;) { + var _step$value = _step.value, + key = _step$value[0], + value = _step$value[1]; + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) return false; + for (var _iterator2 = _createForOfIteratorHelperLoose(objA), _step2; !(_step2 = _iterator2()).done;) { + var _value = _step2.value; + if (!objB.has(_value)) { + return false; + } + } + return true; + } + var keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (var _i = 0, _keysA = keysA; _i < _keysA.length; _i++) { + var keyA = _keysA[_i]; + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; + } + + exports.shallow = shallow; + +})); diff --git a/umd/vanilla/shallow.production.js b/umd/vanilla/shallow.production.js new file mode 100644 index 0000000..0f8bbbb --- /dev/null +++ b/umd/vanilla/shallow.production.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).zustandVanillaShallow={})}(this,(function(e){"use strict";function t(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}e.shallow=function(e,t){if(Object.is(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;if(e instanceof Map&&t instanceof Map){if(e.size!==t.size)return!1;for(var n,o=r(e);!(n=o()).done;){var i=n.value,a=i[0],f=i[1];if(!Object.is(f,t.get(a)))return!1}return!0}if(e instanceof Set&&t instanceof Set){if(e.size!==t.size)return!1;for(var l,u=r(e);!(l=u()).done;){var s=l.value;if(!t.has(s))return!1}return!0}var c=Object.keys(e);if(c.length!==Object.keys(t).length)return!1;for(var y=0,p=c;y = { + _(partial: T | Partial | { + _(state: T): T | Partial; + }['_'], replace?: boolean | undefined): void; +}['_']; +export interface StoreApi { + setState: SetStateInternal; + getState: () => T; + getInitialState: () => T; + subscribe: (listener: (state: T, prevState: T) => void) => () => void; + /** + * @deprecated Use `unsubscribe` returned by `subscribe` + */ + destroy: () => void; +} +type Get = K extends keyof T ? T[K] : F; +export type Mutate = number extends Ms['length' & keyof Ms] ? S : Ms extends [] ? S : Ms extends [[infer Mi, infer Ma], ...infer Mrs] ? Mutate[Mi & StoreMutatorIdentifier], Mrs> : never; +export type StateCreator = ((setState: Get, Mis>, 'setState', never>, getState: Get, Mis>, 'getState', never>, store: Mutate, Mis>) => U) & { + $$storeMutators?: Mos; +}; +export interface StoreMutators { +} +export type StoreMutatorIdentifier = keyof StoreMutators; +type CreateStore = { + (initializer: StateCreator): Mutate, Mos>; + (): (initializer: StateCreator) => Mutate, Mos>; +}; +type Configs = { + cacheTime?: number; +}; +type CreateStoreImpl = (initializer: StateCreator, configs?: Configs) => Mutate, Mos>; +export declare const createStore: CreateStoreImpl; +/** + * @deprecated Use `import { createStore } from 'zustand/vanilla'` + */ +declare const _default: CreateStore; +export default _default; +/** + * @deprecated Use `unknown` instead of `State` + */ +export type State = unknown; +/** + * @deprecated Use `Partial | ((s: T) => Partial)` instead of `PartialState` + */ +export type PartialState = Partial | ((state: T) => Partial); +/** + * @deprecated Use `(s: T) => U` instead of `StateSelector` + */ +export type StateSelector = (state: T) => U; +/** + * @deprecated Use `(a: T, b: T) => boolean` instead of `EqualityChecker` + */ +export type EqualityChecker = (state: T, newState: T) => boolean; +/** + * @deprecated Use `(state: T, previousState: T) => void` instead of `StateListener` + */ +export type StateListener = (state: T, previousState: T) => void; +/** + * @deprecated Use `(slice: T, previousSlice: T) => void` instead of `StateSliceListener`. + */ +export type StateSliceListener = (slice: T, previousSlice: T) => void; +/** + * @deprecated Use `(listener: (state: T) => void) => void` instead of `Subscribe`. + */ +export type Subscribe = { + (listener: (state: T, previousState: T) => void): () => void; +}; +/** + * @deprecated You might be looking for `StateCreator`, if not then + * use `StoreApi['setState']` instead of `SetState`. + */ +export type SetState = { + _(partial: T | Partial | { + _(state: T): T | Partial; + }['_'], replace?: boolean | undefined): void; +}['_']; +/** + * @deprecated You might be looking for `StateCreator`, if not then + * use `StoreApi['getState']` instead of `GetState`. + */ +export type GetState = () => T; +/** + * @deprecated Use `StoreApi['destroy']` instead of `Destroy`. + */ +export type Destroy = () => void; diff --git a/vanilla.js b/vanilla.js new file mode 100644 index 0000000..bd20d7b --- /dev/null +++ b/vanilla.js @@ -0,0 +1,70 @@ +'use strict'; + +var createStoreImpl = function createStoreImpl(createState, configs) { + var timeout; + var state; + var listeners = new Set(); + var setState = function setState(partial, replace) { + var nextState = typeof partial === 'function' ? partial(state) : partial; + if (!Object.is(nextState, state)) { + var _previousState = state; + state = (replace != null ? replace : typeof nextState !== 'object' || nextState === null) ? nextState : Object.assign({}, state, nextState); + listeners.forEach(function (listener) { + return listener(state, _previousState); + }); + } + }; + var getState = function getState() { + return state; + }; + var getInitialState = function getInitialState() { + return initialState; + }; + var resetBasedOnCachTime = function resetBasedOnCachTime() { + if (!(configs != null && configs.cacheTime)) return; + if (timeout) clearTimeout(timeout); + timeout = setTimeout(function () { + state = createState(setState, getState, api); + }, configs == null ? void 0 : configs.cacheTime); + }; + var subscribe = function subscribe(listener) { + listeners.add(listener); + if (timeout) clearTimeout(timeout); + return function () { + var responseDelete = listeners.delete(listener); + if (!listeners.size) { + resetBasedOnCachTime(); + } + return responseDelete; + }; + }; + var destroy = function destroy() { + if (process.env.NODE_ENV !== 'production') { + console.warn('[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected.'); + } + listeners.clear(); + }; + var api = { + setState: setState, + getState: getState, + getInitialState: getInitialState, + subscribe: subscribe, + destroy: destroy + }; + var initialState = state = createState(setState, getState, api); + return api; +}; +var createStore = createStoreImpl; +var vanilla = (function (createState) { + if (process.env.NODE_ENV !== 'production') { + console.warn("[DEPRECATED] Default export is deprecated. Instead use import { createStore } from 'zustand/vanilla'."); + } + return createStore(createState); +}); + +exports.createStore = createStore; +exports.default = vanilla; + +module.exports = vanilla; +module.exports.createStore = createStore; +exports.default = module.exports; diff --git a/vanilla/shallow.d.ts b/vanilla/shallow.d.ts new file mode 100644 index 0000000..a8d2354 --- /dev/null +++ b/vanilla/shallow.d.ts @@ -0,0 +1 @@ +export declare function shallow(objA: T, objB: T): boolean; diff --git a/vanilla/shallow.js b/vanilla/shallow.js new file mode 100644 index 0000000..0cd078e --- /dev/null +++ b/vanilla/shallow.js @@ -0,0 +1,77 @@ +'use strict'; + +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(o); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); +} +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; + return arr2; +} +function _createForOfIteratorHelperLoose(o, allowArrayLike) { + var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; + if (it) return (it = it.call(o)).next.bind(it); + if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { + if (it) o = it; + var i = 0; + return function () { + if (i >= o.length) return { + done: true + }; + return { + done: false, + value: o[i++] + }; + }; + } + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} + +function shallow(objA, objB) { + if (Object.is(objA, objB)) { + return true; + } + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + if (objA instanceof Map && objB instanceof Map) { + if (objA.size !== objB.size) return false; + for (var _iterator = _createForOfIteratorHelperLoose(objA), _step; !(_step = _iterator()).done;) { + var _step$value = _step.value, + key = _step$value[0], + value = _step$value[1]; + if (!Object.is(value, objB.get(key))) { + return false; + } + } + return true; + } + if (objA instanceof Set && objB instanceof Set) { + if (objA.size !== objB.size) return false; + for (var _iterator2 = _createForOfIteratorHelperLoose(objA), _step2; !(_step2 = _iterator2()).done;) { + var _value = _step2.value; + if (!objB.has(_value)) { + return false; + } + } + return true; + } + var keysA = Object.keys(objA); + if (keysA.length !== Object.keys(objB).length) { + return false; + } + for (var _i = 0, _keysA = keysA; _i < _keysA.length; _i++) { + var keyA = _keysA[_i]; + if (!Object.prototype.hasOwnProperty.call(objB, keyA) || !Object.is(objA[keyA], objB[keyA])) { + return false; + } + } + return true; +} + +exports.shallow = shallow;