react-mirrorstate 0.1.0-alpha.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 ADDED
@@ -0,0 +1,19 @@
1
+ # `react-mirrorstate`
2
+
3
+ React hook for bi-directional state synchronization with JSON files on disk.
4
+
5
+ **Note:** Both `react-mirrorstate` and `vite-plugin-mirrorstate` are required to use MirrorState.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install react-mirrorstate vite-plugin-mirrorstate
11
+ ```
12
+
13
+ ## Documentation
14
+
15
+ For full documentation, examples, and API reference, see the [main MirrorState repository](https://github.com/szymonkaliski/mirrorstate).
16
+
17
+ ## License
18
+
19
+ MIT
@@ -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-alpha.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"
@@ -27,7 +32,7 @@
27
32
  "typescript": "^5.3.3"
28
33
  },
29
34
  "peerDependencies": {
30
- "react": "^18.0.0"
35
+ "react": ">=18.0.0"
31
36
  },
32
37
  "files": [
33
38
  "dist"