├── .gitattributes ├── .gitignore ├── src ├── feature │ ├── provider │ │ ├── index.ts │ │ └── hook │ │ │ ├── index.ts │ │ │ ├── useWebSocket.spec.tsx │ │ │ ├── useIndexedDb.ts │ │ │ ├── useWebSocket.ts │ │ │ ├── useIndexedDb.spec.tsx │ │ │ ├── useWebRtc.ts │ │ │ └── useWebRtc.spec.tsx │ ├── shared-type │ │ ├── index.ts │ │ ├── hook.ts │ │ └── index.spec.tsx │ ├── awareness │ │ ├── index.ts │ │ ├── hook.ts │ │ └── hook.spec.ts │ └── doc │ │ ├── index.ts │ │ ├── type.ts │ │ ├── hook.ts │ │ ├── component.tsx │ │ └── index.spec.tsx ├── index.ts └── util │ └── index.ts ├── .vscode ├── y-react.code-workspace ├── launch.json └── settings.json ├── .husky ├── commit-msg └── pre-push ├── tsconfig.eslint.json ├── readme.md ├── example ├── awareness │ ├── vite.config.js │ ├── index.html │ ├── package.json │ ├── index.jsx │ └── package-lock.json └── hello │ ├── vite.config.js │ ├── index.html │ ├── package.json │ ├── index.jsx │ └── package-lock.json ├── jest.config.ts ├── tsconfig.json ├── .github └── workflows │ └── release.yml ├── license ├── rollup.config.ts └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ -------------------------------------------------------------------------------- /src/feature/provider/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hook' 2 | -------------------------------------------------------------------------------- /src/feature/shared-type/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hook' 2 | -------------------------------------------------------------------------------- /src/feature/awareness/index.ts: -------------------------------------------------------------------------------- 1 | export { useAwareness } from './hook' 2 | -------------------------------------------------------------------------------- /.vscode/y-react.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": ".." 5 | } 6 | ] 7 | } -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "dist/**/*" 5 | ] 6 | } -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install ts-standard --fix 5 | npm test 6 | -------------------------------------------------------------------------------- /src/feature/doc/index.ts: -------------------------------------------------------------------------------- 1 | export { DocumentProvider } from './component' 2 | export { useDoc, useProviders } from './hook' 3 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Yjs React Bindings 2 | 3 | This project provides a set of React components and React hooks for working with 4 | Yjs in an idiomatic way. -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './feature/doc' 2 | export * from './feature/shared-type' 3 | export * from './feature/provider' 4 | export * from './feature/awareness' 5 | -------------------------------------------------------------------------------- /src/feature/provider/hook/index.ts: -------------------------------------------------------------------------------- 1 | export { useIndexedDb } from './useIndexedDb' 2 | export { useWebRtc } from './useWebRtc' 3 | export { useWebSocket } from './useWebSocket' 4 | -------------------------------------------------------------------------------- /example/awareness/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | commonjs() 7 | ] 8 | }) 9 | -------------------------------------------------------------------------------- /example/hello/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import commonjs from '@rollup/plugin-commonjs' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | commonjs() 7 | ] 8 | }) 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "attach", 7 | "name": "Attach", 8 | "port": 9229 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /src/feature/doc/type.ts: -------------------------------------------------------------------------------- 1 | import { IndexeddbPersistence } from 'y-indexeddb' 2 | import { WebrtcProvider } from 'y-webrtc' 3 | import { WebsocketProvider } from 'y-websocket' 4 | 5 | export type Provider = 6 | | WebrtcProvider 7 | | WebsocketProvider 8 | | IndexeddbPersistence 9 | -------------------------------------------------------------------------------- /src/util/index.ts: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react' 2 | 3 | export const useForceUpdate = (): () => void => { 4 | const [, dispatch] = React.useState<{}>(Object.create(null)) 5 | 6 | return useCallback( 7 | () => dispatch(Object.create(null)), 8 | [dispatch] 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "[typescript]": { 4 | "editor.formatOnSave": false, 5 | }, 6 | "[typescriptreact]": { 7 | "editor.formatOnSave": false, 8 | }, 9 | "standard.enable": true, 10 | "standard.engine": "ts-standard", 11 | "standard.autoFixOnSave": true, 12 | } -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from '@jest/types' 2 | 3 | const config: Config.InitialOptions = { 4 | rootDir: './src', 5 | transform: { 6 | '.tsx?$': 'ts-jest' 7 | }, 8 | moduleNameMapper: { 9 | '^@/(.*)$': '$1' 10 | }, 11 | testEnvironment: 'jsdom' 12 | } 13 | 14 | export default config 15 | -------------------------------------------------------------------------------- /example/hello/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Hello, Yjs! 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/awareness/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Hello, Yjs! 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/awareness/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awareness", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "vite" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2", 15 | "y-webrtc": "^10.2.0", 16 | "yjs": "^13.5.12", 17 | "@joebobmiles/y-react": "^0.0.0" 18 | } 19 | } -------------------------------------------------------------------------------- /example/hello/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "vite" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "vite": "^2.5.0" 14 | }, 15 | "dependencies": { 16 | "react": "^17.0.2", 17 | "react-dom": "^17.0.2", 18 | "yjs": "^13.5.12", 19 | "@joebobmiles/y-react": "^0.0.0" 20 | } 21 | } -------------------------------------------------------------------------------- /src/feature/provider/hook/useWebSocket.spec.tsx: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react-hooks' 2 | 3 | import React from 'react' 4 | import { WebsocketProvider } from 'y-websocket' 5 | 6 | import { useWebSocket } from './useWebSocket' 7 | import { DocumentProvider } from '@/feature/doc' 8 | 9 | describe('useWebSocket', () => { 10 | it('Returns a WebSocket provider', () => { 11 | const { result } = renderHook( 12 | () => useWebSocket('ws://localhost:1234', 'room'), 13 | { 14 | wrapper: ({ children }) => 15 | ( 16 | 17 | {children} 18 | 19 | ) 20 | } 21 | ) 22 | 23 | expect(result.current).toBeInstanceOf(WebsocketProvider) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /example/hello/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { render } from 'react-dom' 3 | 4 | import { 5 | DocumentProvider, 6 | useDoc, 7 | useMap, 8 | useWebRtc 9 | } from '@joebobmiles/y-react' 10 | 11 | const App = () => { 12 | const doc = useDoc() 13 | 14 | useWebRtc(doc, 'counter-example-y-react') 15 | 16 | const { get, set } = useMap('state') 17 | 18 | useEffect( 19 | () => { 20 | set('count', 0) 21 | }, 22 | [] 23 | ) 24 | 25 | return ( 26 |
27 |

{get('count')}

