UNPKG

rhine-var

Version:

Variables that support multi-user collaboration and persistence, making collaboration and variable operations as simple as possible, with strict and well-defined type hints.

509 lines (508 loc) 20 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RHINE_VAR_PREDEFINED_PROPERTIES = void 0; const awareness_1 = require("y-protocols/awareness"); const yjs_1 = require("yjs"); const config_1 = __importDefault(require("../../config/config")); const rhine_proxy_1 = require("../proxy/rhine-proxy"); const event_type_enum_1 = require("../subscriber/event-type.enum"); const data_utils_1 = require("../utils/data.utils"); const native_utils_1 = require("../utils/native.utils"); const index_1 = require("../../index"); const logger_1 = require("../../utils/logger"); class RhineVarBase { constructor(_native, _parent = null, _origin = this) { this._native = _native; this._parent = _parent; this._origin = _origin; this._options = {}; this._connector = null; this._undoManager = null; this._awareness = null; this._clientId = -1; this.syncedSubscribers = []; this.subscribers = []; this.keySubscribers = new Map(); this.deepSubscribers = []; this.observer = (event, transaction) => { }; this.syncedObserver = (synced) => { }; this.observe(); } isRoot() { return Boolean(!this._parent); } getParent() { return this._parent; } getNative() { return this._native; } getRoot() { if (this.isRoot()) { return this; } else { return this._parent.getRoot(); } } getOptions() { return this.getRoot()._options; } getConnector() { return this.getRoot()._connector; } getUndoManager() { if (this.getOptions().awareness !== undefined && !this.getOptions().awareness) { (0, logger_1.error)('You need to enable awareness to use undoManager'); return null; } return this.getRoot()._undoManager; } getAwareness() { if (this.getOptions().awareness !== undefined && !this.getOptions().awareness) { (0, logger_1.error)('You need to enable awareness to use awareness'); return null; } return this.getRoot()._awareness; } getClientId() { if (this.getOptions().awareness !== undefined && !this.getOptions().awareness) { (0, logger_1.error)('You need to enable awareness to use clientId'); return -1; } return this.getRoot()._clientId; } transact(fn, origin) { const doc = this._native.doc; if (!doc) { throw new Error('[RhineVar] Failed to execute transact: Doc not found.'); } return doc.transact(fn, origin); } _initialize(native) { var _a; // initialize function will call after every synced if (config_1.default.ENABLE_ERROR) { (0, logger_1.log)('Synced initialize:', this.json(), native.toJSON()); } const recursiveKeys = []; if (this._native instanceof index_1.YMap || this._native instanceof index_1.YArray) { this._native.forEach((value, key) => { if ((0, native_utils_1.nativeHas)(native, key)) { recursiveKeys.push(key); } else { Reflect.deleteProperty(this._origin, key); } }); } this.unobserve(); this._native = native; if (this.isRoot()) { if (this._options.undoManager === undefined || this._options.undoManager) { if (!native) { (0, logger_1.error)('Base map is not available for undoManager'); } else { this._undoManager = new yjs_1.UndoManager(native, (0, data_utils_1.isObject)(this._options.undoManager) ? this._options.undoManager : undefined); } } if (this._options.awareness === undefined || this._options.awareness) { const doc = (_a = this._connector) === null || _a === void 0 ? void 0 : _a.yDoc; if (!doc) { (0, logger_1.error)('YDoc is not available for awareness'); } else { this._awareness = new awareness_1.Awareness(doc); this._clientId = this._awareness.clientID; } } } this.observe(); if (this._native instanceof index_1.YMap || this._native instanceof index_1.YArray) { this._native.forEach((value, key) => { if (recursiveKeys.includes(key)) { const child = Reflect.get(this, key); if (child instanceof RhineVarBase) { child._initialize(value); } else { Reflect.set(this._origin, key, value); } return; } if ((0, native_utils_1.isNative)(value)) { Reflect.set(this._origin, key, (0, rhine_proxy_1.rhineProxyGeneral)(value, this)); } else { Reflect.set(this._origin, key, value); } }); } } afterSynced(callback) { var _a; const connector = (_a = this.getRoot()) === null || _a === void 0 ? void 0 : _a._connector; if (connector) { connector.afterSynced(callback); } } async waitSynced() { return new Promise((resolve) => { this.afterSynced(resolve); }); } json() { return this._removeBuiltInProperty(this._native.toJSON()); } _removeBuiltInProperty(obj) { if (obj === null || typeof obj !== 'object') { return obj; } if (obj instanceof Date || obj instanceof RegExp) { return obj; } const builtInProperties = ['_type', '_class']; if (Array.isArray(obj)) { return obj.map((item) => this._removeBuiltInProperty(item)); } if (typeof obj === 'object') { const result = {}; for (const key in obj) { if (builtInProperties.includes(key)) continue; if (obj.hasOwnProperty(key)) { result[key] = this._removeBuiltInProperty(obj[key]); } } return result; } return obj; } frozenJson() { const origin = this._origin; if (this._native instanceof index_1.YMap) { const result = {}; for (const key in origin) { if (!exports.RHINE_VAR_PREDEFINED_PROPERTIES.has(key) && typeof origin[key] !== 'function' && this.hasOwnProperty(key)) { let value = origin[key]; if (value instanceof RhineVarBase) { value = value.frozenJson(); } if (!isNaN(Number(key))) { result[Number(key)] = value; } else { result[key] = value; } } } return result; } else if (this._native instanceof index_1.YArray) { const result = []; for (let i = 0;; i++) { if (i in origin) { let value = origin[i]; if (value instanceof RhineVarBase) { value = value.frozenJson(); } result.push(value); } else { break; } } return result; } return {}; } jsonString(indent = 2) { return JSON.stringify(this.json(), null, indent); } subscribeSynced(callback) { this.syncedSubscribers.push(callback); return () => { this.unsubscribeSynced(callback); }; } unsubscribeSynced(callback) { this.syncedSubscribers = this.syncedSubscribers.filter((subscriber) => subscriber !== callback); } unsubscribeAllSynced() { this.syncedSubscribers = []; } emitSynced(synced) { this.syncedSubscribers.forEach((subscriber) => { subscriber(synced); }); } subscribe(subscriber) { this.subscribers.push(subscriber); return () => { this.unsubscribe(subscriber); }; } unsubscribe(subscriber) { this.subscribers = this.subscribers.filter((s) => s !== subscriber); } unsubscribeAll() { this.subscribers = []; } subscribeKey(key, subscriber) { if (!this.keySubscribers.has(key)) { this.keySubscribers.set(key, []); } this.keySubscribers.get(key).push(subscriber); return () => { this.unsubscribeKey(subscriber); }; } unsubscribeKey(subscriber) { this.keySubscribers.forEach((subscribers, key) => { this.keySubscribers.set(key, subscribers.filter((s) => s !== subscriber)); }); } unsubscribeAllKey() { this.keySubscribers = new Map(); } emit(type, key, value, oldValue, nativeEvent, nativeTransaction) { this.subscribers.forEach((subscriber) => { subscriber(type, key, value, oldValue, nativeEvent, nativeTransaction); }); if (this.keySubscribers.has(key)) { this.keySubscribers.get(key).forEach((subscriber) => { subscriber(type, value, oldValue, nativeEvent, nativeTransaction); }); } } subscribeDeep(subscriber) { this.deepSubscribers.push(subscriber); return () => { this.unsubscribeDeep(subscriber); }; } unsubscribeDeep(subscriber) { this.deepSubscribers = this.deepSubscribers.filter((s) => s !== subscriber); } unsubscribeAllDeep() { this.deepSubscribers = []; } emitDeep(type, path, value, oldValue, nativeEvent, nativeTransaction) { this.deepSubscribers.forEach((subscriber) => { subscriber(type, path, value, oldValue, nativeEvent, nativeTransaction); }); if (this._parent) { const key = (0, native_utils_1.getKeyFromParent)(this._native); if (key !== undefined) { this._parent.emitDeep(type, [key, ...path], value, oldValue, nativeEvent, nativeTransaction); } } } observe() { const connector = this.getConnector(); if (connector) { this.syncedObserver = (synced) => { this.emitSynced(synced); }; connector.subscribeSynced(this.syncedObserver); this.emitSynced(connector.synced); } const target = this._native; if (target instanceof index_1.YMap) { this.observer = (event, transaction) => { event.changes.keys.forEach(({ action, oldValue }, key) => { const type = action === 'add' ? event_type_enum_1.EventType.ADD : action === 'delete' ? event_type_enum_1.EventType.DELETE : event_type_enum_1.EventType.UPDATE; if ((0, data_utils_1.isObjectOrArray)(oldValue)) { oldValue = Reflect.get(this, key); if (oldValue instanceof RhineVarBase) { oldValue = oldValue.frozenJson(); } } let value = undefined; if (type === event_type_enum_1.EventType.ADD || type === event_type_enum_1.EventType.UPDATE) { value = target.get(key); if ((0, native_utils_1.isNative)(value)) { Reflect.set(this._origin, key, (0, rhine_proxy_1.rhineProxyGeneral)(value, this)); } else { Reflect.set(this._origin, key, value); } } else if (type === event_type_enum_1.EventType.DELETE) { Reflect.deleteProperty(this._origin, key); } const newValue = key in this ? Reflect.get(this, key) : value; (0, logger_1.log)('Proxy.event: Map', action, key + ':', oldValue, '->', newValue); this.emit(type, key, newValue, oldValue, event, transaction); this.emitDeep(type, [key], newValue, oldValue, event, transaction); }); }; } else if (target instanceof index_1.YArray) { this.observer = (event, transaction) => { let i = 0; event.delta.forEach((deltaItem) => { if (deltaItem.retain !== undefined) { i += deltaItem.retain; } else if (deltaItem.delete !== undefined) { for (let j = 0; j < deltaItem.delete; j++) { let oldValue = i in this ? Reflect.get(this, i) : target.get(i); if (oldValue instanceof RhineVarBase) { oldValue = oldValue.frozenJson(); } Reflect.deleteProperty(this._origin, i); for (let k = i + 1; k < target.length + deltaItem.delete; k++) { const value = Reflect.get(this, k); Reflect.set(this._origin, k - 1, value); Reflect.deleteProperty(this._origin, k); } (0, logger_1.log)('Proxy.event: Array delete', i + ':', oldValue, '->', undefined); this.emit(event_type_enum_1.EventType.DELETE, i, undefined, oldValue, event, transaction); this.emitDeep(event_type_enum_1.EventType.DELETE, [i], undefined, oldValue, event, transaction); i++; } } else if (deltaItem.insert !== undefined && Array.isArray(deltaItem.insert)) { deltaItem.insert.forEach((value) => { for (let k = target.length - 1; k >= i; k--) { const existingValue = Reflect.get(this, k); Reflect.set(this._origin, k + 1, existingValue); } if ((0, data_utils_1.isObjectOrArray)(value)) { Reflect.set(this._origin, i, (0, rhine_proxy_1.rhineProxyGeneral)(value, this)); } else { Reflect.set(this._origin, i, value); } const newValue = i in this ? Reflect.get(this, i) : target.get(i); (0, logger_1.log)('Proxy.event: Array add', i, ':', undefined, '->', newValue); this.emit(event_type_enum_1.EventType.ADD, i, newValue, undefined, event, transaction); this.emitDeep(event_type_enum_1.EventType.ADD, [i], newValue, undefined, event, transaction); i++; }); } }); }; } else if (target instanceof index_1.YText) { this.observer = (event, transaction) => { let hasDelete = false; let hasInsert = false; event.delta.forEach((deltaItem) => { if (deltaItem.delete !== undefined) { hasDelete = true; } else if (deltaItem.insert !== undefined) { hasInsert = true; } }); const isUpdate = hasDelete && hasInsert; const oldValue = Reflect.get(this, 'value'); const newValue = this._native.toString(); Reflect.set(this._origin, 'value', newValue); let i = 0; if (isUpdate) { (0, logger_1.log)('Proxy.event: Text update', ':', oldValue, '->', newValue); this.emit(event_type_enum_1.EventType.UPDATE, i, newValue, oldValue, event, transaction); this.emitDeep(event_type_enum_1.EventType.UPDATE, [i], newValue, oldValue, event, transaction); } else { event.delta.forEach((deltaItem) => { if (deltaItem.retain !== undefined) { i += deltaItem.retain; return; } if (deltaItem.delete !== undefined) { (0, logger_1.log)('Proxy.event: Text delete', i, ':', oldValue, '->', newValue); this.emit(event_type_enum_1.EventType.DELETE, i, newValue, oldValue, event, transaction); this.emitDeep(event_type_enum_1.EventType.DELETE, [i], newValue, oldValue, event, transaction); i += deltaItem.delete; } else if (deltaItem.insert !== undefined) { (0, logger_1.log)('Proxy.event: Text add', i, ':', oldValue, '->', newValue); this.emit(event_type_enum_1.EventType.ADD, i, newValue, oldValue, event, transaction); this.emitDeep(event_type_enum_1.EventType.ADD, [i], newValue, oldValue, event, transaction); i += newValue.length; } }); } }; } else { this.observer = (event, transaction) => { this.emit(event_type_enum_1.EventType.UPDATE, undefined, undefined, undefined, event, transaction); this.emitDeep(event_type_enum_1.EventType.UPDATE, undefined, undefined, undefined, event, transaction); }; } if (this.observer) { target.observe(this.observer); } } unobserve() { var _a; if (this.observer) { this._native.unobserve(this.observer); } if (this.syncedObserver) { (_a = this.getConnector()) === null || _a === void 0 ? void 0 : _a.unsubscribeSynced(this.syncedObserver); } } } exports.default = RhineVarBase; exports.RHINE_VAR_PREDEFINED_PROPERTIES = new Set([ '_origin', '_class', '_type', '_initialize', '_native', '_parent', 'json', 'jsonString', 'getParent', 'isRoot', 'getRoot', 'getNative', 'transact', '_options', '_connector', '_undoManager', '_awareness', '_clientId', 'getOptions', 'getConnector', 'getUndoManager', 'getAwareness', 'getClientId', 'afterSynced', 'waitSynced', 'syncedSubscribers', 'subscribeSynced', 'unsubscribeSynced', 'unsubscribeAllSynced', 'subscribers', 'subscribe', 'unsubscribe', 'unsubscribeAll', 'keySubscribers', 'subscribeKey', 'unsubscribeKey', 'unsubscribeAllKey', 'deepSubscribers', 'subscribeDeep', 'unsubscribeDeep', 'unsubscribeAllDeep', 'emitSynced', 'emit', 'emitDeep', 'observer', 'syncedObserver', 'observe', 'unobserve', ]);