implemented frontend including separate message system; started to implement backend
This commit is contained in:
21
yjs-poll/node_modules/y-protocols/LICENSE
generated
vendored
Normal file
21
yjs-poll/node_modules/y-protocols/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Kevin Jahns <kevin.jahns@protonmail.com>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
78
yjs-poll/node_modules/y-protocols/README.md
generated
vendored
Normal file
78
yjs-poll/node_modules/y-protocols/README.md
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
# Yjs Protocols
|
||||
> Binary encoding protocols for *syncing*, *awareness*, and *history information*
|
||||
|
||||
This API is unstable and subject to change.
|
||||
|
||||
## API
|
||||
|
||||
### Awareness Protocol
|
||||
|
||||
```js
|
||||
import * as awarenessProtocol from 'y-protocols/awareness.js'
|
||||
```
|
||||
|
||||
The Awareness protocol implements a simple network agnostic algorithm that
|
||||
manages user status (who is online?) and propagate awareness information like
|
||||
cursor location, username, or email address. Each client can update its own
|
||||
local state and listen to state changes of remote clients.
|
||||
|
||||
Each client has an awareness state. Remote awareness are stored in a Map that
|
||||
maps from remote client id to remote awareness state. An *awareness state* is an
|
||||
increasing clock attached to a schemaless json object.
|
||||
|
||||
Whenever the client changes its local state, it increases the clock and
|
||||
propagates its own awareness state to all peers. When a client receives a remote
|
||||
awareness state, and overwrites the clients state if the received state is newer
|
||||
than the local awareness state for that client. If the state is `null`, the
|
||||
client is marked as offline. If a client doesn't receive updates from a remote
|
||||
peer for 30 seconds, it marks the remote client as offline. Hence each client
|
||||
must broadcast its own awareness state in a regular interval to make sure that
|
||||
remote clients don't mark it as offline.
|
||||
|
||||
#### awarenessProtocol.Awareness Class
|
||||
|
||||
```js
|
||||
const ydoc = new Y.Doc()
|
||||
const awareness = new awarenessProtocol.Awareness(ydoc)
|
||||
```
|
||||
|
||||
<dl>
|
||||
<b><code>clientID:number</code></b>
|
||||
<dd>A unique identifier that identifies this client.</dd>
|
||||
<b><code>getLocalState():Object<string,any>|null</code></b>
|
||||
<dd>Get the local awareness state.</dd>
|
||||
<b><code>setLocalState(Object<string,any>|null)</code></b>
|
||||
<dd>
|
||||
Set/Update the local awareness state. Set `null` to mark the local client as
|
||||
offline.
|
||||
</dd>
|
||||
<b><code>setLocalStateField(string, any)</code></b>
|
||||
<dd>
|
||||
Only update a single field on the local awareness object. Does not do
|
||||
anything if the local state is not set.
|
||||
</dd>
|
||||
<b><code>getStates():Map<number,Object<string,any>></code></b>
|
||||
<dd>
|
||||
Get all client awareness states (remote and local). Maps from clientID to
|
||||
awareness state.
|
||||
</dd>
|
||||
<b><code>
|
||||
on('change', ({ added: Array<number>, updated: Array<number>
|
||||
removed: Array<number> }, [transactionOrigin:any]) => ..)
|
||||
</code></b>
|
||||
<dd>
|
||||
Listen to remote and local state changes on the awareness instance.
|
||||
</dd>
|
||||
<b><code>
|
||||
on('update', ({ added: Array<number>, updated: Array<number>
|
||||
removed: Array<number> }, [transactionOrigin:any]) => ..)
|
||||
</code></b>
|
||||
<dd>
|
||||
Listen to remote and local awareness changes on the awareness instance.
|
||||
This event is called even when the awarenes state does not change.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
### License
|
||||
|
||||
[The MIT License](./LICENSE) © Kevin Jahns
|
||||
8
yjs-poll/node_modules/y-protocols/auth.d.ts
generated
vendored
Normal file
8
yjs-poll/node_modules/y-protocols/auth.d.ts
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
export const messagePermissionDenied: 0;
|
||||
export function writePermissionDenied(encoder: encoding.Encoder, reason: string): void;
|
||||
export function readAuthMessage(decoder: decoding.Decoder, y: Y.Doc, permissionDeniedHandler: PermissionDeniedHandler): void;
|
||||
export type PermissionDeniedHandler = (y: any, reason: string) => any;
|
||||
import * as encoding from 'lib0/encoding';
|
||||
import * as decoding from 'lib0/decoding';
|
||||
import * as Y from 'yjs';
|
||||
//# sourceMappingURL=auth.d.ts.map
|
||||
1
yjs-poll/node_modules/y-protocols/auth.d.ts.map
generated
vendored
Normal file
1
yjs-poll/node_modules/y-protocols/auth.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["auth.js"],"names":[],"mappings":"AAKA,wCAAwC;AAMjC,+CAHI,SAAS,OAAO,UAChB,MAAM,QAKhB;AAcM,yCAJI,SAAS,OAAO,KAChB,EAAE,GAAG,2BACL,uBAAuB,QAMjC;0CAdU,GAAG,UACH,MAAM;0BAjBS,eAAe;0BACf,eAAe;mBAFtB,KAAK"}
|
||||
33
yjs-poll/node_modules/y-protocols/auth.js
generated
vendored
Normal file
33
yjs-poll/node_modules/y-protocols/auth.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
import * as Y from 'yjs' // eslint-disable-line
|
||||
import * as encoding from 'lib0/encoding'
|
||||
import * as decoding from 'lib0/decoding'
|
||||
|
||||
export const messagePermissionDenied = 0
|
||||
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {string} reason
|
||||
*/
|
||||
export const writePermissionDenied = (encoder, reason) => {
|
||||
encoding.writeVarUint(encoder, messagePermissionDenied)
|
||||
encoding.writeVarString(encoder, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* @callback PermissionDeniedHandler
|
||||
* @param {any} y
|
||||
* @param {string} reason
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @param {Y.Doc} y
|
||||
* @param {PermissionDeniedHandler} permissionDeniedHandler
|
||||
*/
|
||||
export const readAuthMessage = (decoder, y, permissionDeniedHandler) => {
|
||||
switch (decoding.readVarUint(decoder)) {
|
||||
case messagePermissionDenied: permissionDeniedHandler(y, decoding.readVarString(decoder))
|
||||
}
|
||||
}
|
||||
86
yjs-poll/node_modules/y-protocols/awareness.d.ts
generated
vendored
Normal file
86
yjs-poll/node_modules/y-protocols/awareness.d.ts
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
export const outdatedTimeout: 30000;
|
||||
/**
|
||||
* @typedef {Object} MetaClientState
|
||||
* @property {number} MetaClientState.clock
|
||||
* @property {number} MetaClientState.lastUpdated unix timestamp
|
||||
*/
|
||||
/**
|
||||
* The Awareness class implements a simple shared state protocol that can be used for non-persistent data like awareness information
|
||||
* (cursor, username, status, ..). Each client can update its own local state and listen to state changes of
|
||||
* remote clients. Every client may set a state of a remote peer to `null` to mark the client as offline.
|
||||
*
|
||||
* Each client is identified by a unique client id (something we borrow from `doc.clientID`). A client can override
|
||||
* its own state by propagating a message with an increasing timestamp (`clock`). If such a message is received, it is
|
||||
* applied if the known state of that client is older than the new state (`clock < newClock`). If a client thinks that
|
||||
* a remote client is offline, it may propagate a message with
|
||||
* `{ clock: currentClientClock, state: null, client: remoteClient }`. If such a
|
||||
* message is received, and the known clock of that client equals the received clock, it will override the state with `null`.
|
||||
*
|
||||
* Before a client disconnects, it should propagate a `null` state with an updated clock.
|
||||
*
|
||||
* Awareness states must be updated every 30 seconds. Otherwise the Awareness instance will delete the client state.
|
||||
*
|
||||
* @extends {Observable<string>}
|
||||
*/
|
||||
export class Awareness extends Observable<string> {
|
||||
/**
|
||||
* @param {Y.Doc} doc
|
||||
*/
|
||||
constructor(doc: Y.Doc);
|
||||
doc: Y.Doc;
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
clientID: number;
|
||||
/**
|
||||
* Maps from client id to client state
|
||||
* @type {Map<number, Object<string, any>>}
|
||||
*/
|
||||
states: Map<number, {
|
||||
[x: string]: any;
|
||||
}>;
|
||||
/**
|
||||
* @type {Map<number, MetaClientState>}
|
||||
*/
|
||||
meta: Map<number, MetaClientState>;
|
||||
_checkInterval: any;
|
||||
/**
|
||||
* @return {Object<string,any>|null}
|
||||
*/
|
||||
getLocalState(): {
|
||||
[x: string]: any;
|
||||
} | null;
|
||||
/**
|
||||
* @param {Object<string,any>|null} state
|
||||
*/
|
||||
setLocalState(state: {
|
||||
[x: string]: any;
|
||||
} | null): void;
|
||||
/**
|
||||
* @param {string} field
|
||||
* @param {any} value
|
||||
*/
|
||||
setLocalStateField(field: string, value: any): void;
|
||||
/**
|
||||
* @return {Map<number,Object<string,any>>}
|
||||
*/
|
||||
getStates(): Map<number, {
|
||||
[x: string]: any;
|
||||
}>;
|
||||
}
|
||||
export function removeAwarenessStates(awareness: Awareness, clients: Array<number>, origin: any): void;
|
||||
export function encodeAwarenessUpdate(awareness: Awareness, clients: Array<number>, states?: Map<number, {
|
||||
[x: string]: any;
|
||||
}>): Uint8Array;
|
||||
export function modifyAwarenessUpdate(update: Uint8Array, modify: (arg0: any) => any): Uint8Array;
|
||||
export function applyAwarenessUpdate(awareness: Awareness, update: Uint8Array, origin: any): void;
|
||||
export type MetaClientState = {
|
||||
clock: number;
|
||||
/**
|
||||
* unix timestamp
|
||||
*/
|
||||
lastUpdated: number;
|
||||
};
|
||||
import { Observable } from 'lib0/observable';
|
||||
import * as Y from 'yjs';
|
||||
//# sourceMappingURL=awareness.d.ts.map
|
||||
1
yjs-poll/node_modules/y-protocols/awareness.d.ts.map
generated
vendored
Normal file
1
yjs-poll/node_modules/y-protocols/awareness.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"awareness.d.ts","sourceRoot":"","sources":["awareness.js"],"names":[],"mappings":"AAYA,oCAAoC;AAEpC;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH;IACE;;OAEG;IACH,iBAFW,EAAE,GAAG,EAyCf;IArCC,WAAc;IACd;;OAEG;IACH,UAFU,MAAM,CAEY;IAC5B;;;OAGG;IACH,QAFU,IAAI,MAAM;YAAS,MAAM,GAAE,GAAG;MAAE,CAEnB;IACvB;;OAEG;IACH,MAFU,IAAI,MAAM,EAAE,eAAe,CAAC,CAEjB;IACrB,oBAkBqC;IAcvC;;OAEG;IACH,iBAFY;YAAO,MAAM,GAAC,GAAG;QAAE,IAAI,CAIlC;IAED;;OAEG;IACH,qBAFW;YAAO,MAAM,GAAC,GAAG;QAAE,IAAI,QAoCjC;IAED;;;OAGG;IACH,0BAHW,MAAM,SACN,GAAG,QAUb;IAED;;OAEG;IACH,aAFY,IAAI,MAAM;YAAQ,MAAM,GAAC,GAAG;MAAE,CAIzC;CACF;AAUM,iDAJI,SAAS,WACT,MAAM,MAAM,CAAC,UACb,GAAG,QAsBb;AAOM,iDAJI,SAAS,WACT,MAAM,MAAM,CAAC;;KACZ,UAAU,CAerB;AAYM,8CAJI,UAAU,iBACD,GAAG,KAAE,GAAG,GAChB,UAAU,CAiBrB;AAOM,gDAJI,SAAS,UACT,UAAU,UACV,GAAG,QAwDb;;WAtRa,MAAM;;;;iBACN,MAAM;;2BATO,iBAAiB;mBAEzB,KAAK"}
|
||||
295
yjs-poll/node_modules/y-protocols/awareness.js
generated
vendored
Normal file
295
yjs-poll/node_modules/y-protocols/awareness.js
generated
vendored
Normal file
@@ -0,0 +1,295 @@
|
||||
/**
|
||||
* @module awareness-protocol
|
||||
*/
|
||||
|
||||
import * as encoding from 'lib0/encoding'
|
||||
import * as decoding from 'lib0/decoding'
|
||||
import * as time from 'lib0/time'
|
||||
import * as math from 'lib0/math'
|
||||
import { Observable } from 'lib0/observable'
|
||||
import * as f from 'lib0/function'
|
||||
import * as Y from 'yjs' // eslint-disable-line
|
||||
|
||||
export const outdatedTimeout = 30000
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetaClientState
|
||||
* @property {number} MetaClientState.clock
|
||||
* @property {number} MetaClientState.lastUpdated unix timestamp
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Awareness class implements a simple shared state protocol that can be used for non-persistent data like awareness information
|
||||
* (cursor, username, status, ..). Each client can update its own local state and listen to state changes of
|
||||
* remote clients. Every client may set a state of a remote peer to `null` to mark the client as offline.
|
||||
*
|
||||
* Each client is identified by a unique client id (something we borrow from `doc.clientID`). A client can override
|
||||
* its own state by propagating a message with an increasing timestamp (`clock`). If such a message is received, it is
|
||||
* applied if the known state of that client is older than the new state (`clock < newClock`). If a client thinks that
|
||||
* a remote client is offline, it may propagate a message with
|
||||
* `{ clock: currentClientClock, state: null, client: remoteClient }`. If such a
|
||||
* message is received, and the known clock of that client equals the received clock, it will override the state with `null`.
|
||||
*
|
||||
* Before a client disconnects, it should propagate a `null` state with an updated clock.
|
||||
*
|
||||
* Awareness states must be updated every 30 seconds. Otherwise the Awareness instance will delete the client state.
|
||||
*
|
||||
* @extends {Observable<string>}
|
||||
*/
|
||||
export class Awareness extends Observable {
|
||||
/**
|
||||
* @param {Y.Doc} doc
|
||||
*/
|
||||
constructor (doc) {
|
||||
super()
|
||||
this.doc = doc
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.clientID = doc.clientID
|
||||
/**
|
||||
* Maps from client id to client state
|
||||
* @type {Map<number, Object<string, any>>}
|
||||
*/
|
||||
this.states = new Map()
|
||||
/**
|
||||
* @type {Map<number, MetaClientState>}
|
||||
*/
|
||||
this.meta = new Map()
|
||||
this._checkInterval = /** @type {any} */ (setInterval(() => {
|
||||
const now = time.getUnixTime()
|
||||
if (this.getLocalState() !== null && (outdatedTimeout / 2 <= now - /** @type {{lastUpdated:number}} */ (this.meta.get(this.clientID)).lastUpdated)) {
|
||||
// renew local clock
|
||||
this.setLocalState(this.getLocalState())
|
||||
}
|
||||
/**
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
const remove = []
|
||||
this.meta.forEach((meta, clientid) => {
|
||||
if (clientid !== this.clientID && outdatedTimeout <= now - meta.lastUpdated && this.states.has(clientid)) {
|
||||
remove.push(clientid)
|
||||
}
|
||||
})
|
||||
if (remove.length > 0) {
|
||||
removeAwarenessStates(this, remove, 'timeout')
|
||||
}
|
||||
}, math.floor(outdatedTimeout / 10)))
|
||||
doc.on('destroy', () => {
|
||||
this.destroy()
|
||||
})
|
||||
this.setLocalState({})
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.emit('destroy', [this])
|
||||
this.setLocalState(null)
|
||||
super.destroy()
|
||||
clearInterval(this._checkInterval)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object<string,any>|null}
|
||||
*/
|
||||
getLocalState () {
|
||||
return this.states.get(this.clientID) || null
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object<string,any>|null} state
|
||||
*/
|
||||
setLocalState (state) {
|
||||
const clientID = this.clientID
|
||||
const currLocalMeta = this.meta.get(clientID)
|
||||
const clock = currLocalMeta === undefined ? 0 : currLocalMeta.clock + 1
|
||||
const prevState = this.states.get(clientID)
|
||||
if (state === null) {
|
||||
this.states.delete(clientID)
|
||||
} else {
|
||||
this.states.set(clientID, state)
|
||||
}
|
||||
this.meta.set(clientID, {
|
||||
clock,
|
||||
lastUpdated: time.getUnixTime()
|
||||
})
|
||||
const added = []
|
||||
const updated = []
|
||||
const filteredUpdated = []
|
||||
const removed = []
|
||||
if (state === null) {
|
||||
removed.push(clientID)
|
||||
} else if (prevState == null) {
|
||||
if (state != null) {
|
||||
added.push(clientID)
|
||||
}
|
||||
} else {
|
||||
updated.push(clientID)
|
||||
if (!f.equalityDeep(prevState, state)) {
|
||||
filteredUpdated.push(clientID)
|
||||
}
|
||||
}
|
||||
if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
|
||||
this.emit('change', [{ added, updated: filteredUpdated, removed }, 'local'])
|
||||
}
|
||||
this.emit('update', [{ added, updated, removed }, 'local'])
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} field
|
||||
* @param {any} value
|
||||
*/
|
||||
setLocalStateField (field, value) {
|
||||
const state = this.getLocalState()
|
||||
if (state !== null) {
|
||||
this.setLocalState({
|
||||
...state,
|
||||
[field]: value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Map<number,Object<string,any>>}
|
||||
*/
|
||||
getStates () {
|
||||
return this.states
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark (remote) clients as inactive and remove them from the list of active peers.
|
||||
* This change will be propagated to remote clients.
|
||||
*
|
||||
* @param {Awareness} awareness
|
||||
* @param {Array<number>} clients
|
||||
* @param {any} origin
|
||||
*/
|
||||
export const removeAwarenessStates = (awareness, clients, origin) => {
|
||||
const removed = []
|
||||
for (let i = 0; i < clients.length; i++) {
|
||||
const clientID = clients[i]
|
||||
if (awareness.states.has(clientID)) {
|
||||
awareness.states.delete(clientID)
|
||||
if (clientID === awareness.clientID) {
|
||||
const curMeta = /** @type {MetaClientState} */ (awareness.meta.get(clientID))
|
||||
awareness.meta.set(clientID, {
|
||||
clock: curMeta.clock + 1,
|
||||
lastUpdated: time.getUnixTime()
|
||||
})
|
||||
}
|
||||
removed.push(clientID)
|
||||
}
|
||||
}
|
||||
if (removed.length > 0) {
|
||||
awareness.emit('change', [{ added: [], updated: [], removed }, origin])
|
||||
awareness.emit('update', [{ added: [], updated: [], removed }, origin])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Awareness} awareness
|
||||
* @param {Array<number>} clients
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
export const encodeAwarenessUpdate = (awareness, clients, states = awareness.states) => {
|
||||
const len = clients.length
|
||||
const encoder = encoding.createEncoder()
|
||||
encoding.writeVarUint(encoder, len)
|
||||
for (let i = 0; i < len; i++) {
|
||||
const clientID = clients[i]
|
||||
const state = states.get(clientID) || null
|
||||
const clock = /** @type {MetaClientState} */ (awareness.meta.get(clientID)).clock
|
||||
encoding.writeVarUint(encoder, clientID)
|
||||
encoding.writeVarUint(encoder, clock)
|
||||
encoding.writeVarString(encoder, JSON.stringify(state))
|
||||
}
|
||||
return encoding.toUint8Array(encoder)
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the content of an awareness update before re-encoding it to an awareness update.
|
||||
*
|
||||
* This might be useful when you have a central server that wants to ensure that clients
|
||||
* cant hijack somebody elses identity.
|
||||
*
|
||||
* @param {Uint8Array} update
|
||||
* @param {function(any):any} modify
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
export const modifyAwarenessUpdate = (update, modify) => {
|
||||
const decoder = decoding.createDecoder(update)
|
||||
const encoder = encoding.createEncoder()
|
||||
const len = decoding.readVarUint(decoder)
|
||||
encoding.writeVarUint(encoder, len)
|
||||
for (let i = 0; i < len; i++) {
|
||||
const clientID = decoding.readVarUint(decoder)
|
||||
const clock = decoding.readVarUint(decoder)
|
||||
const state = JSON.parse(decoding.readVarString(decoder))
|
||||
const modifiedState = modify(state)
|
||||
encoding.writeVarUint(encoder, clientID)
|
||||
encoding.writeVarUint(encoder, clock)
|
||||
encoding.writeVarString(encoder, JSON.stringify(modifiedState))
|
||||
}
|
||||
return encoding.toUint8Array(encoder)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Awareness} awareness
|
||||
* @param {Uint8Array} update
|
||||
* @param {any} origin This will be added to the emitted change event
|
||||
*/
|
||||
export const applyAwarenessUpdate = (awareness, update, origin) => {
|
||||
const decoder = decoding.createDecoder(update)
|
||||
const timestamp = time.getUnixTime()
|
||||
const added = []
|
||||
const updated = []
|
||||
const filteredUpdated = []
|
||||
const removed = []
|
||||
const len = decoding.readVarUint(decoder)
|
||||
for (let i = 0; i < len; i++) {
|
||||
const clientID = decoding.readVarUint(decoder)
|
||||
let clock = decoding.readVarUint(decoder)
|
||||
const state = JSON.parse(decoding.readVarString(decoder))
|
||||
const clientMeta = awareness.meta.get(clientID)
|
||||
const prevState = awareness.states.get(clientID)
|
||||
const currClock = clientMeta === undefined ? 0 : clientMeta.clock
|
||||
if (currClock < clock || (currClock === clock && state === null && awareness.states.has(clientID))) {
|
||||
if (state === null) {
|
||||
// never let a remote client remove this local state
|
||||
if (clientID === awareness.clientID && awareness.getLocalState() != null) {
|
||||
// remote client removed the local state. Do not remote state. Broadcast a message indicating
|
||||
// that this client still exists by increasing the clock
|
||||
clock++
|
||||
} else {
|
||||
awareness.states.delete(clientID)
|
||||
}
|
||||
} else {
|
||||
awareness.states.set(clientID, state)
|
||||
}
|
||||
awareness.meta.set(clientID, {
|
||||
clock,
|
||||
lastUpdated: timestamp
|
||||
})
|
||||
if (clientMeta === undefined && state !== null) {
|
||||
added.push(clientID)
|
||||
} else if (clientMeta !== undefined && state === null) {
|
||||
removed.push(clientID)
|
||||
} else if (state !== null) {
|
||||
if (!f.equalityDeep(state, prevState)) {
|
||||
filteredUpdated.push(clientID)
|
||||
}
|
||||
updated.push(clientID)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
|
||||
awareness.emit('change', [{
|
||||
added, updated: filteredUpdated, removed
|
||||
}, origin])
|
||||
}
|
||||
if (added.length > 0 || updated.length > 0 || removed.length > 0) {
|
||||
awareness.emit('update', [{
|
||||
added, updated, removed
|
||||
}, origin])
|
||||
}
|
||||
}
|
||||
3
yjs-poll/node_modules/y-protocols/awareness.test.d.ts
generated
vendored
Normal file
3
yjs-poll/node_modules/y-protocols/awareness.test.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export function testAwareness(tc: t.TestCase): void;
|
||||
import * as t from 'lib0/testing';
|
||||
//# sourceMappingURL=awareness.test.d.ts.map
|
||||
1
yjs-poll/node_modules/y-protocols/awareness.test.d.ts.map
generated
vendored
Normal file
1
yjs-poll/node_modules/y-protocols/awareness.test.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"awareness.test.d.ts","sourceRoot":"","sources":["awareness.test.js"],"names":[],"mappings":"AAQO,kCAFI,EAAE,QAAQ,QA8CpB;mBAlDkB,cAAc"}
|
||||
53
yjs-poll/node_modules/y-protocols/awareness.test.js
generated
vendored
Normal file
53
yjs-poll/node_modules/y-protocols/awareness.test.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
import * as Y from 'yjs'
|
||||
import * as t from 'lib0/testing'
|
||||
import * as awareness from './awareness'
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testAwareness = tc => {
|
||||
const doc1 = new Y.Doc()
|
||||
doc1.clientID = 0
|
||||
const doc2 = new Y.Doc()
|
||||
doc2.clientID = 1
|
||||
const aw1 = new awareness.Awareness(doc1)
|
||||
const aw2 = new awareness.Awareness(doc2)
|
||||
aw1.on('update', /** @param {any} p */ ({ added, updated, removed }) => {
|
||||
const enc = awareness.encodeAwarenessUpdate(aw1, added.concat(updated).concat(removed))
|
||||
awareness.applyAwarenessUpdate(aw2, enc, 'custom')
|
||||
})
|
||||
let lastChangeLocal = /** @type {any} */ (null)
|
||||
aw1.on('change', /** @param {any} change */ change => {
|
||||
lastChangeLocal = change
|
||||
})
|
||||
let lastChange = /** @type {any} */ (null)
|
||||
aw2.on('change', /** @param {any} change */ change => {
|
||||
lastChange = change
|
||||
})
|
||||
aw1.setLocalState({ x: 3 })
|
||||
t.compare(aw2.getStates().get(0), { x: 3 })
|
||||
t.assert(/** @type {any} */ (aw2.meta.get(0)).clock === 1)
|
||||
t.compare(lastChange.added, [0])
|
||||
// When creating an Awareness instance, the the local client is already marked as available, so it is not updated.
|
||||
t.compare(lastChangeLocal, { added: [], updated: [0], removed: [] })
|
||||
|
||||
// update state
|
||||
lastChange = null
|
||||
lastChangeLocal = null
|
||||
aw1.setLocalState({ x: 4 })
|
||||
t.compare(aw2.getStates().get(0), { x: 4 })
|
||||
t.compare(lastChangeLocal, { added: [], updated: [0], removed: [] })
|
||||
t.compare(lastChangeLocal, lastChange)
|
||||
|
||||
lastChange = null
|
||||
lastChangeLocal = null
|
||||
aw1.setLocalState({ x: 4 })
|
||||
t.assert(lastChange === null)
|
||||
t.assert(/** @type {any} */ (aw2.meta.get(0)).clock === 3)
|
||||
t.compare(lastChangeLocal, lastChange)
|
||||
aw1.setLocalState(null)
|
||||
t.assert(lastChange.removed.length === 1)
|
||||
t.compare(aw1.getStates().get(0), undefined)
|
||||
t.compare(lastChangeLocal, lastChange)
|
||||
}
|
||||
59
yjs-poll/node_modules/y-protocols/dist/auth.cjs
generated
vendored
Normal file
59
yjs-poll/node_modules/y-protocols/dist/auth.cjs
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
'use strict';
|
||||
|
||||
require('yjs');
|
||||
var encoding = require('lib0/dist/encoding.cjs');
|
||||
var decoding = require('lib0/dist/decoding.cjs');
|
||||
|
||||
function _interopNamespaceDefault(e) {
|
||||
var n = Object.create(null);
|
||||
if (e) {
|
||||
Object.keys(e).forEach(function (k) {
|
||||
if (k !== 'default') {
|
||||
var d = Object.getOwnPropertyDescriptor(e, k);
|
||||
Object.defineProperty(n, k, d.get ? d : {
|
||||
enumerable: true,
|
||||
get: function () { return e[k]; }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
n.default = e;
|
||||
return Object.freeze(n);
|
||||
}
|
||||
|
||||
var encoding__namespace = /*#__PURE__*/_interopNamespaceDefault(encoding);
|
||||
var decoding__namespace = /*#__PURE__*/_interopNamespaceDefault(decoding);
|
||||
|
||||
const messagePermissionDenied = 0;
|
||||
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {string} reason
|
||||
*/
|
||||
const writePermissionDenied = (encoder, reason) => {
|
||||
encoding__namespace.writeVarUint(encoder, messagePermissionDenied);
|
||||
encoding__namespace.writeVarString(encoder, reason);
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback PermissionDeniedHandler
|
||||
* @param {any} y
|
||||
* @param {string} reason
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @param {Y.Doc} y
|
||||
* @param {PermissionDeniedHandler} permissionDeniedHandler
|
||||
*/
|
||||
const readAuthMessage = (decoder, y, permissionDeniedHandler) => {
|
||||
switch (decoding__namespace.readVarUint(decoder)) {
|
||||
case messagePermissionDenied: permissionDeniedHandler(y, decoding__namespace.readVarString(decoder));
|
||||
}
|
||||
};
|
||||
|
||||
exports.messagePermissionDenied = messagePermissionDenied;
|
||||
exports.readAuthMessage = readAuthMessage;
|
||||
exports.writePermissionDenied = writePermissionDenied;
|
||||
//# sourceMappingURL=auth.cjs.map
|
||||
1
yjs-poll/node_modules/y-protocols/dist/auth.cjs.map
generated
vendored
Normal file
1
yjs-poll/node_modules/y-protocols/dist/auth.cjs.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"auth.cjs","sources":["../auth.js"],"sourcesContent":["\nimport * as Y from 'yjs' // eslint-disable-line\nimport * as encoding from 'lib0/encoding'\nimport * as decoding from 'lib0/decoding'\n\nexport const messagePermissionDenied = 0\n\n/**\n * @param {encoding.Encoder} encoder\n * @param {string} reason\n */\nexport const writePermissionDenied = (encoder, reason) => {\n encoding.writeVarUint(encoder, messagePermissionDenied)\n encoding.writeVarString(encoder, reason)\n}\n\n/**\n * @callback PermissionDeniedHandler\n * @param {any} y\n * @param {string} reason\n */\n\n/**\n *\n * @param {decoding.Decoder} decoder\n * @param {Y.Doc} y\n * @param {PermissionDeniedHandler} permissionDeniedHandler\n */\nexport const readAuthMessage = (decoder, y, permissionDeniedHandler) => {\n switch (decoding.readVarUint(decoder)) {\n case messagePermissionDenied: permissionDeniedHandler(y, decoding.readVarString(decoder))\n }\n}\n"],"names":["encoding","decoding"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAKY,MAAC,uBAAuB,GAAG,EAAC;AACxC;AACA;AACA;AACA;AACA;AACY,MAAC,qBAAqB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK;AAC1D,EAAEA,mBAAQ,CAAC,YAAY,CAAC,OAAO,EAAE,uBAAuB,EAAC;AACzD,EAAEA,mBAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAC;AAC1C,EAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,eAAe,GAAG,CAAC,OAAO,EAAE,CAAC,EAAE,uBAAuB,KAAK;AACxE,EAAE,QAAQC,mBAAQ,CAAC,WAAW,CAAC,OAAO,CAAC;AACvC,IAAI,KAAK,uBAAuB,EAAE,uBAAuB,CAAC,CAAC,EAAEA,mBAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,EAAC;AAC7F,GAAG;AACH;;;;;;"}
|
||||
329
yjs-poll/node_modules/y-protocols/dist/awareness.cjs
generated
vendored
Normal file
329
yjs-poll/node_modules/y-protocols/dist/awareness.cjs
generated
vendored
Normal file
@@ -0,0 +1,329 @@
|
||||
'use strict';
|
||||
|
||||
var encoding = require('lib0/dist/encoding.cjs');
|
||||
var decoding = require('lib0/dist/decoding.cjs');
|
||||
var time = require('lib0/dist/time.cjs');
|
||||
var math = require('lib0/dist/math.cjs');
|
||||
var observable = require('lib0/dist/observable.cjs');
|
||||
var f = require('lib0/dist/function.cjs');
|
||||
require('yjs');
|
||||
|
||||
function _interopNamespaceDefault(e) {
|
||||
var n = Object.create(null);
|
||||
if (e) {
|
||||
Object.keys(e).forEach(function (k) {
|
||||
if (k !== 'default') {
|
||||
var d = Object.getOwnPropertyDescriptor(e, k);
|
||||
Object.defineProperty(n, k, d.get ? d : {
|
||||
enumerable: true,
|
||||
get: function () { return e[k]; }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
n.default = e;
|
||||
return Object.freeze(n);
|
||||
}
|
||||
|
||||
var encoding__namespace = /*#__PURE__*/_interopNamespaceDefault(encoding);
|
||||
var decoding__namespace = /*#__PURE__*/_interopNamespaceDefault(decoding);
|
||||
var time__namespace = /*#__PURE__*/_interopNamespaceDefault(time);
|
||||
var math__namespace = /*#__PURE__*/_interopNamespaceDefault(math);
|
||||
var f__namespace = /*#__PURE__*/_interopNamespaceDefault(f);
|
||||
|
||||
/**
|
||||
* @module awareness-protocol
|
||||
*/
|
||||
|
||||
|
||||
const outdatedTimeout = 30000;
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetaClientState
|
||||
* @property {number} MetaClientState.clock
|
||||
* @property {number} MetaClientState.lastUpdated unix timestamp
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Awareness class implements a simple shared state protocol that can be used for non-persistent data like awareness information
|
||||
* (cursor, username, status, ..). Each client can update its own local state and listen to state changes of
|
||||
* remote clients. Every client may set a state of a remote peer to `null` to mark the client as offline.
|
||||
*
|
||||
* Each client is identified by a unique client id (something we borrow from `doc.clientID`). A client can override
|
||||
* its own state by propagating a message with an increasing timestamp (`clock`). If such a message is received, it is
|
||||
* applied if the known state of that client is older than the new state (`clock < newClock`). If a client thinks that
|
||||
* a remote client is offline, it may propagate a message with
|
||||
* `{ clock: currentClientClock, state: null, client: remoteClient }`. If such a
|
||||
* message is received, and the known clock of that client equals the received clock, it will override the state with `null`.
|
||||
*
|
||||
* Before a client disconnects, it should propagate a `null` state with an updated clock.
|
||||
*
|
||||
* Awareness states must be updated every 30 seconds. Otherwise the Awareness instance will delete the client state.
|
||||
*
|
||||
* @extends {Observable<string>}
|
||||
*/
|
||||
class Awareness extends observable.Observable {
|
||||
/**
|
||||
* @param {Y.Doc} doc
|
||||
*/
|
||||
constructor (doc) {
|
||||
super();
|
||||
this.doc = doc;
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.clientID = doc.clientID;
|
||||
/**
|
||||
* Maps from client id to client state
|
||||
* @type {Map<number, Object<string, any>>}
|
||||
*/
|
||||
this.states = new Map();
|
||||
/**
|
||||
* @type {Map<number, MetaClientState>}
|
||||
*/
|
||||
this.meta = new Map();
|
||||
this._checkInterval = /** @type {any} */ (setInterval(() => {
|
||||
const now = time__namespace.getUnixTime();
|
||||
if (this.getLocalState() !== null && (outdatedTimeout / 2 <= now - /** @type {{lastUpdated:number}} */ (this.meta.get(this.clientID)).lastUpdated)) {
|
||||
// renew local clock
|
||||
this.setLocalState(this.getLocalState());
|
||||
}
|
||||
/**
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
const remove = [];
|
||||
this.meta.forEach((meta, clientid) => {
|
||||
if (clientid !== this.clientID && outdatedTimeout <= now - meta.lastUpdated && this.states.has(clientid)) {
|
||||
remove.push(clientid);
|
||||
}
|
||||
});
|
||||
if (remove.length > 0) {
|
||||
removeAwarenessStates(this, remove, 'timeout');
|
||||
}
|
||||
}, math__namespace.floor(outdatedTimeout / 10)));
|
||||
doc.on('destroy', () => {
|
||||
this.destroy();
|
||||
});
|
||||
this.setLocalState({});
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.emit('destroy', [this]);
|
||||
this.setLocalState(null);
|
||||
super.destroy();
|
||||
clearInterval(this._checkInterval);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object<string,any>|null}
|
||||
*/
|
||||
getLocalState () {
|
||||
return this.states.get(this.clientID) || null
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object<string,any>|null} state
|
||||
*/
|
||||
setLocalState (state) {
|
||||
const clientID = this.clientID;
|
||||
const currLocalMeta = this.meta.get(clientID);
|
||||
const clock = currLocalMeta === undefined ? 0 : currLocalMeta.clock + 1;
|
||||
const prevState = this.states.get(clientID);
|
||||
if (state === null) {
|
||||
this.states.delete(clientID);
|
||||
} else {
|
||||
this.states.set(clientID, state);
|
||||
}
|
||||
this.meta.set(clientID, {
|
||||
clock,
|
||||
lastUpdated: time__namespace.getUnixTime()
|
||||
});
|
||||
const added = [];
|
||||
const updated = [];
|
||||
const filteredUpdated = [];
|
||||
const removed = [];
|
||||
if (state === null) {
|
||||
removed.push(clientID);
|
||||
} else if (prevState == null) {
|
||||
if (state != null) {
|
||||
added.push(clientID);
|
||||
}
|
||||
} else {
|
||||
updated.push(clientID);
|
||||
if (!f__namespace.equalityDeep(prevState, state)) {
|
||||
filteredUpdated.push(clientID);
|
||||
}
|
||||
}
|
||||
if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
|
||||
this.emit('change', [{ added, updated: filteredUpdated, removed }, 'local']);
|
||||
}
|
||||
this.emit('update', [{ added, updated, removed }, 'local']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} field
|
||||
* @param {any} value
|
||||
*/
|
||||
setLocalStateField (field, value) {
|
||||
const state = this.getLocalState();
|
||||
if (state !== null) {
|
||||
this.setLocalState({
|
||||
...state,
|
||||
[field]: value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Map<number,Object<string,any>>}
|
||||
*/
|
||||
getStates () {
|
||||
return this.states
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark (remote) clients as inactive and remove them from the list of active peers.
|
||||
* This change will be propagated to remote clients.
|
||||
*
|
||||
* @param {Awareness} awareness
|
||||
* @param {Array<number>} clients
|
||||
* @param {any} origin
|
||||
*/
|
||||
const removeAwarenessStates = (awareness, clients, origin) => {
|
||||
const removed = [];
|
||||
for (let i = 0; i < clients.length; i++) {
|
||||
const clientID = clients[i];
|
||||
if (awareness.states.has(clientID)) {
|
||||
awareness.states.delete(clientID);
|
||||
if (clientID === awareness.clientID) {
|
||||
const curMeta = /** @type {MetaClientState} */ (awareness.meta.get(clientID));
|
||||
awareness.meta.set(clientID, {
|
||||
clock: curMeta.clock + 1,
|
||||
lastUpdated: time__namespace.getUnixTime()
|
||||
});
|
||||
}
|
||||
removed.push(clientID);
|
||||
}
|
||||
}
|
||||
if (removed.length > 0) {
|
||||
awareness.emit('change', [{ added: [], updated: [], removed }, origin]);
|
||||
awareness.emit('update', [{ added: [], updated: [], removed }, origin]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Awareness} awareness
|
||||
* @param {Array<number>} clients
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
const encodeAwarenessUpdate = (awareness, clients, states = awareness.states) => {
|
||||
const len = clients.length;
|
||||
const encoder = encoding__namespace.createEncoder();
|
||||
encoding__namespace.writeVarUint(encoder, len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const clientID = clients[i];
|
||||
const state = states.get(clientID) || null;
|
||||
const clock = /** @type {MetaClientState} */ (awareness.meta.get(clientID)).clock;
|
||||
encoding__namespace.writeVarUint(encoder, clientID);
|
||||
encoding__namespace.writeVarUint(encoder, clock);
|
||||
encoding__namespace.writeVarString(encoder, JSON.stringify(state));
|
||||
}
|
||||
return encoding__namespace.toUint8Array(encoder)
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify the content of an awareness update before re-encoding it to an awareness update.
|
||||
*
|
||||
* This might be useful when you have a central server that wants to ensure that clients
|
||||
* cant hijack somebody elses identity.
|
||||
*
|
||||
* @param {Uint8Array} update
|
||||
* @param {function(any):any} modify
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
const modifyAwarenessUpdate = (update, modify) => {
|
||||
const decoder = decoding__namespace.createDecoder(update);
|
||||
const encoder = encoding__namespace.createEncoder();
|
||||
const len = decoding__namespace.readVarUint(decoder);
|
||||
encoding__namespace.writeVarUint(encoder, len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const clientID = decoding__namespace.readVarUint(decoder);
|
||||
const clock = decoding__namespace.readVarUint(decoder);
|
||||
const state = JSON.parse(decoding__namespace.readVarString(decoder));
|
||||
const modifiedState = modify(state);
|
||||
encoding__namespace.writeVarUint(encoder, clientID);
|
||||
encoding__namespace.writeVarUint(encoder, clock);
|
||||
encoding__namespace.writeVarString(encoder, JSON.stringify(modifiedState));
|
||||
}
|
||||
return encoding__namespace.toUint8Array(encoder)
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Awareness} awareness
|
||||
* @param {Uint8Array} update
|
||||
* @param {any} origin This will be added to the emitted change event
|
||||
*/
|
||||
const applyAwarenessUpdate = (awareness, update, origin) => {
|
||||
const decoder = decoding__namespace.createDecoder(update);
|
||||
const timestamp = time__namespace.getUnixTime();
|
||||
const added = [];
|
||||
const updated = [];
|
||||
const filteredUpdated = [];
|
||||
const removed = [];
|
||||
const len = decoding__namespace.readVarUint(decoder);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const clientID = decoding__namespace.readVarUint(decoder);
|
||||
let clock = decoding__namespace.readVarUint(decoder);
|
||||
const state = JSON.parse(decoding__namespace.readVarString(decoder));
|
||||
const clientMeta = awareness.meta.get(clientID);
|
||||
const prevState = awareness.states.get(clientID);
|
||||
const currClock = clientMeta === undefined ? 0 : clientMeta.clock;
|
||||
if (currClock < clock || (currClock === clock && state === null && awareness.states.has(clientID))) {
|
||||
if (state === null) {
|
||||
// never let a remote client remove this local state
|
||||
if (clientID === awareness.clientID && awareness.getLocalState() != null) {
|
||||
// remote client removed the local state. Do not remote state. Broadcast a message indicating
|
||||
// that this client still exists by increasing the clock
|
||||
clock++;
|
||||
} else {
|
||||
awareness.states.delete(clientID);
|
||||
}
|
||||
} else {
|
||||
awareness.states.set(clientID, state);
|
||||
}
|
||||
awareness.meta.set(clientID, {
|
||||
clock,
|
||||
lastUpdated: timestamp
|
||||
});
|
||||
if (clientMeta === undefined && state !== null) {
|
||||
added.push(clientID);
|
||||
} else if (clientMeta !== undefined && state === null) {
|
||||
removed.push(clientID);
|
||||
} else if (state !== null) {
|
||||
if (!f__namespace.equalityDeep(state, prevState)) {
|
||||
filteredUpdated.push(clientID);
|
||||
}
|
||||
updated.push(clientID);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (added.length > 0 || filteredUpdated.length > 0 || removed.length > 0) {
|
||||
awareness.emit('change', [{
|
||||
added, updated: filteredUpdated, removed
|
||||
}, origin]);
|
||||
}
|
||||
if (added.length > 0 || updated.length > 0 || removed.length > 0) {
|
||||
awareness.emit('update', [{
|
||||
added, updated, removed
|
||||
}, origin]);
|
||||
}
|
||||
};
|
||||
|
||||
exports.Awareness = Awareness;
|
||||
exports.applyAwarenessUpdate = applyAwarenessUpdate;
|
||||
exports.encodeAwarenessUpdate = encodeAwarenessUpdate;
|
||||
exports.modifyAwarenessUpdate = modifyAwarenessUpdate;
|
||||
exports.outdatedTimeout = outdatedTimeout;
|
||||
exports.removeAwarenessStates = removeAwarenessStates;
|
||||
//# sourceMappingURL=awareness.cjs.map
|
||||
1
yjs-poll/node_modules/y-protocols/dist/awareness.cjs.map
generated
vendored
Normal file
1
yjs-poll/node_modules/y-protocols/dist/awareness.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
170
yjs-poll/node_modules/y-protocols/dist/sync.cjs
generated
vendored
Normal file
170
yjs-poll/node_modules/y-protocols/dist/sync.cjs
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
'use strict';
|
||||
|
||||
var encoding = require('lib0/dist/encoding.cjs');
|
||||
var decoding = require('lib0/dist/decoding.cjs');
|
||||
var Y = require('yjs');
|
||||
|
||||
function _interopNamespaceDefault(e) {
|
||||
var n = Object.create(null);
|
||||
if (e) {
|
||||
Object.keys(e).forEach(function (k) {
|
||||
if (k !== 'default') {
|
||||
var d = Object.getOwnPropertyDescriptor(e, k);
|
||||
Object.defineProperty(n, k, d.get ? d : {
|
||||
enumerable: true,
|
||||
get: function () { return e[k]; }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
n.default = e;
|
||||
return Object.freeze(n);
|
||||
}
|
||||
|
||||
var encoding__namespace = /*#__PURE__*/_interopNamespaceDefault(encoding);
|
||||
var decoding__namespace = /*#__PURE__*/_interopNamespaceDefault(decoding);
|
||||
var Y__namespace = /*#__PURE__*/_interopNamespaceDefault(Y);
|
||||
|
||||
/**
|
||||
* @module sync-protocol
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Map<number, number>} StateMap
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core Yjs defines two message types:
|
||||
* • YjsSyncStep1: Includes the State Set of the sending client. When received, the client should reply with YjsSyncStep2.
|
||||
* • YjsSyncStep2: Includes all missing structs and the complete delete set. When received, the client is assured that it
|
||||
* received all information from the remote client.
|
||||
*
|
||||
* In a peer-to-peer network, you may want to introduce a SyncDone message type. Both parties should initiate the connection
|
||||
* with SyncStep1. When a client received SyncStep2, it should reply with SyncDone. When the local client received both
|
||||
* SyncStep2 and SyncDone, it is assured that it is synced to the remote client.
|
||||
*
|
||||
* In a client-server model, you want to handle this differently: The client should initiate the connection with SyncStep1.
|
||||
* When the server receives SyncStep1, it should reply with SyncStep2 immediately followed by SyncStep1. The client replies
|
||||
* with SyncStep2 when it receives SyncStep1. Optionally the server may send a SyncDone after it received SyncStep2, so the
|
||||
* client knows that the sync is finished. There are two reasons for this more elaborated sync model: 1. This protocol can
|
||||
* easily be implemented on top of http and websockets. 2. The server should only reply to requests, and not initiate them.
|
||||
* Therefore it is necessary that the client initiates the sync.
|
||||
*
|
||||
* Construction of a message:
|
||||
* [messageType : varUint, message definition..]
|
||||
*
|
||||
* Note: A message does not include information about the room name. This must to be handled by the upper layer protocol!
|
||||
*
|
||||
* stringify[messageType] stringifies a message definition (messageType is already read from the bufffer)
|
||||
*/
|
||||
|
||||
const messageYjsSyncStep1 = 0;
|
||||
const messageYjsSyncStep2 = 1;
|
||||
const messageYjsUpdate = 2;
|
||||
|
||||
/**
|
||||
* Create a sync step 1 message based on the state of the current shared document.
|
||||
*
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {Y.Doc} doc
|
||||
*/
|
||||
const writeSyncStep1 = (encoder, doc) => {
|
||||
encoding__namespace.writeVarUint(encoder, messageYjsSyncStep1);
|
||||
const sv = Y__namespace.encodeStateVector(doc);
|
||||
encoding__namespace.writeVarUint8Array(encoder, sv);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {Y.Doc} doc
|
||||
* @param {Uint8Array} [encodedStateVector]
|
||||
*/
|
||||
const writeSyncStep2 = (encoder, doc, encodedStateVector) => {
|
||||
encoding__namespace.writeVarUint(encoder, messageYjsSyncStep2);
|
||||
encoding__namespace.writeVarUint8Array(encoder, Y__namespace.encodeStateAsUpdate(doc, encodedStateVector));
|
||||
};
|
||||
|
||||
/**
|
||||
* Read SyncStep1 message and reply with SyncStep2.
|
||||
*
|
||||
* @param {decoding.Decoder} decoder The reply to the received message
|
||||
* @param {encoding.Encoder} encoder The received message
|
||||
* @param {Y.Doc} doc
|
||||
*/
|
||||
const readSyncStep1 = (decoder, encoder, doc) =>
|
||||
writeSyncStep2(encoder, doc, decoding__namespace.readVarUint8Array(decoder));
|
||||
|
||||
/**
|
||||
* Read and apply Structs and then DeleteStore to a y instance.
|
||||
*
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @param {Y.Doc} doc
|
||||
* @param {any} transactionOrigin
|
||||
* @param {(error:Error)=>any} [errorHandler]
|
||||
*/
|
||||
const readSyncStep2 = (decoder, doc, transactionOrigin, errorHandler) => {
|
||||
try {
|
||||
Y__namespace.applyUpdate(doc, decoding__namespace.readVarUint8Array(decoder), transactionOrigin);
|
||||
} catch (error) {
|
||||
if (errorHandler != null) errorHandler(/** @type {Error} */ (error));
|
||||
// This catches errors that are thrown by event handlers
|
||||
console.error('Caught error while handling a Yjs update', error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {Uint8Array} update
|
||||
*/
|
||||
const writeUpdate = (encoder, update) => {
|
||||
encoding__namespace.writeVarUint(encoder, messageYjsUpdate);
|
||||
encoding__namespace.writeVarUint8Array(encoder, update);
|
||||
};
|
||||
|
||||
/**
|
||||
* Read and apply Structs and then DeleteStore to a y instance.
|
||||
*
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @param {Y.Doc} doc
|
||||
* @param {any} transactionOrigin
|
||||
* @param {(error:Error)=>any} [errorHandler]
|
||||
*/
|
||||
const readUpdate = readSyncStep2;
|
||||
|
||||
/**
|
||||
* @param {decoding.Decoder} decoder A message received from another client
|
||||
* @param {encoding.Encoder} encoder The reply message. Does not need to be sent if empty.
|
||||
* @param {Y.Doc} doc
|
||||
* @param {any} transactionOrigin
|
||||
* @param {(error:Error)=>any} [errorHandler] Optional error handler that catches errors when reading Yjs messages.
|
||||
*/
|
||||
const readSyncMessage = (decoder, encoder, doc, transactionOrigin, errorHandler) => {
|
||||
const messageType = decoding__namespace.readVarUint(decoder);
|
||||
switch (messageType) {
|
||||
case messageYjsSyncStep1:
|
||||
readSyncStep1(decoder, encoder, doc);
|
||||
break
|
||||
case messageYjsSyncStep2:
|
||||
readSyncStep2(decoder, doc, transactionOrigin, errorHandler);
|
||||
break
|
||||
case messageYjsUpdate:
|
||||
readUpdate(decoder, doc, transactionOrigin, errorHandler);
|
||||
break
|
||||
default:
|
||||
throw new Error('Unknown message type')
|
||||
}
|
||||
return messageType
|
||||
};
|
||||
|
||||
exports.messageYjsSyncStep1 = messageYjsSyncStep1;
|
||||
exports.messageYjsSyncStep2 = messageYjsSyncStep2;
|
||||
exports.messageYjsUpdate = messageYjsUpdate;
|
||||
exports.readSyncMessage = readSyncMessage;
|
||||
exports.readSyncStep1 = readSyncStep1;
|
||||
exports.readSyncStep2 = readSyncStep2;
|
||||
exports.readUpdate = readUpdate;
|
||||
exports.writeSyncStep1 = writeSyncStep1;
|
||||
exports.writeSyncStep2 = writeSyncStep2;
|
||||
exports.writeUpdate = writeUpdate;
|
||||
//# sourceMappingURL=sync.cjs.map
|
||||
1
yjs-poll/node_modules/y-protocols/dist/sync.cjs.map
generated
vendored
Normal file
1
yjs-poll/node_modules/y-protocols/dist/sync.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
103
yjs-poll/node_modules/y-protocols/dist/test.cjs
generated
vendored
Normal file
103
yjs-poll/node_modules/y-protocols/dist/test.cjs
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
'use strict';
|
||||
|
||||
var t = require('lib0/dist/testing.cjs');
|
||||
var log = require('lib0/dist/logging.cjs');
|
||||
var Y = require('yjs');
|
||||
var awareness$1 = require('./awareness.cjs');
|
||||
var environment = require('lib0/dist/environment.cjs');
|
||||
require('lib0/dist/encoding.cjs');
|
||||
require('lib0/dist/decoding.cjs');
|
||||
require('lib0/dist/time.cjs');
|
||||
require('lib0/dist/math.cjs');
|
||||
require('lib0/dist/observable.cjs');
|
||||
require('lib0/dist/function.cjs');
|
||||
|
||||
function _interopNamespaceDefault(e) {
|
||||
var n = Object.create(null);
|
||||
if (e) {
|
||||
Object.keys(e).forEach(function (k) {
|
||||
if (k !== 'default') {
|
||||
var d = Object.getOwnPropertyDescriptor(e, k);
|
||||
Object.defineProperty(n, k, d.get ? d : {
|
||||
enumerable: true,
|
||||
get: function () { return e[k]; }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
n.default = e;
|
||||
return Object.freeze(n);
|
||||
}
|
||||
|
||||
var t__namespace = /*#__PURE__*/_interopNamespaceDefault(t);
|
||||
var log__namespace = /*#__PURE__*/_interopNamespaceDefault(log);
|
||||
var Y__namespace = /*#__PURE__*/_interopNamespaceDefault(Y);
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
const testAwareness = tc => {
|
||||
const doc1 = new Y__namespace.Doc();
|
||||
doc1.clientID = 0;
|
||||
const doc2 = new Y__namespace.Doc();
|
||||
doc2.clientID = 1;
|
||||
const aw1 = new awareness$1.Awareness(doc1);
|
||||
const aw2 = new awareness$1.Awareness(doc2);
|
||||
aw1.on('update', /** @param {any} p */ ({ added, updated, removed }) => {
|
||||
const enc = awareness$1.encodeAwarenessUpdate(aw1, added.concat(updated).concat(removed));
|
||||
awareness$1.applyAwarenessUpdate(aw2, enc, 'custom');
|
||||
});
|
||||
let lastChangeLocal = /** @type {any} */ (null);
|
||||
aw1.on('change', /** @param {any} change */ change => {
|
||||
lastChangeLocal = change;
|
||||
});
|
||||
let lastChange = /** @type {any} */ (null);
|
||||
aw2.on('change', /** @param {any} change */ change => {
|
||||
lastChange = change;
|
||||
});
|
||||
aw1.setLocalState({ x: 3 });
|
||||
t__namespace.compare(aw2.getStates().get(0), { x: 3 });
|
||||
t__namespace.assert(/** @type {any} */ (aw2.meta.get(0)).clock === 1);
|
||||
t__namespace.compare(lastChange.added, [0]);
|
||||
// When creating an Awareness instance, the the local client is already marked as available, so it is not updated.
|
||||
t__namespace.compare(lastChangeLocal, { added: [], updated: [0], removed: [] });
|
||||
|
||||
// update state
|
||||
lastChange = null;
|
||||
lastChangeLocal = null;
|
||||
aw1.setLocalState({ x: 4 });
|
||||
t__namespace.compare(aw2.getStates().get(0), { x: 4 });
|
||||
t__namespace.compare(lastChangeLocal, { added: [], updated: [0], removed: [] });
|
||||
t__namespace.compare(lastChangeLocal, lastChange);
|
||||
|
||||
lastChange = null;
|
||||
lastChangeLocal = null;
|
||||
aw1.setLocalState({ x: 4 });
|
||||
t__namespace.assert(lastChange === null);
|
||||
t__namespace.assert(/** @type {any} */ (aw2.meta.get(0)).clock === 3);
|
||||
t__namespace.compare(lastChangeLocal, lastChange);
|
||||
aw1.setLocalState(null);
|
||||
t__namespace.assert(lastChange.removed.length === 1);
|
||||
t__namespace.compare(aw1.getStates().get(0), undefined);
|
||||
t__namespace.compare(lastChangeLocal, lastChange);
|
||||
};
|
||||
|
||||
var awareness = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
testAwareness: testAwareness
|
||||
});
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (environment.isBrowser) {
|
||||
log__namespace.createVConsole(document.body);
|
||||
}
|
||||
|
||||
t.runTests({
|
||||
awareness
|
||||
}).then(success => {
|
||||
/* istanbul ignore next */
|
||||
if (environment.isNode) {
|
||||
process.exit(success ? 0 : 1);
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=test.cjs.map
|
||||
1
yjs-poll/node_modules/y-protocols/dist/test.cjs.map
generated
vendored
Normal file
1
yjs-poll/node_modules/y-protocols/dist/test.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
9921
yjs-poll/node_modules/y-protocols/dist/test.js
generated
vendored
Normal file
9921
yjs-poll/node_modules/y-protocols/dist/test.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
yjs-poll/node_modules/y-protocols/dist/test.js.map
generated
vendored
Normal file
1
yjs-poll/node_modules/y-protocols/dist/test.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
92
yjs-poll/node_modules/y-protocols/package.json
generated
vendored
Normal file
92
yjs-poll/node_modules/y-protocols/package.json
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"name": "y-protocols",
|
||||
"version": "1.0.7",
|
||||
"description": "Yjs encoding protocols",
|
||||
"type": "module",
|
||||
"funding": {
|
||||
"type": "GitHub Sponsors ❤",
|
||||
"url": "https://github.com/sponsors/dmonad"
|
||||
},
|
||||
"files": [
|
||||
"dist/*",
|
||||
"auth.*",
|
||||
"sync.*",
|
||||
"awareness.*"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist *.d.ts */*.d.ts *.d.ts.map */*.d.ts.map",
|
||||
"dist": "rm -rf dist && rollup -c",
|
||||
"test": "npm run lint && npm run dist && node dist/test.cjs",
|
||||
"lint": "standard && tsc",
|
||||
"types": "tsc --outDir .",
|
||||
"debug": "rollup -c && concurrently 'rollup -wc' 'http-server -o test.html'",
|
||||
"preversion": "npm run dist && npm run test && npm run types",
|
||||
"postpublish": "npm run clean"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/yjs/y-protocols.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Yjs"
|
||||
],
|
||||
"author": "Kevin Jahns <kevin.jahns@protonmail.com>",
|
||||
"license": "MIT",
|
||||
"standard": {
|
||||
"ignore": [
|
||||
"/dist",
|
||||
"/node_modules"
|
||||
]
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/yjs/y-protocols/issues"
|
||||
},
|
||||
"homepage": "https://github.com/yjs/y-protocols#readme",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./sync.js": "./sync.js",
|
||||
"./dist/sync.cjs": "./dist/sync.cjs",
|
||||
"./sync": {
|
||||
"types": "./sync.d.ts",
|
||||
"module": "./sync.js",
|
||||
"import": "./sync.js",
|
||||
"require": "./dist/sync.cjs"
|
||||
},
|
||||
"./awareness.js": "./awareness.js",
|
||||
"./dist/awareness.cjs": "./dist/awareness.cjs",
|
||||
"./awareness": {
|
||||
"types": "./awareness.d.ts",
|
||||
"module": "./awareness.js",
|
||||
"import": "./awareness.js",
|
||||
"require": "./dist/awareness.cjs"
|
||||
},
|
||||
"./auth.js": "./auth.js",
|
||||
"./dist/auth.cjs": "./dist/auth.cjs",
|
||||
"./auth": {
|
||||
"types": "./auth.d.ts",
|
||||
"module": "./auth.js",
|
||||
"import": "./auth.js",
|
||||
"require": "./dist/auth.cjs"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"lib0": "^0.2.85"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^25.0.4",
|
||||
"@rollup/plugin-node-resolve": "^15.2.1",
|
||||
"@types/node": "^20.6.2",
|
||||
"concurrently": "^5.3.0",
|
||||
"rollup": "^3.29.2",
|
||||
"standard": "^12.0.1",
|
||||
"typescript": "^5.2.2",
|
||||
"yjs": "^13.5.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"yjs": "^13.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">=8.0.0",
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
}
|
||||
42
yjs-poll/node_modules/y-protocols/sync.d.ts
generated
vendored
Normal file
42
yjs-poll/node_modules/y-protocols/sync.d.ts
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @typedef {Map<number, number>} StateMap
|
||||
*/
|
||||
/**
|
||||
* Core Yjs defines two message types:
|
||||
* • YjsSyncStep1: Includes the State Set of the sending client. When received, the client should reply with YjsSyncStep2.
|
||||
* • YjsSyncStep2: Includes all missing structs and the complete delete set. When received, the client is assured that it
|
||||
* received all information from the remote client.
|
||||
*
|
||||
* In a peer-to-peer network, you may want to introduce a SyncDone message type. Both parties should initiate the connection
|
||||
* with SyncStep1. When a client received SyncStep2, it should reply with SyncDone. When the local client received both
|
||||
* SyncStep2 and SyncDone, it is assured that it is synced to the remote client.
|
||||
*
|
||||
* In a client-server model, you want to handle this differently: The client should initiate the connection with SyncStep1.
|
||||
* When the server receives SyncStep1, it should reply with SyncStep2 immediately followed by SyncStep1. The client replies
|
||||
* with SyncStep2 when it receives SyncStep1. Optionally the server may send a SyncDone after it received SyncStep2, so the
|
||||
* client knows that the sync is finished. There are two reasons for this more elaborated sync model: 1. This protocol can
|
||||
* easily be implemented on top of http and websockets. 2. The server should only reply to requests, and not initiate them.
|
||||
* Therefore it is necessary that the client initiates the sync.
|
||||
*
|
||||
* Construction of a message:
|
||||
* [messageType : varUint, message definition..]
|
||||
*
|
||||
* Note: A message does not include information about the room name. This must to be handled by the upper layer protocol!
|
||||
*
|
||||
* stringify[messageType] stringifies a message definition (messageType is already read from the bufffer)
|
||||
*/
|
||||
export const messageYjsSyncStep1: 0;
|
||||
export const messageYjsSyncStep2: 1;
|
||||
export const messageYjsUpdate: 2;
|
||||
export function writeSyncStep1(encoder: encoding.Encoder, doc: Y.Doc): void;
|
||||
export function writeSyncStep2(encoder: encoding.Encoder, doc: Y.Doc, encodedStateVector?: Uint8Array | undefined): void;
|
||||
export function readSyncStep1(decoder: decoding.Decoder, encoder: encoding.Encoder, doc: Y.Doc): void;
|
||||
export function readSyncStep2(decoder: decoding.Decoder, doc: Y.Doc, transactionOrigin: any, errorHandler?: ((error: Error) => any) | undefined): void;
|
||||
export function writeUpdate(encoder: encoding.Encoder, update: Uint8Array): void;
|
||||
export function readUpdate(decoder: decoding.Decoder, doc: Y.Doc, transactionOrigin: any, errorHandler?: ((error: Error) => any) | undefined): void;
|
||||
export function readSyncMessage(decoder: decoding.Decoder, encoder: encoding.Encoder, doc: Y.Doc, transactionOrigin: any, errorHandler?: ((error: Error) => any) | undefined): 0 | 2 | 1;
|
||||
export type StateMap = Map<number, number>;
|
||||
import * as encoding from 'lib0/encoding';
|
||||
import * as Y from 'yjs';
|
||||
import * as decoding from 'lib0/decoding';
|
||||
//# sourceMappingURL=sync.d.ts.map
|
||||
1
yjs-poll/node_modules/y-protocols/sync.d.ts.map
generated
vendored
Normal file
1
yjs-poll/node_modules/y-protocols/sync.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["sync.js"],"names":[],"mappings":"AAQA;;GAEG;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,oCAAoC;AACpC,oCAAoC;AACpC,iCAAiC;AAQ1B,wCAHI,SAAS,OAAO,OAChB,EAAE,GAAG,QAMf;AAOM,wCAJI,SAAS,OAAO,OAChB,EAAE,GAAG,qDAMf;AASM,uCAJI,SAAS,OAAO,WAChB,SAAS,OAAO,OAChB,EAAE,GAAG,QAGmD;AAU5D,uCALI,SAAS,OAAO,OAChB,EAAE,GAAG,qBACL,GAAG,0BACI,KAAK,KAAG,GAAG,qBAU5B;AAMM,qCAHI,SAAS,OAAO,UAChB,UAAU,QAKpB;AAjBM,oCALI,SAAS,OAAO,OAChB,EAAE,GAAG,qBACL,GAAG,0BACI,KAAK,KAAG,GAAG,qBAU5B;AA4BM,yCANI,SAAS,OAAO,WAChB,SAAS,OAAO,OAChB,EAAE,GAAG,qBACL,GAAG,0BACI,KAAK,KAAG,GAAG,0BAkB5B;uBA5HY,IAAI,MAAM,EAAE,MAAM,CAAC;0BALN,eAAe;mBAEtB,KAAK;0BADE,eAAe"}
|
||||
134
yjs-poll/node_modules/y-protocols/sync.js
generated
vendored
Normal file
134
yjs-poll/node_modules/y-protocols/sync.js
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* @module sync-protocol
|
||||
*/
|
||||
|
||||
import * as encoding from 'lib0/encoding'
|
||||
import * as decoding from 'lib0/decoding'
|
||||
import * as Y from 'yjs'
|
||||
|
||||
/**
|
||||
* @typedef {Map<number, number>} StateMap
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core Yjs defines two message types:
|
||||
* • YjsSyncStep1: Includes the State Set of the sending client. When received, the client should reply with YjsSyncStep2.
|
||||
* • YjsSyncStep2: Includes all missing structs and the complete delete set. When received, the client is assured that it
|
||||
* received all information from the remote client.
|
||||
*
|
||||
* In a peer-to-peer network, you may want to introduce a SyncDone message type. Both parties should initiate the connection
|
||||
* with SyncStep1. When a client received SyncStep2, it should reply with SyncDone. When the local client received both
|
||||
* SyncStep2 and SyncDone, it is assured that it is synced to the remote client.
|
||||
*
|
||||
* In a client-server model, you want to handle this differently: The client should initiate the connection with SyncStep1.
|
||||
* When the server receives SyncStep1, it should reply with SyncStep2 immediately followed by SyncStep1. The client replies
|
||||
* with SyncStep2 when it receives SyncStep1. Optionally the server may send a SyncDone after it received SyncStep2, so the
|
||||
* client knows that the sync is finished. There are two reasons for this more elaborated sync model: 1. This protocol can
|
||||
* easily be implemented on top of http and websockets. 2. The server should only reply to requests, and not initiate them.
|
||||
* Therefore it is necessary that the client initiates the sync.
|
||||
*
|
||||
* Construction of a message:
|
||||
* [messageType : varUint, message definition..]
|
||||
*
|
||||
* Note: A message does not include information about the room name. This must to be handled by the upper layer protocol!
|
||||
*
|
||||
* stringify[messageType] stringifies a message definition (messageType is already read from the bufffer)
|
||||
*/
|
||||
|
||||
export const messageYjsSyncStep1 = 0
|
||||
export const messageYjsSyncStep2 = 1
|
||||
export const messageYjsUpdate = 2
|
||||
|
||||
/**
|
||||
* Create a sync step 1 message based on the state of the current shared document.
|
||||
*
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {Y.Doc} doc
|
||||
*/
|
||||
export const writeSyncStep1 = (encoder, doc) => {
|
||||
encoding.writeVarUint(encoder, messageYjsSyncStep1)
|
||||
const sv = Y.encodeStateVector(doc)
|
||||
encoding.writeVarUint8Array(encoder, sv)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {Y.Doc} doc
|
||||
* @param {Uint8Array} [encodedStateVector]
|
||||
*/
|
||||
export const writeSyncStep2 = (encoder, doc, encodedStateVector) => {
|
||||
encoding.writeVarUint(encoder, messageYjsSyncStep2)
|
||||
encoding.writeVarUint8Array(encoder, Y.encodeStateAsUpdate(doc, encodedStateVector))
|
||||
}
|
||||
|
||||
/**
|
||||
* Read SyncStep1 message and reply with SyncStep2.
|
||||
*
|
||||
* @param {decoding.Decoder} decoder The reply to the received message
|
||||
* @param {encoding.Encoder} encoder The received message
|
||||
* @param {Y.Doc} doc
|
||||
*/
|
||||
export const readSyncStep1 = (decoder, encoder, doc) =>
|
||||
writeSyncStep2(encoder, doc, decoding.readVarUint8Array(decoder))
|
||||
|
||||
/**
|
||||
* Read and apply Structs and then DeleteStore to a y instance.
|
||||
*
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @param {Y.Doc} doc
|
||||
* @param {any} transactionOrigin
|
||||
* @param {(error:Error)=>any} [errorHandler]
|
||||
*/
|
||||
export const readSyncStep2 = (decoder, doc, transactionOrigin, errorHandler) => {
|
||||
try {
|
||||
Y.applyUpdate(doc, decoding.readVarUint8Array(decoder), transactionOrigin)
|
||||
} catch (error) {
|
||||
if (errorHandler != null) errorHandler(/** @type {Error} */ (error))
|
||||
// This catches errors that are thrown by event handlers
|
||||
console.error('Caught error while handling a Yjs update', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {encoding.Encoder} encoder
|
||||
* @param {Uint8Array} update
|
||||
*/
|
||||
export const writeUpdate = (encoder, update) => {
|
||||
encoding.writeVarUint(encoder, messageYjsUpdate)
|
||||
encoding.writeVarUint8Array(encoder, update)
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and apply Structs and then DeleteStore to a y instance.
|
||||
*
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @param {Y.Doc} doc
|
||||
* @param {any} transactionOrigin
|
||||
* @param {(error:Error)=>any} [errorHandler]
|
||||
*/
|
||||
export const readUpdate = readSyncStep2
|
||||
|
||||
/**
|
||||
* @param {decoding.Decoder} decoder A message received from another client
|
||||
* @param {encoding.Encoder} encoder The reply message. Does not need to be sent if empty.
|
||||
* @param {Y.Doc} doc
|
||||
* @param {any} transactionOrigin
|
||||
* @param {(error:Error)=>any} [errorHandler] Optional error handler that catches errors when reading Yjs messages.
|
||||
*/
|
||||
export const readSyncMessage = (decoder, encoder, doc, transactionOrigin, errorHandler) => {
|
||||
const messageType = decoding.readVarUint(decoder)
|
||||
switch (messageType) {
|
||||
case messageYjsSyncStep1:
|
||||
readSyncStep1(decoder, encoder, doc)
|
||||
break
|
||||
case messageYjsSyncStep2:
|
||||
readSyncStep2(decoder, doc, transactionOrigin, errorHandler)
|
||||
break
|
||||
case messageYjsUpdate:
|
||||
readUpdate(decoder, doc, transactionOrigin, errorHandler)
|
||||
break
|
||||
default:
|
||||
throw new Error('Unknown message type')
|
||||
}
|
||||
return messageType
|
||||
}
|
||||
Reference in New Issue
Block a user