@liveblocks/yjs
Advanced tools
Comparing version 1.1.0-yjs5 to 1.1.1-dual1
@@ -5,5 +5,2 @@ import { Room, JsonObject, LsonObject, BaseUserMeta, Json } from '@liveblocks/client'; | ||
declare type LiveblocksYjsOptions = { | ||
httpEndpoint?: string; | ||
}; | ||
declare type MetaClientState = { | ||
@@ -13,7 +10,13 @@ clock: number; | ||
}; | ||
declare class Awareness extends Observable<any> { | ||
/** | ||
* This class will store Yjs awareness in Liveblock's presence under the __yjs key | ||
* IMPORTANT: The Yjs awareness protocol uses ydoc.clientId to reference users | ||
* to their respective documents. To avoid mapping Yjs clientIds to liveblock's connectionId, | ||
* we simply set the clientId of the doc to the connectionId. Then no further mapping is required | ||
*/ | ||
declare class Awareness extends Observable<unknown> { | ||
private room; | ||
doc: Y.Doc; | ||
clientID: number; | ||
states: Map<number, any>; | ||
states: Map<number, unknown>; | ||
meta: Map<number, MetaClientState>; | ||
@@ -27,18 +30,20 @@ _checkInterval: number; | ||
setLocalStateField(field: string, value: JsonObject | null): void; | ||
getStates(): Map<number, any>; | ||
getStates(): Map<number, unknown>; | ||
} | ||
declare class LiveblocksProvider<P extends JsonObject, S extends LsonObject, U extends BaseUserMeta, E extends Json> { | ||
declare class LiveblocksProvider<P extends JsonObject, S extends LsonObject, U extends BaseUserMeta, E extends Json> extends Observable<unknown> { | ||
private room; | ||
private httpEndpoint?; | ||
private lastUpdateDate; | ||
private doc; | ||
private unsubscribers; | ||
awareness: Awareness; | ||
constructor(room: Room<P, S, U, E>, doc: Y.Doc, config?: LiveblocksYjsOptions); | ||
private _synced; | ||
constructor(room: Room<P, S, U, E>, doc: Y.Doc); | ||
private syncDoc; | ||
get synced(): boolean; | ||
set synced(state: boolean); | ||
private updateHandler; | ||
private resyncHttp; | ||
destroy(): void; | ||
disconnect(): void; | ||
connect(): void; | ||
} | ||
export { Awareness, LiveblocksProvider as default }; |
@@ -1,273 +0,2 @@ | ||
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }var __defProp = Object.defineProperty; | ||
var __getOwnPropSymbols = Object.getOwnPropertySymbols; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __propIsEnum = Object.prototype.propertyIsEnumerable; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __spreadValues = (a, b) => { | ||
for (var prop in b || (b = {})) | ||
if (__hasOwnProp.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
if (__getOwnPropSymbols) | ||
for (var prop of __getOwnPropSymbols(b)) { | ||
if (__propIsEnum.call(b, prop)) | ||
__defNormalProp(a, prop, b[prop]); | ||
} | ||
return a; | ||
}; | ||
var __async = (__this, __arguments, generator) => { | ||
return new Promise((resolve, reject) => { | ||
var fulfilled = (value) => { | ||
try { | ||
step(generator.next(value)); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
}; | ||
var rejected = (value) => { | ||
try { | ||
step(generator.throw(value)); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
}; | ||
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); | ||
step((generator = generator.apply(__this, __arguments)).next()); | ||
}); | ||
}; | ||
// src/index.ts | ||
var _jsbase64 = require('js-base64'); | ||
// ../../node_modules/lib0/map.js | ||
var create = () => /* @__PURE__ */ new Map(); | ||
var setIfUndefined = (map, key, createT) => { | ||
let set = map.get(key); | ||
if (set === void 0) { | ||
map.set(key, set = createT()); | ||
} | ||
return set; | ||
}; | ||
// ../../node_modules/lib0/set.js | ||
var create2 = () => /* @__PURE__ */ new Set(); | ||
// ../../node_modules/lib0/array.js | ||
var from = Array.from; | ||
var isArray = Array.isArray; | ||
// ../../node_modules/lib0/observable.js | ||
var Observable = class { | ||
constructor() { | ||
this._observers = create(); | ||
} | ||
/** | ||
* @param {N} name | ||
* @param {function} f | ||
*/ | ||
on(name, f) { | ||
setIfUndefined(this._observers, name, create2).add(f); | ||
} | ||
/** | ||
* @param {N} name | ||
* @param {function} f | ||
*/ | ||
once(name, f) { | ||
const _f = (...args) => { | ||
this.off(name, _f); | ||
f(...args); | ||
}; | ||
this.on(name, _f); | ||
} | ||
/** | ||
* @param {N} name | ||
* @param {function} f | ||
*/ | ||
off(name, f) { | ||
const observers = this._observers.get(name); | ||
if (observers !== void 0) { | ||
observers.delete(f); | ||
if (observers.size === 0) { | ||
this._observers.delete(name); | ||
} | ||
} | ||
} | ||
/** | ||
* Emit a named event. All registered event listeners that listen to the | ||
* specified name will receive the event. | ||
* | ||
* @todo This should catch exceptions | ||
* | ||
* @param {N} name The event name. | ||
* @param {Array<any>} args The arguments that are applied to the event listener. | ||
*/ | ||
emit(name, args) { | ||
return from((this._observers.get(name) || create()).values()).forEach((f) => f(...args)); | ||
} | ||
destroy() { | ||
this._observers = create(); | ||
} | ||
}; | ||
// src/index.ts | ||
var _yjs = require('yjs'); var Y = _interopRequireWildcard(_yjs); | ||
var Awareness = class extends Observable { | ||
constructor(doc, room) { | ||
super(); | ||
this.states = /* @__PURE__ */ new Map(); | ||
// Meta is used to keep track and timeout users who disconnect. Liveblocks provides this for us, so we don't need to | ||
// manage it here. Unfortunately, it's expected to exist by various integrations, so it's an empty map. | ||
this.meta = /* @__PURE__ */ new Map(); | ||
// _checkInterval this would hold a timer to remove users, but Liveblock's presence already handles this | ||
// unfortunately it's expected to exist by various integrations. | ||
this._checkInterval = 0; | ||
this.doc = doc; | ||
this.room = room; | ||
this.clientID = doc.clientID; | ||
this.othersUnsub = this.room.events.others.subscribe(({ event }) => { | ||
if (event.type === "leave") { | ||
this.emit("change", [ | ||
{ added: [], updated: [], removed: [event.user.connectionId] }, | ||
"local" | ||
]); | ||
} | ||
if (event.type === "enter") { | ||
this.emit("change", [ | ||
{ added: [event.user.connectionId], updated: [], removed: [] }, | ||
"local" | ||
]); | ||
} | ||
if (event.type === "update") { | ||
this.emit("change", [ | ||
{ added: [], updated: [event.user.connectionId], removed: [] }, | ||
"local" | ||
]); | ||
} | ||
}); | ||
} | ||
destroy() { | ||
this.emit("destroy", [this]); | ||
this.othersUnsub(); | ||
this.setLocalState(null); | ||
super.destroy(); | ||
} | ||
getLocalState() { | ||
const presence = this.room.getPresence(); | ||
if (Object.keys(this.room.getPresence()).length === 0) { | ||
return null; | ||
} | ||
return presence["__yjs"]; | ||
} | ||
setLocalState(state) { | ||
var _a; | ||
const presence = (_a = this.room.getSelf()) == null ? void 0 : _a.presence["__yjs"]; | ||
this.room.updatePresence({ | ||
__yjs: __spreadValues(__spreadValues({}, presence || {}), state || {}) | ||
}); | ||
} | ||
setLocalStateField(field, value) { | ||
var _a; | ||
const presence = (_a = this.room.getSelf()) == null ? void 0 : _a.presence["__yjs"]; | ||
const update = { [field]: value }; | ||
this.room.updatePresence({ | ||
__yjs: __spreadValues(__spreadValues({}, presence || {}), update) | ||
}); | ||
} | ||
// Translate liveblocks presence to yjs awareness | ||
getStates() { | ||
const others = this.room.getOthers(); | ||
const states = others.reduce((acc, currentValue) => { | ||
if (currentValue.connectionId) { | ||
acc.set( | ||
currentValue.connectionId, | ||
currentValue.presence["__yjs"] || {} | ||
); | ||
} | ||
return acc; | ||
}, /* @__PURE__ */ new Map()); | ||
return states; | ||
} | ||
}; | ||
var LiveblocksProvider = class { | ||
constructor(room, doc, config) { | ||
this.lastUpdateDate = null; | ||
this.unsubscribers = []; | ||
this.syncDoc = () => { | ||
var _a; | ||
this.doc.clientID = ((_a = this.room.getSelf()) == null ? void 0 : _a.connectionId) || this.doc.clientID; | ||
this.awareness.clientID = this.doc.clientID; | ||
const encodedVector = _jsbase64.Base64.fromUint8Array(Y.encodeStateVector(this.doc)); | ||
this.room.getYDoc(encodedVector); | ||
}; | ||
this.updateHandler = (update, origin) => __async(this, null, function* () { | ||
if (origin !== "backend") { | ||
const encodedUpdate = _jsbase64.Base64.fromUint8Array(update); | ||
this.room.updateYDoc(encodedUpdate); | ||
if (this.httpEndpoint) { | ||
yield fetch(this.httpEndpoint, { | ||
method: "POST", | ||
body: encodedUpdate | ||
}); | ||
} | ||
} | ||
}); | ||
var _a; | ||
this.doc = doc; | ||
this.room = room; | ||
const connectionId = (_a = this.room.getSelf()) == null ? void 0 : _a.connectionId; | ||
if (connectionId) { | ||
this.doc.clientID = connectionId; | ||
} | ||
this.awareness = new Awareness(this.doc, this.room); | ||
this.doc.on("update", this.updateHandler); | ||
this.unsubscribers.push( | ||
this.room.events.connection.subscribe((e) => { | ||
if (e === "open") { | ||
this.syncDoc(); | ||
} | ||
}) | ||
); | ||
this.unsubscribers.push( | ||
this.room.events.docUpdated.subscribe((update) => { | ||
Y.applyUpdate(this.doc, _jsbase64.Base64.toUint8Array(update), "backend"); | ||
}) | ||
); | ||
if (config == null ? void 0 : config.httpEndpoint) { | ||
this.httpEndpoint = config.httpEndpoint + "?room=" + this.room.id; | ||
this.unsubscribers.push( | ||
this.room.events.customEvent.subscribe(({ event }) => { | ||
if ((event == null ? void 0 : event.type) === "REFRESH") { | ||
void this.resyncHttp(); | ||
} | ||
}) | ||
); | ||
void this.resyncHttp(); | ||
} | ||
this.syncDoc(); | ||
} | ||
resyncHttp() { | ||
return __async(this, null, function* () { | ||
if (!this.httpEndpoint) { | ||
return; | ||
} | ||
const response = yield fetch( | ||
`${this.httpEndpoint}${this.lastUpdateDate !== null ? `&after=${this.lastUpdateDate.toISOString()}` : ""}` | ||
); | ||
const { updates, lastUpdate } = yield response.json(); | ||
if (updates.length === 0) { | ||
return; | ||
} | ||
this.lastUpdateDate = new Date(lastUpdate); | ||
const update = Y.mergeUpdates(updates.map(_jsbase64.Base64.toUint8Array)); | ||
Y.applyUpdate(this.doc, update, "backend"); | ||
}); | ||
} | ||
destroy() { | ||
this.doc.off("update", this.updateHandler); | ||
this.unsubscribers.forEach((unsub) => unsub()); | ||
this.awareness.destroy(); | ||
} | ||
}; | ||
exports.Awareness = Awareness; exports.default = LiveblocksProvider; | ||
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }var g=Object.defineProperty;var m=Object.getOwnPropertySymbols;var _=Object.prototype.hasOwnProperty,j=Object.prototype.propertyIsEnumerable;var b=(n,s,e)=>s in n?g(n,s,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[s]=e,i=(n,s)=>{for(var e in s||(s={}))_.call(s,e)&&b(n,e,s[e]);if(m)for(var e of m(s))j.call(s,e)&&b(n,e,s[e]);return n};var _jsbase64 = require('js-base64');var p=()=>new Map;var y=(n,s,e)=>{let t=n.get(s);return t===void 0&&n.set(s,t=e()),t};var x=()=>new Set;var v=Array.from;var D=Array.isArray;var a=class{constructor(){this._observers=p()}on(s,e){y(this._observers,s,x).add(e)}once(s,e){let t=(...o)=>{this.off(s,t),e(...o)};this.on(s,t)}off(s,e){let t=this._observers.get(s);t!==void 0&&(t.delete(e),t.size===0&&this._observers.delete(s))}emit(s,e){return v((this._observers.get(s)||p()).values()).forEach(t=>t(...e))}destroy(){this._observers=p()}};var _yjs = require('yjs'); var u = _interopRequireWildcard(_yjs);var d="__yjs",h= exports.Awareness =class extends a{constructor(e,t){super();this.states=new Map;this.meta=new Map;this._checkInterval=0;this.doc=e,this.room=t,this.clientID=e.clientID,this.othersUnsub=this.room.events.others.subscribe(({event:o})=>{o.type==="leave"&&this.emit("change",[{added:[],updated:[],removed:[o.user.connectionId]},"local"]),o.type==="enter"&&this.emit("change",[{added:[o.user.connectionId],updated:[],removed:[]},"local"]),o.type==="update"&&this.emit("change",[{added:[],updated:[o.user.connectionId],removed:[]},"local"])})}destroy(){this.emit("destroy",[this]),this.othersUnsub(),this.setLocalState(null),super.destroy()}getLocalState(){let e=this.room.getPresence();return Object.keys(e).length===0||typeof e[d]=="undefined"?null:e[d]}setLocalState(e){var o;let t=(o=this.room.getSelf())==null?void 0:o.presence[d];this.room.updatePresence({__yjs:i(i({},t||{}),e||{})})}setLocalStateField(e,t){var c;let o=(c=this.room.getSelf())==null?void 0:c.presence[d],r={[e]:t};this.room.updatePresence({__yjs:i(i({},o||{}),r)})}getStates(){return this.room.getOthers().reduce((o,r)=>(r.connectionId&&o.set(r.connectionId,r.presence[d]||{}),o),new Map)}},f= exports.default =class extends a{constructor(e,t){var r;super();this.unsubscribers=[];this._synced=!1;this.syncDoc=()=>{var t;this.synced=!1,this.doc.clientID=((t=this.room.getSelf())==null?void 0:t.connectionId)||this.doc.clientID,this.awareness.clientID=this.doc.clientID;let e=_jsbase64.Base64.fromUint8Array(u.encodeStateVector(this.doc));this.room.fetchYDoc(e)};this.updateHandler=(e,t)=>{if(t!=="backend"){let o=_jsbase64.Base64.fromUint8Array(e);this.room.updateYDoc(o)}};this.doc=t,this.room=e;let o=(r=this.room.getSelf())==null?void 0:r.connectionId;o&&(this.doc.clientID=o),this.awareness=new h(this.doc,this.room),this.doc.on("update",this.updateHandler),this.unsubscribers.push(this.room.events.status.subscribe(c=>{c==="connected"&&this.syncDoc()})),this.unsubscribers.push(this.room.events.ydoc.subscribe(c=>{u.applyUpdate(this.doc,_jsbase64.Base64.toUint8Array(c),"backend"),this.synced=!0})),this.syncDoc()}get synced(){return this._synced}set synced(e){this._synced!==e&&(this._synced=e,this.emit("synced",[e]),this.emit("sync",[e]))}destroy(){this.doc.off("update",this.updateHandler),this.unsubscribers.forEach(e=>e()),this.awareness.destroy()}disconnect(){}connect(){}};exports.Awareness = h; exports.default = f; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@liveblocks/yjs", | ||
"version": "1.1.0-yjs5", | ||
"version": "1.1.1-dual1", | ||
"description": "An integration with . Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.", | ||
@@ -8,2 +8,15 @@ "license": "Apache-2.0", | ||
"types": "./dist/index.d.ts", | ||
"exports": { | ||
".": { | ||
"import": { | ||
"types": "./dist/index.d.mts", | ||
"default": "./dist/index.mjs" | ||
}, | ||
"require": { | ||
"types": "./dist/index.d.ts", | ||
"module": "./dist/index.mjs", | ||
"default": "./dist/index.js" | ||
} | ||
} | ||
}, | ||
"files": [ | ||
@@ -15,5 +28,6 @@ "dist/**", | ||
"dev": "tsup --watch", | ||
"build": "tsup --format cjs,esm --dts --clean", | ||
"build": "tsup && cp dist/index.d.ts dist/index.d.mts", | ||
"format": "eslint --fix src/; prettier --write src/", | ||
"lint": "eslint src/", | ||
"lint:package": "publint --strict && attw --pack", | ||
"test": "jest --silent --verbose --color=always", | ||
@@ -23,12 +37,5 @@ "test:types": "tsd", | ||
}, | ||
"exports": { | ||
".": { | ||
"require": "./dist/index.js", | ||
"import": "./dist/index.mjs", | ||
"types": "./dist/index.d.ts" | ||
} | ||
}, | ||
"dependencies": { | ||
"@liveblocks/client": "1.1.0-yjs5", | ||
"@liveblocks/core": "1.1.0-yjs5", | ||
"@liveblocks/client": "1.1.1-dual1", | ||
"@liveblocks/core": "1.1.1-dual1", | ||
"js-base64": "^3.7.5" | ||
@@ -35,0 +42,0 @@ }, |
@@ -12,3 +12,3 @@ <p align="center"> | ||
Provides YJS integration to effortlessly back your YJS apps with Liveblocks | ||
Provides Yjs integration to effortlessly back your Yjs apps with Liveblocks | ||
@@ -15,0 +15,0 @@ ## Installation |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
52117
8
1
71
2
+ Added@liveblocks/client@1.1.1-dual1(transitive)
+ Added@liveblocks/core@1.1.1-dual1(transitive)
- Removed@liveblocks/client@1.1.0-yjs5(transitive)
- Removed@liveblocks/core@1.1.0-yjs5(transitive)
Updated@liveblocks/core@1.1.1-dual1