28 | 29 |
30 | ) 31 | } 32 | 33 | render( 34 | 35 | 36 | , 37 | document.getElementById('app-root') 38 | ) 39 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es5", 5 | "module": "esnext", 6 | "rootDir": "./src", 7 | "outDir": "./dist", 8 | "declaration": true, 9 | "declarationMap": true, 10 | "sourceMap": true, 11 | "removeComments": true, 12 | "jsx": "react", 13 | /* Strict Type-Checking Options */ 14 | "strict": true, 15 | "alwaysStrict": true, 16 | /* Module Resolution Options */ 17 | "moduleResolution": "node", 18 | "esModuleInterop": true, 19 | "paths": { 20 | "@/*": [ 21 | "./src/*" 22 | ] 23 | }, 24 | /* Advanced Options */ 25 | "skipLibCheck": true, 26 | "forceConsistentCasingInFileNames": true 27 | }, 28 | "exclude": [ 29 | "**/*.config.ts", 30 | "dist/**/*" 31 | ], 32 | } -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [ master, staging ] 6 | 7 | jobs: 8 | release: 9 | name: Build & Release 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | persist-credentials: false 16 | 17 | - uses: actions/setup-node@v2 18 | with: 19 | node-version: 15 20 | 21 | - run: npm ci 22 | 23 | - run: npm run build 24 | 25 | - run: npx semantic-release 26 | env: 27 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 28 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 29 | 30 | - uses: actions/upload-artifact@v2 31 | if: ${{ failure() }} 32 | with: 33 | name: npm-install-log-test 34 | path: /home/runner/.npm/_logs -------------------------------------------------------------------------------- /src/feature/doc/hook.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import * as Y from 'yjs' 3 | 4 | import { DocumentContext } from './component' 5 | import { Provider } from './type' 6 | 7 | export const useDoc = (): Y.Doc => { 8 | const { doc } = React.useContext(DocumentContext) 9 | 10 | if (doc !== null) { 11 | return doc 12 | } else { 13 | throw new Error( 14 | 'Could not retrieve a document. Please wrap in a DocumentProvider.' 15 | ) 16 | } 17 | } 18 | 19 | export const useProviders = (): Map Provider, Map> => { 20 | const { providers } = React.useContext(DocumentContext) 21 | 22 | if (providers !== null) { 23 | return providers 24 | } else { 25 | throw new Error( 26 | 'Could not retrieve a set of providers. Please wrap in a DocumentProvider.' 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/feature/provider/hook/useIndexedDb.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { IndexeddbPersistence } from 'y-indexeddb' 3 | 4 | import { useDoc, useProviders } from '@/feature/doc' 5 | 6 | export const useIndexedDb = (name: string): IndexeddbPersistence => { 7 | const doc = useDoc() 8 | const providers = useProviders() 9 | 10 | const existingProvider = 11 | providers.get(IndexeddbPersistence)?.get(name) as IndexeddbPersistence | undefined 12 | 13 | const provider = React.useMemo( 14 | () => new IndexeddbPersistence(name, doc), 15 | [doc, name] 16 | ) 17 | 18 | if (existingProvider !== undefined) { 19 | return existingProvider 20 | } else { 21 | if (!(providers.has(IndexeddbPersistence))) { 22 | providers.set(IndexeddbPersistence, new Map()) 23 | } 24 | 25 | providers.get(IndexeddbPersistence)?.set(name, provider) 26 | 27 | return provider 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/feature/provider/hook/useWebSocket.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { WebsocketProvider } from 'y-websocket' 3 | 4 | import { useDoc, useProviders } from '@/feature/doc' 5 | 6 | export const useWebSocket = ( 7 | url: string, 8 | room: string 9 | ): WebsocketProvider => { 10 | const doc = useDoc() 11 | const providers = useProviders() 12 | 13 | const existingProvider = 14 | providers.get(WebsocketProvider)?.get(room) as WebsocketProvider | undefined 15 | 16 | const provider = React.useMemo( 17 | () => new WebsocketProvider(url, room, doc), 18 | [doc, room] 19 | ) 20 | 21 | if (existingProvider !== undefined) { 22 | return existingProvider 23 | } else { 24 | if (!(providers.has(WebsocketProvider))) { 25 | providers.set(WebsocketProvider, new Map()) 26 | } 27 | 28 | providers.get(WebsocketProvider)?.set(room, provider) 29 | 30 | return provider 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/feature/provider/hook/useIndexedDb.spec.tsx: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react-hooks' 2 | 3 | import React from 'react' 4 | import { IndexeddbPersistence } from 'y-indexeddb' 5 | 6 | import { useIndexedDb } from './useIndexedDb' 7 | import { DocumentProvider } from '@/feature/doc' 8 | 9 | jest.mock('y-indexeddb', () => ({ 10 | IndexeddbPersistence: class { 11 | whenSynced = new Promise((resolve) => resolve()) 12 | } 13 | })) 14 | 15 | describe('useIndexedDb', () => { 16 | it('Returns an IndexedDB provider', async () => { 17 | const { result } = renderHook( 18 | () => useIndexedDb('test'), 19 | { 20 | wrapper: ({ children }) => 21 | ( 22 | 23 | {children} 24 | 25 | ) 26 | } 27 | ) 28 | 29 | await result.current.whenSynced 30 | 31 | expect(result.current).toBeInstanceOf(IndexeddbPersistence) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Joseph R Miles 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the “Software”), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /rollup.config.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path' 2 | 3 | import { defineConfig } from 'rollup' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import commonjs from '@rollup/plugin-commonjs' 6 | import typescript from '@rollup/plugin-typescript' 7 | import alias from '@rollup/plugin-alias' 8 | 9 | export default defineConfig({ 10 | input: 'src/index.ts', 11 | plugins: [ 12 | alias({ 13 | entries: [ 14 | { find: /^@(?=\/)/, replacement: path.resolve(__dirname, 'src') } 15 | ] 16 | }), 17 | resolve(), 18 | commonjs(), 19 | typescript({ 20 | tsconfig: './tsconfig.json', 21 | declarationDir: './' 22 | }) 23 | ], 24 | output: [ 25 | { 26 | name: 'ESM', 27 | file: 'dist/y-react.mjs', 28 | format: 'es', 29 | sourcemap: true 30 | }, 31 | { 32 | name: 'CommonJS', 33 | file: 'dist/y-react.cjs', 34 | format: 'cjs', 35 | exports: 'named', 36 | sourcemap: true 37 | } 38 | ], 39 | external: [ 40 | 'yjs', 41 | 'y-indexeddb', 42 | 'y-protocols', 43 | 'y-webrtc', 44 | 'y-websocket', 45 | 'react' 46 | ] 47 | }) 48 | -------------------------------------------------------------------------------- /src/feature/awareness/hook.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Awareness } from 'y-protocols/awareness' 3 | import { useForceUpdate } from '@/util' 4 | 5 | export const useAwareness = ( 6 | awareness: Awareness 7 | ): { 8 | states: Map 9 | localID: number 10 | localState: T 11 | setLocalState: React.Dispatch> 12 | } => { 13 | const forceUpdate = useForceUpdate() 14 | React.useEffect( 15 | () => { 16 | const forceUpdateOnAwarenessChange = (): void => forceUpdate() 17 | 18 | awareness.on('change', forceUpdateOnAwarenessChange) 19 | 20 | return () => awareness.off('change', forceUpdateOnAwarenessChange) 21 | }, 22 | [] 23 | ) 24 | 25 | const [localState, setLocalState] = React.useState({} as unknown as T) 26 | 27 | return ({ 28 | states: React.useMemo( 29 | () => awareness.getStates() as Map, 30 | [awareness] 31 | ), 32 | localID: awareness.clientID, 33 | localState, 34 | setLocalState: React.useCallback( 35 | (nextState) => { 36 | awareness.setLocalState( 37 | typeof nextState === 'function' 38 | /* @ts-expect-error */ 39 | ? nextState(awareness.getLocalState() as T) 40 | : nextState 41 | ) 42 | 43 | setLocalState(nextState) 44 | }, 45 | [awareness] 46 | ) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /src/feature/doc/component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import * as Y from 'yjs' 3 | 4 | import { Provider } from './type' 5 | import { useDoc } from './hook' 6 | 7 | export const DocumentContext = React.createContext<{ 8 | doc: Y.Doc | null 9 | providers: Map Provider, Map> | null 10 | }>({ 11 | doc: null, 12 | providers: null 13 | }) 14 | 15 | interface DocumentProviderProps { 16 | children: React.ReactNode 17 | doc?: Y.Doc 18 | folderName?: string 19 | documentName?: string 20 | } 21 | 22 | export const DocumentProvider = ({ 23 | children, 24 | doc = new Y.Doc(), 25 | folderName, 26 | documentName 27 | }: DocumentProviderProps): JSX.Element => { 28 | let superDoc: Y.Doc | null = null 29 | try { superDoc = useDoc() } catch { } 30 | 31 | if (superDoc !== null) { 32 | superDoc.getMap(folderName ?? '').set(documentName ?? doc.guid, doc) 33 | } 34 | 35 | const providers = React.useRef Provider, Map>>(new Map()) 36 | 37 | React.useEffect( 38 | () => 39 | () => { 40 | providers.current.forEach((map) => { 41 | map.forEach((provider) => provider.destroy()) 42 | }) 43 | }, 44 | [] 45 | ) 46 | 47 | return ( 48 | 54 | {children} 55 | 56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /src/feature/provider/hook/useWebRtc.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { WebrtcProvider } from 'y-webrtc' 3 | 4 | import { useDoc, useProviders } from '@/feature/doc' 5 | import { Awareness } from 'y-protocols/awareness' 6 | 7 | export const useWebRtc = ( 8 | room: string, 9 | options: { 10 | signaling?: string[] 11 | password?: string 12 | awareness?: Awareness 13 | maxConns?: number 14 | filterBcConns?: boolean 15 | peerOpts?: any 16 | } = {} 17 | ): WebrtcProvider => { 18 | const doc = useDoc() 19 | const providers = useProviders() 20 | 21 | const existingProvider = 22 | providers.get(WebrtcProvider)?.get(room) as WebrtcProvider | undefined 23 | 24 | return React.useMemo( 25 | () => { 26 | if (existingProvider !== undefined) { 27 | return existingProvider 28 | } else { 29 | const provider = new WebrtcProvider( 30 | room, 31 | doc, 32 | options as { 33 | signaling: string[] 34 | password: string | null 35 | awareness: Awareness 36 | maxConns: number 37 | filterBcConns: boolean 38 | peerOpts: any 39 | } 40 | ) 41 | 42 | if (!(providers.has(WebrtcProvider))) { 43 | providers.set(WebrtcProvider, new Map()) 44 | } 45 | 46 | providers.get(WebrtcProvider)?.set(room, provider) 47 | 48 | return provider 49 | } 50 | }, 51 | [existingProvider] 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /example/awareness/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | 4 | import { 5 | DocumentProvider, 6 | useDoc, 7 | useMap, 8 | useWebRtc, 9 | useAwareness 10 | } from '@joebobmiles/y-react' 11 | 12 | const App = () => { 13 | const doc = useDoc() 14 | 15 | const provider = useWebRtc(doc, 'counter-example-y-react') 16 | const { states, localID, setLocalState } = useAwareness(provider.awareness) 17 | 18 | const { get, set } = useMap('state') 19 | 20 | React.useEffect( 21 | () => { 22 | set('count', 0) 23 | 24 | window.addEventListener('pointermove', (e) => { 25 | setLocalState((prevState) => ({ 26 | ...prevState, 27 | x: e.clientX, 28 | y: e.clientY 29 | })) 30 | }) 31 | }, 32 | [] 33 | ) 34 | 35 | return ( 36 |
37 | 50 | { 51 | Array.from(states.entries()) 52 | .filter(([id]) => id !== localID) 53 | .map( 54 | ([id, state]) => ( 55 | 56 | ) 57 | ) 58 | } 59 | 60 |

{get('count')}

61 | 62 |
63 | ) 64 | } 65 | 66 | render( 67 | 68 | 69 | , 70 | document.getElementById('app-root') 71 | ) 72 | -------------------------------------------------------------------------------- /src/feature/doc/index.spec.tsx: -------------------------------------------------------------------------------- 1 | import { renderHook } from '@testing-library/react-hooks' 2 | 3 | import React from 'react' 4 | import * as Y from 'yjs' 5 | 6 | import { DocumentProvider, useDoc } from '.' 7 | 8 | describe('useDoc', () => { 9 | it('Provides access to a Yjs Doc.', () => { 10 | const { result } = renderHook( 11 | () => useDoc(), 12 | { 13 | wrapper: ({ children }) => 14 | 15 | {children} 16 | 17 | } 18 | ) 19 | 20 | expect(result.current).toBeInstanceOf(Y.Doc) 21 | }) 22 | 23 | it('Nests documents with nested document providers.', () => { 24 | const { result } = renderHook( 25 | () => useDoc(), 26 | { 27 | wrapper: ({ children }) => 28 | 29 | {children} 30 | 31 | <> 32 | 33 | 34 | } 35 | ) 36 | 37 | expect(result.current.getSubdocs().size).toBe(1) 38 | }) 39 | 40 | it('Properly nests deeply nested document providers.', () => { 41 | const { result } = renderHook( 42 | () => useDoc(), 43 | { 44 | wrapper: ({ children }) => 45 | 46 | {children} 47 | 48 | 49 | <> 50 | 51 | 52 | 53 | } 54 | ) 55 | 56 | expect(result.current.getSubdocs().size).toBe(1) 57 | expect(Array.from(result.current.getSubdocs().values()).length).toBe(1) 58 | }) 59 | 60 | it('Throws an error when not inside a DocumentProvider.', () => { 61 | const { result } = renderHook(() => useDoc()) 62 | 63 | expect(result.error).toEqual(new Error( 64 | 'Could not retrieve a document. Please wrap in a DocumentProvider.' 65 | )) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /src/feature/awareness/hook.spec.ts: -------------------------------------------------------------------------------- 1 | import { act, renderHook } from '@testing-library/react-hooks' 2 | 3 | import * as Y from 'yjs' 4 | import { Awareness } from 'y-protocols/awareness' 5 | 6 | import { useAwareness } from './hook' 7 | 8 | jest.mock( 9 | 'y-protocols/awareness', 10 | () => 11 | ({ 12 | Awareness: class { 13 | clientID: number 14 | states: Map = new Map() 15 | 16 | constructor (doc: Y.Doc) { 17 | this.clientID = doc.clientID 18 | this.setLocalState({}) 19 | } 20 | 21 | getStates (): Map { 22 | return this.states as Map 23 | } 24 | 25 | on (): void {} 26 | 27 | getLocalState (): { [x: string]: any } { 28 | return this.states.get(this.clientID) ?? {} 29 | } 30 | 31 | setLocalState (state: { [x: string]: any }): void { 32 | this.states.set(this.clientID, state) 33 | } 34 | } 35 | }) 36 | ) 37 | 38 | describe('useAwareness', () => { 39 | it('Returns a list of awareness states.', () => { 40 | const doc = new Y.Doc() 41 | const awareness = new Awareness(doc) 42 | 43 | const { result } = renderHook( 44 | () => useAwareness(awareness) 45 | ) 46 | 47 | expect( 48 | Array.from( 49 | result.current.states.entries() 50 | ) 51 | ).toHaveLength(1) 52 | }) 53 | 54 | it('Allows local state to be set.', () => { 55 | const doc = new Y.Doc() 56 | const awareness = new Awareness(doc) 57 | 58 | const { result } = renderHook( 59 | () => useAwareness(awareness) 60 | ) 61 | 62 | act(() => { 63 | result.current.setLocalState({ 64 | foo: 1 65 | }) 66 | }) 67 | 68 | expect(result.current.localState).toEqual({ foo: 1 }) 69 | }) 70 | 71 | it('Allows local state to be set via function.', () => { 72 | const doc = new Y.Doc() 73 | const awareness = new Awareness(doc) 74 | 75 | const { result } = renderHook( 76 | () => useAwareness(awareness) 77 | ) 78 | 79 | act(() => { 80 | result.current.setLocalState( 81 | (prevState) => ({ 82 | ...prevState, 83 | foo: 1 84 | }) 85 | ) 86 | }) 87 | 88 | expect(result.current.localState).toEqual({ foo: 1 }) 89 | }) 90 | }) 91 | -------------------------------------------------------------------------------- /src/feature/provider/hook/useWebRtc.spec.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | import { renderHook } from '@testing-library/react-hooks' 3 | 4 | import React from 'react' 5 | import * as Y from 'yjs' 6 | 7 | jest.mock('y-webrtc', () => ({ 8 | WebrtcProvider: jest.fn() 9 | })) 10 | /* eslint-disable import/first */ 11 | import { WebrtcProvider } from 'y-webrtc' 12 | 13 | import { useWebRtc } from './useWebRtc' 14 | import { DocumentProvider } from '@/feature/doc' 15 | 16 | describe('useWebRtc', () => { 17 | it('Returns a WebRTC provider', () => { 18 | const { result } = renderHook( 19 | () => useWebRtc('room'), 20 | { 21 | wrapper: ({ children }) => 22 | ( 23 | 24 | {children} 25 | 26 | ) 27 | } 28 | ) 29 | 30 | expect(result.current).toBeInstanceOf(WebrtcProvider) 31 | }) 32 | 33 | it('Returns the same WebRTC provider under the same document.', () => { 34 | expect(() => renderHook( 35 | () => useWebRtc('room'), 36 | { 37 | wrapper: ({ children }) => 38 | ( 39 | 40 | {children} 41 | {children} 42 | 43 | ) 44 | } 45 | )).not.toThrowError( 46 | 'Error: A Yjs Doc connected to room "room" already exists!' 47 | ) 48 | }) 49 | 50 | it('Does not error on rerender.', () => { 51 | expect(() => { 52 | const { rerender } = renderHook( 53 | () => useWebRtc('room'), 54 | { 55 | wrapper: ({ children }) => 56 | ( 57 | 58 | {children} 59 | 60 | ) 61 | } 62 | ) 63 | 64 | rerender() 65 | }).not.toThrowError( 66 | 'Error: A Yjs Doc connected to room "room" already exists!' 67 | ) 68 | }) 69 | 70 | it('Passes password option to WebrtcProvider.', () => { 71 | jest.resetAllMocks() 72 | 73 | const doc = new Y.Doc() 74 | 75 | renderHook( 76 | () => useWebRtc('room', { 77 | password: 'password' 78 | }), 79 | { 80 | wrapper: ({ children }) => 81 | ( 82 | 83 | {children} 84 | 85 | ) 86 | } 87 | ) 88 | 89 | expect(WebrtcProvider).toBeCalledWith('room', doc, { 90 | password: 'password' 91 | }) 92 | }) 93 | }) 94 | -------------------------------------------------------------------------------- /src/feature/shared-type/hook.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import * as Y from 'yjs' 3 | import { useForceUpdate } from '@/util' 4 | import { useDoc } from '@/feature/doc' 5 | 6 | const useSharedType = >( 7 | name: string, 8 | constructor: Function | undefined 9 | ): T => { 10 | const doc = useDoc() 11 | return doc.get(name, constructor) as T 12 | } 13 | 14 | export const useMap = (name: string): { 15 | state: { [x: string]: T } 16 | get: (name: string) => T | undefined 17 | set: (name: string, value: T) => void 18 | } => { 19 | const map = useSharedType>(name, Y.Map) 20 | 21 | const forceUpdate = useForceUpdate() 22 | React.useEffect( 23 | () => { 24 | map.observe(() => forceUpdate()) 25 | }, 26 | [] 27 | ) 28 | 29 | return { 30 | state: map.toJSON(), 31 | get: React.useCallback( 32 | (name: string) => map.get(name), 33 | [] 34 | ), 35 | set: React.useCallback( 36 | (name, value) => 37 | map.set(name, value), 38 | [] 39 | ) 40 | } 41 | } 42 | 43 | export const useArray = (name: string): { 44 | state: T[] 45 | get: (index: number) => T | undefined 46 | insert: (index: number, content: T[]) => void 47 | delete: (index: number, length: number) => void 48 | push: (content: T[]) => void 49 | unshift: (content: T[]) => void 50 | slice: (start: number, end?: number) => void 51 | } => { 52 | const array = useSharedType>(name, Y.Array) 53 | 54 | const forceUpdate = useForceUpdate() 55 | React.useEffect( 56 | () => { 57 | array.observe(() => forceUpdate()) 58 | }, 59 | [] 60 | ) 61 | 62 | return { 63 | state: array.toJSON(), 64 | get: React.useCallback( 65 | (index) => array.get(index), 66 | [] 67 | ), 68 | insert: React.useCallback( 69 | (index, content) => 70 | array.insert(index, content), 71 | [] 72 | ), 73 | delete: React.useCallback( 74 | (index, length) => 75 | array.delete(index, length), 76 | [] 77 | ), 78 | push: React.useCallback( 79 | (content) => 80 | array.push(content), 81 | [] 82 | ), 83 | unshift: React.useCallback( 84 | (content) => 85 | array.unshift(content), 86 | [] 87 | ), 88 | slice: React.useCallback( 89 | (start, end) => 90 | array.slice(start, end), 91 | [] 92 | ) 93 | } 94 | } 95 | 96 | export const useText = (name: string): Y.Text => 97 | useSharedType(name, Y.Text) 98 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@joebobmiles/y-react", 3 | "version": "1.2.0", 4 | "description": "React hooks for working with Yjs.", 5 | "main": "./dist/y-react.cjs", 6 | "types": "./dist/index.d.ts", 7 | "module": "./dist/y-react.mjs", 8 | "scripts": { 9 | "test": "jest", 10 | "test:debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand", 11 | "build": "rollup --config rollup.config.ts --configPlugin typescript", 12 | "prepare": "husky install" 13 | }, 14 | "keywords": [ 15 | "react", 16 | "hooks", 17 | "yjs", 18 | "state-management", 19 | "peer-to-peer", 20 | "p2p", 21 | "distributed", 22 | "local-first", 23 | "offline-first", 24 | "crdt", 25 | "decentralized", 26 | "shared-editing", 27 | "realtime" 28 | ], 29 | "author": "Joseph R Miles (https://jrm.dev)", 30 | "license": "MIT", 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/joebobmiles/y-react" 34 | }, 35 | "bugs": { 36 | "url": "https://github.com/joebobmiles/y-react/issues" 37 | }, 38 | "devDependencies": { 39 | "@commitlint/cli": "^13.1.0", 40 | "@commitlint/config-conventional": "^13.1.0", 41 | "@rollup/plugin-alias": "^3.1.5", 42 | "@rollup/plugin-commonjs": "^20.0.0", 43 | "@rollup/plugin-node-resolve": "^13.0.4", 44 | "@rollup/plugin-typescript": "^8.2.5", 45 | "@semantic-release/git": "^9.0.0", 46 | "@testing-library/react": "^12.0.0", 47 | "@testing-library/react-hooks": "^7.0.1", 48 | "@types/jest": "^27.0.1", 49 | "@types/react": "^17.0.19", 50 | "babel-jest": "^27.0.6", 51 | "husky": "^7.0.1", 52 | "jest": "^27.0.6", 53 | "rollup": "^2.56.2", 54 | "semantic-release": "^17.4.6", 55 | "standard": "^16.0.3", 56 | "ts-jest": "^27.0.5", 57 | "ts-node": "^10.2.1", 58 | "ts-standard": "^10.0.0", 59 | "typescript": "^4.3.5" 60 | }, 61 | "dependencies": { 62 | "react": "^17.0.2", 63 | "y-indexeddb": "^9.0.6", 64 | "y-protocols": "^1.0.5", 65 | "y-webrtc": "^10.2.0", 66 | "y-websocket": "^1.3.16", 67 | "yjs": "^13.5.12" 68 | }, 69 | "exports": { 70 | "require": "./dist/y-react.cjs", 71 | "import": "./dist/y-react.mjs" 72 | }, 73 | "files": [ 74 | "dist" 75 | ], 76 | "publishConfig": { 77 | "registry": "https://registry.npmjs.org", 78 | "access": "public" 79 | }, 80 | "commitlint": { 81 | "extends": [ 82 | "@commitlint/config-conventional" 83 | ] 84 | }, 85 | "release": { 86 | "branches": [ 87 | "master", 88 | { 89 | "name": "staging", 90 | "channel": "latest", 91 | "prerelease": "rc" 92 | } 93 | ], 94 | "plugins": [ 95 | "@semantic-release/commit-analyzer", 96 | "@semantic-release/release-notes-generator", 97 | "@semantic-release/npm", 98 | "@semantic-release/github", 99 | [ 100 | "@semantic-release/git", 101 | { 102 | "assets": [ 103 | "package.json" 104 | ], 105 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 106 | } 107 | ] 108 | ] 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/feature/shared-type/index.spec.tsx: -------------------------------------------------------------------------------- 1 | import { act, renderHook } from '@testing-library/react-hooks' 2 | 3 | import React from 'react' 4 | import * as Y from 'yjs' 5 | 6 | import { DocumentProvider } from '../doc' 7 | import { useMap, useArray, useText } from '.' 8 | 9 | describe('useMap', () => { 10 | it('Returns an object representing the current Y.Map state.', () => { 11 | const { result } = renderHook( 12 | () => useMap('test'), 13 | { 14 | wrapper: ({ children }) => ( 15 | 16 | {children} 17 | 18 | ) 19 | } 20 | ) 21 | 22 | expect(result.current.state).toEqual({}) 23 | }) 24 | 25 | it('Returns functions that can get and set Y.Map state.', () => { 26 | const { result } = renderHook( 27 | () => useMap('test'), 28 | { 29 | wrapper: ({ children }) => ( 30 | 31 | {children} 32 | 33 | ) 34 | } 35 | ) 36 | 37 | expect(result.current.get('foo')).toBeUndefined() 38 | 39 | act(() => { 40 | result.current.set('foo', 0) 41 | }) 42 | 43 | expect(result.current.get('foo')).toBe(0) 44 | }) 45 | 46 | it('Outputs correct state after using set.', () => { 47 | const { result } = renderHook( 48 | () => useMap('test'), 49 | { 50 | wrapper: ({ children }) => ( 51 | 52 | {children} 53 | 54 | ) 55 | } 56 | ) 57 | 58 | act(() => { 59 | result.current.set('foo', 1) 60 | }) 61 | 62 | expect(result.current.state).toEqual({ foo: 1 }) 63 | }) 64 | 65 | it('Synchronizes state between peers.', () => { 66 | const doc1 = new Y.Doc() 67 | const doc2 = new Y.Doc() 68 | 69 | doc1.on('update', (update: any) => { 70 | Y.applyUpdate(doc2, update) 71 | }) 72 | doc2.on('update', (update: any) => { 73 | Y.applyUpdate(doc1, update) 74 | }) 75 | 76 | const { result: resultA } = renderHook( 77 | () => useMap('test'), 78 | { 79 | wrapper: ({ children }) => ( 80 | 81 | {children} 82 | 83 | ) 84 | } 85 | ) 86 | 87 | const { result: resultB } = renderHook( 88 | () => useMap('test'), 89 | { 90 | wrapper: ({ children }) => ( 91 | 92 | {children} 93 | 94 | ) 95 | } 96 | ) 97 | 98 | act(() => { 99 | resultA.current.set('foo', 1) 100 | }) 101 | 102 | expect(resultA.current.state).toEqual({ foo: 1 }) 103 | expect(resultB.current.state).toEqual({ foo: 1 }) 104 | }) 105 | }) 106 | 107 | describe('useArray', () => { 108 | it('Returns an object representing the current Y.Array state.', () => { 109 | const { result } = renderHook( 110 | () => useArray('test'), 111 | { 112 | wrapper: ({ children }) => ( 113 | 114 | {children} 115 | 116 | ) 117 | } 118 | ) 119 | 120 | expect(result.current.state).toEqual([]) 121 | }) 122 | 123 | it('Returns functions that can get and insert into Y.Array state.', () => { 124 | const { result } = renderHook( 125 | () => useArray('test'), 126 | { 127 | wrapper: ({ children }) => ( 128 | 129 | {children} 130 | 131 | ) 132 | } 133 | ) 134 | 135 | expect(result.current.get(0)).toBeUndefined() 136 | 137 | act(() => { 138 | result.current.insert(0, [0]) 139 | }) 140 | 141 | expect(result.current.get(0)).toBe(0) 142 | }) 143 | 144 | it('Outputs correct state after using insert.', () => { 145 | const { result } = renderHook( 146 | () => useArray('test'), 147 | { 148 | wrapper: ({ children }) => ( 149 | 150 | {children} 151 | 152 | ) 153 | } 154 | ) 155 | 156 | act(() => { 157 | result.current.insert(0, [1]) 158 | }) 159 | 160 | expect(result.current.state).toEqual([1]) 161 | }) 162 | 163 | it('Deletes items from array when calling delete.', () => { 164 | const { result } = renderHook( 165 | () => useArray('test'), 166 | { 167 | wrapper: ({ children }) => ( 168 | 169 | {children} 170 | 171 | ) 172 | } 173 | ) 174 | 175 | act(() => { 176 | result.current.insert(0, [1, 2, 3]) 177 | }) 178 | 179 | expect(result.current.state).toEqual([1, 2, 3]) 180 | 181 | act(() => { 182 | result.current.delete(1, 2) 183 | }) 184 | 185 | expect(result.current.state).toEqual([1]) 186 | }) 187 | 188 | it('Synchronizes state between peers.', () => { 189 | const doc1 = new Y.Doc() 190 | const doc2 = new Y.Doc() 191 | 192 | doc1.on('update', (update: any) => { 193 | Y.applyUpdate(doc2, update) 194 | }) 195 | doc2.on('update', (update: any) => { 196 | Y.applyUpdate(doc1, update) 197 | }) 198 | 199 | const { result: resultA } = renderHook( 200 | () => useArray('test'), 201 | { 202 | wrapper: ({ children }) => ( 203 | 204 | {children} 205 | 206 | ) 207 | } 208 | ) 209 | 210 | const { result: resultB } = renderHook( 211 | () => useArray('test'), 212 | { 213 | wrapper: ({ children }) => ( 214 | 215 | {children} 216 | 217 | ) 218 | } 219 | ) 220 | 221 | act(() => { 222 | resultA.current.insert(0, [1]) 223 | }) 224 | 225 | expect(resultA.current.state).toEqual([1]) 226 | expect(resultB.current.state).toEqual([1]) 227 | }) 228 | 229 | it('Pushes content to the end of the array with push.', () => { 230 | const { result } = renderHook( 231 | () => useArray('test'), 232 | { 233 | wrapper: ({ children }) => ( 234 | 235 | {children} 236 | 237 | ) 238 | } 239 | ) 240 | 241 | act(() => { 242 | result.current.insert(0, [1]) 243 | }) 244 | 245 | expect(result.current.state).toEqual([1]) 246 | 247 | act(() => { 248 | result.current.push([2, 3]) 249 | }) 250 | 251 | expect(result.current.state).toEqual([1, 2, 3]) 252 | }) 253 | 254 | it('Pushes content to the beginning of the array with unshift.', () => { 255 | const { result } = renderHook( 256 | () => useArray('test'), 257 | { 258 | wrapper: ({ children }) => ( 259 | 260 | {children} 261 | 262 | ) 263 | } 264 | ) 265 | 266 | act(() => { 267 | result.current.insert(0, [1]) 268 | }) 269 | 270 | expect(result.current.state).toEqual([1]) 271 | 272 | act(() => { 273 | result.current.unshift([2, 3]) 274 | }) 275 | 276 | expect(result.current.state).toEqual([2, 3, 1]) 277 | }) 278 | 279 | it('Produces a slice from array with slice.', () => { 280 | const { result } = renderHook( 281 | () => useArray('test'), 282 | { 283 | wrapper: ({ children }) => ( 284 | 285 | {children} 286 | 287 | ) 288 | } 289 | ) 290 | 291 | act(() => { 292 | result.current.insert(0, [1, 2, 3]) 293 | }) 294 | 295 | expect(result.current.state).toEqual([1, 2, 3]) 296 | 297 | expect(result.current.slice(1)).toEqual([2, 3]) 298 | expect(result.current.slice(0, 2)).toEqual([1, 2]) 299 | expect(result.current.slice(0, 3)).toEqual([1, 2, 3]) 300 | expect(result.current.slice(0)).toEqual([1, 2, 3]) 301 | }) 302 | }) 303 | 304 | describe('useText', () => { 305 | it('Returns a Y.Text.', () => { 306 | const { result } = renderHook( 307 | () => useText('test'), 308 | { 309 | wrapper: ({ children }) => ( 310 | 311 | {children} 312 | 313 | ) 314 | } 315 | ) 316 | 317 | expect(result.current).toBeInstanceOf(Y.Text) 318 | }) 319 | }) 320 | -------------------------------------------------------------------------------- /example/hello/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "1.0.0", 9 | "license": "ISC", 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "yjs": "^13.5.12" 14 | }, 15 | "devDependencies": { 16 | "vite": "^2.5.0" 17 | } 18 | }, 19 | "node_modules/colorette": { 20 | "version": "1.3.0", 21 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", 22 | "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", 23 | "dev": true 24 | }, 25 | "node_modules/esbuild": { 26 | "version": "0.12.22", 27 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.22.tgz", 28 | "integrity": "sha512-yWCr9RoFehpqoe/+MwZXJpYOEIt7KOEvNnjIeMZpMSyQt+KCBASM3y7yViiN5dJRphf1wGdUz1+M4rTtWd/ulA==", 29 | "dev": true, 30 | "hasInstallScript": true, 31 | "bin": { 32 | "esbuild": "bin/esbuild" 33 | } 34 | }, 35 | "node_modules/fsevents": { 36 | "version": "2.3.2", 37 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 38 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 39 | "dev": true, 40 | "hasInstallScript": true, 41 | "optional": true, 42 | "os": [ 43 | "darwin" 44 | ], 45 | "engines": { 46 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 47 | } 48 | }, 49 | "node_modules/function-bind": { 50 | "version": "1.1.1", 51 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 52 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 53 | "dev": true 54 | }, 55 | "node_modules/has": { 56 | "version": "1.0.3", 57 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 58 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 59 | "dev": true, 60 | "dependencies": { 61 | "function-bind": "^1.1.1" 62 | }, 63 | "engines": { 64 | "node": ">= 0.4.0" 65 | } 66 | }, 67 | "node_modules/is-core-module": { 68 | "version": "2.6.0", 69 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", 70 | "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", 71 | "dev": true, 72 | "dependencies": { 73 | "has": "^1.0.3" 74 | }, 75 | "funding": { 76 | "url": "https://github.com/sponsors/ljharb" 77 | } 78 | }, 79 | "node_modules/isomorphic.js": { 80 | "version": "0.2.4", 81 | "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.4.tgz", 82 | "integrity": "sha512-Y4NjZceAwaPXctwsHgNsmfuPxR8lJ3f8X7QTAkhltrX4oGIv+eTlgHLXn4tWysC9zGTi929gapnPp+8F8cg7nA==", 83 | "funding": { 84 | "type": "GitHub Sponsors ❤", 85 | "url": "https://github.com/sponsors/dmonad" 86 | } 87 | }, 88 | "node_modules/js-tokens": { 89 | "version": "4.0.0", 90 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 91 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 92 | }, 93 | "node_modules/lib0": { 94 | "version": "0.2.42", 95 | "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.42.tgz", 96 | "integrity": "sha512-8BNM4MiokEKzMvSxTOC3gnCBisJH+jL67CnSnqzHv3jli3pUvGC8wz+0DQ2YvGr4wVQdb2R2uNNPw9LEpVvJ4Q==", 97 | "dependencies": { 98 | "isomorphic.js": "^0.2.4" 99 | }, 100 | "engines": { 101 | "node": ">=12" 102 | }, 103 | "funding": { 104 | "type": "GitHub Sponsors ❤", 105 | "url": "https://github.com/sponsors/dmonad" 106 | } 107 | }, 108 | "node_modules/loose-envify": { 109 | "version": "1.4.0", 110 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 111 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 112 | "dependencies": { 113 | "js-tokens": "^3.0.0 || ^4.0.0" 114 | }, 115 | "bin": { 116 | "loose-envify": "cli.js" 117 | } 118 | }, 119 | "node_modules/nanoid": { 120 | "version": "3.1.25", 121 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", 122 | "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", 123 | "dev": true, 124 | "bin": { 125 | "nanoid": "bin/nanoid.cjs" 126 | }, 127 | "engines": { 128 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 129 | } 130 | }, 131 | "node_modules/object-assign": { 132 | "version": "4.1.1", 133 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 134 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 135 | "engines": { 136 | "node": ">=0.10.0" 137 | } 138 | }, 139 | "node_modules/path-parse": { 140 | "version": "1.0.7", 141 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 142 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 143 | "dev": true 144 | }, 145 | "node_modules/postcss": { 146 | "version": "8.3.6", 147 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", 148 | "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", 149 | "dev": true, 150 | "dependencies": { 151 | "colorette": "^1.2.2", 152 | "nanoid": "^3.1.23", 153 | "source-map-js": "^0.6.2" 154 | }, 155 | "engines": { 156 | "node": "^10 || ^12 || >=14" 157 | }, 158 | "funding": { 159 | "type": "opencollective", 160 | "url": "https://opencollective.com/postcss/" 161 | } 162 | }, 163 | "node_modules/react": { 164 | "version": "17.0.2", 165 | "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", 166 | "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", 167 | "dependencies": { 168 | "loose-envify": "^1.1.0", 169 | "object-assign": "^4.1.1" 170 | }, 171 | "engines": { 172 | "node": ">=0.10.0" 173 | } 174 | }, 175 | "node_modules/react-dom": { 176 | "version": "17.0.2", 177 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", 178 | "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", 179 | "dependencies": { 180 | "loose-envify": "^1.1.0", 181 | "object-assign": "^4.1.1", 182 | "scheduler": "^0.20.2" 183 | }, 184 | "peerDependencies": { 185 | "react": "17.0.2" 186 | } 187 | }, 188 | "node_modules/resolve": { 189 | "version": "1.20.0", 190 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", 191 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", 192 | "dev": true, 193 | "dependencies": { 194 | "is-core-module": "^2.2.0", 195 | "path-parse": "^1.0.6" 196 | }, 197 | "funding": { 198 | "url": "https://github.com/sponsors/ljharb" 199 | } 200 | }, 201 | "node_modules/rollup": { 202 | "version": "2.56.3", 203 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.56.3.tgz", 204 | "integrity": "sha512-Au92NuznFklgQCUcV96iXlxUbHuB1vQMaH76DHl5M11TotjOHwqk9CwcrT78+Tnv4FN9uTBxq6p4EJoYkpyekg==", 205 | "dev": true, 206 | "dependencies": { 207 | "fsevents": "~2.3.2" 208 | }, 209 | "bin": { 210 | "rollup": "dist/bin/rollup" 211 | }, 212 | "engines": { 213 | "node": ">=10.0.0" 214 | }, 215 | "optionalDependencies": { 216 | "fsevents": "~2.3.2" 217 | } 218 | }, 219 | "node_modules/scheduler": { 220 | "version": "0.20.2", 221 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", 222 | "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", 223 | "dependencies": { 224 | "loose-envify": "^1.1.0", 225 | "object-assign": "^4.1.1" 226 | } 227 | }, 228 | "node_modules/source-map-js": { 229 | "version": "0.6.2", 230 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", 231 | "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", 232 | "dev": true, 233 | "engines": { 234 | "node": ">=0.10.0" 235 | } 236 | }, 237 | "node_modules/vite": { 238 | "version": "2.5.0", 239 | "resolved": "https://registry.npmjs.org/vite/-/vite-2.5.0.tgz", 240 | "integrity": "sha512-Dn4B+g54PJsMG5WCc4QeFy1ygMXRdTtFrUPegqfk4+vzVQcbF/DqqmI/1bxezArzbujBJg/67QeT5wz8edfJVQ==", 241 | "dev": true, 242 | "dependencies": { 243 | "esbuild": "^0.12.17", 244 | "fsevents": "~2.3.2", 245 | "postcss": "^8.3.6", 246 | "resolve": "^1.20.0", 247 | "rollup": "^2.38.5" 248 | }, 249 | "bin": { 250 | "vite": "bin/vite.js" 251 | }, 252 | "engines": { 253 | "node": ">=12.2.0" 254 | }, 255 | "optionalDependencies": { 256 | "fsevents": "~2.3.2" 257 | } 258 | }, 259 | "node_modules/yjs": { 260 | "version": "13.5.12", 261 | "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.5.12.tgz", 262 | "integrity": "sha512-/buy1kh8Ls+t733Lgov9hiNxCsjHSCymTuZNahj2hsPNoGbvnSdDmCz9Z4F19Yr1eUAAXQLJF3q7fiBcvPC6Qg==", 263 | "hasInstallScript": true, 264 | "dependencies": { 265 | "lib0": "^0.2.41" 266 | }, 267 | "funding": { 268 | "type": "GitHub Sponsors ❤", 269 | "url": "https://github.com/sponsors/dmonad" 270 | } 271 | } 272 | }, 273 | "dependencies": { 274 | "colorette": { 275 | "version": "1.3.0", 276 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", 277 | "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", 278 | "dev": true 279 | }, 280 | "esbuild": { 281 | "version": "0.12.22", 282 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.22.tgz", 283 | "integrity": "sha512-yWCr9RoFehpqoe/+MwZXJpYOEIt7KOEvNnjIeMZpMSyQt+KCBASM3y7yViiN5dJRphf1wGdUz1+M4rTtWd/ulA==", 284 | "dev": true 285 | }, 286 | "fsevents": { 287 | "version": "2.3.2", 288 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 289 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 290 | "dev": true, 291 | "optional": true 292 | }, 293 | "function-bind": { 294 | "version": "1.1.1", 295 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 296 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 297 | "dev": true 298 | }, 299 | "has": { 300 | "version": "1.0.3", 301 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 302 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 303 | "dev": true, 304 | "requires": { 305 | "function-bind": "^1.1.1" 306 | } 307 | }, 308 | "is-core-module": { 309 | "version": "2.6.0", 310 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", 311 | "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", 312 | "dev": true, 313 | "requires": { 314 | "has": "^1.0.3" 315 | } 316 | }, 317 | "isomorphic.js": { 318 | "version": "0.2.4", 319 | "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.4.tgz", 320 | "integrity": "sha512-Y4NjZceAwaPXctwsHgNsmfuPxR8lJ3f8X7QTAkhltrX4oGIv+eTlgHLXn4tWysC9zGTi929gapnPp+8F8cg7nA==" 321 | }, 322 | "js-tokens": { 323 | "version": "4.0.0", 324 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 325 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 326 | }, 327 | "lib0": { 328 | "version": "0.2.42", 329 | "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.42.tgz", 330 | "integrity": "sha512-8BNM4MiokEKzMvSxTOC3gnCBisJH+jL67CnSnqzHv3jli3pUvGC8wz+0DQ2YvGr4wVQdb2R2uNNPw9LEpVvJ4Q==", 331 | "requires": { 332 | "isomorphic.js": "^0.2.4" 333 | } 334 | }, 335 | "loose-envify": { 336 | "version": "1.4.0", 337 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 338 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 339 | "requires": { 340 | "js-tokens": "^3.0.0 || ^4.0.0" 341 | } 342 | }, 343 | "nanoid": { 344 | "version": "3.1.25", 345 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", 346 | "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", 347 | "dev": true 348 | }, 349 | "object-assign": { 350 | "version": "4.1.1", 351 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 352 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 353 | }, 354 | "path-parse": { 355 | "version": "1.0.7", 356 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 357 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 358 | "dev": true 359 | }, 360 | "postcss": { 361 | "version": "8.3.6", 362 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", 363 | "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", 364 | "dev": true, 365 | "requires": { 366 | "colorette": "^1.2.2", 367 | "nanoid": "^3.1.23", 368 | "source-map-js": "^0.6.2" 369 | } 370 | }, 371 | "react": { 372 | "version": "17.0.2", 373 | "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", 374 | "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", 375 | "requires": { 376 | "loose-envify": "^1.1.0", 377 | "object-assign": "^4.1.1" 378 | } 379 | }, 380 | "react-dom": { 381 | "version": "17.0.2", 382 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", 383 | "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", 384 | "requires": { 385 | "loose-envify": "^1.1.0", 386 | "object-assign": "^4.1.1", 387 | "scheduler": "^0.20.2" 388 | } 389 | }, 390 | "resolve": { 391 | "version": "1.20.0", 392 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", 393 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", 394 | "dev": true, 395 | "requires": { 396 | "is-core-module": "^2.2.0", 397 | "path-parse": "^1.0.6" 398 | } 399 | }, 400 | "rollup": { 401 | "version": "2.56.3", 402 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.56.3.tgz", 403 | "integrity": "sha512-Au92NuznFklgQCUcV96iXlxUbHuB1vQMaH76DHl5M11TotjOHwqk9CwcrT78+Tnv4FN9uTBxq6p4EJoYkpyekg==", 404 | "dev": true, 405 | "requires": { 406 | "fsevents": "~2.3.2" 407 | } 408 | }, 409 | "scheduler": { 410 | "version": "0.20.2", 411 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", 412 | "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", 413 | "requires": { 414 | "loose-envify": "^1.1.0", 415 | "object-assign": "^4.1.1" 416 | } 417 | }, 418 | "source-map-js": { 419 | "version": "0.6.2", 420 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", 421 | "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", 422 | "dev": true 423 | }, 424 | "vite": { 425 | "version": "2.5.0", 426 | "resolved": "https://registry.npmjs.org/vite/-/vite-2.5.0.tgz", 427 | "integrity": "sha512-Dn4B+g54PJsMG5WCc4QeFy1ygMXRdTtFrUPegqfk4+vzVQcbF/DqqmI/1bxezArzbujBJg/67QeT5wz8edfJVQ==", 428 | "dev": true, 429 | "requires": { 430 | "esbuild": "^0.12.17", 431 | "fsevents": "~2.3.2", 432 | "postcss": "^8.3.6", 433 | "resolve": "^1.20.0", 434 | "rollup": "^2.38.5" 435 | } 436 | }, 437 | "yjs": { 438 | "version": "13.5.12", 439 | "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.5.12.tgz", 440 | "integrity": "sha512-/buy1kh8Ls+t733Lgov9hiNxCsjHSCymTuZNahj2hsPNoGbvnSdDmCz9Z4F19Yr1eUAAXQLJF3q7fiBcvPC6Qg==", 441 | "requires": { 442 | "lib0": "^0.2.41" 443 | } 444 | } 445 | } 446 | } 447 | -------------------------------------------------------------------------------- /example/awareness/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awareness", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "1.0.0", 9 | "license": "ISC", 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "y-webrtc": "^10.2.0", 14 | "yjs": "^13.5.12" 15 | } 16 | }, 17 | "node_modules/base64-js": { 18 | "version": "1.5.1", 19 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 20 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 21 | "funding": [ 22 | { 23 | "type": "github", 24 | "url": "https://github.com/sponsors/feross" 25 | }, 26 | { 27 | "type": "patreon", 28 | "url": "https://www.patreon.com/feross" 29 | }, 30 | { 31 | "type": "consulting", 32 | "url": "https://feross.org/support" 33 | } 34 | ] 35 | }, 36 | "node_modules/buffer": { 37 | "version": "6.0.3", 38 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 39 | "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 40 | "funding": [ 41 | { 42 | "type": "github", 43 | "url": "https://github.com/sponsors/feross" 44 | }, 45 | { 46 | "type": "patreon", 47 | "url": "https://www.patreon.com/feross" 48 | }, 49 | { 50 | "type": "consulting", 51 | "url": "https://feross.org/support" 52 | } 53 | ], 54 | "dependencies": { 55 | "base64-js": "^1.3.1", 56 | "ieee754": "^1.2.1" 57 | } 58 | }, 59 | "node_modules/debug": { 60 | "version": "4.3.2", 61 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", 62 | "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", 63 | "dependencies": { 64 | "ms": "2.1.2" 65 | }, 66 | "engines": { 67 | "node": ">=6.0" 68 | }, 69 | "peerDependenciesMeta": { 70 | "supports-color": { 71 | "optional": true 72 | } 73 | } 74 | }, 75 | "node_modules/err-code": { 76 | "version": "3.0.1", 77 | "resolved": "https://registry.npmjs.org/err-code/-/err-code-3.0.1.tgz", 78 | "integrity": "sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA==" 79 | }, 80 | "node_modules/get-browser-rtc": { 81 | "version": "1.1.0", 82 | "resolved": "https://registry.npmjs.org/get-browser-rtc/-/get-browser-rtc-1.1.0.tgz", 83 | "integrity": "sha512-MghbMJ61EJrRsDe7w1Bvqt3ZsBuqhce5nrn/XAwgwOXhcsz53/ltdxOse1h/8eKXj5slzxdsz56g5rzOFSGwfQ==" 84 | }, 85 | "node_modules/ieee754": { 86 | "version": "1.2.1", 87 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 88 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 89 | "funding": [ 90 | { 91 | "type": "github", 92 | "url": "https://github.com/sponsors/feross" 93 | }, 94 | { 95 | "type": "patreon", 96 | "url": "https://www.patreon.com/feross" 97 | }, 98 | { 99 | "type": "consulting", 100 | "url": "https://feross.org/support" 101 | } 102 | ] 103 | }, 104 | "node_modules/inherits": { 105 | "version": "2.0.4", 106 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 107 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 108 | }, 109 | "node_modules/isomorphic.js": { 110 | "version": "0.2.4", 111 | "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.4.tgz", 112 | "integrity": "sha512-Y4NjZceAwaPXctwsHgNsmfuPxR8lJ3f8X7QTAkhltrX4oGIv+eTlgHLXn4tWysC9zGTi929gapnPp+8F8cg7nA==", 113 | "funding": { 114 | "type": "GitHub Sponsors ❤", 115 | "url": "https://github.com/sponsors/dmonad" 116 | } 117 | }, 118 | "node_modules/js-tokens": { 119 | "version": "4.0.0", 120 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 121 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 122 | }, 123 | "node_modules/lib0": { 124 | "version": "0.2.42", 125 | "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.42.tgz", 126 | "integrity": "sha512-8BNM4MiokEKzMvSxTOC3gnCBisJH+jL67CnSnqzHv3jli3pUvGC8wz+0DQ2YvGr4wVQdb2R2uNNPw9LEpVvJ4Q==", 127 | "dependencies": { 128 | "isomorphic.js": "^0.2.4" 129 | }, 130 | "engines": { 131 | "node": ">=12" 132 | }, 133 | "funding": { 134 | "type": "GitHub Sponsors ❤", 135 | "url": "https://github.com/sponsors/dmonad" 136 | } 137 | }, 138 | "node_modules/loose-envify": { 139 | "version": "1.4.0", 140 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 141 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 142 | "dependencies": { 143 | "js-tokens": "^3.0.0 || ^4.0.0" 144 | }, 145 | "bin": { 146 | "loose-envify": "cli.js" 147 | } 148 | }, 149 | "node_modules/ms": { 150 | "version": "2.1.2", 151 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 152 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 153 | }, 154 | "node_modules/object-assign": { 155 | "version": "4.1.1", 156 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 157 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 158 | "engines": { 159 | "node": ">=0.10.0" 160 | } 161 | }, 162 | "node_modules/queue-microtask": { 163 | "version": "1.2.3", 164 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 165 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 166 | "funding": [ 167 | { 168 | "type": "github", 169 | "url": "https://github.com/sponsors/feross" 170 | }, 171 | { 172 | "type": "patreon", 173 | "url": "https://www.patreon.com/feross" 174 | }, 175 | { 176 | "type": "consulting", 177 | "url": "https://feross.org/support" 178 | } 179 | ] 180 | }, 181 | "node_modules/randombytes": { 182 | "version": "2.1.0", 183 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 184 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 185 | "dependencies": { 186 | "safe-buffer": "^5.1.0" 187 | } 188 | }, 189 | "node_modules/react": { 190 | "version": "17.0.2", 191 | "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", 192 | "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", 193 | "dependencies": { 194 | "loose-envify": "^1.1.0", 195 | "object-assign": "^4.1.1" 196 | }, 197 | "engines": { 198 | "node": ">=0.10.0" 199 | } 200 | }, 201 | "node_modules/react-dom": { 202 | "version": "17.0.2", 203 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", 204 | "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", 205 | "dependencies": { 206 | "loose-envify": "^1.1.0", 207 | "object-assign": "^4.1.1", 208 | "scheduler": "^0.20.2" 209 | }, 210 | "peerDependencies": { 211 | "react": "17.0.2" 212 | } 213 | }, 214 | "node_modules/readable-stream": { 215 | "version": "3.6.0", 216 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 217 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 218 | "dependencies": { 219 | "inherits": "^2.0.3", 220 | "string_decoder": "^1.1.1", 221 | "util-deprecate": "^1.0.1" 222 | }, 223 | "engines": { 224 | "node": ">= 6" 225 | } 226 | }, 227 | "node_modules/safe-buffer": { 228 | "version": "5.2.1", 229 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 230 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 231 | "funding": [ 232 | { 233 | "type": "github", 234 | "url": "https://github.com/sponsors/feross" 235 | }, 236 | { 237 | "type": "patreon", 238 | "url": "https://www.patreon.com/feross" 239 | }, 240 | { 241 | "type": "consulting", 242 | "url": "https://feross.org/support" 243 | } 244 | ] 245 | }, 246 | "node_modules/scheduler": { 247 | "version": "0.20.2", 248 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", 249 | "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", 250 | "dependencies": { 251 | "loose-envify": "^1.1.0", 252 | "object-assign": "^4.1.1" 253 | } 254 | }, 255 | "node_modules/simple-peer": { 256 | "version": "9.11.0", 257 | "resolved": "https://registry.npmjs.org/simple-peer/-/simple-peer-9.11.0.tgz", 258 | "integrity": "sha512-qvdNu/dGMHBm2uQ7oLhQBMhYlrOZC1ywXNCH/i8I4etxR1vrjCnU6ZSQBptndB1gcakjo2+w4OHo7Sjza1SHxg==", 259 | "funding": [ 260 | { 261 | "type": "github", 262 | "url": "https://github.com/sponsors/feross" 263 | }, 264 | { 265 | "type": "patreon", 266 | "url": "https://www.patreon.com/feross" 267 | }, 268 | { 269 | "type": "consulting", 270 | "url": "https://feross.org/support" 271 | } 272 | ], 273 | "dependencies": { 274 | "buffer": "^6.0.3", 275 | "debug": "^4.3.1", 276 | "err-code": "^3.0.1", 277 | "get-browser-rtc": "^1.1.0", 278 | "queue-microtask": "^1.2.3", 279 | "randombytes": "^2.1.0", 280 | "readable-stream": "^3.6.0" 281 | } 282 | }, 283 | "node_modules/string_decoder": { 284 | "version": "1.3.0", 285 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 286 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 287 | "dependencies": { 288 | "safe-buffer": "~5.2.0" 289 | } 290 | }, 291 | "node_modules/util-deprecate": { 292 | "version": "1.0.2", 293 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 294 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 295 | }, 296 | "node_modules/ws": { 297 | "version": "7.5.3", 298 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", 299 | "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", 300 | "optional": true, 301 | "engines": { 302 | "node": ">=8.3.0" 303 | }, 304 | "peerDependencies": { 305 | "bufferutil": "^4.0.1", 306 | "utf-8-validate": "^5.0.2" 307 | }, 308 | "peerDependenciesMeta": { 309 | "bufferutil": { 310 | "optional": true 311 | }, 312 | "utf-8-validate": { 313 | "optional": true 314 | } 315 | } 316 | }, 317 | "node_modules/y-protocols": { 318 | "version": "1.0.5", 319 | "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.5.tgz", 320 | "integrity": "sha512-Wil92b7cGk712lRHDqS4T90IczF6RkcvCwAD0A2OPg+adKmOe+nOiT/N2hvpQIWS3zfjmtL4CPaH5sIW1Hkm/A==", 321 | "dependencies": { 322 | "lib0": "^0.2.42" 323 | }, 324 | "funding": { 325 | "type": "GitHub Sponsors ❤", 326 | "url": "https://github.com/sponsors/dmonad" 327 | } 328 | }, 329 | "node_modules/y-webrtc": { 330 | "version": "10.2.0", 331 | "resolved": "https://registry.npmjs.org/y-webrtc/-/y-webrtc-10.2.0.tgz", 332 | "integrity": "sha512-YhHXJx46zc8En8Jpy5FUpAksAAndTV0dXbEKxJw/Zb8+Y6xVGaeB4+x44k0xo5i+SgZ00OjlXAQXgkJQesLc4Q==", 333 | "dependencies": { 334 | "lib0": "^0.2.42", 335 | "simple-peer": "^9.11.0", 336 | "ws": "^7.2.0", 337 | "y-protocols": "^1.0.5" 338 | }, 339 | "bin": { 340 | "y-webrtc-signaling": "bin/server.js" 341 | }, 342 | "engines": { 343 | "node": ">=12" 344 | }, 345 | "funding": { 346 | "type": "GitHub Sponsors ❤", 347 | "url": "https://github.com/sponsors/dmonad" 348 | }, 349 | "optionalDependencies": { 350 | "ws": "^7.2.0" 351 | } 352 | }, 353 | "node_modules/yjs": { 354 | "version": "13.5.12", 355 | "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.5.12.tgz", 356 | "integrity": "sha512-/buy1kh8Ls+t733Lgov9hiNxCsjHSCymTuZNahj2hsPNoGbvnSdDmCz9Z4F19Yr1eUAAXQLJF3q7fiBcvPC6Qg==", 357 | "hasInstallScript": true, 358 | "dependencies": { 359 | "lib0": "^0.2.41" 360 | }, 361 | "funding": { 362 | "type": "GitHub Sponsors ❤", 363 | "url": "https://github.com/sponsors/dmonad" 364 | } 365 | } 366 | }, 367 | "dependencies": { 368 | "base64-js": { 369 | "version": "1.5.1", 370 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 371 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 372 | }, 373 | "buffer": { 374 | "version": "6.0.3", 375 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 376 | "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 377 | "requires": { 378 | "base64-js": "^1.3.1", 379 | "ieee754": "^1.2.1" 380 | } 381 | }, 382 | "debug": { 383 | "version": "4.3.2", 384 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", 385 | "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", 386 | "requires": { 387 | "ms": "2.1.2" 388 | } 389 | }, 390 | "err-code": { 391 | "version": "3.0.1", 392 | "resolved": "https://registry.npmjs.org/err-code/-/err-code-3.0.1.tgz", 393 | "integrity": "sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA==" 394 | }, 395 | "get-browser-rtc": { 396 | "version": "1.1.0", 397 | "resolved": "https://registry.npmjs.org/get-browser-rtc/-/get-browser-rtc-1.1.0.tgz", 398 | "integrity": "sha512-MghbMJ61EJrRsDe7w1Bvqt3ZsBuqhce5nrn/XAwgwOXhcsz53/ltdxOse1h/8eKXj5slzxdsz56g5rzOFSGwfQ==" 399 | }, 400 | "ieee754": { 401 | "version": "1.2.1", 402 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 403 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 404 | }, 405 | "inherits": { 406 | "version": "2.0.4", 407 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 408 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 409 | }, 410 | "isomorphic.js": { 411 | "version": "0.2.4", 412 | "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.4.tgz", 413 | "integrity": "sha512-Y4NjZceAwaPXctwsHgNsmfuPxR8lJ3f8X7QTAkhltrX4oGIv+eTlgHLXn4tWysC9zGTi929gapnPp+8F8cg7nA==" 414 | }, 415 | "js-tokens": { 416 | "version": "4.0.0", 417 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 418 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 419 | }, 420 | "lib0": { 421 | "version": "0.2.42", 422 | "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.42.tgz", 423 | "integrity": "sha512-8BNM4MiokEKzMvSxTOC3gnCBisJH+jL67CnSnqzHv3jli3pUvGC8wz+0DQ2YvGr4wVQdb2R2uNNPw9LEpVvJ4Q==", 424 | "requires": { 425 | "isomorphic.js": "^0.2.4" 426 | } 427 | }, 428 | "loose-envify": { 429 | "version": "1.4.0", 430 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 431 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 432 | "requires": { 433 | "js-tokens": "^3.0.0 || ^4.0.0" 434 | } 435 | }, 436 | "ms": { 437 | "version": "2.1.2", 438 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 439 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 440 | }, 441 | "object-assign": { 442 | "version": "4.1.1", 443 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 444 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 445 | }, 446 | "queue-microtask": { 447 | "version": "1.2.3", 448 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 449 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" 450 | }, 451 | "randombytes": { 452 | "version": "2.1.0", 453 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 454 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 455 | "requires": { 456 | "safe-buffer": "^5.1.0" 457 | } 458 | }, 459 | "react": { 460 | "version": "17.0.2", 461 | "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", 462 | "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", 463 | "requires": { 464 | "loose-envify": "^1.1.0", 465 | "object-assign": "^4.1.1" 466 | } 467 | }, 468 | "react-dom": { 469 | "version": "17.0.2", 470 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", 471 | "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", 472 | "requires": { 473 | "loose-envify": "^1.1.0", 474 | "object-assign": "^4.1.1", 475 | "scheduler": "^0.20.2" 476 | } 477 | }, 478 | "readable-stream": { 479 | "version": "3.6.0", 480 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 481 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 482 | "requires": { 483 | "inherits": "^2.0.3", 484 | "string_decoder": "^1.1.1", 485 | "util-deprecate": "^1.0.1" 486 | } 487 | }, 488 | "safe-buffer": { 489 | "version": "5.2.1", 490 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 491 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 492 | }, 493 | "scheduler": { 494 | "version": "0.20.2", 495 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", 496 | "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", 497 | "requires": { 498 | "loose-envify": "^1.1.0", 499 | "object-assign": "^4.1.1" 500 | } 501 | }, 502 | "simple-peer": { 503 | "version": "9.11.0", 504 | "resolved": "https://registry.npmjs.org/simple-peer/-/simple-peer-9.11.0.tgz", 505 | "integrity": "sha512-qvdNu/dGMHBm2uQ7oLhQBMhYlrOZC1ywXNCH/i8I4etxR1vrjCnU6ZSQBptndB1gcakjo2+w4OHo7Sjza1SHxg==", 506 | "requires": { 507 | "buffer": "^6.0.3", 508 | "debug": "^4.3.1", 509 | "err-code": "^3.0.1", 510 | "get-browser-rtc": "^1.1.0", 511 | "queue-microtask": "^1.2.3", 512 | "randombytes": "^2.1.0", 513 | "readable-stream": "^3.6.0" 514 | } 515 | }, 516 | "string_decoder": { 517 | "version": "1.3.0", 518 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 519 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 520 | "requires": { 521 | "safe-buffer": "~5.2.0" 522 | } 523 | }, 524 | "util-deprecate": { 525 | "version": "1.0.2", 526 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 527 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 528 | }, 529 | "ws": { 530 | "version": "7.5.3", 531 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", 532 | "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", 533 | "optional": true, 534 | "requires": {} 535 | }, 536 | "y-protocols": { 537 | "version": "1.0.5", 538 | "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.5.tgz", 539 | "integrity": "sha512-Wil92b7cGk712lRHDqS4T90IczF6RkcvCwAD0A2OPg+adKmOe+nOiT/N2hvpQIWS3zfjmtL4CPaH5sIW1Hkm/A==", 540 | "requires": { 541 | "lib0": "^0.2.42" 542 | } 543 | }, 544 | "y-webrtc": { 545 | "version": "10.2.0", 546 | "resolved": "https://registry.npmjs.org/y-webrtc/-/y-webrtc-10.2.0.tgz", 547 | "integrity": "sha512-YhHXJx46zc8En8Jpy5FUpAksAAndTV0dXbEKxJw/Zb8+Y6xVGaeB4+x44k0xo5i+SgZ00OjlXAQXgkJQesLc4Q==", 548 | "requires": { 549 | "lib0": "^0.2.42", 550 | "simple-peer": "^9.11.0", 551 | "ws": "^7.2.0", 552 | "y-protocols": "^1.0.5" 553 | } 554 | }, 555 | "yjs": { 556 | "version": "13.5.12", 557 | "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.5.12.tgz", 558 | "integrity": "sha512-/buy1kh8Ls+t733Lgov9hiNxCsjHSCymTuZNahj2hsPNoGbvnSdDmCz9Z4F19Yr1eUAAXQLJF3q7fiBcvPC6Qg==", 559 | "requires": { 560 | "lib0": "^0.2.41" 561 | } 562 | } 563 | } 564 | } 565 | --------------------------------------------------------------------------------