@lunarhue/store 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LunarHUE
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # `@lunarhue/store`
2
+
3
+ Framework-agnostic state built on top of `@tanstack/store`, with plugin
4
+ composition and React bindings.
5
+
6
+ ## Install
7
+
8
+ ```sh
9
+ npm install @lunarhue/store
10
+ ```
11
+
12
+ ## Entry points
13
+
14
+ - `@lunarhue/store/core`
15
+ - `@lunarhue/store/react`
16
+ - `@lunarhue/store/plugins/actions`
17
+ - `@lunarhue/store/plugins/persist`
18
+
19
+ There is no root package barrel export.
20
+
21
+ ## Basic usage
22
+
23
+ ```ts
24
+ import { createStore } from '@lunarhue/store/core'
25
+
26
+ const CounterStore = createStore({
27
+ count: 0,
28
+ })
29
+
30
+ const store = CounterStore.create()
31
+
32
+ store.setState((prev) => ({
33
+ ...prev,
34
+ count: prev.count + 1,
35
+ }))
36
+ ```
37
+
38
+ ## React usage
39
+
40
+ ```tsx
41
+ import { createStore } from '@lunarhue/store/core'
42
+ import { StoreProvider, useStoreSelector } from '@lunarhue/store/react'
43
+
44
+ const CounterStore = createStore({ count: 0 })
45
+
46
+ function CounterValue() {
47
+ const count = useStoreSelector(CounterStore, (state) => state.count)
48
+ return <span>{count}</span>
49
+ }
50
+
51
+ function App() {
52
+ return (
53
+ <StoreProvider builder={CounterStore}>
54
+ <CounterValue />
55
+ </StoreProvider>
56
+ )
57
+ }
58
+ ```
59
+
60
+ ## Docs
61
+
62
+ - Repo README: https://github.com/LunarHUE/store#readme
63
+ - API and architecture docs: https://github.com/LunarHUE/store/tree/main/docs
@@ -0,0 +1,103 @@
1
+ import { getStoreBuilder } from './chunk-PCSRXZL4.js';
2
+ import { useRef, useEffect, useContext, createContext } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+ import { useStore as useStore$1 } from '@tanstack/react-store';
5
+
6
+ var contextMap = /* @__PURE__ */ new WeakMap();
7
+ function getStoreContext(builder) {
8
+ let context = contextMap.get(builder);
9
+ if (!context) {
10
+ context = createContext(void 0);
11
+ contextMap.set(builder, context);
12
+ }
13
+ return context;
14
+ }
15
+ function StoreProvider(props) {
16
+ if (props.builder !== void 0) {
17
+ return /* @__PURE__ */ jsx(BuilderOwnedStoreProvider, { builder: props.builder, children: props.children });
18
+ }
19
+ return /* @__PURE__ */ jsx(ExternalStoreProvider, { store: props.store, children: props.children });
20
+ }
21
+ function BuilderOwnedStoreProvider({
22
+ builder,
23
+ children
24
+ }) {
25
+ const context = getStoreContext(builder);
26
+ const builderRef = useRef(null);
27
+ const storeRef = useRef(null);
28
+ if (!builderRef.current) {
29
+ builderRef.current = builder;
30
+ } else if (builderRef.current !== builder) {
31
+ throw new Error("StoreProvider builder prop must remain stable.");
32
+ }
33
+ if (!storeRef.current) {
34
+ storeRef.current = builder.create();
35
+ }
36
+ useEffect(() => {
37
+ const ownedStore = storeRef.current;
38
+ return () => {
39
+ if (!ownedStore) {
40
+ return;
41
+ }
42
+ void ownedStore.dispose();
43
+ };
44
+ }, []);
45
+ const content = typeof children === "function" ? children({ store: storeRef.current }) : children;
46
+ return /* @__PURE__ */ jsx(context.Provider, { value: storeRef.current, children: content });
47
+ }
48
+ function ExternalStoreProvider({
49
+ children,
50
+ store
51
+ }) {
52
+ const builder = getStoreBuilder(store);
53
+ if (!builder) {
54
+ throw new Error(
55
+ "StoreProvider could not resolve a builder for the provided store. Pass a store created by @lunarhue/store."
56
+ );
57
+ }
58
+ const context = getStoreContext(builder);
59
+ const content = typeof children === "function" ? children({ store }) : children;
60
+ return /* @__PURE__ */ jsx(context.Provider, { value: store, children: content });
61
+ }
62
+ function useLocalStore(builder) {
63
+ const localStoreRef = useRef(null);
64
+ if (!localStoreRef.current) {
65
+ localStoreRef.current = builder.create();
66
+ }
67
+ useEffect(() => {
68
+ const localStore = localStoreRef.current;
69
+ return () => {
70
+ if (!localStore) {
71
+ return;
72
+ }
73
+ void localStore.dispose();
74
+ };
75
+ }, []);
76
+ return localStoreRef.current;
77
+ }
78
+ function useSelector(store, selector, compare) {
79
+ return useStore$1(
80
+ store,
81
+ selector,
82
+ compare
83
+ );
84
+ }
85
+ function useStore(builder) {
86
+ const contextValue = useContext(getStoreContext(builder));
87
+ if (!contextValue) {
88
+ throw new Error(
89
+ "useStore(builder) requires a matching <StoreProvider builder={...}> or <StoreProvider store={...}> ancestor."
90
+ );
91
+ }
92
+ return contextValue;
93
+ }
94
+
95
+ // src/react/use-store-selector.ts
96
+ function useStoreSelector(builder, selector, compare) {
97
+ const store = useStore(builder);
98
+ return useSelector(store, selector, compare);
99
+ }
100
+
101
+ export { StoreProvider, useLocalStore, useSelector, useStore, useStoreSelector };
102
+ //# sourceMappingURL=chunk-IP5Y4BHA.js.map
103
+ //# sourceMappingURL=chunk-IP5Y4BHA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/context.ts","../src/react/provider.tsx","../src/react/use-local-store.ts","../src/react/use-selector.ts","../src/react/use-store.ts","../src/react/use-store-selector.ts"],"names":["useRef","useEffect","useTanStackStore"],"mappings":"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","file":"chunk-IP5Y4BHA.js","sourcesContent":["import { createContext, type Context } from 'react'\n\nimport type { Store, StoreBuilder } from '../core'\n\nexport type StoreContext<TState, TPlugins> = Context<\n Store<TState, TPlugins> | undefined\n>\n\nconst contextMap = new WeakMap<StoreBuilder<any, any>, StoreContext<any, any>>()\n\nexport function getStoreContext<TState, TPlugins>(\n builder: StoreBuilder<TState, TPlugins>,\n): StoreContext<TState, TPlugins> {\n let context = contextMap.get(builder)\n\n if (!context) {\n context = createContext<Store<TState, TPlugins> | undefined>(undefined)\n contextMap.set(builder, context)\n }\n\n return context\n}\n","import { useEffect, useRef, type ReactNode } from 'react'\n\nimport { getStoreBuilder } from '../core/builder-registry'\nimport { getStoreContext } from './context'\n\nimport type { Store, StoreBuilder } from '../core'\n\ntype StoreProviderChildren<TState, TPlugins> =\n | ReactNode\n | ((args: { store: Store<TState, TPlugins> }) => ReactNode)\n\ntype BuilderProviderProps<TState, TPlugins> = {\n builder: StoreBuilder<TState, TPlugins>\n children?: StoreProviderChildren<TState, TPlugins>\n store?: never\n}\n\ntype StoreProviderProps<TState, TPlugins> = {\n builder?: never\n children?: StoreProviderChildren<TState, TPlugins>\n store: Store<TState, TPlugins>\n}\n\nexport type ProviderProps<TState, TPlugins> =\n | BuilderProviderProps<TState, TPlugins>\n | StoreProviderProps<TState, TPlugins>\n\nexport function StoreProvider<TState, TPlugins>(\n props: ProviderProps<TState, TPlugins>,\n) {\n if (props.builder !== undefined) {\n return (\n <BuilderOwnedStoreProvider builder={props.builder}>\n {props.children}\n </BuilderOwnedStoreProvider>\n )\n }\n\n return (\n <ExternalStoreProvider store={props.store}>\n {props.children}\n </ExternalStoreProvider>\n )\n}\n\nfunction BuilderOwnedStoreProvider<TState, TPlugins>({\n builder,\n children,\n}: BuilderProviderProps<TState, TPlugins>) {\n const context = getStoreContext(builder)\n const builderRef = useRef<StoreBuilder<TState, TPlugins> | null>(null)\n const storeRef = useRef<Store<TState, TPlugins> | null>(null)\n\n if (!builderRef.current) {\n builderRef.current = builder\n } else if (builderRef.current !== builder) {\n throw new Error('StoreProvider builder prop must remain stable.')\n }\n\n if (!storeRef.current) {\n storeRef.current = builder.create()\n }\n\n useEffect(() => {\n const ownedStore = storeRef.current\n\n return () => {\n if (!ownedStore) {\n return\n }\n\n void ownedStore.dispose()\n }\n }, [])\n\n const content =\n typeof children === 'function'\n ? children({ store: storeRef.current })\n : children\n\n return <context.Provider value={storeRef.current}>{content}</context.Provider>\n}\n\nfunction ExternalStoreProvider<TState, TPlugins>({\n children,\n store,\n}: StoreProviderProps<TState, TPlugins>) {\n const builder = getStoreBuilder(store)\n\n if (!builder) {\n throw new Error(\n 'StoreProvider could not resolve a builder for the provided store. Pass a store created by @lunarhue/store.',\n )\n }\n\n const context = getStoreContext(builder)\n const content = typeof children === 'function' ? children({ store }) : children\n\n return <context.Provider value={store}>{content}</context.Provider>\n}\n","import { useEffect, useRef } from 'react'\n\nimport type { Store, StoreBuilder } from '../core'\n\nexport function useLocalStore<TState, TPlugins>(\n builder: StoreBuilder<TState, TPlugins>,\n): Store<TState, TPlugins> {\n const localStoreRef = useRef<Store<TState, TPlugins> | null>(null)\n\n if (!localStoreRef.current) {\n localStoreRef.current = builder.create()\n }\n\n useEffect(() => {\n const localStore = localStoreRef.current\n\n return () => {\n if (!localStore) {\n return\n }\n\n void localStore.dispose()\n }\n }, [])\n\n return localStoreRef.current\n}\n","import { useStore as useTanStackStore } from '@tanstack/react-store'\n\nimport type { AnyStore, StoreState, TanStackStore } from '../core'\n\nexport function useSelector<\n TStore extends AnyStore,\n TSelected,\n TState extends StoreState<TStore>,\n>(\n store: TStore,\n selector: (snapshot: TState) => TSelected,\n compare?: (a: TSelected, b: TSelected) => boolean,\n): TSelected {\n return useTanStackStore<TanStackStore<TState>, TSelected>(\n store as TanStackStore<TState>,\n selector,\n compare,\n )\n}\n","import { useContext } from 'react'\n\nimport { getStoreContext } from './context'\n\nimport type { Store, StoreBuilder } from '../core'\n\nexport function useStore<TState, TPlugins>(\n builder: StoreBuilder<TState, TPlugins>,\n): Store<TState, TPlugins> {\n const contextValue = useContext(getStoreContext(builder))\n\n if (!contextValue) {\n throw new Error(\n 'useStore(builder) requires a matching <StoreProvider builder={...}> or <StoreProvider store={...}> ancestor.',\n )\n }\n\n return contextValue\n}\n","import { useStore } from './use-store'\nimport { useSelector } from './use-selector'\n\nimport type { StoreBuilder, StoreState } from '../core'\n\nexport function useStoreSelector<\n TState,\n TPlugins,\n TSelected,\n TBuilder extends StoreBuilder<TState, TPlugins>,\n>(\n builder: TBuilder,\n selector: (snapshot: StoreState<ReturnType<TBuilder['create']>>) => TSelected,\n compare?: (a: TSelected, b: TSelected) => boolean,\n): TSelected {\n const store = useStore(builder)\n\n return useSelector(store, selector, compare)\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import { createStore } from '@tanstack/store';
2
+
3
+ // src/core/store-instance.ts
4
+ function createStoreInstance(initialState) {
5
+ const store = createStore(initialState);
6
+ const cleanups = [];
7
+ let disposePromise = null;
8
+ const instance = store;
9
+ Object.defineProperty(instance, "dispose", {
10
+ configurable: true,
11
+ enumerable: true,
12
+ value: async () => {
13
+ if (disposePromise) {
14
+ return disposePromise;
15
+ }
16
+ disposePromise = (async () => {
17
+ for (const cleanup of [...cleanups].reverse()) {
18
+ await cleanup();
19
+ }
20
+ })();
21
+ return disposePromise;
22
+ }
23
+ });
24
+ return {
25
+ store: instance,
26
+ onDispose(cleanup) {
27
+ cleanups.push(cleanup);
28
+ },
29
+ attachSurface(surface) {
30
+ for (const key of Reflect.ownKeys(surface)) {
31
+ if (key in instance) {
32
+ throw new Error(`Store plugin surface collision on "${String(key)}".`);
33
+ }
34
+ }
35
+ Object.defineProperties(
36
+ instance,
37
+ Object.getOwnPropertyDescriptors(surface)
38
+ );
39
+ }
40
+ };
41
+ }
42
+
43
+ export { createStoreInstance };
44
+ //# sourceMappingURL=chunk-MIBJNMOV.js.map
45
+ //# sourceMappingURL=chunk-MIBJNMOV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/store-instance.ts"],"names":["createTanStackStore"],"mappings":";;;AAUO,SAAS,oBACd,YAAA,EACyB;AACzB,EAAA,MAAM,KAAA,GAAQA,YAAoB,YAAY,CAAA;AAC9C,EAAA,MAAM,WAA2B,EAAC;AAClC,EAAA,IAAI,cAAA,GAAuC,IAAA;AAE3C,EAAA,MAAM,QAAA,GAAW,KAAA;AAEjB,EAAA,MAAA,CAAO,cAAA,CAAe,UAAU,SAAA,EAAW;AAAA,IACzC,YAAA,EAAc,IAAA;AAAA,IACd,UAAA,EAAY,IAAA;AAAA,IACZ,OAAO,YAAY;AACjB,MAAA,IAAI,cAAA,EAAgB;AAClB,QAAA,OAAO,cAAA;AAAA,MACT;AAEA,MAAA,cAAA,GAAA,CAAkB,YAAY;AAC5B,QAAA,KAAA,MAAW,WAAW,CAAC,GAAG,QAAQ,CAAA,CAAE,SAAQ,EAAG;AAC7C,UAAA,MAAM,OAAA,EAAQ;AAAA,QAChB;AAAA,MACF,CAAA,GAAG;AAEH,MAAA,OAAO,cAAA;AAAA,IACT;AAAA,GACD,CAAA;AAED,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,QAAA;AAAA,IACP,UAAU,OAAA,EAAS;AACjB,MAAA,QAAA,CAAS,KAAK,OAAO,CAAA;AAAA,IACvB,CAAA;AAAA,IACA,cAAc,OAAA,EAAS;AACrB,MAAA,KAAA,MAAW,GAAA,IAAO,OAAA,CAAQ,OAAA,CAAQ,OAAO,CAAA,EAAG;AAC1C,QAAA,IAAI,OAAO,QAAA,EAAU;AACnB,UAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mCAAA,EAAsC,MAAA,CAAO,GAAG,CAAC,CAAA,EAAA,CAAI,CAAA;AAAA,QACvE;AAAA,MACF;AAEA,MAAA,MAAA,CAAO,gBAAA;AAAA,QACL,QAAA;AAAA,QACA,MAAA,CAAO,0BAA0B,OAAO;AAAA,OAC1C;AAAA,IACF;AAAA,GACF;AACF","file":"chunk-MIBJNMOV.js","sourcesContent":["import { createStore as createTanStackStore } from '@tanstack/store'\n\nimport type { StoreCleanup, Store } from './types'\n\ntype StoreController<TState> = {\n store: Store<TState>\n onDispose(cleanup: StoreCleanup): void\n attachSurface(surface: object): void\n}\n\nexport function createStoreInstance<TState>(\n initialState: TState,\n): StoreController<TState> {\n const store = createTanStackStore(initialState)\n const cleanups: StoreCleanup[] = []\n let disposePromise: Promise<void> | null = null\n\n const instance = store as Store<TState>\n\n Object.defineProperty(instance, 'dispose', {\n configurable: true,\n enumerable: true,\n value: async () => {\n if (disposePromise) {\n return disposePromise\n }\n\n disposePromise = (async () => {\n for (const cleanup of [...cleanups].reverse()) {\n await cleanup()\n }\n })()\n\n return disposePromise\n },\n })\n\n return {\n store: instance,\n onDispose(cleanup) {\n cleanups.push(cleanup)\n },\n attachSurface(surface) {\n for (const key of Reflect.ownKeys(surface)) {\n if (key in instance) {\n throw new Error(`Store plugin surface collision on \"${String(key)}\".`)\n }\n }\n\n Object.defineProperties(\n instance,\n Object.getOwnPropertyDescriptors(surface),\n )\n },\n }\n}\n"]}
@@ -0,0 +1,12 @@
1
+ // src/core/builder-registry.ts
2
+ var storeBuilderMap = /* @__PURE__ */ new WeakMap();
3
+ function registerStoreBuilder(store, builder) {
4
+ storeBuilderMap.set(store, builder);
5
+ }
6
+ function getStoreBuilder(store) {
7
+ return storeBuilderMap.get(store);
8
+ }
9
+
10
+ export { getStoreBuilder, registerStoreBuilder };
11
+ //# sourceMappingURL=chunk-PCSRXZL4.js.map
12
+ //# sourceMappingURL=chunk-PCSRXZL4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/builder-registry.ts"],"names":[],"mappings":";AAEA,IAAM,eAAA,uBAAsB,OAAA,EAAiD;AAEtE,SAAS,oBAAA,CACd,OACA,OAAA,EACM;AACN,EAAA,eAAA,CAAgB,GAAA,CAAI,OAAO,OAAO,CAAA;AACpC;AAEO,SAAS,gBACd,KAAA,EAC4C;AAC5C,EAAA,OAAO,eAAA,CAAgB,IAAI,KAAK,CAAA;AAClC","file":"chunk-PCSRXZL4.js","sourcesContent":["import type { Store, StoreBuilder } from './types'\n\nconst storeBuilderMap = new WeakMap<Store<any, any>, StoreBuilder<any, any>>()\n\nexport function registerStoreBuilder<TState, TPlugins>(\n store: Store<TState, TPlugins>,\n builder: StoreBuilder<TState, TPlugins>,\n): void {\n storeBuilderMap.set(store, builder)\n}\n\nexport function getStoreBuilder<TState, TPlugins>(\n store: Store<TState, TPlugins>,\n): StoreBuilder<TState, TPlugins> | undefined {\n return storeBuilderMap.get(store) as StoreBuilder<TState, TPlugins> | undefined\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import { S as StoreBuilder } from '../types-ByMGdUb-.js';
2
+ export { A as AnyStore, a as Store, b as StoreBrand, c as StoreCleanup, d as StorePlugin, e as StorePluginContext, f as StorePlugins, g as StoreState, T as TanStackStore } from '../types-ByMGdUb-.js';
3
+ import '@tanstack/store';
4
+
5
+ declare function createStore<TState>(initialState: TState): StoreBuilder<TState>;
6
+
7
+ export { StoreBuilder, createStore };
@@ -0,0 +1,32 @@
1
+ import { createStoreInstance } from '../chunk-MIBJNMOV.js';
2
+ import { registerStoreBuilder } from '../chunk-PCSRXZL4.js';
3
+
4
+ // src/core/create-store.ts
5
+ function createStore(initialState) {
6
+ const createBuilder = (plugins) => {
7
+ const builder = {
8
+ create() {
9
+ const controller = createStoreInstance(initialState);
10
+ for (const plugin of plugins) {
11
+ const surface = plugin({
12
+ store: controller.store,
13
+ onDispose: (cleanup) => controller.onDispose(cleanup)
14
+ });
15
+ controller.attachSurface(surface);
16
+ }
17
+ const store = controller.store;
18
+ registerStoreBuilder(store, builder);
19
+ return store;
20
+ },
21
+ extend(plugin) {
22
+ return createBuilder([...plugins, plugin]);
23
+ }
24
+ };
25
+ return builder;
26
+ };
27
+ return createBuilder([]);
28
+ }
29
+
30
+ export { createStore };
31
+ //# sourceMappingURL=index.js.map
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/create-store.ts"],"names":[],"mappings":";;;;AAMO,SAAS,YACd,YAAA,EACsB;AAGtB,EAAA,MAAM,aAAA,GAAgB,CACpB,OAAA,KACmC;AACnC,IAAA,MAAM,OAAA,GAA0C;AAAA,MAC9C,MAAA,GAAS;AACP,QAAA,MAAM,UAAA,GAAa,oBAAoB,YAAY,CAAA;AAEnD,QAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,UAAA,MAAM,UAAU,MAAA,CAAO;AAAA,YACrB,OAAO,UAAA,CAAW,KAAA;AAAA,YAClB,SAAA,EAAW,CAAC,OAAA,KAAY,UAAA,CAAW,UAAU,OAAO;AAAA,WACrD,CAAA;AAED,UAAA,UAAA,CAAW,cAAc,OAAO,CAAA;AAAA,QAClC;AAEA,QAAA,MAAM,QAAQ,UAAA,CAAW,KAAA;AACzB,QAAA,oBAAA,CAAqB,OAAO,OAAO,CAAA;AAEnC,QAAA,OAAO,KAAA;AAAA,MACT,CAAA;AAAA,MACA,OACE,MAAA,EACA;AACA,QAAA,OAAO,aAAA,CAAuC,CAAC,GAAG,OAAA,EAAS,MAAM,CAAC,CAAA;AAAA,MACpE;AAAA,KACF;AAEA,IAAA,OAAO,OAAA;AAAA,EACT,CAAA;AAEA,EAAA,OAAO,aAAA,CAAkB,EAAE,CAAA;AAC7B","file":"index.js","sourcesContent":["\nimport { createStoreInstance } from './store-instance'\nimport { registerStoreBuilder } from './builder-registry'\n\nimport type { Store, StoreBuilder, StorePlugin } from './types'\n\nexport function createStore<TState>(\n initialState: TState,\n): StoreBuilder<TState> {\n type PluginList = ReadonlyArray<StorePlugin<TState, any, any>>\n\n const createBuilder = <TPlugins>(\n plugins: PluginList,\n ): StoreBuilder<TState, TPlugins> => {\n const builder: StoreBuilder<TState, TPlugins> = {\n create() {\n const controller = createStoreInstance(initialState)\n\n for (const plugin of plugins) {\n const surface = plugin({\n store: controller.store as Store<TState, any>,\n onDispose: (cleanup) => controller.onDispose(cleanup),\n })\n\n controller.attachSurface(surface)\n }\n\n const store = controller.store as Store<TState, TPlugins>\n registerStoreBuilder(store, builder)\n\n return store\n },\n extend<TNextPlugins>(\n plugin: StorePlugin<TState, TPlugins, TNextPlugins>,\n ) {\n return createBuilder<TPlugins & TNextPlugins>([...plugins, plugin])\n },\n }\n\n return builder\n }\n\n return createBuilder<{}>([])\n}\n"]}
@@ -0,0 +1,33 @@
1
+ import { b as StoreBrand, d as StorePlugin, a as Store } from '../../types-ByMGdUb-.js';
2
+ import '@tanstack/store';
3
+
4
+ declare const actionsBrand: unique symbol;
5
+ declare const actionDefinitionBrand: unique symbol;
6
+ declare const bindActionDefinition: unique symbol;
7
+ type ActionsBrand = StoreBrand<typeof actionsBrand>;
8
+ type ActionsPluginSurface<TState, TActions> = {
9
+ readonly actions: BoundActions<TState, TActions>;
10
+ } & ActionsBrand;
11
+ type ActionsPlugin<TState, TActions> = StorePlugin<TState, any, ActionsPluginSurface<TState, TActions>>;
12
+ type ActionsBuilderHelpers<TState> = {
13
+ getState: () => TState;
14
+ setState: (updater: (prev: TState) => TState) => void;
15
+ };
16
+ type ActionCallback<TState, TArgs extends unknown[] = [], TReturn = void> = (helpers: ActionsBuilderHelpers<TState>, ...args: TArgs) => TReturn;
17
+ type ActionDefinition<TState, TArgs extends unknown[] = [], TReturn = void> = {
18
+ readonly [actionDefinitionBrand]: true;
19
+ readonly [bindActionDefinition]: (helpers: ActionsBuilderHelpers<TState>) => (...args: TArgs) => TReturn;
20
+ };
21
+ type BoundActions<TState, TActions> = {
22
+ [TKey in keyof TActions]: TActions[TKey] extends ActionDefinition<TState, infer TArgs, infer TReturn> ? (...args: TArgs) => TReturn : TActions[TKey];
23
+ };
24
+
25
+ declare function createAction<TState, TArgs extends unknown[] = [], TReturn = void>(callback: ActionCallback<TState, TArgs, TReturn>): ActionDefinition<TState, TArgs, TReturn>;
26
+ declare function actions<TState, TActions>(builder: (helpers: {
27
+ getState: () => TState;
28
+ setState: (updater: (prev: TState) => TState) => void;
29
+ }) => TActions): ActionsPlugin<TState, TActions>;
30
+
31
+ declare function useActions<TState, TActions>(store: Store<TState, ActionsPluginSurface<TState, TActions>>): ActionsPluginSurface<TState, TActions>['actions'];
32
+
33
+ export { type ActionCallback, type ActionDefinition, type ActionsBrand, type ActionsBuilderHelpers, type ActionsPluginSurface, type BoundActions, actionDefinitionBrand, actions, actionsBrand, createAction, useActions };
@@ -0,0 +1,46 @@
1
+ // src/plugins/actions/types.ts
2
+ var actionsBrand = /* @__PURE__ */ Symbol("lunarhue.store.actions");
3
+ var actionDefinitionBrand = /* @__PURE__ */ Symbol("lunarhue.store.actionDefinition");
4
+ var bindActionDefinition = /* @__PURE__ */ Symbol("lunarhue.store.bindActionDefinition");
5
+
6
+ // src/plugins/actions/plugin.ts
7
+ function isActionDefinition(value) {
8
+ return typeof value === "object" && value !== null && actionDefinitionBrand in value && bindActionDefinition in value;
9
+ }
10
+ function bindActions(declaredActions, helpers) {
11
+ const boundActions = {};
12
+ for (const key of Reflect.ownKeys(declaredActions)) {
13
+ const action = declaredActions[key];
14
+ boundActions[key] = isActionDefinition(action) ? action[bindActionDefinition](helpers) : action;
15
+ }
16
+ return boundActions;
17
+ }
18
+ function createAction(callback) {
19
+ return {
20
+ [actionDefinitionBrand]: true,
21
+ [bindActionDefinition](helpers) {
22
+ return (...args) => callback(helpers, ...args);
23
+ }
24
+ };
25
+ }
26
+ function actions(builder) {
27
+ return ({ store }) => {
28
+ const helpers = {
29
+ getState: () => store.get(),
30
+ setState: (updater) => store.setState(updater)
31
+ };
32
+ return {
33
+ [actionsBrand]: true,
34
+ actions: bindActions(builder(helpers), helpers)
35
+ };
36
+ };
37
+ }
38
+
39
+ // src/plugins/actions/react.tsx
40
+ function useActions(store) {
41
+ return store.actions;
42
+ }
43
+
44
+ export { actionDefinitionBrand, actions, actionsBrand, createAction, useActions };
45
+ //# sourceMappingURL=index.js.map
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/plugins/actions/types.ts","../../../src/plugins/actions/plugin.ts","../../../src/plugins/actions/react.tsx"],"names":[],"mappings":";AAEO,IAAM,YAAA,0BAAsB,wBAAwB;AACpD,IAAM,qBAAA,0BAA+B,iCAAiC;AACtE,IAAM,oBAAA,0BAA8B,qCAAqC,CAAA;;;ACUhF,SAAS,mBACP,KAAA,EACuD;AACvD,EAAA,OACE,OAAO,KAAA,KAAU,QAAA,IACjB,UAAU,IAAA,IACV,qBAAA,IAAyB,SACzB,oBAAA,IAAwB,KAAA;AAE5B;AAEA,SAAS,WAAA,CACP,iBACA,OAAA,EACgC;AAChC,EAAA,MAAM,eAAe,EAAC;AAEtB,EAAA,KAAA,MAAW,GAAA,IAAO,OAAA,CAAQ,OAAA,CAAQ,eAAyB,CAAA,EAExD;AACD,IAAA,MAAM,MAAA,GAAS,gBAAgB,GAAG,CAAA;AAElC,IAAA,YAAA,CAAa,GAAG,IACd,kBAAA,CAA2B,MAAM,IAC7B,MAAA,CAAO,oBAAoB,CAAA,CAAE,OAAO,CAAA,GACpC,MAAA;AAAA,EAER;AAEA,EAAA,OAAO,YAAA;AACT;AAEO,SAAS,aAKd,QAAA,EAC0C;AAC1C,EAAA,OAAO;AAAA,IACL,CAAC,qBAAqB,GAAG,IAAA;AAAA,IACzB,CAAC,oBAAoB,CAAA,CAAE,OAAA,EAAS;AAC9B,MAAA,OAAO,CAAA,GAAI,IAAA,KAAS,QAAA,CAAS,OAAA,EAAS,GAAG,IAAI,CAAA;AAAA,IAC/C;AAAA,GACF;AACF;AAEO,SAAS,QACd,OAAA,EAIiC;AACjC,EAAA,OAAO,CAAC,EAAE,KAAA,EAAM,KAAM;AACpB,IAAA,MAAM,OAAA,GAAU;AAAA,MACd,QAAA,EAAU,MAAM,KAAA,CAAM,GAAA,EAAI;AAAA,MAC1B,QAAA,EAAU,CAAC,OAAA,KAAsC,KAAA,CAAM,SAAS,OAAO;AAAA,KACzE;AAEA,IAAA,OAAO;AAAA,MACL,CAAC,YAAY,GAAG,IAAA;AAAA,MAChB,OAAA,EAAS,WAAA,CAAY,OAAA,CAAQ,OAAO,GAAG,OAAO;AAAA,KAChD;AAAA,EACF,CAAA;AACF;;;AC1EO,SAAS,WACd,KAAA,EACmD;AACnD,EAAA,OAAO,KAAA,CAAM,OAAA;AACf","file":"index.js","sourcesContent":["import type { StoreBrand, StorePlugin } from '../../core'\n\nexport const actionsBrand = Symbol('lunarhue.store.actions')\nexport const actionDefinitionBrand = Symbol('lunarhue.store.actionDefinition')\nexport const bindActionDefinition = Symbol('lunarhue.store.bindActionDefinition')\n\nexport type ActionsBrand = StoreBrand<typeof actionsBrand>\n\nexport type ActionsPluginSurface<TState, TActions> = {\n readonly actions: BoundActions<TState, TActions>\n} & ActionsBrand\n\nexport type ActionsPlugin<TState, TActions> = StorePlugin<\n TState,\n any,\n ActionsPluginSurface<TState, TActions>\n>\n\nexport type ActionsBuilderHelpers<TState> = {\n getState: () => TState\n setState: (updater: (prev: TState) => TState) => void\n}\n\nexport type ActionCallback<\n TState,\n TArgs extends unknown[] = [],\n TReturn = void,\n> = (\n helpers: ActionsBuilderHelpers<TState>,\n ...args: TArgs\n) => TReturn\n\nexport type ActionDefinition<\n TState,\n TArgs extends unknown[] = [],\n TReturn = void,\n> = {\n readonly [actionDefinitionBrand]: true\n readonly [bindActionDefinition]: (\n helpers: ActionsBuilderHelpers<TState>,\n ) => (...args: TArgs) => TReturn\n}\n\nexport type BoundActions<TState, TActions> = {\n [TKey in keyof TActions]: TActions[TKey] extends ActionDefinition<\n TState,\n infer TArgs,\n infer TReturn\n >\n ? (...args: TArgs) => TReturn\n : TActions[TKey]\n}\n","import {\n actionDefinitionBrand,\n actionsBrand,\n bindActionDefinition,\n} from './types'\n\nimport type {\n ActionCallback,\n ActionDefinition,\n ActionsBuilderHelpers,\n ActionsPlugin,\n BoundActions,\n} from './types'\n\nfunction isActionDefinition<TState>(\n value: unknown,\n): value is ActionDefinition<TState, unknown[], unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n actionDefinitionBrand in value &&\n bindActionDefinition in value\n )\n}\n\nfunction bindActions<TState, TActions>(\n declaredActions: TActions,\n helpers: ActionsBuilderHelpers<TState>,\n): BoundActions<TState, TActions> {\n const boundActions = {} as BoundActions<TState, TActions>\n\n for (const key of Reflect.ownKeys(declaredActions as object) as Array<\n keyof TActions\n >) {\n const action = declaredActions[key]\n\n boundActions[key] = (\n isActionDefinition<TState>(action)\n ? action[bindActionDefinition](helpers)\n : action\n ) as BoundActions<TState, TActions>[typeof key]\n }\n\n return boundActions\n}\n\nexport function createAction<\n TState,\n TArgs extends unknown[] = [],\n TReturn = void,\n>(\n callback: ActionCallback<TState, TArgs, TReturn>,\n): ActionDefinition<TState, TArgs, TReturn> {\n return {\n [actionDefinitionBrand]: true,\n [bindActionDefinition](helpers) {\n return (...args) => callback(helpers, ...args)\n },\n }\n}\n\nexport function actions<TState, TActions>(\n builder: (helpers: {\n getState: () => TState\n setState: (updater: (prev: TState) => TState) => void\n }) => TActions,\n): ActionsPlugin<TState, TActions> {\n return ({ store }) => {\n const helpers = {\n getState: () => store.get(),\n setState: (updater: (prev: TState) => TState) => store.setState(updater),\n }\n\n return {\n [actionsBrand]: true,\n actions: bindActions(builder(helpers), helpers),\n }\n }\n}\n","import type { Store } from '../../core'\n\nimport type { ActionsPluginSurface } from './types'\n\nexport function useActions<TState, TActions>(\n store: Store<TState, ActionsPluginSurface<TState, TActions>>,\n): ActionsPluginSurface<TState, TActions>['actions'] {\n return store.actions\n}\n"]}
@@ -0,0 +1,100 @@
1
+ import { b as StoreBrand, a as Store, d as StorePlugin, S as StoreBuilder } from '../../types-ByMGdUb-.js';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { ReactNode } from 'react';
4
+ import '@tanstack/store';
5
+
6
+ declare const persistBrand: unique symbol;
7
+ declare const persistControllerKey: unique symbol;
8
+ type PersistBrand = StoreBrand<typeof persistBrand>;
9
+ type PersistMeta = {
10
+ isHydrated: boolean;
11
+ pending: boolean;
12
+ persisting: boolean;
13
+ lastPersistedAt: number | null;
14
+ error: unknown | null;
15
+ };
16
+ type PersistHydrateArgs<TState> = {
17
+ key: string;
18
+ store: PersistedStore<TState>;
19
+ };
20
+ type PersistPersistArgs<TState> = {
21
+ key: string;
22
+ previousState: TState;
23
+ nextState: TState;
24
+ };
25
+ type PersistRuntimeOptions<TState> = {
26
+ key?: string;
27
+ enabled?: boolean;
28
+ delay?: number;
29
+ hydrate?: (args: PersistHydrateArgs<TState>) => Promise<void>;
30
+ onPersist?: (args: PersistPersistArgs<TState>) => Promise<void>;
31
+ };
32
+ type PersistPluginOptions<TState> = {
33
+ flushOnDispose?: boolean;
34
+ hydratedOnCreate?: boolean;
35
+ serializeState?: (state: TState) => TState;
36
+ } & Omit<PersistRuntimeOptions<TState>, 'key'>;
37
+ type PersistController<TState> = {
38
+ meta: Store<PersistMeta>;
39
+ connect(store: PersistedStore<TState>, options: PersistRuntimeOptions<TState>): () => void;
40
+ flush(): Promise<void>;
41
+ hydrate(nextState: TState): Promise<void>;
42
+ };
43
+ type PersistPluginSurface<TState> = PersistBrand & {
44
+ hydrate(nextState: TState): Promise<void>;
45
+ persist: {
46
+ flush(): Promise<void>;
47
+ hydrate(nextState: TState): Promise<void>;
48
+ meta: Store<PersistMeta>;
49
+ [persistControllerKey]: PersistController<TState>;
50
+ };
51
+ };
52
+ type PersistedStore<TState, TPlugins = {}> = Store<TState, TPlugins & PersistPluginSurface<TState>>;
53
+ type PersistPlugin<TState> = StorePlugin<TState, any, PersistPluginSurface<TState>>;
54
+
55
+ declare function persist<TState>(options?: PersistPluginOptions<TState>): PersistPlugin<TState>;
56
+
57
+ type PersistentStoreResult<TState, TPlugins = {}> = {
58
+ store: PersistedStore<TState, TPlugins>;
59
+ flush(): Promise<void>;
60
+ };
61
+ type PersistStoreProviderChildren<TState, TPlugins> = ReactNode | ((args: PersistentStoreResult<TState, TPlugins>) => ReactNode);
62
+ type BuilderPersistStoreProviderProps<TState, TPlugins> = {
63
+ builder: StoreBuilder<TState, TPlugins & PersistPluginSurface<TState>>;
64
+ children?: PersistStoreProviderChildren<TState, TPlugins>;
65
+ flushOnBackground?: boolean;
66
+ flushOnPageHide?: boolean;
67
+ flushOnUnmount?: boolean;
68
+ persist?: PersistRuntimeOptions<TState>;
69
+ store?: never;
70
+ };
71
+ type ExternalPersistStoreProviderProps<TState, TPlugins> = {
72
+ builder?: never;
73
+ children?: PersistStoreProviderChildren<TState, TPlugins>;
74
+ flushOnBackground?: boolean;
75
+ flushOnPageHide?: boolean;
76
+ flushOnUnmount?: boolean;
77
+ persist?: PersistRuntimeOptions<TState>;
78
+ store: PersistedStore<TState, TPlugins>;
79
+ };
80
+ type PersistStoreProviderProps<TState, TPlugins = {}> = BuilderPersistStoreProviderProps<TState, TPlugins> | ExternalPersistStoreProviderProps<TState, TPlugins>;
81
+ /**
82
+ * @deprecated Prefer PersistStoreProvider flush options. This boundary remains
83
+ * as a compatibility escape hatch for sub-tree flush behavior.
84
+ */
85
+ type PersistenceBoundaryProps<TState> = {
86
+ store: PersistedStore<TState>;
87
+ flushOnUnmount?: boolean;
88
+ flushOnPageHide?: boolean;
89
+ flushOnBackground?: boolean;
90
+ children?: ReactNode;
91
+ };
92
+ declare function usePersistentStore<TState, TPlugins>(builder: StoreBuilder<TState, TPlugins & PersistPluginSurface<TState>>): PersistentStoreResult<TState, TPlugins>;
93
+ declare function PersistStoreProvider<TState, TPlugins = {}>(props: PersistStoreProviderProps<TState, TPlugins>): react_jsx_runtime.JSX.Element;
94
+ /**
95
+ * @deprecated Prefer PersistStoreProvider flush options. This boundary remains
96
+ * as a compatibility escape hatch for sub-tree flush behavior.
97
+ */
98
+ declare function PersistenceBoundary<TState>({ children, flushOnBackground, flushOnPageHide, flushOnUnmount, store, }: PersistenceBoundaryProps<TState>): react_jsx_runtime.JSX.Element;
99
+
100
+ export { type PersistBrand, type PersistController, type PersistHydrateArgs, type PersistMeta, type PersistPersistArgs, type PersistPluginOptions, type PersistPluginSurface, type PersistRuntimeOptions, PersistStoreProvider, type PersistStoreProviderProps, type PersistedStore, PersistenceBoundary, type PersistenceBoundaryProps, type PersistentStoreResult, persist, persistBrand, usePersistentStore };
@@ -0,0 +1,408 @@
1
+ import { createStoreInstance } from '../../chunk-MIBJNMOV.js';
2
+ import { StoreProvider } from '../../chunk-IP5Y4BHA.js';
3
+ import { getStoreBuilder } from '../../chunk-PCSRXZL4.js';
4
+ import { useContext, createContext, useEffect, useEffectEvent } from 'react';
5
+ import { jsx, Fragment } from 'react/jsx-runtime';
6
+
7
+ // src/plugins/persist/controller.ts
8
+ var DEFAULT_META = {
9
+ isHydrated: false,
10
+ pending: false,
11
+ persisting: false,
12
+ lastPersistedAt: null,
13
+ error: null
14
+ };
15
+ var nextGeneratedPersistKeyId = 0;
16
+ function getInitialMeta(options) {
17
+ return {
18
+ ...DEFAULT_META,
19
+ isHydrated: options?.hydratedOnCreate ?? false
20
+ };
21
+ }
22
+ function createPersistController(store, pluginOptions) {
23
+ const meta = createStoreInstance(getInitialMeta(pluginOptions)).store;
24
+ const fallbackKey = `persist:${++nextGeneratedPersistKeyId}`;
25
+ let runtimeOptions = null;
26
+ let subscription = null;
27
+ let timer = null;
28
+ let queuedTransition = null;
29
+ let lastObservedState = store.get();
30
+ let currentKey = null;
31
+ let currentFlushPromise = null;
32
+ let hydrating = false;
33
+ let isConnected = false;
34
+ let hasRequestedHydrationForKey = false;
35
+ const resolveKey = (key) => key ?? fallbackKey;
36
+ const clearTimer = () => {
37
+ if (!timer) {
38
+ return;
39
+ }
40
+ clearTimeout(timer);
41
+ timer = null;
42
+ };
43
+ const updateMeta = (updater) => {
44
+ meta.setState(updater);
45
+ };
46
+ const resolveRuntimeOptions = (options) => {
47
+ const onPersist = options.onPersist ?? pluginOptions?.onPersist;
48
+ if (!onPersist) {
49
+ throw new Error(
50
+ "Persist runtime requires onPersist to be provided either in persist(...) or at runtime."
51
+ );
52
+ }
53
+ return {
54
+ key: resolveKey(options.key),
55
+ enabled: options.enabled ?? pluginOptions?.enabled ?? true,
56
+ delay: options.delay ?? pluginOptions?.delay ?? 0,
57
+ hydrate: options.hydrate ?? pluginOptions?.hydrate,
58
+ onPersist
59
+ };
60
+ };
61
+ const resetForKey = (key) => {
62
+ currentKey = key;
63
+ queuedTransition = null;
64
+ clearTimer();
65
+ hasRequestedHydrationForKey = false;
66
+ lastObservedState = store.get();
67
+ meta.setState(() => getInitialMeta(pluginOptions));
68
+ };
69
+ const ensureSubscription = () => {
70
+ if (subscription) {
71
+ return;
72
+ }
73
+ subscription = store.subscribe((nextState) => {
74
+ const previousState = lastObservedState;
75
+ lastObservedState = nextState;
76
+ if (hydrating || !isConnected || !runtimeOptions?.enabled || Object.is(previousState, nextState)) {
77
+ return;
78
+ }
79
+ queuedTransition = queuedTransition ? {
80
+ previousState: queuedTransition.previousState,
81
+ nextState
82
+ } : {
83
+ previousState,
84
+ nextState
85
+ };
86
+ updateMeta((prev) => ({
87
+ ...prev,
88
+ pending: true,
89
+ error: null
90
+ }));
91
+ clearTimer();
92
+ timer = setTimeout(() => {
93
+ void flush();
94
+ }, runtimeOptions.delay);
95
+ });
96
+ };
97
+ const maybeHydrate = async (runtimeStore, options) => {
98
+ if (!options.enabled || hasRequestedHydrationForKey) {
99
+ return;
100
+ }
101
+ hasRequestedHydrationForKey = true;
102
+ if (!options.hydrate) {
103
+ updateMeta((prev) => ({
104
+ ...prev,
105
+ isHydrated: true,
106
+ error: null
107
+ }));
108
+ return;
109
+ }
110
+ try {
111
+ await options.hydrate({
112
+ key: options.key,
113
+ store: runtimeStore
114
+ });
115
+ } catch (error) {
116
+ updateMeta((prev) => ({
117
+ ...prev,
118
+ error
119
+ }));
120
+ throw error;
121
+ }
122
+ };
123
+ const flush = () => {
124
+ if (currentFlushPromise) {
125
+ return currentFlushPromise;
126
+ }
127
+ const runFlush = async () => {
128
+ clearTimer();
129
+ while (queuedTransition) {
130
+ if (!runtimeOptions?.enabled) {
131
+ return;
132
+ }
133
+ const transition = queuedTransition;
134
+ queuedTransition = null;
135
+ updateMeta((prev) => ({
136
+ ...prev,
137
+ pending: false,
138
+ persisting: true,
139
+ error: null
140
+ }));
141
+ try {
142
+ const nextState = pluginOptions?.serializeState ? pluginOptions.serializeState(transition.nextState) : transition.nextState;
143
+ await runtimeOptions.onPersist({
144
+ key: resolveKey(runtimeOptions.key),
145
+ previousState: transition.previousState,
146
+ nextState
147
+ });
148
+ updateMeta((prev) => ({
149
+ ...prev,
150
+ persisting: false,
151
+ lastPersistedAt: Date.now(),
152
+ error: null
153
+ }));
154
+ } catch (error) {
155
+ queuedTransition = transition;
156
+ updateMeta((prev) => ({
157
+ ...prev,
158
+ pending: true,
159
+ persisting: false,
160
+ error
161
+ }));
162
+ throw error;
163
+ }
164
+ }
165
+ };
166
+ currentFlushPromise = runFlush().finally(() => {
167
+ currentFlushPromise = null;
168
+ });
169
+ return currentFlushPromise;
170
+ };
171
+ return {
172
+ meta,
173
+ connect(runtimeStore, options) {
174
+ const resolvedOptions = resolveRuntimeOptions(options);
175
+ runtimeOptions = resolvedOptions;
176
+ if (currentKey !== resolvedOptions.key) {
177
+ resetForKey(resolvedOptions.key);
178
+ }
179
+ isConnected = true;
180
+ ensureSubscription();
181
+ void maybeHydrate(runtimeStore, resolvedOptions).catch(() => {
182
+ });
183
+ return () => {
184
+ if (currentKey !== resolvedOptions.key) {
185
+ return;
186
+ }
187
+ isConnected = false;
188
+ clearTimer();
189
+ updateMeta((prev) => ({
190
+ ...prev,
191
+ persisting: false
192
+ }));
193
+ };
194
+ },
195
+ flush() {
196
+ return flush();
197
+ },
198
+ async hydrate(nextState) {
199
+ clearTimer();
200
+ queuedTransition = null;
201
+ hydrating = true;
202
+ try {
203
+ store.setState(() => nextState);
204
+ lastObservedState = nextState;
205
+ updateMeta((prev) => ({
206
+ ...prev,
207
+ isHydrated: true,
208
+ pending: false,
209
+ error: null
210
+ }));
211
+ } finally {
212
+ hydrating = false;
213
+ }
214
+ }
215
+ };
216
+ }
217
+
218
+ // src/plugins/persist/types.ts
219
+ var persistBrand = /* @__PURE__ */ Symbol("lunarhue.store.persist");
220
+ var persistControllerKey = /* @__PURE__ */ Symbol("lunarhue.store.persist.controller");
221
+
222
+ // src/plugins/persist/plugin.ts
223
+ function persist(options) {
224
+ return ({ onDispose, store }) => {
225
+ const controller = createPersistController(store, options);
226
+ if (options?.flushOnDispose) {
227
+ onDispose(async () => {
228
+ await controller.flush();
229
+ });
230
+ }
231
+ return {
232
+ [persistBrand]: true,
233
+ hydrate(nextState) {
234
+ return controller.hydrate(nextState);
235
+ },
236
+ persist: {
237
+ flush() {
238
+ return controller.flush();
239
+ },
240
+ hydrate(nextState) {
241
+ return controller.hydrate(nextState);
242
+ },
243
+ meta: controller.meta,
244
+ [persistControllerKey]: controller
245
+ }
246
+ };
247
+ };
248
+ }
249
+ var persistContextMap = /* @__PURE__ */ new WeakMap();
250
+ function getPersistStoreContext(builder) {
251
+ let context = persistContextMap.get(builder);
252
+ if (!context) {
253
+ context = createContext(void 0);
254
+ persistContextMap.set(builder, context);
255
+ }
256
+ return context;
257
+ }
258
+ function usePersistentRuntime(store, options) {
259
+ const onPersist = useEffectEvent(async (args) => {
260
+ if (!options.onPersist) {
261
+ return;
262
+ }
263
+ await options.onPersist(args);
264
+ });
265
+ const hydrate = useEffectEvent(async (args) => {
266
+ if (!options.hydrate) {
267
+ return;
268
+ }
269
+ await options.hydrate(args);
270
+ });
271
+ useEffect(() => {
272
+ return store.persist[persistControllerKey].connect(store, {
273
+ key: options.key,
274
+ enabled: options.enabled,
275
+ delay: options.delay,
276
+ onPersist: options.onPersist ? (args) => onPersist(args) : void 0,
277
+ hydrate: options.hydrate ? (args) => hydrate(args) : void 0
278
+ });
279
+ }, [
280
+ options.delay,
281
+ options.enabled,
282
+ options.key,
283
+ store,
284
+ Boolean(options.hydrate)
285
+ ]);
286
+ return {
287
+ store,
288
+ flush() {
289
+ return store.persist.flush();
290
+ }
291
+ };
292
+ }
293
+ function usePersistentStore(builder) {
294
+ const contextValue = useContext(
295
+ getPersistStoreContext(builder)
296
+ );
297
+ if (!contextValue) {
298
+ throw new Error(
299
+ "usePersistentStore(builder) requires a matching <PersistStoreProvider builder={...}> or <PersistStoreProvider store={...}> ancestor."
300
+ );
301
+ }
302
+ return contextValue;
303
+ }
304
+ function usePersistenceBoundary(store, {
305
+ flushOnBackground,
306
+ flushOnPageHide,
307
+ flushOnUnmount
308
+ }) {
309
+ useEffect(() => {
310
+ if (!flushOnUnmount) {
311
+ return;
312
+ }
313
+ return () => {
314
+ void store.persist.flush();
315
+ };
316
+ }, [flushOnUnmount, store]);
317
+ useEffect(() => {
318
+ if (!flushOnPageHide || typeof window === "undefined") {
319
+ return;
320
+ }
321
+ const handlePageHide = () => {
322
+ void store.persist.flush();
323
+ };
324
+ window.addEventListener("pagehide", handlePageHide);
325
+ return () => {
326
+ window.removeEventListener("pagehide", handlePageHide);
327
+ };
328
+ }, [flushOnPageHide, store]);
329
+ useEffect(() => {
330
+ if (!flushOnBackground) {
331
+ return;
332
+ }
333
+ }, [flushOnBackground]);
334
+ }
335
+ function PersistStoreProviderContent({
336
+ builder,
337
+ flushOnBackground,
338
+ flushOnPageHide,
339
+ flushOnUnmount,
340
+ children,
341
+ persist: persist2,
342
+ store
343
+ }) {
344
+ const persistentStore = usePersistentRuntime(
345
+ store,
346
+ persist2 ?? {}
347
+ );
348
+ const Context = getPersistStoreContext(builder);
349
+ usePersistenceBoundary(store, {
350
+ flushOnBackground,
351
+ flushOnPageHide,
352
+ flushOnUnmount
353
+ });
354
+ const content = typeof children === "function" ? children(persistentStore) : children;
355
+ return /* @__PURE__ */ jsx(Context.Provider, { value: persistentStore, children: content });
356
+ }
357
+ function PersistStoreProvider(props) {
358
+ if (props.builder !== void 0) {
359
+ return /* @__PURE__ */ jsx(StoreProvider, { builder: props.builder, children: ({ store }) => /* @__PURE__ */ jsx(
360
+ PersistStoreProviderContent,
361
+ {
362
+ builder: props.builder,
363
+ store,
364
+ persist: props.persist,
365
+ flushOnUnmount: props.flushOnUnmount,
366
+ flushOnPageHide: props.flushOnPageHide,
367
+ flushOnBackground: props.flushOnBackground,
368
+ children: props.children
369
+ }
370
+ ) });
371
+ }
372
+ const builder = getStoreBuilder(props.store);
373
+ if (!builder) {
374
+ throw new Error(
375
+ "PersistStoreProvider could not resolve a builder for the provided store. Pass a persisted store created by @lunarhue/store."
376
+ );
377
+ }
378
+ return /* @__PURE__ */ jsx(StoreProvider, { store: props.store, children: ({ store }) => /* @__PURE__ */ jsx(
379
+ PersistStoreProviderContent,
380
+ {
381
+ builder,
382
+ store,
383
+ persist: props.persist,
384
+ flushOnUnmount: props.flushOnUnmount,
385
+ flushOnPageHide: props.flushOnPageHide,
386
+ flushOnBackground: props.flushOnBackground,
387
+ children: props.children
388
+ }
389
+ ) });
390
+ }
391
+ function PersistenceBoundary({
392
+ children,
393
+ flushOnBackground,
394
+ flushOnPageHide,
395
+ flushOnUnmount,
396
+ store
397
+ }) {
398
+ usePersistenceBoundary(store, {
399
+ flushOnBackground,
400
+ flushOnPageHide,
401
+ flushOnUnmount
402
+ });
403
+ return /* @__PURE__ */ jsx(Fragment, { children });
404
+ }
405
+
406
+ export { PersistStoreProvider, PersistenceBoundary, persist, persistBrand, usePersistentStore };
407
+ //# sourceMappingURL=index.js.map
408
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/plugins/persist/controller.ts","../../../src/plugins/persist/types.ts","../../../src/plugins/persist/plugin.ts","../../../src/plugins/persist/react.tsx"],"names":["persist"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n PersistController,\n PersistMeta,\n PersistPluginOptions,\n PersistRuntimeOptions,\n PersistedStore,\n} from './types'\nimport type { Store } from '../../core'\nimport { createStoreInstance } from '../../core/store-instance'\n\nconst DEFAULT_META: PersistMeta = {\n isHydrated: false,\n pending: false,\n persisting: false,\n lastPersistedAt: null,\n error: null,\n}\n\nlet nextGeneratedPersistKeyId = 0\n\nfunction getInitialMeta<TState>(\n options?: PersistPluginOptions<TState>,\n): PersistMeta {\n return {\n ...DEFAULT_META,\n isHydrated: options?.hydratedOnCreate ?? false,\n }\n}\n\ntype RuntimeOptions<TState> = Required<\n Omit<PersistRuntimeOptions<TState>, 'hydrate'>\n> & {\n hydrate?: PersistRuntimeOptions<TState>['hydrate']\n}\n\nexport function createPersistController<TState>(\n store: Store<TState>,\n pluginOptions?: PersistPluginOptions<TState>,\n): PersistController<TState> {\n const meta = createStoreInstance(getInitialMeta(pluginOptions)).store\n const fallbackKey = `persist:${++nextGeneratedPersistKeyId}`\n let runtimeOptions: RuntimeOptions<TState> | null = null\n let subscription: { unsubscribe(): void } | null = null\n let timer: ReturnType<typeof setTimeout> | null = null\n let queuedTransition: {\n previousState: TState\n nextState: TState\n } | null = null\n let lastObservedState = store.get()\n let currentKey: string | null = null\n let currentFlushPromise: Promise<void> | null = null\n let hydrating = false\n let isConnected = false\n let hasRequestedHydrationForKey = false\n\n const resolveKey = (key?: string) => key ?? fallbackKey\n\n const clearTimer = () => {\n if (!timer) {\n return\n }\n\n clearTimeout(timer)\n timer = null\n }\n\n const updateMeta = (updater: (prev: PersistMeta) => PersistMeta) => {\n meta.setState(updater)\n }\n\n const resolveRuntimeOptions = (\n options: PersistRuntimeOptions<TState>,\n ): RuntimeOptions<TState> => {\n const onPersist = options.onPersist ?? pluginOptions?.onPersist\n\n if (!onPersist) {\n throw new Error(\n 'Persist runtime requires onPersist to be provided either in persist(...) or at runtime.',\n )\n }\n\n return {\n key: resolveKey(options.key),\n enabled: options.enabled ?? pluginOptions?.enabled ?? true,\n delay: options.delay ?? pluginOptions?.delay ?? 0,\n hydrate: options.hydrate ?? pluginOptions?.hydrate,\n onPersist,\n }\n }\n\n const resetForKey = (key: string) => {\n currentKey = key\n queuedTransition = null\n clearTimer()\n hasRequestedHydrationForKey = false\n lastObservedState = store.get()\n meta.setState(() => getInitialMeta(pluginOptions))\n }\n\n const ensureSubscription = () => {\n if (subscription) {\n return\n }\n\n subscription = store.subscribe((nextState) => {\n const previousState = lastObservedState\n lastObservedState = nextState\n\n if (\n hydrating ||\n !isConnected ||\n !runtimeOptions?.enabled ||\n Object.is(previousState, nextState)\n ) {\n return\n }\n\n queuedTransition = queuedTransition\n ? {\n previousState: queuedTransition.previousState,\n nextState,\n }\n : {\n previousState,\n nextState,\n }\n\n updateMeta((prev) => ({\n ...prev,\n pending: true,\n error: null,\n }))\n\n clearTimer()\n timer = setTimeout(() => {\n void flush()\n }, runtimeOptions.delay)\n })\n }\n\n const maybeHydrate = async (\n runtimeStore: PersistedStore<TState>,\n options: RuntimeOptions<TState>,\n ) => {\n if (!options.enabled || hasRequestedHydrationForKey) {\n return\n }\n\n hasRequestedHydrationForKey = true\n\n if (!options.hydrate) {\n updateMeta((prev) => ({\n ...prev,\n isHydrated: true,\n error: null,\n }))\n return\n }\n\n try {\n await options.hydrate({\n key: options.key,\n store: runtimeStore,\n })\n } catch (error) {\n updateMeta((prev) => ({\n ...prev,\n error,\n }))\n throw error\n }\n }\n\n const flush = (): Promise<void> => {\n if (currentFlushPromise) {\n return currentFlushPromise\n }\n\n const runFlush = async () => {\n clearTimer()\n\n while (queuedTransition) {\n if (!runtimeOptions?.enabled) {\n return\n }\n\n const transition = queuedTransition\n queuedTransition = null\n\n updateMeta((prev) => ({\n ...prev,\n pending: false,\n persisting: true,\n error: null,\n }))\n\n try {\n const nextState = pluginOptions?.serializeState\n ? pluginOptions.serializeState(transition.nextState)\n : transition.nextState\n\n await runtimeOptions.onPersist({\n key: resolveKey(runtimeOptions.key),\n previousState: transition.previousState,\n nextState,\n })\n\n updateMeta((prev) => ({\n ...prev,\n persisting: false,\n lastPersistedAt: Date.now(),\n error: null,\n }))\n } catch (error) {\n queuedTransition = transition\n updateMeta((prev) => ({\n ...prev,\n pending: true,\n persisting: false,\n error,\n }))\n throw error\n }\n }\n }\n\n currentFlushPromise = runFlush().finally(() => {\n currentFlushPromise = null\n })\n\n return currentFlushPromise\n }\n\n return {\n meta,\n connect(runtimeStore, options) {\n const resolvedOptions = resolveRuntimeOptions(options)\n runtimeOptions = resolvedOptions\n\n if (currentKey !== resolvedOptions.key) {\n resetForKey(resolvedOptions.key)\n }\n\n isConnected = true\n ensureSubscription()\n void maybeHydrate(runtimeStore, resolvedOptions).catch(() => {})\n\n return () => {\n if (currentKey !== resolvedOptions.key) {\n return\n }\n\n isConnected = false\n clearTimer()\n updateMeta((prev) => ({\n ...prev,\n persisting: false,\n }))\n }\n },\n flush() {\n return flush()\n },\n async hydrate(nextState) {\n clearTimer()\n queuedTransition = null\n hydrating = true\n\n try {\n store.setState(() => nextState)\n lastObservedState = nextState\n updateMeta((prev) => ({\n ...prev,\n isHydrated: true,\n pending: false,\n error: null,\n }))\n } finally {\n hydrating = false\n }\n },\n }\n}\n","import type { Store, StoreBrand, StorePlugin } from '../../core'\n\nexport const persistBrand = Symbol('lunarhue.store.persist')\nexport const persistControllerKey = Symbol('lunarhue.store.persist.controller')\n\nexport type PersistBrand = StoreBrand<typeof persistBrand>\n\nexport type PersistMeta = {\n isHydrated: boolean\n pending: boolean\n persisting: boolean\n lastPersistedAt: number | null\n error: unknown | null\n}\n\nexport type PersistHydrateArgs<TState> = {\n key: string\n store: PersistedStore<TState>\n}\n\nexport type PersistPersistArgs<TState> = {\n key: string\n previousState: TState\n nextState: TState\n}\n\nexport type PersistRuntimeOptions<TState> = {\n key?: string\n enabled?: boolean\n delay?: number\n hydrate?: (args: PersistHydrateArgs<TState>) => Promise<void>\n onPersist?: (args: PersistPersistArgs<TState>) => Promise<void>\n}\n\nexport type PersistPluginOptions<TState> = {\n flushOnDispose?: boolean\n hydratedOnCreate?: boolean\n serializeState?: (state: TState) => TState\n} & Omit<PersistRuntimeOptions<TState>, 'key'>\n\nexport type PersistController<TState> = {\n meta: Store<PersistMeta>\n connect(\n store: PersistedStore<TState>,\n options: PersistRuntimeOptions<TState>,\n ): () => void\n flush(): Promise<void>\n hydrate(nextState: TState): Promise<void>\n}\n\nexport type PersistPluginSurface<TState> = PersistBrand & {\n hydrate(nextState: TState): Promise<void>\n persist: {\n flush(): Promise<void>\n hydrate(nextState: TState): Promise<void>\n meta: Store<PersistMeta>\n [persistControllerKey]: PersistController<TState>\n }\n}\n\nexport type PersistedStore<TState, TPlugins = {}> = Store<\n TState,\n TPlugins & PersistPluginSurface<TState>\n>\n\nexport type PersistPlugin<TState> = StorePlugin<\n TState,\n any,\n PersistPluginSurface<TState>\n>\n","import { createPersistController } from './controller'\nimport { persistBrand, persistControllerKey } from './types'\n\nimport type { PersistPlugin, PersistPluginOptions } from './types'\n\nexport function persist<TState>(\n options?: PersistPluginOptions<TState>,\n): PersistPlugin<TState> {\n return ({ onDispose, store }) => {\n const controller = createPersistController(store, options)\n\n if (options?.flushOnDispose) {\n onDispose(async () => {\n await controller.flush()\n })\n }\n\n return {\n [persistBrand]: true,\n hydrate(nextState: TState) {\n return controller.hydrate(nextState)\n },\n persist: {\n flush() {\n return controller.flush()\n },\n hydrate(nextState: TState) {\n return controller.hydrate(nextState)\n },\n meta: controller.meta,\n [persistControllerKey]: controller,\n },\n }\n }\n}\n","import {\n createContext,\n type Context,\n type ReactNode,\n useContext,\n useEffect,\n useEffectEvent,\n} from 'react'\n\nimport type { StoreBuilder } from '../../core'\nimport { getStoreBuilder } from '../../core/builder-registry'\nimport { StoreProvider, useSelector } from '../../react'\n\nimport {\n persistControllerKey,\n type PersistHydrateArgs,\n type PersistMeta,\n type PersistPersistArgs,\n type PersistPluginSurface,\n type PersistRuntimeOptions,\n type PersistedStore,\n} from './types'\n\nexport type PersistentStoreResult<TState, TPlugins = {}> = {\n store: PersistedStore<TState, TPlugins>\n flush(): Promise<void>\n}\n\ntype PersistStoreContext<TState, TPlugins> = Context<\n PersistentStoreResult<TState, TPlugins> | undefined\n>\n\nconst persistContextMap = new WeakMap<\n StoreBuilder<any, any>,\n PersistStoreContext<any, any>\n>()\n\ntype PersistStoreProviderChildren<TState, TPlugins> =\n | ReactNode\n | ((args: PersistentStoreResult<TState, TPlugins>) => ReactNode)\n\ntype BuilderPersistStoreProviderProps<TState, TPlugins> = {\n builder: StoreBuilder<TState, TPlugins & PersistPluginSurface<TState>>\n children?: PersistStoreProviderChildren<TState, TPlugins>\n flushOnBackground?: boolean\n flushOnPageHide?: boolean\n flushOnUnmount?: boolean\n persist?: PersistRuntimeOptions<TState>\n store?: never\n}\n\ntype ExternalPersistStoreProviderProps<TState, TPlugins> = {\n builder?: never\n children?: PersistStoreProviderChildren<TState, TPlugins>\n flushOnBackground?: boolean\n flushOnPageHide?: boolean\n flushOnUnmount?: boolean\n persist?: PersistRuntimeOptions<TState>\n store: PersistedStore<TState, TPlugins>\n}\n\nexport type PersistStoreProviderProps<TState, TPlugins = {}> =\n | BuilderPersistStoreProviderProps<TState, TPlugins>\n | ExternalPersistStoreProviderProps<TState, TPlugins>\n\n/**\n * @deprecated Prefer PersistStoreProvider flush options. This boundary remains\n * as a compatibility escape hatch for sub-tree flush behavior.\n */\nexport type PersistenceBoundaryProps<TState> = {\n store: PersistedStore<TState>\n flushOnUnmount?: boolean\n flushOnPageHide?: boolean\n flushOnBackground?: boolean\n children?: ReactNode\n}\n\ntype PersistenceBoundaryOptions = {\n flushOnBackground?: boolean\n flushOnPageHide?: boolean\n flushOnUnmount?: boolean\n}\n\nfunction getPersistStoreContext<TState, TPlugins>(\n builder: StoreBuilder<TState, TPlugins & PersistPluginSurface<TState>>,\n): PersistStoreContext<TState, TPlugins> {\n let context = persistContextMap.get(builder)\n\n if (!context) {\n context = createContext<\n PersistentStoreResult<TState, TPlugins> | undefined\n >(undefined)\n persistContextMap.set(builder, context)\n }\n\n return context\n}\n\nfunction usePersistentRuntime<TState, TPlugins = {}>(\n store: PersistedStore<TState, TPlugins>,\n options: PersistRuntimeOptions<TState>,\n): PersistentStoreResult<TState, TPlugins> {\n const onPersist = useEffectEvent(async (args: PersistPersistArgs<TState>) => {\n if (!options.onPersist) {\n return\n }\n\n await options.onPersist(args)\n })\n const hydrate = useEffectEvent(async (args: PersistHydrateArgs<TState>) => {\n if (!options.hydrate) {\n return\n }\n\n await options.hydrate(args)\n })\n\n useEffect(() => {\n return store.persist[persistControllerKey].connect(store, {\n key: options.key,\n enabled: options.enabled,\n delay: options.delay,\n onPersist: options.onPersist ? (args) => onPersist(args) : undefined,\n hydrate: options.hydrate ? (args) => hydrate(args) : undefined,\n })\n }, [\n options.delay,\n options.enabled,\n options.key,\n store,\n Boolean(options.hydrate),\n ])\n\n return {\n store,\n flush() {\n return store.persist.flush()\n },\n }\n}\n\nexport function usePersistentStore<TState, TPlugins>(\n builder: StoreBuilder<TState, TPlugins & PersistPluginSurface<TState>>,\n): PersistentStoreResult<TState, TPlugins> {\n const contextValue = useContext(\n getPersistStoreContext<TState, TPlugins>(builder),\n )\n\n if (!contextValue) {\n throw new Error(\n 'usePersistentStore(builder) requires a matching <PersistStoreProvider builder={...}> or <PersistStoreProvider store={...}> ancestor.',\n )\n }\n\n return contextValue\n}\n\nfunction usePersistenceBoundary<TState>(\n store: PersistedStore<TState>,\n {\n flushOnBackground,\n flushOnPageHide,\n flushOnUnmount,\n }: PersistenceBoundaryOptions,\n) {\n useEffect(() => {\n if (!flushOnUnmount) {\n return\n }\n\n return () => {\n void store.persist.flush()\n }\n }, [flushOnUnmount, store])\n\n useEffect(() => {\n if (!flushOnPageHide || typeof window === 'undefined') {\n return\n }\n\n const handlePageHide = () => {\n void store.persist.flush()\n }\n\n window.addEventListener('pagehide', handlePageHide)\n\n return () => {\n window.removeEventListener('pagehide', handlePageHide)\n }\n }, [flushOnPageHide, store])\n\n useEffect(() => {\n if (!flushOnBackground) {\n return\n }\n\n // Web has no app background lifecycle equivalent here yet.\n }, [flushOnBackground])\n}\n\ninterface PersistStoreProviderContentProps<TState, TPlugins> {\n builder: StoreBuilder<TState, TPlugins & PersistPluginSurface<TState>>\n children?: PersistStoreProviderChildren<TState, TPlugins>\n flushOnBackground?: boolean\n flushOnPageHide?: boolean\n flushOnUnmount?: boolean\n persist?: PersistRuntimeOptions<TState>\n store: PersistedStore<TState, TPlugins>\n}\n\nfunction PersistStoreProviderContent<TState, TPlugins>({\n builder,\n flushOnBackground,\n flushOnPageHide,\n flushOnUnmount,\n children,\n persist,\n store,\n}: PersistStoreProviderContentProps<TState, TPlugins>) {\n const persistentStore = usePersistentRuntime<TState, TPlugins>(\n store,\n persist ?? {},\n )\n const Context = getPersistStoreContext<TState, TPlugins>(builder)\n\n usePersistenceBoundary(store, {\n flushOnBackground,\n flushOnPageHide,\n flushOnUnmount,\n })\n\n const content =\n typeof children === 'function' ? children(persistentStore) : children\n\n return <Context.Provider value={persistentStore}>{content}</Context.Provider>\n}\n\nexport function PersistStoreProvider<TState, TPlugins = {}>(\n props: PersistStoreProviderProps<TState, TPlugins>,\n) {\n if (props.builder !== undefined) {\n return (\n <StoreProvider builder={props.builder}>\n {({ store }) => (\n <PersistStoreProviderContent\n builder={props.builder}\n store={store}\n persist={props.persist}\n flushOnUnmount={props.flushOnUnmount}\n flushOnPageHide={props.flushOnPageHide}\n flushOnBackground={props.flushOnBackground}\n >\n {props.children}\n </PersistStoreProviderContent>\n )}\n </StoreProvider>\n )\n }\n\n const builder = getStoreBuilder(props.store)\n\n if (!builder) {\n throw new Error(\n 'PersistStoreProvider could not resolve a builder for the provided store. Pass a persisted store created by @lunarhue/store.',\n )\n }\n\n return (\n <StoreProvider store={props.store}>\n {({ store }) => (\n <PersistStoreProviderContent\n builder={\n builder as StoreBuilder<\n TState,\n TPlugins & PersistPluginSurface<TState>\n >\n }\n store={store as PersistedStore<TState, TPlugins>}\n persist={props.persist}\n flushOnUnmount={props.flushOnUnmount}\n flushOnPageHide={props.flushOnPageHide}\n flushOnBackground={props.flushOnBackground}\n >\n {props.children}\n </PersistStoreProviderContent>\n )}\n </StoreProvider>\n )\n}\n\n/**\n * @deprecated Prefer PersistStoreProvider flush options. This boundary remains\n * as a compatibility escape hatch for sub-tree flush behavior.\n */\nexport function PersistenceBoundary<TState>({\n children,\n flushOnBackground,\n flushOnPageHide,\n flushOnUnmount,\n store,\n}: PersistenceBoundaryProps<TState>) {\n usePersistenceBoundary(store, {\n flushOnBackground,\n flushOnPageHide,\n flushOnUnmount,\n })\n\n return <>{children}</>\n}\n"]}
@@ -0,0 +1,30 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { S as StoreBuilder, a as Store, A as AnyStore, g as StoreState } from '../types-ByMGdUb-.js';
4
+ import '@tanstack/store';
5
+
6
+ type StoreProviderChildren<TState, TPlugins> = ReactNode | ((args: {
7
+ store: Store<TState, TPlugins>;
8
+ }) => ReactNode);
9
+ type BuilderProviderProps<TState, TPlugins> = {
10
+ builder: StoreBuilder<TState, TPlugins>;
11
+ children?: StoreProviderChildren<TState, TPlugins>;
12
+ store?: never;
13
+ };
14
+ type StoreProviderProps<TState, TPlugins> = {
15
+ builder?: never;
16
+ children?: StoreProviderChildren<TState, TPlugins>;
17
+ store: Store<TState, TPlugins>;
18
+ };
19
+ type ProviderProps<TState, TPlugins> = BuilderProviderProps<TState, TPlugins> | StoreProviderProps<TState, TPlugins>;
20
+ declare function StoreProvider<TState, TPlugins>(props: ProviderProps<TState, TPlugins>): react_jsx_runtime.JSX.Element;
21
+
22
+ declare function useLocalStore<TState, TPlugins>(builder: StoreBuilder<TState, TPlugins>): Store<TState, TPlugins>;
23
+
24
+ declare function useSelector<TStore extends AnyStore, TSelected, TState extends StoreState<TStore>>(store: TStore, selector: (snapshot: TState) => TSelected, compare?: (a: TSelected, b: TSelected) => boolean): TSelected;
25
+
26
+ declare function useStore<TState, TPlugins>(builder: StoreBuilder<TState, TPlugins>): Store<TState, TPlugins>;
27
+
28
+ declare function useStoreSelector<TState, TPlugins, TSelected, TBuilder extends StoreBuilder<TState, TPlugins>>(builder: TBuilder, selector: (snapshot: StoreState<ReturnType<TBuilder['create']>>) => TSelected, compare?: (a: TSelected, b: TSelected) => boolean): TSelected;
29
+
30
+ export { StoreProvider, type ProviderProps as StoreProviderProps, useLocalStore, useSelector, useStore, useStoreSelector };
@@ -0,0 +1,4 @@
1
+ export { StoreProvider, useLocalStore, useSelector, useStore, useStoreSelector } from '../chunk-IP5Y4BHA.js';
2
+ import '../chunk-PCSRXZL4.js';
3
+ //# sourceMappingURL=index.js.map
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,28 @@
1
+ import { Store as Store$1 } from '@tanstack/store';
2
+
3
+ type StoreBrand<TKey extends symbol> = {
4
+ readonly [K in TKey]: true;
5
+ };
6
+ type StoreCleanup = () => void | Promise<void>;
7
+ type TanStackStore<TState> = Store$1<TState>;
8
+ type Store<TState, TPlugins = {}> = TanStackStore<TState> & {
9
+ dispose(): Promise<void>;
10
+ } & TPlugins;
11
+ type AnyStore = Store<any, any>;
12
+ type StorePlugins<TStore extends AnyStore> = TStore extends {
13
+ [K in keyof TStore]: TStore[K] extends (...args: any[]) => any ? TStore[K] : never;
14
+ } ? TStore : never;
15
+ type StoreState<TStore extends AnyStore> = TStore extends {
16
+ get: () => infer TState;
17
+ } ? TState : never;
18
+ type StorePluginContext<TState, TPlugins> = {
19
+ store: Store<TState, TPlugins>;
20
+ onDispose(cleanup: StoreCleanup): void;
21
+ };
22
+ type StorePlugin<TState, TPlugins, TNextPlugins> = (context: StorePluginContext<TState, TPlugins>) => TNextPlugins;
23
+ type StoreBuilder<TState, TPlugins = {}> = {
24
+ create(): Store<TState, TPlugins>;
25
+ extend<TNextPlugins>(plugin: StorePlugin<TState, TPlugins, TNextPlugins>): StoreBuilder<TState, TPlugins & TNextPlugins>;
26
+ };
27
+
28
+ export type { AnyStore as A, StoreBuilder as S, TanStackStore as T, Store as a, StoreBrand as b, StoreCleanup as c, StorePlugin as d, StorePluginContext as e, StorePlugins as f, StoreState as g };
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@lunarhue/store",
3
+ "version": "0.1.0",
4
+ "description": "Framework-agnostic state built on top of @tanstack/store with typed plugins and React bindings.",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/LunarHUE/store#readme",
7
+ "bugs": {
8
+ "url": "https://github.com/LunarHUE/store/issues"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/LunarHUE/store.git",
13
+ "directory": "packages/store"
14
+ },
15
+ "keywords": [
16
+ "state",
17
+ "store",
18
+ "tanstack",
19
+ "react",
20
+ "typescript"
21
+ ],
22
+ "type": "module",
23
+ "sideEffects": false,
24
+ "types": "./dist/core/index.d.ts",
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "exports": {
32
+ "./core": {
33
+ "types": "./dist/core/index.d.ts",
34
+ "import": "./dist/core/index.js"
35
+ },
36
+ "./react": {
37
+ "types": "./dist/react/index.d.ts",
38
+ "import": "./dist/react/index.js"
39
+ },
40
+ "./plugins/actions": {
41
+ "types": "./dist/plugins/actions/index.d.ts",
42
+ "import": "./dist/plugins/actions/index.js"
43
+ },
44
+ "./plugins/persist": {
45
+ "types": "./dist/plugins/persist/index.d.ts",
46
+ "import": "./dist/plugins/persist/index.js"
47
+ }
48
+ },
49
+ "scripts": {
50
+ "build": "tsup",
51
+ "test": "vitest run",
52
+ "typecheck": "tsc --noEmit",
53
+ "check": "bun run typecheck && bun run test"
54
+ },
55
+ "dependencies": {
56
+ "@tanstack/react-store": "0.9.2",
57
+ "@tanstack/store": "0.9.2"
58
+ },
59
+ "peerDependencies": {
60
+ "react": "^19.1.1"
61
+ },
62
+ "devDependencies": {
63
+ "@testing-library/react": "^16.3.0",
64
+ "@types/react": "^19.1.12",
65
+ "@types/react-dom": "^19.1.9",
66
+ "jsdom": "^27.0.0",
67
+ "react": "^19.1.1",
68
+ "react-dom": "^19.1.1",
69
+ "tsup": "^8.5.0",
70
+ "typescript": "^5.9.2",
71
+ "vitest": "^3.2.4"
72
+ }
73
+ }