@crdt-sync/react 0.3.1 → 0.3.2
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 +29 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -126,3 +126,32 @@ export function App() {
|
|
|
126
126
|
return <div>{state.count}</div>;
|
|
127
127
|
}
|
|
128
128
|
```
|
|
129
|
+
|
|
130
|
+
Additionally, if Vite fails to start or throws errors during pre-bundling, add this to your `vite.config.ts`:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// vite.config.ts
|
|
134
|
+
import { defineConfig } from 'vite';
|
|
135
|
+
|
|
136
|
+
export default defineConfig({
|
|
137
|
+
optimizeDeps: {
|
|
138
|
+
// Prevent Vite from pre-bundling @crdt-sync/core.
|
|
139
|
+
// The package uses dynamic WebAssembly imports that Vite cannot
|
|
140
|
+
// handle during pre-bundling.
|
|
141
|
+
exclude: ['@crdt-sync/core'],
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Server Setup
|
|
149
|
+
|
|
150
|
+
`@crdt-sync/react` connects to any WebSocket endpoint that speaks the `crdt-sync` envelope protocol. The simplest option is to run the official relay server:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npm install @crdt-sync/server
|
|
154
|
+
npx crdt-sync-server --port 8080
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
See the [@crdt-sync/server README](https://www.npmjs.com/package/@crdt-sync/server) for full documentation.
|
package/dist/index.d.mts
CHANGED
|
@@ -2,9 +2,9 @@ import { CrdtStateProxy } from '@crdt-sync/core';
|
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
4
|
type CrdtStatus = 'connecting' | 'open' | 'error';
|
|
5
|
-
interface UseCrdtStateResult<T
|
|
5
|
+
interface UseCrdtStateResult<T extends Record<string, unknown>> {
|
|
6
6
|
state: T;
|
|
7
|
-
proxy: CrdtStateProxy | null;
|
|
7
|
+
proxy: CrdtStateProxy<T> | null;
|
|
8
8
|
status: CrdtStatus;
|
|
9
9
|
}
|
|
10
10
|
interface UseCrdtStateOptions {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { CrdtStateProxy } from '@crdt-sync/core';
|
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
4
|
type CrdtStatus = 'connecting' | 'open' | 'error';
|
|
5
|
-
interface UseCrdtStateResult<T
|
|
5
|
+
interface UseCrdtStateResult<T extends Record<string, unknown>> {
|
|
6
6
|
state: T;
|
|
7
|
-
proxy: CrdtStateProxy | null;
|
|
7
|
+
proxy: CrdtStateProxy<T> | null;
|
|
8
8
|
status: CrdtStatus;
|
|
9
9
|
}
|
|
10
10
|
interface UseCrdtStateOptions {
|
package/dist/index.js
CHANGED
|
@@ -64,8 +64,9 @@ function useCrdtState(url, initialState, options) {
|
|
|
64
64
|
const clientId = "client-" + Math.random().toString(36).substring(2, 11);
|
|
65
65
|
const store = new import_core.WasmStateStore(clientId);
|
|
66
66
|
currentProxy = new import_core.CrdtStateProxy(store);
|
|
67
|
+
const mutableState = currentProxy.state;
|
|
67
68
|
for (const [key, value] of Object.entries(initialRef)) {
|
|
68
|
-
|
|
69
|
+
mutableState[key] = value;
|
|
69
70
|
}
|
|
70
71
|
if (!active) return;
|
|
71
72
|
setProxy(currentProxy);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/useCrdtState.ts","../src/CrdtSyncContext.tsx"],"sourcesContent":["export { useCrdtState } from './useCrdtState';\nexport type { CrdtStatus, UseCrdtStateResult, UseCrdtStateOptions } from './useCrdtState';\nexport { CrdtSyncProvider } from './CrdtSyncContext';\nexport type { CrdtSyncContextValue, CrdtSyncProviderProps } from './CrdtSyncContext';\n","import { useState, useEffect, useContext } from 'react';\nimport { CrdtStateProxy, WebSocketManager, initWasm, WasmStateStore } from '@crdt-sync/core';\nimport { CrdtSyncContext } from './CrdtSyncContext.js';\n\nexport type CrdtStatus = 'connecting' | 'open' | 'error';\n\nexport interface UseCrdtStateResult<T
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/useCrdtState.ts","../src/CrdtSyncContext.tsx"],"sourcesContent":["export { useCrdtState } from './useCrdtState';\nexport type { CrdtStatus, UseCrdtStateResult, UseCrdtStateOptions } from './useCrdtState';\nexport { CrdtSyncProvider } from './CrdtSyncContext';\nexport type { CrdtSyncContextValue, CrdtSyncProviderProps } from './CrdtSyncContext';\n","import { useState, useEffect, useContext } from 'react';\nimport { CrdtStateProxy, WebSocketManager, initWasm, WasmStateStore } from '@crdt-sync/core';\nimport { CrdtSyncContext } from './CrdtSyncContext.js';\n\nexport type CrdtStatus = 'connecting' | 'open' | 'error';\n\nexport interface UseCrdtStateResult<T extends Record<string, unknown>> {\n state: T;\n proxy: CrdtStateProxy<T> | null;\n status: CrdtStatus;\n}\n\nexport interface UseCrdtStateOptions {\n wasmUrl?: string;\n}\n\nexport function useCrdtState<T extends Record<string, unknown>>(\n url: string,\n initialState: T,\n options?: UseCrdtStateOptions\n): UseCrdtStateResult<T> {\n const [proxy, setProxy] = useState<CrdtStateProxy<T> | null>(null);\n const [status, setStatus] = useState<CrdtStatus>('connecting');\n const [, setTick] = useState(0);\n\n // Note: we're ignoring initialState updates (this behaves like useState).\n const [initialRef] = useState(initialState);\n\n const { wasmUrl: contextWasmUrl } = useContext(CrdtSyncContext);\n\n useEffect(() => {\n let active = true;\n let manager: WebSocketManager | null = null;\n let currentProxy: CrdtStateProxy<T> | null = null;\n\n async function setup() {\n try {\n await initWasm(options?.wasmUrl ?? contextWasmUrl);\n if (!active) return;\n\n // Create a unique client ID\n const clientId = 'client-' + Math.random().toString(36).substring(2, 11);\n const store = new WasmStateStore(clientId);\n\n currentProxy = new CrdtStateProxy<T>(store);\n\n // Initialize state. Cast needed: TypeScript disallows index-writes on a\n // generic T even though T extends Record<string, unknown>.\n const mutableState = currentProxy.state as Record<string, unknown>;\n for (const [key, value] of Object.entries(initialRef)) {\n mutableState[key] = value;\n }\n\n if (!active) return;\n setProxy(currentProxy);\n\n const ws = new WebSocket(url);\n\n ws.onopen = () => {\n if (active) setStatus('open');\n };\n\n ws.onerror = () => {\n if (active) setStatus('error');\n };\n\n ws.onclose = () => {\n if (active) setStatus('connecting');\n };\n\n manager = new WebSocketManager(store, currentProxy, ws as any);\n } catch (err) {\n console.error('Failed to initialize CRDT sync:', err);\n if (active) setStatus('error');\n }\n }\n\n setup();\n\n return () => {\n active = false;\n if (manager) manager.disconnect();\n };\n }, [url, initialRef, contextWasmUrl]);\n\n useEffect(() => {\n if (!proxy) return;\n\n // Re-render on any state change: local writes and incoming remote updates.\n return proxy.onChange(() => {\n setTick(t => t + 1);\n });\n }, [proxy]);\n\n const state = proxy ? (proxy.state as T) : initialRef;\n\n return { state, proxy, status };\n}\n","import React, { createContext } from 'react';\nimport type { ReactNode } from 'react';\n\nexport interface CrdtSyncContextValue {\n wasmUrl?: string;\n}\n\nexport const CrdtSyncContext = createContext<CrdtSyncContextValue>({});\n\nexport interface CrdtSyncProviderProps {\n wasmUrl?: string;\n children: ReactNode;\n}\n\nexport function CrdtSyncProvider({ wasmUrl, children }: CrdtSyncProviderProps): JSX.Element {\n return (\n <CrdtSyncContext.Provider value={{ wasmUrl }}>\n {children}\n </CrdtSyncContext.Provider>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAAgD;AAChD,kBAA2E;;;ACD3E,mBAAqC;AAO9B,IAAM,sBAAkB,4BAAoC,CAAC,CAAC;AAO9D,SAAS,iBAAiB,EAAE,SAAS,SAAS,GAAuC;AACxF,SACI,6BAAAC,QAAA,cAAC,gBAAgB,UAAhB,EAAyB,OAAO,EAAE,QAAQ,KACtC,QACL;AAER;;;ADJO,SAAS,aACZ,KACA,cACA,SACqB;AACrB,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAmC,IAAI;AACjE,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAqB,YAAY;AAC7D,QAAM,CAAC,EAAE,OAAO,QAAI,wBAAS,CAAC;AAG9B,QAAM,CAAC,UAAU,QAAI,wBAAS,YAAY;AAE1C,QAAM,EAAE,SAAS,eAAe,QAAI,0BAAW,eAAe;AAE9D,+BAAU,MAAM;AACZ,QAAI,SAAS;AACb,QAAI,UAAmC;AACvC,QAAI,eAAyC;AAE7C,mBAAe,QAAQ;AACnB,UAAI;AACA,kBAAM,sBAAS,SAAS,WAAW,cAAc;AACjD,YAAI,CAAC,OAAQ;AAGb,cAAM,WAAW,YAAY,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,EAAE;AACvE,cAAM,QAAQ,IAAI,2BAAe,QAAQ;AAEzC,uBAAe,IAAI,2BAAkB,KAAK;AAI1C,cAAM,eAAe,aAAa;AAClC,mBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,UAAU,GAAG;AACnD,uBAAa,GAAG,IAAI;AAAA,QACxB;AAEA,YAAI,CAAC,OAAQ;AACb,iBAAS,YAAY;AAErB,cAAM,KAAK,IAAI,UAAU,GAAG;AAE5B,WAAG,SAAS,MAAM;AACd,cAAI,OAAQ,WAAU,MAAM;AAAA,QAChC;AAEA,WAAG,UAAU,MAAM;AACf,cAAI,OAAQ,WAAU,OAAO;AAAA,QACjC;AAEA,WAAG,UAAU,MAAM;AACf,cAAI,OAAQ,WAAU,YAAY;AAAA,QACtC;AAEA,kBAAU,IAAI,6BAAiB,OAAO,cAAc,EAAS;AAAA,MACjE,SAAS,KAAK;AACV,gBAAQ,MAAM,mCAAmC,GAAG;AACpD,YAAI,OAAQ,WAAU,OAAO;AAAA,MACjC;AAAA,IACJ;AAEA,UAAM;AAEN,WAAO,MAAM;AACT,eAAS;AACT,UAAI,QAAS,SAAQ,WAAW;AAAA,IACpC;AAAA,EACJ,GAAG,CAAC,KAAK,YAAY,cAAc,CAAC;AAEpC,+BAAU,MAAM;AACZ,QAAI,CAAC,MAAO;AAGZ,WAAO,MAAM,SAAS,MAAM;AACxB,cAAQ,OAAK,IAAI,CAAC;AAAA,IACtB,CAAC;AAAA,EACL,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,QAAQ,QAAS,MAAM,QAAc;AAE3C,SAAO,EAAE,OAAO,OAAO,OAAO;AAClC;","names":["import_react","React"]}
|
package/dist/index.mjs
CHANGED
|
@@ -27,8 +27,9 @@ function useCrdtState(url, initialState, options) {
|
|
|
27
27
|
const clientId = "client-" + Math.random().toString(36).substring(2, 11);
|
|
28
28
|
const store = new WasmStateStore(clientId);
|
|
29
29
|
currentProxy = new CrdtStateProxy(store);
|
|
30
|
+
const mutableState = currentProxy.state;
|
|
30
31
|
for (const [key, value] of Object.entries(initialRef)) {
|
|
31
|
-
|
|
32
|
+
mutableState[key] = value;
|
|
32
33
|
}
|
|
33
34
|
if (!active) return;
|
|
34
35
|
setProxy(currentProxy);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/useCrdtState.ts","../src/CrdtSyncContext.tsx"],"sourcesContent":["import { useState, useEffect, useContext } from 'react';\nimport { CrdtStateProxy, WebSocketManager, initWasm, WasmStateStore } from '@crdt-sync/core';\nimport { CrdtSyncContext } from './CrdtSyncContext.js';\n\nexport type CrdtStatus = 'connecting' | 'open' | 'error';\n\nexport interface UseCrdtStateResult<T
|
|
1
|
+
{"version":3,"sources":["../src/useCrdtState.ts","../src/CrdtSyncContext.tsx"],"sourcesContent":["import { useState, useEffect, useContext } from 'react';\nimport { CrdtStateProxy, WebSocketManager, initWasm, WasmStateStore } from '@crdt-sync/core';\nimport { CrdtSyncContext } from './CrdtSyncContext.js';\n\nexport type CrdtStatus = 'connecting' | 'open' | 'error';\n\nexport interface UseCrdtStateResult<T extends Record<string, unknown>> {\n state: T;\n proxy: CrdtStateProxy<T> | null;\n status: CrdtStatus;\n}\n\nexport interface UseCrdtStateOptions {\n wasmUrl?: string;\n}\n\nexport function useCrdtState<T extends Record<string, unknown>>(\n url: string,\n initialState: T,\n options?: UseCrdtStateOptions\n): UseCrdtStateResult<T> {\n const [proxy, setProxy] = useState<CrdtStateProxy<T> | null>(null);\n const [status, setStatus] = useState<CrdtStatus>('connecting');\n const [, setTick] = useState(0);\n\n // Note: we're ignoring initialState updates (this behaves like useState).\n const [initialRef] = useState(initialState);\n\n const { wasmUrl: contextWasmUrl } = useContext(CrdtSyncContext);\n\n useEffect(() => {\n let active = true;\n let manager: WebSocketManager | null = null;\n let currentProxy: CrdtStateProxy<T> | null = null;\n\n async function setup() {\n try {\n await initWasm(options?.wasmUrl ?? contextWasmUrl);\n if (!active) return;\n\n // Create a unique client ID\n const clientId = 'client-' + Math.random().toString(36).substring(2, 11);\n const store = new WasmStateStore(clientId);\n\n currentProxy = new CrdtStateProxy<T>(store);\n\n // Initialize state. Cast needed: TypeScript disallows index-writes on a\n // generic T even though T extends Record<string, unknown>.\n const mutableState = currentProxy.state as Record<string, unknown>;\n for (const [key, value] of Object.entries(initialRef)) {\n mutableState[key] = value;\n }\n\n if (!active) return;\n setProxy(currentProxy);\n\n const ws = new WebSocket(url);\n\n ws.onopen = () => {\n if (active) setStatus('open');\n };\n\n ws.onerror = () => {\n if (active) setStatus('error');\n };\n\n ws.onclose = () => {\n if (active) setStatus('connecting');\n };\n\n manager = new WebSocketManager(store, currentProxy, ws as any);\n } catch (err) {\n console.error('Failed to initialize CRDT sync:', err);\n if (active) setStatus('error');\n }\n }\n\n setup();\n\n return () => {\n active = false;\n if (manager) manager.disconnect();\n };\n }, [url, initialRef, contextWasmUrl]);\n\n useEffect(() => {\n if (!proxy) return;\n\n // Re-render on any state change: local writes and incoming remote updates.\n return proxy.onChange(() => {\n setTick(t => t + 1);\n });\n }, [proxy]);\n\n const state = proxy ? (proxy.state as T) : initialRef;\n\n return { state, proxy, status };\n}\n","import React, { createContext } from 'react';\nimport type { ReactNode } from 'react';\n\nexport interface CrdtSyncContextValue {\n wasmUrl?: string;\n}\n\nexport const CrdtSyncContext = createContext<CrdtSyncContextValue>({});\n\nexport interface CrdtSyncProviderProps {\n wasmUrl?: string;\n children: ReactNode;\n}\n\nexport function CrdtSyncProvider({ wasmUrl, children }: CrdtSyncProviderProps): JSX.Element {\n return (\n <CrdtSyncContext.Provider value={{ wasmUrl }}>\n {children}\n </CrdtSyncContext.Provider>\n );\n}\n"],"mappings":";AAAA,SAAS,UAAU,WAAW,kBAAkB;AAChD,SAAS,gBAAgB,kBAAkB,UAAU,sBAAsB;;;ACD3E,OAAO,SAAS,qBAAqB;AAO9B,IAAM,kBAAkB,cAAoC,CAAC,CAAC;AAO9D,SAAS,iBAAiB,EAAE,SAAS,SAAS,GAAuC;AACxF,SACI,oCAAC,gBAAgB,UAAhB,EAAyB,OAAO,EAAE,QAAQ,KACtC,QACL;AAER;;;ADJO,SAAS,aACZ,KACA,cACA,SACqB;AACrB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAmC,IAAI;AACjE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAqB,YAAY;AAC7D,QAAM,CAAC,EAAE,OAAO,IAAI,SAAS,CAAC;AAG9B,QAAM,CAAC,UAAU,IAAI,SAAS,YAAY;AAE1C,QAAM,EAAE,SAAS,eAAe,IAAI,WAAW,eAAe;AAE9D,YAAU,MAAM;AACZ,QAAI,SAAS;AACb,QAAI,UAAmC;AACvC,QAAI,eAAyC;AAE7C,mBAAe,QAAQ;AACnB,UAAI;AACA,cAAM,SAAS,SAAS,WAAW,cAAc;AACjD,YAAI,CAAC,OAAQ;AAGb,cAAM,WAAW,YAAY,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,EAAE;AACvE,cAAM,QAAQ,IAAI,eAAe,QAAQ;AAEzC,uBAAe,IAAI,eAAkB,KAAK;AAI1C,cAAM,eAAe,aAAa;AAClC,mBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,UAAU,GAAG;AACnD,uBAAa,GAAG,IAAI;AAAA,QACxB;AAEA,YAAI,CAAC,OAAQ;AACb,iBAAS,YAAY;AAErB,cAAM,KAAK,IAAI,UAAU,GAAG;AAE5B,WAAG,SAAS,MAAM;AACd,cAAI,OAAQ,WAAU,MAAM;AAAA,QAChC;AAEA,WAAG,UAAU,MAAM;AACf,cAAI,OAAQ,WAAU,OAAO;AAAA,QACjC;AAEA,WAAG,UAAU,MAAM;AACf,cAAI,OAAQ,WAAU,YAAY;AAAA,QACtC;AAEA,kBAAU,IAAI,iBAAiB,OAAO,cAAc,EAAS;AAAA,MACjE,SAAS,KAAK;AACV,gBAAQ,MAAM,mCAAmC,GAAG;AACpD,YAAI,OAAQ,WAAU,OAAO;AAAA,MACjC;AAAA,IACJ;AAEA,UAAM;AAEN,WAAO,MAAM;AACT,eAAS;AACT,UAAI,QAAS,SAAQ,WAAW;AAAA,IACpC;AAAA,EACJ,GAAG,CAAC,KAAK,YAAY,cAAc,CAAC;AAEpC,YAAU,MAAM;AACZ,QAAI,CAAC,MAAO;AAGZ,WAAO,MAAM,SAAS,MAAM;AACxB,cAAQ,OAAK,IAAI,CAAC;AAAA,IACtB,CAAC;AAAA,EACL,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,QAAQ,QAAS,MAAM,QAAc;AAE3C,SAAO,EAAE,OAAO,OAAO,OAAO;AAClC;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crdt-sync/react",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "React hook adapter for crdt-sync",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"react-dom": ">=16.8"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@crdt-sync/core": "^0.3.
|
|
27
|
+
"@crdt-sync/core": "^0.3.2"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@testing-library/jest-dom": "^6.9.1",
|