react-mirrorstate 0.1.0 → 0.2.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/README.md CHANGED
@@ -1,10 +1,8 @@
1
1
  # `react-mirrorstate`
2
2
 
3
- React hook for bi-directional state synchronization in JSON on disk.
3
+ React hook for bi-directional state synchronization with JSON files on disk.
4
4
 
5
- Requires Vite and `vite-plugin-mirrorstate` plugin.
6
-
7
- Changes made in the UI are reflected on disk, and changes on disk propagate to the UI.
5
+ **Note:** Both `react-mirrorstate` and `vite-plugin-mirrorstate` are required to use MirrorState.
8
6
 
9
7
  ## Installation
10
8
 
@@ -12,70 +10,10 @@ Changes made in the UI are reflected on disk, and changes on disk propagate to t
12
10
  npm install react-mirrorstate vite-plugin-mirrorstate
13
11
  ```
14
12
 
15
- ## Example
16
-
17
- Create a `state.mirror.json` file:
18
-
19
- ```json
20
- {
21
- "count": 20,
22
- "message": "Hello World"
23
- }
24
- ```
25
-
26
- Start `vite` with `vite-plugin-mirrorstate` enabled:
27
-
28
- ```typescript
29
- import { defineConfig } from "vite";
30
- import react from "@vitejs/plugin-react";
31
- import mirrorstate from "vite-plugin-mirrorstate";
32
-
33
- export default defineConfig({
34
- plugins: [react(), mirrorstate()],
35
- });
36
- ```
37
-
38
- Set up a React component:
13
+ ## Documentation
39
14
 
40
- ```tsx
41
- import { useMirrorState } from "react-mirrorstate";
42
-
43
- function App() {
44
- const [state, updateState] = useMirrorState("state", {
45
- count: 0,
46
- message: "",
47
- });
48
-
49
- return (
50
- <div>
51
- <p>
52
- {state.message}: {state.count}
53
- </p>
54
- <button
55
- onClick={() =>
56
- updateState((draft) => {
57
- draft.count++;
58
- })
59
- }
60
- >
61
- Increment
62
- </button>
63
- </div>
64
- );
65
- }
66
- ```
67
-
68
- ## API: `useMirrorState([key], [initialState])`
69
-
70
- - `key`: String identifier for the state file (without `.mirror.json` extension)
71
- - `initialState`: Default state if no persisted state exists
72
-
73
- Returns `[state, updateState]` similar to React's `useState`, but:
74
-
75
- - `state` is synchronized with `[key].mirror.json` file
76
- - `updateState` accepts an Immer draft function for mutations
15
+ For full documentation, examples, and API reference, see the [main MirrorState repository](https://github.com/szymonkaliski/mirrorstate).
77
16
 
78
17
  ## License
79
18
 
80
19
  MIT
81
-
@@ -4,17 +4,13 @@ declare class WebSocketConnectionManager {
4
4
  private isConnecting;
5
5
  private listeners;
6
6
  private currentStates;
7
- private initialized;
8
7
  private getWebSocketConfig;
9
8
  private buildWebSocketURL;
10
9
  connect(): Promise<void>;
11
- getInlinedInitialStates(): Promise<Record<string, any>>;
12
- loadInitialStateFromInline(name: string): Promise<any>;
13
10
  subscribe(name: string, listener: StateListener): () => void;
14
11
  private lastSentState;
15
12
  private pendingUpdates;
16
13
  updateState(name: string, state: any): void;
17
- isInitialized(name: string): boolean;
18
14
  getCurrentState(name: string): any;
19
15
  private notifyListeners;
20
16
  }
@@ -5,7 +5,6 @@ class WebSocketConnectionManager {
5
5
  isConnecting = false;
6
6
  listeners = new Map();
7
7
  currentStates = new Map();
8
- initialized = new Set();
9
8
  getWebSocketConfig() {
10
9
  try {
11
10
  const config = require("virtual:mirrorstate/config");
@@ -55,7 +54,6 @@ class WebSocketConnectionManager {
55
54
  const data = JSON.parse(event.data);
56
55
  if (data.type === "initialState") {
57
56
  this.currentStates.set(data.name, data.state);
58
- this.initialized.add(data.name);
59
57
  this.notifyListeners(data.name, data.state);
60
58
  logger(`Initial state loaded: ${data.name}`, data.state);
61
59
  }
@@ -70,29 +68,6 @@ class WebSocketConnectionManager {
70
68
  }
71
69
  };
72
70
  }
73
- async getInlinedInitialStates() {
74
- try {
75
- // Import the virtual module with inlined states
76
- const module = await import("virtual:mirrorstate/initial-states");
77
- return module.INITIAL_STATES || {};
78
- }
79
- catch (error) {
80
- logger("Failed to load virtual module:", error);
81
- return {};
82
- }
83
- }
84
- async loadInitialStateFromInline(name) {
85
- // Always try to load inlined states - both in dev and production
86
- const inlinedStates = await this.getInlinedInitialStates();
87
- const state = inlinedStates[name];
88
- if (state !== undefined) {
89
- this.currentStates.set(name, state);
90
- this.initialized.add(name);
91
- logger(`Loaded inlined initial state: ${name}`, state);
92
- return state;
93
- }
94
- return undefined;
95
- }
96
71
  subscribe(name, listener) {
97
72
  if (!this.listeners.has(name)) {
98
73
  this.listeners.set(name, new Set());
@@ -104,14 +79,6 @@ class WebSocketConnectionManager {
104
79
  if (this.currentStates.has(name)) {
105
80
  listener(this.currentStates.get(name));
106
81
  }
107
- else {
108
- // Try to load from inlined states (production) or wait for WebSocket (dev)
109
- this.loadInitialStateFromInline(name).then((state) => {
110
- if (state !== undefined) {
111
- this.notifyListeners(name, state);
112
- }
113
- });
114
- }
115
82
  return () => {
116
83
  const nameListeners = this.listeners.get(name);
117
84
  if (nameListeners) {
@@ -152,9 +119,6 @@ class WebSocketConnectionManager {
152
119
  }, 10);
153
120
  this.pendingUpdates.set(name, timeout);
154
121
  }
155
- isInitialized(name) {
156
- return this.initialized.has(name);
157
- }
158
122
  getCurrentState(name) {
159
123
  return this.currentStates.get(name);
160
124
  }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import { Draft } from "immer";
2
- export declare function useMirrorState<T>(name: string, initialValue: T): readonly [T, (updater: (draft: Draft<T>) => void) => void];
2
+ export declare function useMirrorState<T>(name: string): [T | undefined, (updater: (draft: Draft<T>) => void) => void];
3
+ export declare function useMirrorState<T>(name: string, initialValue: T): [T, (updater: (draft: Draft<T>) => void) => void];
3
4
  export default useMirrorState;
package/dist/index.js CHANGED
@@ -1,35 +1,26 @@
1
- import { useEffect, useState } from "react";
1
+ import { useEffect, useState, useRef } from "react";
2
2
  import { produce } from "immer";
3
3
  import { connectionManager } from "./connection-manager";
4
+ import { INITIAL_STATES } from "virtual:mirrorstate/initial-states";
4
5
  export function useMirrorState(name, initialValue) {
5
- const [state, setState] = useState(initialValue);
6
- const [isInitialized, setIsInitialized] = useState(false);
7
- // The connection manager handles both WebSocket (dev) and inlined state (production)
6
+ const [state, setState] = useState(() => INITIAL_STATES?.[name] ?? initialValue);
7
+ const hasCreatedFile = useRef(false);
8
8
  useEffect(() => {
9
9
  // Subscribe to state changes for this name
10
10
  const unsubscribe = connectionManager.subscribe(name, (newState) => {
11
11
  setState(newState);
12
- setIsInitialized(true);
13
12
  });
14
- // Check if already initialized and has current state
15
- if (connectionManager.isInitialized(name)) {
16
- const currentState = connectionManager.getCurrentState(name);
17
- if (currentState !== undefined) {
18
- setState(currentState);
19
- setIsInitialized(true);
20
- }
13
+ // If file doesn't exist and initialValue was provided, create it
14
+ if (INITIAL_STATES?.[name] === undefined &&
15
+ initialValue !== undefined &&
16
+ !hasCreatedFile.current) {
17
+ hasCreatedFile.current = true;
18
+ connectionManager.updateState(name, initialValue);
21
19
  }
22
- // Set a timeout to mark as initialized even if no file exists
23
- const initTimeout = setTimeout(() => {
24
- if (!isInitialized && !connectionManager.isInitialized(name)) {
25
- setIsInitialized(true);
26
- }
27
- }, 100);
28
20
  return () => {
29
21
  unsubscribe();
30
- clearTimeout(initTimeout);
31
22
  };
32
- }, [name, isInitialized]);
23
+ }, [name, initialValue]);
33
24
  const updateMirrorState = (updater) => {
34
25
  setState((prevState) => {
35
26
  const newState = produce(prevState, updater);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-mirrorstate",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "React library for bidirectional state synchronization with MirrorState",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -16,6 +16,11 @@
16
16
  ],
17
17
  "author": "",
18
18
  "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/szymonkaliski/mirrorstate.git",
22
+ "directory": "packages/react-mirrorstate"
23
+ },
19
24
  "dependencies": {
20
25
  "debug": "^4.4.1",
21
26
  "immer": "^10.0.3"