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
JavaScript
"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',
]);