nukejs 0.0.22 → 0.0.23

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { createStore, useStore } from './store';
1
+ export { createStore, createPersistedStore, useStore } from './store';
2
2
  export type { Store } from './store';
3
3
  export { useHtml } from './use-html';
4
4
  export type { HtmlOptions } from './use-html';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createStore, useStore } from "./store.js";
1
+ import { createStore, createPersistedStore, useStore } from "./store.js";
2
2
  import { useHtml } from "./use-html.js";
3
3
  import { default as default2 } from "./use-router.js";
4
4
  import { useRequest } from "./use-request.js";
@@ -11,6 +11,7 @@ export {
11
11
  default3 as Link,
12
12
  ansi,
13
13
  c,
14
+ createPersistedStore,
14
15
  createStore,
15
16
  escapeHtml,
16
17
  getDebugLevel,
package/dist/store.d.ts CHANGED
@@ -65,6 +65,7 @@ interface StoreEntry<T> {
65
65
  declare global {
66
66
  interface Window {
67
67
  __nukeStores?: Map<string, StoreEntry<any>>;
68
+ __nukePersisted?: Set<string>;
68
69
  }
69
70
  }
70
71
  /**
@@ -80,6 +81,47 @@ declare global {
80
81
  * @param initialState Default state used when the store is first created.
81
82
  */
82
83
  export declare function createStore<T extends object>(name: string, initialState: T): Store<T>;
84
+ /**
85
+ * Creates a `Store` that survives full page refreshes by mirroring its state
86
+ * into `localStorage` (or `sessionStorage`).
87
+ *
88
+ * A plain `createStore` only lives in `window.__nukeStores`, which is wiped
89
+ * on every hard reload — fine for SPA navigations, not for data you want to
90
+ * keep around. `createPersistedStore` wraps `createStore` and:
91
+ *
92
+ * 1. On first creation in the browser, reads any previously saved value
93
+ * from storage and applies it via `setState`.
94
+ * 2. Subscribes to the store and writes the new state to storage on every
95
+ * change.
96
+ *
97
+ * `store.initialState` (used by `useStore` as the SSR snapshot) is left
98
+ * untouched as the value you passed in — the persisted value is applied
99
+ * *after* creation via `setState`, not by changing `initialState`. This
100
+ * keeps the server-rendered HTML and the client's first hydration pass in
101
+ * sync (no hydration mismatch); components simply re-render with the
102
+ * persisted value immediately after mount, the same way `useSyncExternalStore`
103
+ * already reconciles store mutations.
104
+ *
105
+ * Because `createStore` itself is idempotent per `name` but storage I/O is
106
+ * not, a `window.__nukePersisted` set guards against re-running the
107
+ * read/subscribe wiring if multiple bundles import the same persisted store.
108
+ *
109
+ * @param name Unique store key — also used to derive the storage key.
110
+ * @param initialState Default state used when nothing is in storage yet.
111
+ * @param options.storage `'local'` (default) or `'session'`.
112
+ * @param options.key Override the storage key (defaults to `nuke-store:${name}`).
113
+ *
114
+ * @example
115
+ * export const cartStore = createPersistedStore('cart', { items: [], total: 0 })
116
+ *
117
+ * @example
118
+ * // Cleared when the tab closes, kept across refreshes within the session
119
+ * export const draftStore = createPersistedStore('draft', { text: '' }, { storage: 'session' })
120
+ */
121
+ export declare function createPersistedStore<T extends object>(name: string, initialState: T, options?: {
122
+ storage?: 'local' | 'session';
123
+ key?: string;
124
+ }): Store<T>;
83
125
  /**
84
126
  * React hook that subscribes a component to a store.
85
127
  *
package/dist/store.js CHANGED
@@ -30,6 +30,27 @@ function createStore(name, initialState) {
30
30
  };
31
31
  return { name, initialState, getState, setState, subscribe };
32
32
  }
33
+ function createPersistedStore(name, initialState, options) {
34
+ const store = createStore(name, initialState);
35
+ if (typeof window === "undefined") return store;
36
+ const storageKey = options?.key ?? `nuke-store:${name}`;
37
+ const backend = options?.storage === "session" ? window.sessionStorage : window.localStorage;
38
+ if (!window.__nukePersisted) window.__nukePersisted = /* @__PURE__ */ new Set();
39
+ if (window.__nukePersisted.has(storageKey)) return store;
40
+ window.__nukePersisted.add(storageKey);
41
+ try {
42
+ const raw = backend.getItem(storageKey);
43
+ if (raw !== null) store.setState(JSON.parse(raw));
44
+ } catch {
45
+ }
46
+ store.subscribe(() => {
47
+ try {
48
+ backend.setItem(storageKey, JSON.stringify(store.getState()));
49
+ } catch {
50
+ }
51
+ });
52
+ return store;
53
+ }
33
54
  function useStore(store, selector) {
34
55
  const getSnapshot = selector ? () => selector(store.getState()) : () => store.getState();
35
56
  const getServerSnapshot = selector ? () => selector(store.initialState) : () => store.initialState;
@@ -40,6 +61,7 @@ function useStore(store, selector) {
40
61
  );
41
62
  }
42
63
  export {
64
+ createPersistedStore,
43
65
  createStore,
44
66
  useStore
45
67
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nukejs",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "A minimal, opinionated full-stack React framework on Node.js that server-renders everything and hydrates only interactive parts.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",