19456 lines
612 KiB
JavaScript
19456 lines
612 KiB
JavaScript
"use strict";
|
|
var wp;
|
|
(wp ||= {}).coreData = (() => {
|
|
var __create = Object.create;
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __getProtoOf = Object.getPrototypeOf;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __commonJS = (cb, mod) => function __require() {
|
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
};
|
|
var __export = (target, all2) => {
|
|
for (var name in all2)
|
|
__defProp(target, name, { get: all2[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from2, except, desc) => {
|
|
if (from2 && typeof from2 === "object" || typeof from2 === "function") {
|
|
for (let key of __getOwnPropNames(from2))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from2[key], enumerable: !(desc = __getOwnPropDesc(from2, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
// If the importer is in node compatibility mode or this is not an ESM
|
|
// file that has been converted to a CommonJS file using a Babel-
|
|
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
mod
|
|
));
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
|
|
// package-external:@wordpress/data
|
|
var require_data = __commonJS({
|
|
"package-external:@wordpress/data"(exports, module) {
|
|
module.exports = window.wp.data;
|
|
}
|
|
});
|
|
|
|
// node_modules/fast-deep-equal/es6/index.js
|
|
var require_es6 = __commonJS({
|
|
"node_modules/fast-deep-equal/es6/index.js"(exports, module) {
|
|
"use strict";
|
|
module.exports = function equal(a, b) {
|
|
if (a === b) return true;
|
|
if (a && b && typeof a == "object" && typeof b == "object") {
|
|
if (a.constructor !== b.constructor) return false;
|
|
var length3, i, keys2;
|
|
if (Array.isArray(a)) {
|
|
length3 = a.length;
|
|
if (length3 != b.length) return false;
|
|
for (i = length3; i-- !== 0; )
|
|
if (!equal(a[i], b[i])) return false;
|
|
return true;
|
|
}
|
|
if (a instanceof Map && b instanceof Map) {
|
|
if (a.size !== b.size) return false;
|
|
for (i of a.entries())
|
|
if (!b.has(i[0])) return false;
|
|
for (i of a.entries())
|
|
if (!equal(i[1], b.get(i[0]))) return false;
|
|
return true;
|
|
}
|
|
if (a instanceof Set && b instanceof Set) {
|
|
if (a.size !== b.size) return false;
|
|
for (i of a.entries())
|
|
if (!b.has(i[0])) return false;
|
|
return true;
|
|
}
|
|
if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
|
|
length3 = a.length;
|
|
if (length3 != b.length) return false;
|
|
for (i = length3; i-- !== 0; )
|
|
if (a[i] !== b[i]) return false;
|
|
return true;
|
|
}
|
|
if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
|
|
if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
|
|
if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
|
|
keys2 = Object.keys(a);
|
|
length3 = keys2.length;
|
|
if (length3 !== Object.keys(b).length) return false;
|
|
for (i = length3; i-- !== 0; )
|
|
if (!Object.prototype.hasOwnProperty.call(b, keys2[i])) return false;
|
|
for (i = length3; i-- !== 0; ) {
|
|
var key = keys2[i];
|
|
if (!equal(a[key], b[key])) return false;
|
|
}
|
|
return true;
|
|
}
|
|
return a !== a && b !== b;
|
|
};
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/compose
|
|
var require_compose = __commonJS({
|
|
"package-external:@wordpress/compose"(exports, module) {
|
|
module.exports = window.wp.compose;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/undo-manager
|
|
var require_undo_manager = __commonJS({
|
|
"package-external:@wordpress/undo-manager"(exports, module) {
|
|
module.exports = window.wp.undoManager;
|
|
}
|
|
});
|
|
|
|
// node_modules/equivalent-key-map/equivalent-key-map.js
|
|
var require_equivalent_key_map = __commonJS({
|
|
"node_modules/equivalent-key-map/equivalent-key-map.js"(exports, module) {
|
|
"use strict";
|
|
function _typeof(obj) {
|
|
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
|
|
_typeof = function(obj2) {
|
|
return typeof obj2;
|
|
};
|
|
} else {
|
|
_typeof = function(obj2) {
|
|
return obj2 && typeof Symbol === "function" && obj2.constructor === Symbol && obj2 !== Symbol.prototype ? "symbol" : typeof obj2;
|
|
};
|
|
}
|
|
return _typeof(obj);
|
|
}
|
|
function _classCallCheck(instance, Constructor) {
|
|
if (!(instance instanceof Constructor)) {
|
|
throw new TypeError("Cannot call a class as a function");
|
|
}
|
|
}
|
|
function _defineProperties(target, props) {
|
|
for (var i = 0; i < props.length; i++) {
|
|
var descriptor = props[i];
|
|
descriptor.enumerable = descriptor.enumerable || false;
|
|
descriptor.configurable = true;
|
|
if ("value" in descriptor) descriptor.writable = true;
|
|
Object.defineProperty(target, descriptor.key, descriptor);
|
|
}
|
|
}
|
|
function _createClass(Constructor, protoProps, staticProps) {
|
|
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
return Constructor;
|
|
}
|
|
function getValuePair(instance, key) {
|
|
var _map = instance._map, _arrayTreeMap = instance._arrayTreeMap, _objectTreeMap = instance._objectTreeMap;
|
|
if (_map.has(key)) {
|
|
return _map.get(key);
|
|
}
|
|
var properties = Object.keys(key).sort();
|
|
var map2 = Array.isArray(key) ? _arrayTreeMap : _objectTreeMap;
|
|
for (var i = 0; i < properties.length; i++) {
|
|
var property = properties[i];
|
|
map2 = map2.get(property);
|
|
if (map2 === void 0) {
|
|
return;
|
|
}
|
|
var propertyValue = key[property];
|
|
map2 = map2.get(propertyValue);
|
|
if (map2 === void 0) {
|
|
return;
|
|
}
|
|
}
|
|
var valuePair = map2.get("_ekm_value");
|
|
if (!valuePair) {
|
|
return;
|
|
}
|
|
_map.delete(valuePair[0]);
|
|
valuePair[0] = key;
|
|
map2.set("_ekm_value", valuePair);
|
|
_map.set(key, valuePair);
|
|
return valuePair;
|
|
}
|
|
var EquivalentKeyMap2 = /* @__PURE__ */ (function() {
|
|
function EquivalentKeyMap3(iterable) {
|
|
_classCallCheck(this, EquivalentKeyMap3);
|
|
this.clear();
|
|
if (iterable instanceof EquivalentKeyMap3) {
|
|
var iterablePairs = [];
|
|
iterable.forEach(function(value, key) {
|
|
iterablePairs.push([key, value]);
|
|
});
|
|
iterable = iterablePairs;
|
|
}
|
|
if (iterable != null) {
|
|
for (var i = 0; i < iterable.length; i++) {
|
|
this.set(iterable[i][0], iterable[i][1]);
|
|
}
|
|
}
|
|
}
|
|
_createClass(EquivalentKeyMap3, [{
|
|
key: "set",
|
|
/**
|
|
* Add or update an element with a specified key and value.
|
|
*
|
|
* @param {*} key The key of the element to add.
|
|
* @param {*} value The value of the element to add.
|
|
*
|
|
* @return {EquivalentKeyMap} Map instance.
|
|
*/
|
|
value: function set(key, value) {
|
|
if (key === null || _typeof(key) !== "object") {
|
|
this._map.set(key, value);
|
|
return this;
|
|
}
|
|
var properties = Object.keys(key).sort();
|
|
var valuePair = [key, value];
|
|
var map2 = Array.isArray(key) ? this._arrayTreeMap : this._objectTreeMap;
|
|
for (var i = 0; i < properties.length; i++) {
|
|
var property = properties[i];
|
|
if (!map2.has(property)) {
|
|
map2.set(property, new EquivalentKeyMap3());
|
|
}
|
|
map2 = map2.get(property);
|
|
var propertyValue = key[property];
|
|
if (!map2.has(propertyValue)) {
|
|
map2.set(propertyValue, new EquivalentKeyMap3());
|
|
}
|
|
map2 = map2.get(propertyValue);
|
|
}
|
|
var previousValuePair = map2.get("_ekm_value");
|
|
if (previousValuePair) {
|
|
this._map.delete(previousValuePair[0]);
|
|
}
|
|
map2.set("_ekm_value", valuePair);
|
|
this._map.set(key, valuePair);
|
|
return this;
|
|
}
|
|
/**
|
|
* Returns a specified element.
|
|
*
|
|
* @param {*} key The key of the element to return.
|
|
*
|
|
* @return {?*} The element associated with the specified key or undefined
|
|
* if the key can't be found.
|
|
*/
|
|
}, {
|
|
key: "get",
|
|
value: function get(key) {
|
|
if (key === null || _typeof(key) !== "object") {
|
|
return this._map.get(key);
|
|
}
|
|
var valuePair = getValuePair(this, key);
|
|
if (valuePair) {
|
|
return valuePair[1];
|
|
}
|
|
}
|
|
/**
|
|
* Returns a boolean indicating whether an element with the specified key
|
|
* exists or not.
|
|
*
|
|
* @param {*} key The key of the element to test for presence.
|
|
*
|
|
* @return {boolean} Whether an element with the specified key exists.
|
|
*/
|
|
}, {
|
|
key: "has",
|
|
value: function has(key) {
|
|
if (key === null || _typeof(key) !== "object") {
|
|
return this._map.has(key);
|
|
}
|
|
return getValuePair(this, key) !== void 0;
|
|
}
|
|
/**
|
|
* Removes the specified element.
|
|
*
|
|
* @param {*} key The key of the element to remove.
|
|
*
|
|
* @return {boolean} Returns true if an element existed and has been
|
|
* removed, or false if the element does not exist.
|
|
*/
|
|
}, {
|
|
key: "delete",
|
|
value: function _delete(key) {
|
|
if (!this.has(key)) {
|
|
return false;
|
|
}
|
|
this.set(key, void 0);
|
|
return true;
|
|
}
|
|
/**
|
|
* Executes a provided function once per each key/value pair, in insertion
|
|
* order.
|
|
*
|
|
* @param {Function} callback Function to execute for each element.
|
|
* @param {*} thisArg Value to use as `this` when executing
|
|
* `callback`.
|
|
*/
|
|
}, {
|
|
key: "forEach",
|
|
value: function forEach2(callback) {
|
|
var _this = this;
|
|
var thisArg = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : this;
|
|
this._map.forEach(function(value, key) {
|
|
if (key !== null && _typeof(key) === "object") {
|
|
value = value[1];
|
|
}
|
|
callback.call(thisArg, value, key, _this);
|
|
});
|
|
}
|
|
/**
|
|
* Removes all elements.
|
|
*/
|
|
}, {
|
|
key: "clear",
|
|
value: function clear() {
|
|
this._map = /* @__PURE__ */ new Map();
|
|
this._arrayTreeMap = /* @__PURE__ */ new Map();
|
|
this._objectTreeMap = /* @__PURE__ */ new Map();
|
|
}
|
|
}, {
|
|
key: "size",
|
|
get: function get() {
|
|
return this._map.size;
|
|
}
|
|
}]);
|
|
return EquivalentKeyMap3;
|
|
})();
|
|
module.exports = EquivalentKeyMap2;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/url
|
|
var require_url = __commonJS({
|
|
"package-external:@wordpress/url"(exports, module) {
|
|
module.exports = window.wp.url;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/api-fetch
|
|
var require_api_fetch = __commonJS({
|
|
"package-external:@wordpress/api-fetch"(exports, module) {
|
|
module.exports = window.wp.apiFetch;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/blocks
|
|
var require_blocks = __commonJS({
|
|
"package-external:@wordpress/blocks"(exports, module) {
|
|
module.exports = window.wp.blocks;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/i18n
|
|
var require_i18n = __commonJS({
|
|
"package-external:@wordpress/i18n"(exports, module) {
|
|
module.exports = window.wp.i18n;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/private-apis
|
|
var require_private_apis = __commonJS({
|
|
"package-external:@wordpress/private-apis"(exports, module) {
|
|
module.exports = window.wp.privateApis;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/hooks
|
|
var require_hooks = __commonJS({
|
|
"package-external:@wordpress/hooks"(exports, module) {
|
|
module.exports = window.wp.hooks;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/block-editor
|
|
var require_block_editor = __commonJS({
|
|
"package-external:@wordpress/block-editor"(exports, module) {
|
|
module.exports = window.wp.blockEditor;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/rich-text
|
|
var require_rich_text = __commonJS({
|
|
"package-external:@wordpress/rich-text"(exports, module) {
|
|
module.exports = window.wp.richText;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/deprecated
|
|
var require_deprecated = __commonJS({
|
|
"package-external:@wordpress/deprecated"(exports, module) {
|
|
module.exports = window.wp.deprecated;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/html-entities
|
|
var require_html_entities = __commonJS({
|
|
"package-external:@wordpress/html-entities"(exports, module) {
|
|
module.exports = window.wp.htmlEntities;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/element
|
|
var require_element = __commonJS({
|
|
"package-external:@wordpress/element"(exports, module) {
|
|
module.exports = window.wp.element;
|
|
}
|
|
});
|
|
|
|
// vendor-external:react/jsx-runtime
|
|
var require_jsx_runtime = __commonJS({
|
|
"vendor-external:react/jsx-runtime"(exports, module) {
|
|
module.exports = window.ReactJSXRuntime;
|
|
}
|
|
});
|
|
|
|
// package-external:@wordpress/warning
|
|
var require_warning = __commonJS({
|
|
"package-external:@wordpress/warning"(exports, module) {
|
|
module.exports = window.wp.warning;
|
|
}
|
|
});
|
|
|
|
// packages/core-data/build-module/index.mjs
|
|
var index_exports = {};
|
|
__export(index_exports, {
|
|
EntityProvider: () => EntityProvider,
|
|
__experimentalFetchLinkSuggestions: () => fetchLinkSuggestions,
|
|
__experimentalFetchUrlData: () => experimental_fetch_url_data_default,
|
|
__experimentalUseEntityRecord: () => __experimentalUseEntityRecord,
|
|
__experimentalUseEntityRecords: () => __experimentalUseEntityRecords,
|
|
__experimentalUseResourcePermissions: () => __experimentalUseResourcePermissions,
|
|
fetchBlockPatterns: () => fetchBlockPatterns,
|
|
privateApis: () => privateApis2,
|
|
store: () => store,
|
|
useEntityBlockEditor: () => useEntityBlockEditor,
|
|
useEntityId: () => useEntityId,
|
|
useEntityProp: () => useEntityProp,
|
|
useEntityRecord: () => useEntityRecord,
|
|
useEntityRecords: () => useEntityRecords,
|
|
useResourcePermissions: () => use_resource_permissions_default
|
|
});
|
|
var import_data16 = __toESM(require_data(), 1);
|
|
|
|
// packages/core-data/build-module/reducer.mjs
|
|
var import_es66 = __toESM(require_es6(), 1);
|
|
var import_compose2 = __toESM(require_compose(), 1);
|
|
var import_data8 = __toESM(require_data(), 1);
|
|
var import_undo_manager2 = __toESM(require_undo_manager(), 1);
|
|
|
|
// packages/core-data/build-module/utils/conservative-map-item.mjs
|
|
var import_es6 = __toESM(require_es6(), 1);
|
|
function conservativeMapItem(item, nextItem) {
|
|
if (!item) {
|
|
return nextItem;
|
|
}
|
|
let hasChanges = false;
|
|
const result = {};
|
|
for (const key in nextItem) {
|
|
if ((0, import_es6.default)(item[key], nextItem[key])) {
|
|
result[key] = item[key];
|
|
} else {
|
|
hasChanges = true;
|
|
result[key] = nextItem[key];
|
|
}
|
|
}
|
|
if (!hasChanges) {
|
|
return item;
|
|
}
|
|
for (const key in item) {
|
|
if (!result.hasOwnProperty(key)) {
|
|
result[key] = item[key];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/get-normalized-comma-separable.mjs
|
|
function getNormalizedCommaSeparable(value) {
|
|
if (typeof value === "string") {
|
|
return value.split(",");
|
|
} else if (Array.isArray(value)) {
|
|
return value;
|
|
}
|
|
return null;
|
|
}
|
|
var get_normalized_comma_separable_default = getNormalizedCommaSeparable;
|
|
|
|
// packages/core-data/build-module/utils/if-matching-action.mjs
|
|
var ifMatchingAction = (isMatch) => (reducer) => (state, action) => {
|
|
if (state === void 0 || isMatch(action)) {
|
|
return reducer(state, action);
|
|
}
|
|
return state;
|
|
};
|
|
var if_matching_action_default = ifMatchingAction;
|
|
|
|
// packages/core-data/build-module/utils/forward-resolver.mjs
|
|
var forwardResolver = (resolverName) => (...args2) => async ({ resolveSelect: resolveSelect2 }) => {
|
|
await resolveSelect2[resolverName](...args2);
|
|
};
|
|
var forward_resolver_default = forwardResolver;
|
|
|
|
// packages/core-data/build-module/utils/on-sub-key.mjs
|
|
var onSubKey = (actionProperty) => (reducer) => (state = {}, action) => {
|
|
const key = action[actionProperty];
|
|
if (key === void 0) {
|
|
return state;
|
|
}
|
|
const nextKeyState = reducer(state[key], action);
|
|
if (nextKeyState === state[key]) {
|
|
return state;
|
|
}
|
|
return {
|
|
...state,
|
|
[key]: nextKeyState
|
|
};
|
|
};
|
|
var on_sub_key_default = onSubKey;
|
|
|
|
// packages/core-data/build-module/utils/replace-action.mjs
|
|
var replaceAction = (replacer) => (reducer) => (state, action) => {
|
|
return reducer(state, replacer(action));
|
|
};
|
|
var replace_action_default = replaceAction;
|
|
|
|
// packages/core-data/build-module/utils/with-weak-map-cache.mjs
|
|
function withWeakMapCache(fn) {
|
|
const cache3 = /* @__PURE__ */ new WeakMap();
|
|
return (key) => {
|
|
let value;
|
|
if (cache3.has(key)) {
|
|
value = cache3.get(key);
|
|
} else {
|
|
value = fn(key);
|
|
if (key !== null && typeof key === "object") {
|
|
cache3.set(key, value);
|
|
}
|
|
}
|
|
return value;
|
|
};
|
|
}
|
|
var with_weak_map_cache_default = withWeakMapCache;
|
|
|
|
// packages/core-data/build-module/utils/is-raw-attribute.mjs
|
|
function isRawAttribute(entity2, attribute) {
|
|
return (entity2.rawAttributes || []).includes(attribute);
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/set-nested-value.mjs
|
|
function setNestedValue(object, path, value) {
|
|
if (!object || typeof object !== "object") {
|
|
return object;
|
|
}
|
|
const normalizedPath = Array.isArray(path) ? path : path.split(".");
|
|
normalizedPath.reduce((acc, key, idx) => {
|
|
if (acc[key] === void 0) {
|
|
if (Number.isInteger(normalizedPath[idx + 1])) {
|
|
acc[key] = [];
|
|
} else {
|
|
acc[key] = {};
|
|
}
|
|
}
|
|
if (idx === normalizedPath.length - 1) {
|
|
acc[key] = value;
|
|
}
|
|
return acc[key];
|
|
}, object);
|
|
return object;
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/get-nested-value.mjs
|
|
function getNestedValue(object, path, defaultValue) {
|
|
if (!object || typeof object !== "object" || typeof path !== "string" && !Array.isArray(path)) {
|
|
return object;
|
|
}
|
|
const normalizedPath = Array.isArray(path) ? path : path.split(".");
|
|
let value = object;
|
|
normalizedPath.forEach((fieldName) => {
|
|
value = value?.[fieldName];
|
|
});
|
|
return value !== void 0 ? value : defaultValue;
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/is-numeric-id.mjs
|
|
function isNumericID(id2) {
|
|
return /^\s*\d+\s*$/.test(id2);
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/user-permissions.mjs
|
|
var ALLOWED_RESOURCE_ACTIONS = [
|
|
"create",
|
|
"read",
|
|
"update",
|
|
"delete"
|
|
];
|
|
function getUserPermissionsFromAllowHeader(allowedMethods) {
|
|
const permissions = {};
|
|
if (!allowedMethods) {
|
|
return permissions;
|
|
}
|
|
const methods = {
|
|
create: "POST",
|
|
read: "GET",
|
|
update: "PUT",
|
|
delete: "DELETE"
|
|
};
|
|
for (const [actionName, methodName] of Object.entries(methods)) {
|
|
permissions[actionName] = allowedMethods.includes(methodName);
|
|
}
|
|
return permissions;
|
|
}
|
|
function getUserPermissionCacheKey(action, resource, id2) {
|
|
const key = (typeof resource === "object" ? [action, resource.kind, resource.name, resource.id] : [action, resource, id2]).filter(Boolean).join("/");
|
|
return key;
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/receive-intermediate-results.mjs
|
|
var RECEIVE_INTERMEDIATE_RESULTS = /* @__PURE__ */ Symbol(
|
|
"RECEIVE_INTERMEDIATE_RESULTS"
|
|
);
|
|
|
|
// packages/core-data/build-module/queried-data/actions.mjs
|
|
function receiveItems(items2, edits, meta) {
|
|
return {
|
|
type: "RECEIVE_ITEMS",
|
|
items: items2,
|
|
persistedEdits: edits,
|
|
meta
|
|
};
|
|
}
|
|
function removeItems(kind, name, records, invalidateCache = false) {
|
|
return {
|
|
type: "REMOVE_ITEMS",
|
|
itemIds: Array.isArray(records) ? records : [records],
|
|
kind,
|
|
name,
|
|
invalidateCache
|
|
};
|
|
}
|
|
function receiveQueriedItems(items2, query = {}, edits, meta) {
|
|
return {
|
|
...receiveItems(items2, edits, meta),
|
|
query
|
|
};
|
|
}
|
|
|
|
// packages/core-data/build-module/queried-data/selectors.mjs
|
|
var import_equivalent_key_map = __toESM(require_equivalent_key_map(), 1);
|
|
var import_data = __toESM(require_data(), 1);
|
|
|
|
// packages/core-data/build-module/queried-data/get-query-parts.mjs
|
|
var import_url = __toESM(require_url(), 1);
|
|
function getQueryParts(query) {
|
|
const parts = {
|
|
stableKey: "",
|
|
page: 1,
|
|
perPage: 10,
|
|
fields: null,
|
|
include: null,
|
|
context: "default"
|
|
};
|
|
const keys2 = Object.keys(query).sort();
|
|
for (let i = 0; i < keys2.length; i++) {
|
|
const key = keys2[i];
|
|
let value = query[key];
|
|
switch (key) {
|
|
case "page":
|
|
parts[key] = Number(value);
|
|
break;
|
|
case "per_page":
|
|
parts.perPage = Number(value);
|
|
break;
|
|
case "context":
|
|
parts.context = value;
|
|
break;
|
|
default:
|
|
if (key === "_fields") {
|
|
parts.fields = get_normalized_comma_separable_default(value) ?? [];
|
|
value = parts.fields.join();
|
|
}
|
|
if (key === "include") {
|
|
if (typeof value === "number") {
|
|
value = value.toString();
|
|
}
|
|
parts.include = (get_normalized_comma_separable_default(value) ?? []).map(Number);
|
|
value = parts.include.join();
|
|
}
|
|
parts.stableKey += (parts.stableKey ? "&" : "") + (0, import_url.addQueryArgs)("", { [key]: value }).slice(1);
|
|
}
|
|
}
|
|
return parts;
|
|
}
|
|
var get_query_parts_default = with_weak_map_cache_default(getQueryParts);
|
|
|
|
// packages/core-data/build-module/queried-data/selectors.mjs
|
|
var queriedItemsCacheByState = /* @__PURE__ */ new WeakMap();
|
|
function getQueriedItemsUncached(state, query) {
|
|
const { stableKey, page, perPage, include, fields, context } = get_query_parts_default(query);
|
|
let itemIds;
|
|
if (state.queries?.[context]?.[stableKey]) {
|
|
itemIds = state.queries[context][stableKey].itemIds;
|
|
}
|
|
if (!itemIds) {
|
|
return null;
|
|
}
|
|
const startOffset = perPage === -1 ? 0 : (page - 1) * perPage;
|
|
const endOffset = perPage === -1 ? itemIds.length : Math.min(startOffset + perPage, itemIds.length);
|
|
const items2 = [];
|
|
for (let i = startOffset; i < endOffset; i++) {
|
|
const itemId = itemIds[i];
|
|
if (Array.isArray(include) && !include.includes(itemId)) {
|
|
continue;
|
|
}
|
|
if (itemId === void 0) {
|
|
continue;
|
|
}
|
|
if (!state.items[context]?.hasOwnProperty(itemId)) {
|
|
return null;
|
|
}
|
|
const item = state.items[context][itemId];
|
|
let filteredItem;
|
|
if (Array.isArray(fields)) {
|
|
filteredItem = {};
|
|
for (let f = 0; f < fields.length; f++) {
|
|
const field = fields[f].split(".");
|
|
let value = item;
|
|
field.forEach((fieldName) => {
|
|
value = value?.[fieldName];
|
|
});
|
|
setNestedValue(filteredItem, field, value);
|
|
}
|
|
} else {
|
|
if (!state.itemIsComplete[context]?.[itemId]) {
|
|
return null;
|
|
}
|
|
filteredItem = item;
|
|
}
|
|
items2.push(filteredItem);
|
|
}
|
|
return items2;
|
|
}
|
|
var getQueriedItems = (0, import_data.createSelector)((state, query = {}) => {
|
|
let queriedItemsCache = queriedItemsCacheByState.get(state);
|
|
if (queriedItemsCache) {
|
|
const queriedItems = queriedItemsCache.get(query);
|
|
if (queriedItems !== void 0) {
|
|
return queriedItems;
|
|
}
|
|
} else {
|
|
queriedItemsCache = new import_equivalent_key_map.default();
|
|
queriedItemsCacheByState.set(state, queriedItemsCache);
|
|
}
|
|
const items2 = getQueriedItemsUncached(state, query);
|
|
queriedItemsCache.set(query, items2);
|
|
return items2;
|
|
});
|
|
function getQueriedTotalItems(state, query = {}) {
|
|
const { stableKey, context } = get_query_parts_default(query);
|
|
return state.queries?.[context]?.[stableKey]?.meta?.totalItems ?? null;
|
|
}
|
|
function getQueriedTotalPages(state, query = {}) {
|
|
const { stableKey, context } = get_query_parts_default(query);
|
|
return state.queries?.[context]?.[stableKey]?.meta?.totalPages ?? null;
|
|
}
|
|
|
|
// packages/core-data/build-module/queried-data/reducer.mjs
|
|
var import_data7 = __toESM(require_data(), 1);
|
|
var import_compose = __toESM(require_compose(), 1);
|
|
|
|
// node_modules/tslib/tslib.es6.mjs
|
|
var __assign = function() {
|
|
__assign = Object.assign || function __assign2(t) {
|
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
s = arguments[i];
|
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
}
|
|
return t;
|
|
};
|
|
return __assign.apply(this, arguments);
|
|
};
|
|
|
|
// node_modules/lower-case/dist.es2015/index.js
|
|
function lowerCase(str) {
|
|
return str.toLowerCase();
|
|
}
|
|
|
|
// node_modules/no-case/dist.es2015/index.js
|
|
var DEFAULT_SPLIT_REGEXP = [/([a-z0-9])([A-Z])/g, /([A-Z])([A-Z][a-z])/g];
|
|
var DEFAULT_STRIP_REGEXP = /[^A-Z0-9]+/gi;
|
|
function noCase(input, options) {
|
|
if (options === void 0) {
|
|
options = {};
|
|
}
|
|
var _a = options.splitRegexp, splitRegexp = _a === void 0 ? DEFAULT_SPLIT_REGEXP : _a, _b = options.stripRegexp, stripRegexp = _b === void 0 ? DEFAULT_STRIP_REGEXP : _b, _c = options.transform, transform = _c === void 0 ? lowerCase : _c, _d = options.delimiter, delimiter = _d === void 0 ? " " : _d;
|
|
var result = replace(replace(input, splitRegexp, "$1\0$2"), stripRegexp, "\0");
|
|
var start = 0;
|
|
var end = result.length;
|
|
while (result.charAt(start) === "\0")
|
|
start++;
|
|
while (result.charAt(end - 1) === "\0")
|
|
end--;
|
|
return result.slice(start, end).split("\0").map(transform).join(delimiter);
|
|
}
|
|
function replace(input, re, value) {
|
|
if (re instanceof RegExp)
|
|
return input.replace(re, value);
|
|
return re.reduce(function(input2, re2) {
|
|
return input2.replace(re2, value);
|
|
}, input);
|
|
}
|
|
|
|
// node_modules/pascal-case/dist.es2015/index.js
|
|
function pascalCaseTransform(input, index) {
|
|
var firstChar = input.charAt(0);
|
|
var lowerChars = input.substr(1).toLowerCase();
|
|
if (index > 0 && firstChar >= "0" && firstChar <= "9") {
|
|
return "_" + firstChar + lowerChars;
|
|
}
|
|
return "" + firstChar.toUpperCase() + lowerChars;
|
|
}
|
|
function pascalCase(input, options) {
|
|
if (options === void 0) {
|
|
options = {};
|
|
}
|
|
return noCase(input, __assign({ delimiter: "", transform: pascalCaseTransform }, options));
|
|
}
|
|
|
|
// node_modules/camel-case/dist.es2015/index.js
|
|
function camelCaseTransform(input, index) {
|
|
if (index === 0)
|
|
return input.toLowerCase();
|
|
return pascalCaseTransform(input, index);
|
|
}
|
|
function camelCase(input, options) {
|
|
if (options === void 0) {
|
|
options = {};
|
|
}
|
|
return pascalCase(input, __assign({ transform: camelCaseTransform }, options));
|
|
}
|
|
|
|
// node_modules/upper-case-first/dist.es2015/index.js
|
|
function upperCaseFirst(input) {
|
|
return input.charAt(0).toUpperCase() + input.substr(1);
|
|
}
|
|
|
|
// node_modules/capital-case/dist.es2015/index.js
|
|
function capitalCaseTransform(input) {
|
|
return upperCaseFirst(input.toLowerCase());
|
|
}
|
|
function capitalCase(input, options) {
|
|
if (options === void 0) {
|
|
options = {};
|
|
}
|
|
return noCase(input, __assign({ delimiter: " ", transform: capitalCaseTransform }, options));
|
|
}
|
|
|
|
// packages/core-data/build-module/entities.mjs
|
|
var import_api_fetch2 = __toESM(require_api_fetch(), 1);
|
|
var import_blocks4 = __toESM(require_blocks(), 1);
|
|
var import_i18n = __toESM(require_i18n(), 1);
|
|
|
|
// packages/core-data/build-module/awareness/post-editor-awareness.mjs
|
|
var import_data5 = __toESM(require_data(), 1);
|
|
|
|
// node_modules/yjs/dist/yjs.mjs
|
|
var yjs_exports = {};
|
|
__export(yjs_exports, {
|
|
AbsolutePosition: () => AbsolutePosition,
|
|
AbstractConnector: () => AbstractConnector,
|
|
AbstractStruct: () => AbstractStruct,
|
|
AbstractType: () => AbstractType,
|
|
Array: () => YArray,
|
|
ContentAny: () => ContentAny,
|
|
ContentBinary: () => ContentBinary,
|
|
ContentDeleted: () => ContentDeleted,
|
|
ContentDoc: () => ContentDoc,
|
|
ContentEmbed: () => ContentEmbed,
|
|
ContentFormat: () => ContentFormat,
|
|
ContentJSON: () => ContentJSON,
|
|
ContentString: () => ContentString,
|
|
ContentType: () => ContentType,
|
|
Doc: () => Doc,
|
|
GC: () => GC,
|
|
ID: () => ID,
|
|
Item: () => Item,
|
|
Map: () => YMap,
|
|
PermanentUserData: () => PermanentUserData,
|
|
RelativePosition: () => RelativePosition,
|
|
Skip: () => Skip,
|
|
Snapshot: () => Snapshot,
|
|
Text: () => YText,
|
|
Transaction: () => Transaction,
|
|
UndoManager: () => UndoManager,
|
|
UpdateDecoderV1: () => UpdateDecoderV1,
|
|
UpdateDecoderV2: () => UpdateDecoderV2,
|
|
UpdateEncoderV1: () => UpdateEncoderV1,
|
|
UpdateEncoderV2: () => UpdateEncoderV2,
|
|
XmlElement: () => YXmlElement,
|
|
XmlFragment: () => YXmlFragment,
|
|
XmlHook: () => YXmlHook,
|
|
XmlText: () => YXmlText,
|
|
YArrayEvent: () => YArrayEvent,
|
|
YEvent: () => YEvent,
|
|
YMapEvent: () => YMapEvent,
|
|
YTextEvent: () => YTextEvent,
|
|
YXmlEvent: () => YXmlEvent,
|
|
applyUpdate: () => applyUpdate,
|
|
applyUpdateV2: () => applyUpdateV2,
|
|
cleanupYTextFormatting: () => cleanupYTextFormatting,
|
|
compareIDs: () => compareIDs,
|
|
compareRelativePositions: () => compareRelativePositions,
|
|
convertUpdateFormatV1ToV2: () => convertUpdateFormatV1ToV2,
|
|
convertUpdateFormatV2ToV1: () => convertUpdateFormatV2ToV1,
|
|
createAbsolutePositionFromRelativePosition: () => createAbsolutePositionFromRelativePosition,
|
|
createDeleteSet: () => createDeleteSet,
|
|
createDeleteSetFromStructStore: () => createDeleteSetFromStructStore,
|
|
createDocFromSnapshot: () => createDocFromSnapshot,
|
|
createID: () => createID,
|
|
createRelativePositionFromJSON: () => createRelativePositionFromJSON,
|
|
createRelativePositionFromTypeIndex: () => createRelativePositionFromTypeIndex,
|
|
createSnapshot: () => createSnapshot,
|
|
decodeRelativePosition: () => decodeRelativePosition,
|
|
decodeSnapshot: () => decodeSnapshot,
|
|
decodeSnapshotV2: () => decodeSnapshotV2,
|
|
decodeStateVector: () => decodeStateVector,
|
|
decodeUpdate: () => decodeUpdate,
|
|
decodeUpdateV2: () => decodeUpdateV2,
|
|
diffUpdate: () => diffUpdate,
|
|
diffUpdateV2: () => diffUpdateV2,
|
|
emptySnapshot: () => emptySnapshot,
|
|
encodeRelativePosition: () => encodeRelativePosition,
|
|
encodeSnapshot: () => encodeSnapshot,
|
|
encodeSnapshotV2: () => encodeSnapshotV2,
|
|
encodeStateAsUpdate: () => encodeStateAsUpdate,
|
|
encodeStateAsUpdateV2: () => encodeStateAsUpdateV2,
|
|
encodeStateVector: () => encodeStateVector,
|
|
encodeStateVectorFromUpdate: () => encodeStateVectorFromUpdate,
|
|
encodeStateVectorFromUpdateV2: () => encodeStateVectorFromUpdateV2,
|
|
equalDeleteSets: () => equalDeleteSets,
|
|
equalSnapshots: () => equalSnapshots,
|
|
findIndexSS: () => findIndexSS,
|
|
findRootTypeKey: () => findRootTypeKey,
|
|
getItem: () => getItem,
|
|
getItemCleanEnd: () => getItemCleanEnd,
|
|
getItemCleanStart: () => getItemCleanStart,
|
|
getState: () => getState,
|
|
getTypeChildren: () => getTypeChildren,
|
|
isDeleted: () => isDeleted,
|
|
isParentOf: () => isParentOf,
|
|
iterateDeletedStructs: () => iterateDeletedStructs,
|
|
logType: () => logType,
|
|
logUpdate: () => logUpdate,
|
|
logUpdateV2: () => logUpdateV2,
|
|
mergeDeleteSets: () => mergeDeleteSets,
|
|
mergeUpdates: () => mergeUpdates,
|
|
mergeUpdatesV2: () => mergeUpdatesV2,
|
|
obfuscateUpdate: () => obfuscateUpdate,
|
|
obfuscateUpdateV2: () => obfuscateUpdateV2,
|
|
parseUpdateMeta: () => parseUpdateMeta,
|
|
parseUpdateMetaV2: () => parseUpdateMetaV2,
|
|
readUpdate: () => readUpdate,
|
|
readUpdateV2: () => readUpdateV2,
|
|
relativePositionToJSON: () => relativePositionToJSON,
|
|
snapshot: () => snapshot,
|
|
snapshotContainsUpdate: () => snapshotContainsUpdate,
|
|
transact: () => transact,
|
|
tryGc: () => tryGc,
|
|
typeListToArraySnapshot: () => typeListToArraySnapshot,
|
|
typeMapGetAllSnapshot: () => typeMapGetAllSnapshot,
|
|
typeMapGetSnapshot: () => typeMapGetSnapshot
|
|
});
|
|
|
|
// node_modules/lib0/map.js
|
|
var create = () => /* @__PURE__ */ new Map();
|
|
var copy = (m) => {
|
|
const r = create();
|
|
m.forEach((v, k) => {
|
|
r.set(k, v);
|
|
});
|
|
return r;
|
|
};
|
|
var setIfUndefined = (map2, key, createT) => {
|
|
let set = map2.get(key);
|
|
if (set === void 0) {
|
|
map2.set(key, set = createT());
|
|
}
|
|
return set;
|
|
};
|
|
var map = (m, f) => {
|
|
const res = [];
|
|
for (const [key, value] of m) {
|
|
res.push(f(value, key));
|
|
}
|
|
return res;
|
|
};
|
|
var any = (m, f) => {
|
|
for (const [key, value] of m) {
|
|
if (f(value, key)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
// node_modules/lib0/set.js
|
|
var create2 = () => /* @__PURE__ */ new Set();
|
|
|
|
// node_modules/lib0/array.js
|
|
var last = (arr) => arr[arr.length - 1];
|
|
var appendTo = (dest, src) => {
|
|
for (let i = 0; i < src.length; i++) {
|
|
dest.push(src[i]);
|
|
}
|
|
};
|
|
var from = Array.from;
|
|
var some = (arr, f) => {
|
|
for (let i = 0; i < arr.length; i++) {
|
|
if (f(arr[i], i, arr)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
var unfold = (len, f) => {
|
|
const array = new Array(len);
|
|
for (let i = 0; i < len; i++) {
|
|
array[i] = f(i, array);
|
|
}
|
|
return array;
|
|
};
|
|
var isArray = Array.isArray;
|
|
|
|
// node_modules/lib0/observable.js
|
|
var ObservableV2 = class {
|
|
constructor() {
|
|
this._observers = create();
|
|
}
|
|
/**
|
|
* @template {keyof EVENTS & string} NAME
|
|
* @param {NAME} name
|
|
* @param {EVENTS[NAME]} f
|
|
*/
|
|
on(name, f) {
|
|
setIfUndefined(
|
|
this._observers,
|
|
/** @type {string} */
|
|
name,
|
|
create2
|
|
).add(f);
|
|
return f;
|
|
}
|
|
/**
|
|
* @template {keyof EVENTS & string} NAME
|
|
* @param {NAME} name
|
|
* @param {EVENTS[NAME]} f
|
|
*/
|
|
once(name, f) {
|
|
const _f = (...args2) => {
|
|
this.off(
|
|
name,
|
|
/** @type {any} */
|
|
_f
|
|
);
|
|
f(...args2);
|
|
};
|
|
this.on(
|
|
name,
|
|
/** @type {any} */
|
|
_f
|
|
);
|
|
}
|
|
/**
|
|
* @template {keyof EVENTS & string} NAME
|
|
* @param {NAME} name
|
|
* @param {EVENTS[NAME]} 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
|
|
*
|
|
* @template {keyof EVENTS & string} NAME
|
|
* @param {NAME} name The event name.
|
|
* @param {Parameters<EVENTS[NAME]>} args The arguments that are applied to the event listener.
|
|
*/
|
|
emit(name, args2) {
|
|
return from((this._observers.get(name) || create()).values()).forEach((f) => f(...args2));
|
|
}
|
|
destroy() {
|
|
this._observers = create();
|
|
}
|
|
};
|
|
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 = (...args2) => {
|
|
this.off(name, _f);
|
|
f(...args2);
|
|
};
|
|
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, args2) {
|
|
return from((this._observers.get(name) || create()).values()).forEach((f) => f(...args2));
|
|
}
|
|
destroy() {
|
|
this._observers = create();
|
|
}
|
|
};
|
|
|
|
// node_modules/lib0/math.js
|
|
var floor = Math.floor;
|
|
var abs = Math.abs;
|
|
var min = (a, b) => a < b ? a : b;
|
|
var max = (a, b) => a > b ? a : b;
|
|
var isNaN2 = Number.isNaN;
|
|
var isNegativeZero = (n) => n !== 0 ? n < 0 : 1 / n < 0;
|
|
|
|
// node_modules/lib0/binary.js
|
|
var BIT1 = 1;
|
|
var BIT2 = 2;
|
|
var BIT3 = 4;
|
|
var BIT4 = 8;
|
|
var BIT6 = 32;
|
|
var BIT7 = 64;
|
|
var BIT8 = 128;
|
|
var BIT18 = 1 << 17;
|
|
var BIT19 = 1 << 18;
|
|
var BIT20 = 1 << 19;
|
|
var BIT21 = 1 << 20;
|
|
var BIT22 = 1 << 21;
|
|
var BIT23 = 1 << 22;
|
|
var BIT24 = 1 << 23;
|
|
var BIT25 = 1 << 24;
|
|
var BIT26 = 1 << 25;
|
|
var BIT27 = 1 << 26;
|
|
var BIT28 = 1 << 27;
|
|
var BIT29 = 1 << 28;
|
|
var BIT30 = 1 << 29;
|
|
var BIT31 = 1 << 30;
|
|
var BIT32 = 1 << 31;
|
|
var BITS5 = 31;
|
|
var BITS6 = 63;
|
|
var BITS7 = 127;
|
|
var BITS17 = BIT18 - 1;
|
|
var BITS18 = BIT19 - 1;
|
|
var BITS19 = BIT20 - 1;
|
|
var BITS20 = BIT21 - 1;
|
|
var BITS21 = BIT22 - 1;
|
|
var BITS22 = BIT23 - 1;
|
|
var BITS23 = BIT24 - 1;
|
|
var BITS24 = BIT25 - 1;
|
|
var BITS25 = BIT26 - 1;
|
|
var BITS26 = BIT27 - 1;
|
|
var BITS27 = BIT28 - 1;
|
|
var BITS28 = BIT29 - 1;
|
|
var BITS29 = BIT30 - 1;
|
|
var BITS30 = BIT31 - 1;
|
|
var BITS31 = 2147483647;
|
|
|
|
// node_modules/lib0/number.js
|
|
var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
|
|
var MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER;
|
|
var LOWEST_INT32 = 1 << 31;
|
|
var isInteger = Number.isInteger || ((num) => typeof num === "number" && isFinite(num) && floor(num) === num);
|
|
var isNaN3 = Number.isNaN;
|
|
var parseInt2 = Number.parseInt;
|
|
|
|
// node_modules/lib0/string.js
|
|
var fromCharCode = String.fromCharCode;
|
|
var fromCodePoint = String.fromCodePoint;
|
|
var MAX_UTF16_CHARACTER = fromCharCode(65535);
|
|
var toLowerCase = (s) => s.toLowerCase();
|
|
var trimLeftRegex = /^\s*/g;
|
|
var trimLeft = (s) => s.replace(trimLeftRegex, "");
|
|
var fromCamelCaseRegex = /([A-Z])/g;
|
|
var fromCamelCase = (s, separator) => trimLeft(s.replace(fromCamelCaseRegex, (match) => `${separator}${toLowerCase(match)}`));
|
|
var _encodeUtf8Polyfill = (str) => {
|
|
const encodedString = unescape(encodeURIComponent(str));
|
|
const len = encodedString.length;
|
|
const buf = new Uint8Array(len);
|
|
for (let i = 0; i < len; i++) {
|
|
buf[i] = /** @type {number} */
|
|
encodedString.codePointAt(i);
|
|
}
|
|
return buf;
|
|
};
|
|
var utf8TextEncoder = (
|
|
/** @type {TextEncoder} */
|
|
typeof TextEncoder !== "undefined" ? new TextEncoder() : null
|
|
);
|
|
var _encodeUtf8Native = (str) => utf8TextEncoder.encode(str);
|
|
var encodeUtf8 = utf8TextEncoder ? _encodeUtf8Native : _encodeUtf8Polyfill;
|
|
var utf8TextDecoder = typeof TextDecoder === "undefined" ? null : new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
if (utf8TextDecoder && utf8TextDecoder.decode(new Uint8Array()).length === 1) {
|
|
utf8TextDecoder = null;
|
|
}
|
|
var repeat = (source, n) => unfold(n, () => source).join("");
|
|
|
|
// node_modules/lib0/encoding.js
|
|
var Encoder = class {
|
|
constructor() {
|
|
this.cpos = 0;
|
|
this.cbuf = new Uint8Array(100);
|
|
this.bufs = [];
|
|
}
|
|
};
|
|
var createEncoder = () => new Encoder();
|
|
var length = (encoder) => {
|
|
let len = encoder.cpos;
|
|
for (let i = 0; i < encoder.bufs.length; i++) {
|
|
len += encoder.bufs[i].length;
|
|
}
|
|
return len;
|
|
};
|
|
var toUint8Array = (encoder) => {
|
|
const uint8arr = new Uint8Array(length(encoder));
|
|
let curPos = 0;
|
|
for (let i = 0; i < encoder.bufs.length; i++) {
|
|
const d = encoder.bufs[i];
|
|
uint8arr.set(d, curPos);
|
|
curPos += d.length;
|
|
}
|
|
uint8arr.set(new Uint8Array(encoder.cbuf.buffer, 0, encoder.cpos), curPos);
|
|
return uint8arr;
|
|
};
|
|
var verifyLen = (encoder, len) => {
|
|
const bufferLen = encoder.cbuf.length;
|
|
if (bufferLen - encoder.cpos < len) {
|
|
encoder.bufs.push(new Uint8Array(encoder.cbuf.buffer, 0, encoder.cpos));
|
|
encoder.cbuf = new Uint8Array(max(bufferLen, len) * 2);
|
|
encoder.cpos = 0;
|
|
}
|
|
};
|
|
var write = (encoder, num) => {
|
|
const bufferLen = encoder.cbuf.length;
|
|
if (encoder.cpos === bufferLen) {
|
|
encoder.bufs.push(encoder.cbuf);
|
|
encoder.cbuf = new Uint8Array(bufferLen * 2);
|
|
encoder.cpos = 0;
|
|
}
|
|
encoder.cbuf[encoder.cpos++] = num;
|
|
};
|
|
var writeUint8 = write;
|
|
var writeVarUint = (encoder, num) => {
|
|
while (num > BITS7) {
|
|
write(encoder, BIT8 | BITS7 & num);
|
|
num = floor(num / 128);
|
|
}
|
|
write(encoder, BITS7 & num);
|
|
};
|
|
var writeVarInt = (encoder, num) => {
|
|
const isNegative = isNegativeZero(num);
|
|
if (isNegative) {
|
|
num = -num;
|
|
}
|
|
write(encoder, (num > BITS6 ? BIT8 : 0) | (isNegative ? BIT7 : 0) | BITS6 & num);
|
|
num = floor(num / 64);
|
|
while (num > 0) {
|
|
write(encoder, (num > BITS7 ? BIT8 : 0) | BITS7 & num);
|
|
num = floor(num / 128);
|
|
}
|
|
};
|
|
var _strBuffer = new Uint8Array(3e4);
|
|
var _maxStrBSize = _strBuffer.length / 3;
|
|
var _writeVarStringNative = (encoder, str) => {
|
|
if (str.length < _maxStrBSize) {
|
|
const written = utf8TextEncoder.encodeInto(str, _strBuffer).written || 0;
|
|
writeVarUint(encoder, written);
|
|
for (let i = 0; i < written; i++) {
|
|
write(encoder, _strBuffer[i]);
|
|
}
|
|
} else {
|
|
writeVarUint8Array(encoder, encodeUtf8(str));
|
|
}
|
|
};
|
|
var _writeVarStringPolyfill = (encoder, str) => {
|
|
const encodedString = unescape(encodeURIComponent(str));
|
|
const len = encodedString.length;
|
|
writeVarUint(encoder, len);
|
|
for (let i = 0; i < len; i++) {
|
|
write(
|
|
encoder,
|
|
/** @type {number} */
|
|
encodedString.codePointAt(i)
|
|
);
|
|
}
|
|
};
|
|
var writeVarString = utf8TextEncoder && /** @type {any} */
|
|
utf8TextEncoder.encodeInto ? _writeVarStringNative : _writeVarStringPolyfill;
|
|
var writeBinaryEncoder = (encoder, append2) => writeUint8Array(encoder, toUint8Array(append2));
|
|
var writeUint8Array = (encoder, uint8Array) => {
|
|
const bufferLen = encoder.cbuf.length;
|
|
const cpos = encoder.cpos;
|
|
const leftCopyLen = min(bufferLen - cpos, uint8Array.length);
|
|
const rightCopyLen = uint8Array.length - leftCopyLen;
|
|
encoder.cbuf.set(uint8Array.subarray(0, leftCopyLen), cpos);
|
|
encoder.cpos += leftCopyLen;
|
|
if (rightCopyLen > 0) {
|
|
encoder.bufs.push(encoder.cbuf);
|
|
encoder.cbuf = new Uint8Array(max(bufferLen * 2, rightCopyLen));
|
|
encoder.cbuf.set(uint8Array.subarray(leftCopyLen));
|
|
encoder.cpos = rightCopyLen;
|
|
}
|
|
};
|
|
var writeVarUint8Array = (encoder, uint8Array) => {
|
|
writeVarUint(encoder, uint8Array.byteLength);
|
|
writeUint8Array(encoder, uint8Array);
|
|
};
|
|
var writeOnDataView = (encoder, len) => {
|
|
verifyLen(encoder, len);
|
|
const dview = new DataView(encoder.cbuf.buffer, encoder.cpos, len);
|
|
encoder.cpos += len;
|
|
return dview;
|
|
};
|
|
var writeFloat32 = (encoder, num) => writeOnDataView(encoder, 4).setFloat32(0, num, false);
|
|
var writeFloat64 = (encoder, num) => writeOnDataView(encoder, 8).setFloat64(0, num, false);
|
|
var writeBigInt64 = (encoder, num) => (
|
|
/** @type {any} */
|
|
writeOnDataView(encoder, 8).setBigInt64(0, num, false)
|
|
);
|
|
var floatTestBed = new DataView(new ArrayBuffer(4));
|
|
var isFloat32 = (num) => {
|
|
floatTestBed.setFloat32(0, num);
|
|
return floatTestBed.getFloat32(0) === num;
|
|
};
|
|
var writeAny = (encoder, data) => {
|
|
switch (typeof data) {
|
|
case "string":
|
|
write(encoder, 119);
|
|
writeVarString(encoder, data);
|
|
break;
|
|
case "number":
|
|
if (isInteger(data) && abs(data) <= BITS31) {
|
|
write(encoder, 125);
|
|
writeVarInt(encoder, data);
|
|
} else if (isFloat32(data)) {
|
|
write(encoder, 124);
|
|
writeFloat32(encoder, data);
|
|
} else {
|
|
write(encoder, 123);
|
|
writeFloat64(encoder, data);
|
|
}
|
|
break;
|
|
case "bigint":
|
|
write(encoder, 122);
|
|
writeBigInt64(encoder, data);
|
|
break;
|
|
case "object":
|
|
if (data === null) {
|
|
write(encoder, 126);
|
|
} else if (isArray(data)) {
|
|
write(encoder, 117);
|
|
writeVarUint(encoder, data.length);
|
|
for (let i = 0; i < data.length; i++) {
|
|
writeAny(encoder, data[i]);
|
|
}
|
|
} else if (data instanceof Uint8Array) {
|
|
write(encoder, 116);
|
|
writeVarUint8Array(encoder, data);
|
|
} else {
|
|
write(encoder, 118);
|
|
const keys2 = Object.keys(data);
|
|
writeVarUint(encoder, keys2.length);
|
|
for (let i = 0; i < keys2.length; i++) {
|
|
const key = keys2[i];
|
|
writeVarString(encoder, key);
|
|
writeAny(encoder, data[key]);
|
|
}
|
|
}
|
|
break;
|
|
case "boolean":
|
|
write(encoder, data ? 120 : 121);
|
|
break;
|
|
default:
|
|
write(encoder, 127);
|
|
}
|
|
};
|
|
var RleEncoder = class extends Encoder {
|
|
/**
|
|
* @param {function(Encoder, T):void} writer
|
|
*/
|
|
constructor(writer) {
|
|
super();
|
|
this.w = writer;
|
|
this.s = null;
|
|
this.count = 0;
|
|
}
|
|
/**
|
|
* @param {T} v
|
|
*/
|
|
write(v) {
|
|
if (this.s === v) {
|
|
this.count++;
|
|
} else {
|
|
if (this.count > 0) {
|
|
writeVarUint(this, this.count - 1);
|
|
}
|
|
this.count = 1;
|
|
this.w(this, v);
|
|
this.s = v;
|
|
}
|
|
}
|
|
};
|
|
var flushUintOptRleEncoder = (encoder) => {
|
|
if (encoder.count > 0) {
|
|
writeVarInt(encoder.encoder, encoder.count === 1 ? encoder.s : -encoder.s);
|
|
if (encoder.count > 1) {
|
|
writeVarUint(encoder.encoder, encoder.count - 2);
|
|
}
|
|
}
|
|
};
|
|
var UintOptRleEncoder = class {
|
|
constructor() {
|
|
this.encoder = new Encoder();
|
|
this.s = 0;
|
|
this.count = 0;
|
|
}
|
|
/**
|
|
* @param {number} v
|
|
*/
|
|
write(v) {
|
|
if (this.s === v) {
|
|
this.count++;
|
|
} else {
|
|
flushUintOptRleEncoder(this);
|
|
this.count = 1;
|
|
this.s = v;
|
|
}
|
|
}
|
|
/**
|
|
* Flush the encoded state and transform this to a Uint8Array.
|
|
*
|
|
* Note that this should only be called once.
|
|
*/
|
|
toUint8Array() {
|
|
flushUintOptRleEncoder(this);
|
|
return toUint8Array(this.encoder);
|
|
}
|
|
};
|
|
var flushIntDiffOptRleEncoder = (encoder) => {
|
|
if (encoder.count > 0) {
|
|
const encodedDiff = encoder.diff * 2 + (encoder.count === 1 ? 0 : 1);
|
|
writeVarInt(encoder.encoder, encodedDiff);
|
|
if (encoder.count > 1) {
|
|
writeVarUint(encoder.encoder, encoder.count - 2);
|
|
}
|
|
}
|
|
};
|
|
var IntDiffOptRleEncoder = class {
|
|
constructor() {
|
|
this.encoder = new Encoder();
|
|
this.s = 0;
|
|
this.count = 0;
|
|
this.diff = 0;
|
|
}
|
|
/**
|
|
* @param {number} v
|
|
*/
|
|
write(v) {
|
|
if (this.diff === v - this.s) {
|
|
this.s = v;
|
|
this.count++;
|
|
} else {
|
|
flushIntDiffOptRleEncoder(this);
|
|
this.count = 1;
|
|
this.diff = v - this.s;
|
|
this.s = v;
|
|
}
|
|
}
|
|
/**
|
|
* Flush the encoded state and transform this to a Uint8Array.
|
|
*
|
|
* Note that this should only be called once.
|
|
*/
|
|
toUint8Array() {
|
|
flushIntDiffOptRleEncoder(this);
|
|
return toUint8Array(this.encoder);
|
|
}
|
|
};
|
|
var StringEncoder = class {
|
|
constructor() {
|
|
this.sarr = [];
|
|
this.s = "";
|
|
this.lensE = new UintOptRleEncoder();
|
|
}
|
|
/**
|
|
* @param {string} string
|
|
*/
|
|
write(string) {
|
|
this.s += string;
|
|
if (this.s.length > 19) {
|
|
this.sarr.push(this.s);
|
|
this.s = "";
|
|
}
|
|
this.lensE.write(string.length);
|
|
}
|
|
toUint8Array() {
|
|
const encoder = new Encoder();
|
|
this.sarr.push(this.s);
|
|
this.s = "";
|
|
writeVarString(encoder, this.sarr.join(""));
|
|
writeUint8Array(encoder, this.lensE.toUint8Array());
|
|
return toUint8Array(encoder);
|
|
}
|
|
};
|
|
|
|
// node_modules/lib0/error.js
|
|
var create3 = (s) => new Error(s);
|
|
var methodUnimplemented = () => {
|
|
throw create3("Method unimplemented");
|
|
};
|
|
var unexpectedCase = () => {
|
|
throw create3("Unexpected case");
|
|
};
|
|
|
|
// node_modules/lib0/decoding.js
|
|
var errorUnexpectedEndOfArray = create3("Unexpected end of array");
|
|
var errorIntegerOutOfRange = create3("Integer out of Range");
|
|
var Decoder = class {
|
|
/**
|
|
* @param {Uint8Array} uint8Array Binary data to decode
|
|
*/
|
|
constructor(uint8Array) {
|
|
this.arr = uint8Array;
|
|
this.pos = 0;
|
|
}
|
|
};
|
|
var createDecoder = (uint8Array) => new Decoder(uint8Array);
|
|
var hasContent = (decoder) => decoder.pos !== decoder.arr.length;
|
|
var readUint8Array = (decoder, len) => {
|
|
const view = new Uint8Array(decoder.arr.buffer, decoder.pos + decoder.arr.byteOffset, len);
|
|
decoder.pos += len;
|
|
return view;
|
|
};
|
|
var readVarUint8Array = (decoder) => readUint8Array(decoder, readVarUint(decoder));
|
|
var readUint8 = (decoder) => decoder.arr[decoder.pos++];
|
|
var readVarUint = (decoder) => {
|
|
let num = 0;
|
|
let mult = 1;
|
|
const len = decoder.arr.length;
|
|
while (decoder.pos < len) {
|
|
const r = decoder.arr[decoder.pos++];
|
|
num = num + (r & BITS7) * mult;
|
|
mult *= 128;
|
|
if (r < BIT8) {
|
|
return num;
|
|
}
|
|
if (num > MAX_SAFE_INTEGER) {
|
|
throw errorIntegerOutOfRange;
|
|
}
|
|
}
|
|
throw errorUnexpectedEndOfArray;
|
|
};
|
|
var readVarInt = (decoder) => {
|
|
let r = decoder.arr[decoder.pos++];
|
|
let num = r & BITS6;
|
|
let mult = 64;
|
|
const sign = (r & BIT7) > 0 ? -1 : 1;
|
|
if ((r & BIT8) === 0) {
|
|
return sign * num;
|
|
}
|
|
const len = decoder.arr.length;
|
|
while (decoder.pos < len) {
|
|
r = decoder.arr[decoder.pos++];
|
|
num = num + (r & BITS7) * mult;
|
|
mult *= 128;
|
|
if (r < BIT8) {
|
|
return sign * num;
|
|
}
|
|
if (num > MAX_SAFE_INTEGER) {
|
|
throw errorIntegerOutOfRange;
|
|
}
|
|
}
|
|
throw errorUnexpectedEndOfArray;
|
|
};
|
|
var _readVarStringPolyfill = (decoder) => {
|
|
let remainingLen = readVarUint(decoder);
|
|
if (remainingLen === 0) {
|
|
return "";
|
|
} else {
|
|
let encodedString = String.fromCodePoint(readUint8(decoder));
|
|
if (--remainingLen < 100) {
|
|
while (remainingLen--) {
|
|
encodedString += String.fromCodePoint(readUint8(decoder));
|
|
}
|
|
} else {
|
|
while (remainingLen > 0) {
|
|
const nextLen = remainingLen < 1e4 ? remainingLen : 1e4;
|
|
const bytes = decoder.arr.subarray(decoder.pos, decoder.pos + nextLen);
|
|
decoder.pos += nextLen;
|
|
encodedString += String.fromCodePoint.apply(
|
|
null,
|
|
/** @type {any} */
|
|
bytes
|
|
);
|
|
remainingLen -= nextLen;
|
|
}
|
|
}
|
|
return decodeURIComponent(escape(encodedString));
|
|
}
|
|
};
|
|
var _readVarStringNative = (decoder) => (
|
|
/** @type any */
|
|
utf8TextDecoder.decode(readVarUint8Array(decoder))
|
|
);
|
|
var readVarString = utf8TextDecoder ? _readVarStringNative : _readVarStringPolyfill;
|
|
var readFromDataView = (decoder, len) => {
|
|
const dv = new DataView(decoder.arr.buffer, decoder.arr.byteOffset + decoder.pos, len);
|
|
decoder.pos += len;
|
|
return dv;
|
|
};
|
|
var readFloat32 = (decoder) => readFromDataView(decoder, 4).getFloat32(0, false);
|
|
var readFloat64 = (decoder) => readFromDataView(decoder, 8).getFloat64(0, false);
|
|
var readBigInt64 = (decoder) => (
|
|
/** @type {any} */
|
|
readFromDataView(decoder, 8).getBigInt64(0, false)
|
|
);
|
|
var readAnyLookupTable = [
|
|
(decoder) => void 0,
|
|
// CASE 127: undefined
|
|
(decoder) => null,
|
|
// CASE 126: null
|
|
readVarInt,
|
|
// CASE 125: integer
|
|
readFloat32,
|
|
// CASE 124: float32
|
|
readFloat64,
|
|
// CASE 123: float64
|
|
readBigInt64,
|
|
// CASE 122: bigint
|
|
(decoder) => false,
|
|
// CASE 121: boolean (false)
|
|
(decoder) => true,
|
|
// CASE 120: boolean (true)
|
|
readVarString,
|
|
// CASE 119: string
|
|
(decoder) => {
|
|
const len = readVarUint(decoder);
|
|
const obj = {};
|
|
for (let i = 0; i < len; i++) {
|
|
const key = readVarString(decoder);
|
|
obj[key] = readAny(decoder);
|
|
}
|
|
return obj;
|
|
},
|
|
(decoder) => {
|
|
const len = readVarUint(decoder);
|
|
const arr = [];
|
|
for (let i = 0; i < len; i++) {
|
|
arr.push(readAny(decoder));
|
|
}
|
|
return arr;
|
|
},
|
|
readVarUint8Array
|
|
// CASE 116: Uint8Array
|
|
];
|
|
var readAny = (decoder) => readAnyLookupTable[127 - readUint8(decoder)](decoder);
|
|
var RleDecoder = class extends Decoder {
|
|
/**
|
|
* @param {Uint8Array} uint8Array
|
|
* @param {function(Decoder):T} reader
|
|
*/
|
|
constructor(uint8Array, reader) {
|
|
super(uint8Array);
|
|
this.reader = reader;
|
|
this.s = null;
|
|
this.count = 0;
|
|
}
|
|
read() {
|
|
if (this.count === 0) {
|
|
this.s = this.reader(this);
|
|
if (hasContent(this)) {
|
|
this.count = readVarUint(this) + 1;
|
|
} else {
|
|
this.count = -1;
|
|
}
|
|
}
|
|
this.count--;
|
|
return (
|
|
/** @type {T} */
|
|
this.s
|
|
);
|
|
}
|
|
};
|
|
var UintOptRleDecoder = class extends Decoder {
|
|
/**
|
|
* @param {Uint8Array} uint8Array
|
|
*/
|
|
constructor(uint8Array) {
|
|
super(uint8Array);
|
|
this.s = 0;
|
|
this.count = 0;
|
|
}
|
|
read() {
|
|
if (this.count === 0) {
|
|
this.s = readVarInt(this);
|
|
const isNegative = isNegativeZero(this.s);
|
|
this.count = 1;
|
|
if (isNegative) {
|
|
this.s = -this.s;
|
|
this.count = readVarUint(this) + 2;
|
|
}
|
|
}
|
|
this.count--;
|
|
return (
|
|
/** @type {number} */
|
|
this.s
|
|
);
|
|
}
|
|
};
|
|
var IntDiffOptRleDecoder = class extends Decoder {
|
|
/**
|
|
* @param {Uint8Array} uint8Array
|
|
*/
|
|
constructor(uint8Array) {
|
|
super(uint8Array);
|
|
this.s = 0;
|
|
this.count = 0;
|
|
this.diff = 0;
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
read() {
|
|
if (this.count === 0) {
|
|
const diff = readVarInt(this);
|
|
const hasCount = diff & 1;
|
|
this.diff = floor(diff / 2);
|
|
this.count = 1;
|
|
if (hasCount) {
|
|
this.count = readVarUint(this) + 2;
|
|
}
|
|
}
|
|
this.s += this.diff;
|
|
this.count--;
|
|
return this.s;
|
|
}
|
|
};
|
|
var StringDecoder = class {
|
|
/**
|
|
* @param {Uint8Array} uint8Array
|
|
*/
|
|
constructor(uint8Array) {
|
|
this.decoder = new UintOptRleDecoder(uint8Array);
|
|
this.str = readVarString(this.decoder);
|
|
this.spos = 0;
|
|
}
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
read() {
|
|
const end = this.spos + this.decoder.read();
|
|
const res = this.str.slice(this.spos, end);
|
|
this.spos = end;
|
|
return res;
|
|
}
|
|
};
|
|
|
|
// node_modules/lib0/webcrypto.js
|
|
var subtle = crypto.subtle;
|
|
var getRandomValues = crypto.getRandomValues.bind(crypto);
|
|
|
|
// node_modules/lib0/random.js
|
|
var uint32 = () => getRandomValues(new Uint32Array(1))[0];
|
|
var uuidv4Template = "10000000-1000-4000-8000" + -1e11;
|
|
var uuidv4 = () => uuidv4Template.replace(
|
|
/[018]/g,
|
|
/** @param {number} c */
|
|
(c) => (c ^ uint32() & 15 >> c / 4).toString(16)
|
|
);
|
|
|
|
// node_modules/lib0/time.js
|
|
var getUnixTime = Date.now;
|
|
|
|
// node_modules/lib0/promise.js
|
|
var create4 = (f) => (
|
|
/** @type {Promise<T>} */
|
|
new Promise(f)
|
|
);
|
|
var all = Promise.all.bind(Promise);
|
|
|
|
// node_modules/lib0/conditions.js
|
|
var undefinedToNull = (v) => v === void 0 ? null : v;
|
|
|
|
// node_modules/lib0/storage.js
|
|
var VarStoragePolyfill = class {
|
|
constructor() {
|
|
this.map = /* @__PURE__ */ new Map();
|
|
}
|
|
/**
|
|
* @param {string} key
|
|
* @param {any} newValue
|
|
*/
|
|
setItem(key, newValue) {
|
|
this.map.set(key, newValue);
|
|
}
|
|
/**
|
|
* @param {string} key
|
|
*/
|
|
getItem(key) {
|
|
return this.map.get(key);
|
|
}
|
|
};
|
|
var _localStorage = new VarStoragePolyfill();
|
|
var usePolyfill = true;
|
|
try {
|
|
if (typeof localStorage !== "undefined" && localStorage) {
|
|
_localStorage = localStorage;
|
|
usePolyfill = false;
|
|
}
|
|
} catch (e) {
|
|
}
|
|
var varStorage = _localStorage;
|
|
|
|
// node_modules/lib0/object.js
|
|
var assign = Object.assign;
|
|
var keys = Object.keys;
|
|
var forEach = (obj, f) => {
|
|
for (const key in obj) {
|
|
f(obj[key], key);
|
|
}
|
|
};
|
|
var length2 = (obj) => keys(obj).length;
|
|
var size = (obj) => keys(obj).length;
|
|
var isEmpty = (obj) => {
|
|
for (const _k in obj) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
var every = (obj, f) => {
|
|
for (const key in obj) {
|
|
if (!f(obj[key], key)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
var hasProperty = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
|
|
var equalFlat = (a, b) => a === b || size(a) === size(b) && every(a, (val, key) => (val !== void 0 || hasProperty(b, key)) && b[key] === val);
|
|
var freeze = Object.freeze;
|
|
var deepFreeze = (o) => {
|
|
for (const key in o) {
|
|
const c = o[key];
|
|
if (typeof c === "object" || typeof c === "function") {
|
|
deepFreeze(o[key]);
|
|
}
|
|
}
|
|
return freeze(o);
|
|
};
|
|
|
|
// node_modules/lib0/function.js
|
|
var callAll = (fs, args2, i = 0) => {
|
|
try {
|
|
for (; i < fs.length; i++) {
|
|
fs[i](...args2);
|
|
}
|
|
} finally {
|
|
if (i < fs.length) {
|
|
callAll(fs, args2, i + 1);
|
|
}
|
|
}
|
|
};
|
|
var id = (a) => a;
|
|
var equalityStrict = (a, b) => a === b;
|
|
var equalityDeep = (a, b) => {
|
|
if (a == null || b == null) {
|
|
return equalityStrict(a, b);
|
|
}
|
|
if (a.constructor !== b.constructor) {
|
|
return false;
|
|
}
|
|
if (a === b) {
|
|
return true;
|
|
}
|
|
switch (a.constructor) {
|
|
case ArrayBuffer:
|
|
a = new Uint8Array(a);
|
|
b = new Uint8Array(b);
|
|
// eslint-disable-next-line no-fallthrough
|
|
case Uint8Array: {
|
|
if (a.byteLength !== b.byteLength) {
|
|
return false;
|
|
}
|
|
for (let i = 0; i < a.length; i++) {
|
|
if (a[i] !== b[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Set: {
|
|
if (a.size !== b.size) {
|
|
return false;
|
|
}
|
|
for (const value of a) {
|
|
if (!b.has(value)) {
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Map: {
|
|
if (a.size !== b.size) {
|
|
return false;
|
|
}
|
|
for (const key of a.keys()) {
|
|
if (!b.has(key) || !equalityDeep(a.get(key), b.get(key))) {
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Object:
|
|
if (length2(a) !== length2(b)) {
|
|
return false;
|
|
}
|
|
for (const key in a) {
|
|
if (!hasProperty(a, key) || !equalityDeep(a[key], b[key])) {
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case Array:
|
|
if (a.length !== b.length) {
|
|
return false;
|
|
}
|
|
for (let i = 0; i < a.length; i++) {
|
|
if (!equalityDeep(a[i], b[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
var isOneOf = (value, options) => options.includes(value);
|
|
|
|
// node_modules/lib0/environment.js
|
|
var isNode = typeof process !== "undefined" && process.release && /node|io\.js/.test(process.release.name) && Object.prototype.toString.call(typeof process !== "undefined" ? process : 0) === "[object process]";
|
|
var isBrowser = typeof window !== "undefined" && typeof document !== "undefined" && !isNode;
|
|
var isMac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
|
|
var params;
|
|
var args = [];
|
|
var computeParams = () => {
|
|
if (params === void 0) {
|
|
if (isNode) {
|
|
params = create();
|
|
const pargs = process.argv;
|
|
let currParamName = null;
|
|
for (let i = 0; i < pargs.length; i++) {
|
|
const parg = pargs[i];
|
|
if (parg[0] === "-") {
|
|
if (currParamName !== null) {
|
|
params.set(currParamName, "");
|
|
}
|
|
currParamName = parg;
|
|
} else {
|
|
if (currParamName !== null) {
|
|
params.set(currParamName, parg);
|
|
currParamName = null;
|
|
} else {
|
|
args.push(parg);
|
|
}
|
|
}
|
|
}
|
|
if (currParamName !== null) {
|
|
params.set(currParamName, "");
|
|
}
|
|
} else if (typeof location === "object") {
|
|
params = create();
|
|
(location.search || "?").slice(1).split("&").forEach((kv) => {
|
|
if (kv.length !== 0) {
|
|
const [key, value] = kv.split("=");
|
|
params.set(`--${fromCamelCase(key, "-")}`, value);
|
|
params.set(`-${fromCamelCase(key, "-")}`, value);
|
|
}
|
|
});
|
|
} else {
|
|
params = create();
|
|
}
|
|
}
|
|
return params;
|
|
};
|
|
var hasParam = (name) => computeParams().has(name);
|
|
var getVariable = (name) => isNode ? undefinedToNull(process.env[name.toUpperCase().replaceAll("-", "_")]) : undefinedToNull(varStorage.getItem(name));
|
|
var hasConf = (name) => hasParam("--" + name) || getVariable(name) !== null;
|
|
var production = hasConf("production");
|
|
var forceColor = isNode && isOneOf(process.env.FORCE_COLOR, ["true", "1", "2"]);
|
|
var supportsColor = forceColor || !hasParam("--no-colors") && // @todo deprecate --no-colors
|
|
!hasConf("no-color") && (!isNode || process.stdout.isTTY) && (!isNode || hasParam("--color") || getVariable("COLORTERM") !== null || (getVariable("TERM") || "").includes("color"));
|
|
|
|
// node_modules/lib0/buffer.js
|
|
var createUint8ArrayFromLen = (len) => new Uint8Array(len);
|
|
var createUint8ArrayViewFromArrayBuffer = (buffer, byteOffset, length3) => new Uint8Array(buffer, byteOffset, length3);
|
|
var toBase64Browser = (bytes) => {
|
|
let s = "";
|
|
for (let i = 0; i < bytes.byteLength; i++) {
|
|
s += fromCharCode(bytes[i]);
|
|
}
|
|
return btoa(s);
|
|
};
|
|
var toBase64Node = (bytes) => Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
|
|
var fromBase64Browser = (s) => {
|
|
const a = atob(s);
|
|
const bytes = createUint8ArrayFromLen(a.length);
|
|
for (let i = 0; i < a.length; i++) {
|
|
bytes[i] = a.charCodeAt(i);
|
|
}
|
|
return bytes;
|
|
};
|
|
var fromBase64Node = (s) => {
|
|
const buf = Buffer.from(s, "base64");
|
|
return createUint8ArrayViewFromArrayBuffer(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
};
|
|
var toBase64 = isBrowser ? toBase64Browser : toBase64Node;
|
|
var fromBase64 = isBrowser ? fromBase64Browser : fromBase64Node;
|
|
var copyUint8Array = (uint8Array) => {
|
|
const newBuf = createUint8ArrayFromLen(uint8Array.byteLength);
|
|
newBuf.set(uint8Array);
|
|
return newBuf;
|
|
};
|
|
|
|
// node_modules/lib0/pair.js
|
|
var Pair = class {
|
|
/**
|
|
* @param {L} left
|
|
* @param {R} right
|
|
*/
|
|
constructor(left, right) {
|
|
this.left = left;
|
|
this.right = right;
|
|
}
|
|
};
|
|
var create5 = (left, right) => new Pair(left, right);
|
|
|
|
// node_modules/lib0/dom.js
|
|
var doc = (
|
|
/** @type {Document} */
|
|
typeof document !== "undefined" ? document : {}
|
|
);
|
|
var domParser = (
|
|
/** @type {DOMParser} */
|
|
typeof DOMParser !== "undefined" ? new DOMParser() : null
|
|
);
|
|
var mapToStyleString = (m) => map(m, (value, key) => `${key}:${value};`).join("");
|
|
var ELEMENT_NODE = doc.ELEMENT_NODE;
|
|
var TEXT_NODE = doc.TEXT_NODE;
|
|
var CDATA_SECTION_NODE = doc.CDATA_SECTION_NODE;
|
|
var COMMENT_NODE = doc.COMMENT_NODE;
|
|
var DOCUMENT_NODE = doc.DOCUMENT_NODE;
|
|
var DOCUMENT_TYPE_NODE = doc.DOCUMENT_TYPE_NODE;
|
|
var DOCUMENT_FRAGMENT_NODE = doc.DOCUMENT_FRAGMENT_NODE;
|
|
|
|
// node_modules/lib0/symbol.js
|
|
var create6 = Symbol;
|
|
|
|
// node_modules/lib0/logging.common.js
|
|
var BOLD = create6();
|
|
var UNBOLD = create6();
|
|
var BLUE = create6();
|
|
var GREY = create6();
|
|
var GREEN = create6();
|
|
var RED = create6();
|
|
var PURPLE = create6();
|
|
var ORANGE = create6();
|
|
var UNCOLOR = create6();
|
|
var computeNoColorLoggingArgs = (args2) => {
|
|
if (args2.length === 1 && args2[0]?.constructor === Function) {
|
|
args2 = /** @type {Array<string|Symbol|Object|number>} */
|
|
/** @type {[function]} */
|
|
args2[0]();
|
|
}
|
|
const strBuilder = [];
|
|
const logArgs = [];
|
|
let i = 0;
|
|
for (; i < args2.length; i++) {
|
|
const arg = args2[i];
|
|
if (arg === void 0) {
|
|
break;
|
|
} else if (arg.constructor === String || arg.constructor === Number) {
|
|
strBuilder.push(arg);
|
|
} else if (arg.constructor === Object) {
|
|
break;
|
|
}
|
|
}
|
|
if (i > 0) {
|
|
logArgs.push(strBuilder.join(""));
|
|
}
|
|
for (; i < args2.length; i++) {
|
|
const arg = args2[i];
|
|
if (!(arg instanceof Symbol)) {
|
|
logArgs.push(arg);
|
|
}
|
|
}
|
|
return logArgs;
|
|
};
|
|
var lastLoggingTime = getUnixTime();
|
|
|
|
// node_modules/lib0/logging.js
|
|
var _browserStyleMap = {
|
|
[BOLD]: create5("font-weight", "bold"),
|
|
[UNBOLD]: create5("font-weight", "normal"),
|
|
[BLUE]: create5("color", "blue"),
|
|
[GREEN]: create5("color", "green"),
|
|
[GREY]: create5("color", "grey"),
|
|
[RED]: create5("color", "red"),
|
|
[PURPLE]: create5("color", "purple"),
|
|
[ORANGE]: create5("color", "orange"),
|
|
// not well supported in chrome when debugging node with inspector - TODO: deprecate
|
|
[UNCOLOR]: create5("color", "black")
|
|
};
|
|
var computeBrowserLoggingArgs = (args2) => {
|
|
if (args2.length === 1 && args2[0]?.constructor === Function) {
|
|
args2 = /** @type {Array<string|Symbol|Object|number>} */
|
|
/** @type {[function]} */
|
|
args2[0]();
|
|
}
|
|
const strBuilder = [];
|
|
const styles = [];
|
|
const currentStyle = create();
|
|
let logArgs = [];
|
|
let i = 0;
|
|
for (; i < args2.length; i++) {
|
|
const arg = args2[i];
|
|
const style = _browserStyleMap[arg];
|
|
if (style !== void 0) {
|
|
currentStyle.set(style.left, style.right);
|
|
} else {
|
|
if (arg === void 0) {
|
|
break;
|
|
}
|
|
if (arg.constructor === String || arg.constructor === Number) {
|
|
const style2 = mapToStyleString(currentStyle);
|
|
if (i > 0 || style2.length > 0) {
|
|
strBuilder.push("%c" + arg);
|
|
styles.push(style2);
|
|
} else {
|
|
strBuilder.push(arg);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (i > 0) {
|
|
logArgs = styles;
|
|
logArgs.unshift(strBuilder.join(""));
|
|
}
|
|
for (; i < args2.length; i++) {
|
|
const arg = args2[i];
|
|
if (!(arg instanceof Symbol)) {
|
|
logArgs.push(arg);
|
|
}
|
|
}
|
|
return logArgs;
|
|
};
|
|
var computeLoggingArgs = supportsColor ? computeBrowserLoggingArgs : computeNoColorLoggingArgs;
|
|
var print = (...args2) => {
|
|
console.log(...computeLoggingArgs(args2));
|
|
vconsoles.forEach((vc) => vc.print(args2));
|
|
};
|
|
var warn = (...args2) => {
|
|
console.warn(...computeLoggingArgs(args2));
|
|
args2.unshift(ORANGE);
|
|
vconsoles.forEach((vc) => vc.print(args2));
|
|
};
|
|
var vconsoles = create2();
|
|
|
|
// node_modules/lib0/iterator.js
|
|
var createIterator = (next) => ({
|
|
/**
|
|
* @return {IterableIterator<T>}
|
|
*/
|
|
[Symbol.iterator]() {
|
|
return this;
|
|
},
|
|
// @ts-ignore
|
|
next
|
|
});
|
|
var iteratorFilter = (iterator, filter) => createIterator(() => {
|
|
let res;
|
|
do {
|
|
res = iterator.next();
|
|
} while (!res.done && !filter(res.value));
|
|
return res;
|
|
});
|
|
var iteratorMap = (iterator, fmap) => createIterator(() => {
|
|
const { done, value } = iterator.next();
|
|
return { done, value: done ? void 0 : fmap(value) };
|
|
});
|
|
|
|
// node_modules/yjs/dist/yjs.mjs
|
|
var AbstractConnector = class extends ObservableV2 {
|
|
/**
|
|
* @param {Doc} ydoc
|
|
* @param {any} awareness
|
|
*/
|
|
constructor(ydoc, awareness) {
|
|
super();
|
|
this.doc = ydoc;
|
|
this.awareness = awareness;
|
|
}
|
|
};
|
|
var DeleteItem = class {
|
|
/**
|
|
* @param {number} clock
|
|
* @param {number} len
|
|
*/
|
|
constructor(clock, len) {
|
|
this.clock = clock;
|
|
this.len = len;
|
|
}
|
|
};
|
|
var DeleteSet = class {
|
|
constructor() {
|
|
this.clients = /* @__PURE__ */ new Map();
|
|
}
|
|
};
|
|
var iterateDeletedStructs = (transaction, ds, f) => ds.clients.forEach((deletes, clientid) => {
|
|
const structs = (
|
|
/** @type {Array<GC|Item>} */
|
|
transaction.doc.store.clients.get(clientid)
|
|
);
|
|
if (structs != null) {
|
|
const lastStruct = structs[structs.length - 1];
|
|
const clockState = lastStruct.id.clock + lastStruct.length;
|
|
for (let i = 0, del = deletes[i]; i < deletes.length && del.clock < clockState; del = deletes[++i]) {
|
|
iterateStructs(transaction, structs, del.clock, del.len, f);
|
|
}
|
|
}
|
|
});
|
|
var findIndexDS = (dis, clock) => {
|
|
let left = 0;
|
|
let right = dis.length - 1;
|
|
while (left <= right) {
|
|
const midindex = floor((left + right) / 2);
|
|
const mid = dis[midindex];
|
|
const midclock = mid.clock;
|
|
if (midclock <= clock) {
|
|
if (clock < midclock + mid.len) {
|
|
return midindex;
|
|
}
|
|
left = midindex + 1;
|
|
} else {
|
|
right = midindex - 1;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
var isDeleted = (ds, id2) => {
|
|
const dis = ds.clients.get(id2.client);
|
|
return dis !== void 0 && findIndexDS(dis, id2.clock) !== null;
|
|
};
|
|
var sortAndMergeDeleteSet = (ds) => {
|
|
ds.clients.forEach((dels) => {
|
|
dels.sort((a, b) => a.clock - b.clock);
|
|
let i, j;
|
|
for (i = 1, j = 1; i < dels.length; i++) {
|
|
const left = dels[j - 1];
|
|
const right = dels[i];
|
|
if (left.clock + left.len >= right.clock) {
|
|
left.len = max(left.len, right.clock + right.len - left.clock);
|
|
} else {
|
|
if (j < i) {
|
|
dels[j] = right;
|
|
}
|
|
j++;
|
|
}
|
|
}
|
|
dels.length = j;
|
|
});
|
|
};
|
|
var mergeDeleteSets = (dss) => {
|
|
const merged = new DeleteSet();
|
|
for (let dssI = 0; dssI < dss.length; dssI++) {
|
|
dss[dssI].clients.forEach((delsLeft, client) => {
|
|
if (!merged.clients.has(client)) {
|
|
const dels = delsLeft.slice();
|
|
for (let i = dssI + 1; i < dss.length; i++) {
|
|
appendTo(dels, dss[i].clients.get(client) || []);
|
|
}
|
|
merged.clients.set(client, dels);
|
|
}
|
|
});
|
|
}
|
|
sortAndMergeDeleteSet(merged);
|
|
return merged;
|
|
};
|
|
var addToDeleteSet = (ds, client, clock, length3) => {
|
|
setIfUndefined(ds.clients, client, () => (
|
|
/** @type {Array<DeleteItem>} */
|
|
[]
|
|
)).push(new DeleteItem(clock, length3));
|
|
};
|
|
var createDeleteSet = () => new DeleteSet();
|
|
var createDeleteSetFromStructStore = (ss) => {
|
|
const ds = createDeleteSet();
|
|
ss.clients.forEach((structs, client) => {
|
|
const dsitems = [];
|
|
for (let i = 0; i < structs.length; i++) {
|
|
const struct = structs[i];
|
|
if (struct.deleted) {
|
|
const clock = struct.id.clock;
|
|
let len = struct.length;
|
|
if (i + 1 < structs.length) {
|
|
for (let next = structs[i + 1]; i + 1 < structs.length && next.deleted; next = structs[++i + 1]) {
|
|
len += next.length;
|
|
}
|
|
}
|
|
dsitems.push(new DeleteItem(clock, len));
|
|
}
|
|
}
|
|
if (dsitems.length > 0) {
|
|
ds.clients.set(client, dsitems);
|
|
}
|
|
});
|
|
return ds;
|
|
};
|
|
var writeDeleteSet = (encoder, ds) => {
|
|
writeVarUint(encoder.restEncoder, ds.clients.size);
|
|
from(ds.clients.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, dsitems]) => {
|
|
encoder.resetDsCurVal();
|
|
writeVarUint(encoder.restEncoder, client);
|
|
const len = dsitems.length;
|
|
writeVarUint(encoder.restEncoder, len);
|
|
for (let i = 0; i < len; i++) {
|
|
const item = dsitems[i];
|
|
encoder.writeDsClock(item.clock);
|
|
encoder.writeDsLen(item.len);
|
|
}
|
|
});
|
|
};
|
|
var readDeleteSet = (decoder) => {
|
|
const ds = new DeleteSet();
|
|
const numClients = readVarUint(decoder.restDecoder);
|
|
for (let i = 0; i < numClients; i++) {
|
|
decoder.resetDsCurVal();
|
|
const client = readVarUint(decoder.restDecoder);
|
|
const numberOfDeletes = readVarUint(decoder.restDecoder);
|
|
if (numberOfDeletes > 0) {
|
|
const dsField = setIfUndefined(ds.clients, client, () => (
|
|
/** @type {Array<DeleteItem>} */
|
|
[]
|
|
));
|
|
for (let i2 = 0; i2 < numberOfDeletes; i2++) {
|
|
dsField.push(new DeleteItem(decoder.readDsClock(), decoder.readDsLen()));
|
|
}
|
|
}
|
|
}
|
|
return ds;
|
|
};
|
|
var readAndApplyDeleteSet = (decoder, transaction, store2) => {
|
|
const unappliedDS = new DeleteSet();
|
|
const numClients = readVarUint(decoder.restDecoder);
|
|
for (let i = 0; i < numClients; i++) {
|
|
decoder.resetDsCurVal();
|
|
const client = readVarUint(decoder.restDecoder);
|
|
const numberOfDeletes = readVarUint(decoder.restDecoder);
|
|
const structs = store2.clients.get(client) || [];
|
|
const state = getState(store2, client);
|
|
for (let i2 = 0; i2 < numberOfDeletes; i2++) {
|
|
const clock = decoder.readDsClock();
|
|
const clockEnd = clock + decoder.readDsLen();
|
|
if (clock < state) {
|
|
if (state < clockEnd) {
|
|
addToDeleteSet(unappliedDS, client, state, clockEnd - state);
|
|
}
|
|
let index = findIndexSS(structs, clock);
|
|
let struct = structs[index];
|
|
if (!struct.deleted && struct.id.clock < clock) {
|
|
structs.splice(index + 1, 0, splitItem(transaction, struct, clock - struct.id.clock));
|
|
index++;
|
|
}
|
|
while (index < structs.length) {
|
|
struct = structs[index++];
|
|
if (struct.id.clock < clockEnd) {
|
|
if (!struct.deleted) {
|
|
if (clockEnd < struct.id.clock + struct.length) {
|
|
structs.splice(index, 0, splitItem(transaction, struct, clockEnd - struct.id.clock));
|
|
}
|
|
struct.delete(transaction);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
addToDeleteSet(unappliedDS, client, clock, clockEnd - clock);
|
|
}
|
|
}
|
|
}
|
|
if (unappliedDS.clients.size > 0) {
|
|
const ds = new UpdateEncoderV2();
|
|
writeVarUint(ds.restEncoder, 0);
|
|
writeDeleteSet(ds, unappliedDS);
|
|
return ds.toUint8Array();
|
|
}
|
|
return null;
|
|
};
|
|
var equalDeleteSets = (ds1, ds2) => {
|
|
if (ds1.clients.size !== ds2.clients.size) return false;
|
|
for (const [client, deleteItems1] of ds1.clients.entries()) {
|
|
const deleteItems2 = (
|
|
/** @type {Array<import('../internals.js').DeleteItem>} */
|
|
ds2.clients.get(client)
|
|
);
|
|
if (deleteItems2 === void 0 || deleteItems1.length !== deleteItems2.length) return false;
|
|
for (let i = 0; i < deleteItems1.length; i++) {
|
|
const di1 = deleteItems1[i];
|
|
const di2 = deleteItems2[i];
|
|
if (di1.clock !== di2.clock || di1.len !== di2.len) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
var generateNewClientId = uint32;
|
|
var Doc = class _Doc extends ObservableV2 {
|
|
/**
|
|
* @param {DocOpts} opts configuration
|
|
*/
|
|
constructor({ guid = uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true } = {}) {
|
|
super();
|
|
this.gc = gc;
|
|
this.gcFilter = gcFilter;
|
|
this.clientID = generateNewClientId();
|
|
this.guid = guid;
|
|
this.collectionid = collectionid;
|
|
this.share = /* @__PURE__ */ new Map();
|
|
this.store = new StructStore();
|
|
this._transaction = null;
|
|
this._transactionCleanups = [];
|
|
this.subdocs = /* @__PURE__ */ new Set();
|
|
this._item = null;
|
|
this.shouldLoad = shouldLoad;
|
|
this.autoLoad = autoLoad;
|
|
this.meta = meta;
|
|
this.isLoaded = false;
|
|
this.isSynced = false;
|
|
this.isDestroyed = false;
|
|
this.whenLoaded = create4((resolve) => {
|
|
this.on("load", () => {
|
|
this.isLoaded = true;
|
|
resolve(this);
|
|
});
|
|
});
|
|
const provideSyncedPromise = () => create4((resolve) => {
|
|
const eventHandler = (isSynced) => {
|
|
if (isSynced === void 0 || isSynced === true) {
|
|
this.off("sync", eventHandler);
|
|
resolve();
|
|
}
|
|
};
|
|
this.on("sync", eventHandler);
|
|
});
|
|
this.on("sync", (isSynced) => {
|
|
if (isSynced === false && this.isSynced) {
|
|
this.whenSynced = provideSyncedPromise();
|
|
}
|
|
this.isSynced = isSynced === void 0 || isSynced === true;
|
|
if (this.isSynced && !this.isLoaded) {
|
|
this.emit("load", [this]);
|
|
}
|
|
});
|
|
this.whenSynced = provideSyncedPromise();
|
|
}
|
|
/**
|
|
* Notify the parent document that you request to load data into this subdocument (if it is a subdocument).
|
|
*
|
|
* `load()` might be used in the future to request any provider to load the most current data.
|
|
*
|
|
* It is safe to call `load()` multiple times.
|
|
*/
|
|
load() {
|
|
const item = this._item;
|
|
if (item !== null && !this.shouldLoad) {
|
|
transact(
|
|
/** @type {any} */
|
|
item.parent.doc,
|
|
(transaction) => {
|
|
transaction.subdocsLoaded.add(this);
|
|
},
|
|
null,
|
|
true
|
|
);
|
|
}
|
|
this.shouldLoad = true;
|
|
}
|
|
getSubdocs() {
|
|
return this.subdocs;
|
|
}
|
|
getSubdocGuids() {
|
|
return new Set(from(this.subdocs).map((doc2) => doc2.guid));
|
|
}
|
|
/**
|
|
* Changes that happen inside of a transaction are bundled. This means that
|
|
* the observer fires _after_ the transaction is finished and that all changes
|
|
* that happened inside of the transaction are sent as one message to the
|
|
* other peers.
|
|
*
|
|
* @template T
|
|
* @param {function(Transaction):T} f The function that should be executed as a transaction
|
|
* @param {any} [origin] Origin of who started the transaction. Will be stored on transaction.origin
|
|
* @return T
|
|
*
|
|
* @public
|
|
*/
|
|
transact(f, origin2 = null) {
|
|
return transact(this, f, origin2);
|
|
}
|
|
/**
|
|
* Define a shared data type.
|
|
*
|
|
* Multiple calls of `ydoc.get(name, TypeConstructor)` yield the same result
|
|
* and do not overwrite each other. I.e.
|
|
* `ydoc.get(name, Y.Array) === ydoc.get(name, Y.Array)`
|
|
*
|
|
* After this method is called, the type is also available on `ydoc.share.get(name)`.
|
|
*
|
|
* *Best Practices:*
|
|
* Define all types right after the Y.Doc instance is created and store them in a separate object.
|
|
* Also use the typed methods `getText(name)`, `getArray(name)`, ..
|
|
*
|
|
* @template {typeof AbstractType<any>} Type
|
|
* @example
|
|
* const ydoc = new Y.Doc(..)
|
|
* const appState = {
|
|
* document: ydoc.getText('document')
|
|
* comments: ydoc.getArray('comments')
|
|
* }
|
|
*
|
|
* @param {string} name
|
|
* @param {Type} TypeConstructor The constructor of the type definition. E.g. Y.Text, Y.Array, Y.Map, ...
|
|
* @return {InstanceType<Type>} The created type. Constructed with TypeConstructor
|
|
*
|
|
* @public
|
|
*/
|
|
get(name, TypeConstructor = (
|
|
/** @type {any} */
|
|
AbstractType
|
|
)) {
|
|
const type = setIfUndefined(this.share, name, () => {
|
|
const t = new TypeConstructor();
|
|
t._integrate(this, null);
|
|
return t;
|
|
});
|
|
const Constr = type.constructor;
|
|
if (TypeConstructor !== AbstractType && Constr !== TypeConstructor) {
|
|
if (Constr === AbstractType) {
|
|
const t = new TypeConstructor();
|
|
t._map = type._map;
|
|
type._map.forEach(
|
|
/** @param {Item?} n */
|
|
(n) => {
|
|
for (; n !== null; n = n.left) {
|
|
n.parent = t;
|
|
}
|
|
}
|
|
);
|
|
t._start = type._start;
|
|
for (let n = t._start; n !== null; n = n.right) {
|
|
n.parent = t;
|
|
}
|
|
t._length = type._length;
|
|
this.share.set(name, t);
|
|
t._integrate(this, null);
|
|
return (
|
|
/** @type {InstanceType<Type>} */
|
|
t
|
|
);
|
|
} else {
|
|
throw new Error(`Type with the name ${name} has already been defined with a different constructor`);
|
|
}
|
|
}
|
|
return (
|
|
/** @type {InstanceType<Type>} */
|
|
type
|
|
);
|
|
}
|
|
/**
|
|
* @template T
|
|
* @param {string} [name]
|
|
* @return {YArray<T>}
|
|
*
|
|
* @public
|
|
*/
|
|
getArray(name = "") {
|
|
return (
|
|
/** @type {YArray<T>} */
|
|
this.get(name, YArray)
|
|
);
|
|
}
|
|
/**
|
|
* @param {string} [name]
|
|
* @return {YText}
|
|
*
|
|
* @public
|
|
*/
|
|
getText(name = "") {
|
|
return this.get(name, YText);
|
|
}
|
|
/**
|
|
* @template T
|
|
* @param {string} [name]
|
|
* @return {YMap<T>}
|
|
*
|
|
* @public
|
|
*/
|
|
getMap(name = "") {
|
|
return (
|
|
/** @type {YMap<T>} */
|
|
this.get(name, YMap)
|
|
);
|
|
}
|
|
/**
|
|
* @param {string} [name]
|
|
* @return {YXmlElement}
|
|
*
|
|
* @public
|
|
*/
|
|
getXmlElement(name = "") {
|
|
return (
|
|
/** @type {YXmlElement<{[key:string]:string}>} */
|
|
this.get(name, YXmlElement)
|
|
);
|
|
}
|
|
/**
|
|
* @param {string} [name]
|
|
* @return {YXmlFragment}
|
|
*
|
|
* @public
|
|
*/
|
|
getXmlFragment(name = "") {
|
|
return this.get(name, YXmlFragment);
|
|
}
|
|
/**
|
|
* Converts the entire document into a js object, recursively traversing each yjs type
|
|
* Doesn't log types that have not been defined (using ydoc.getType(..)).
|
|
*
|
|
* @deprecated Do not use this method and rather call toJSON directly on the shared types.
|
|
*
|
|
* @return {Object<string, any>}
|
|
*/
|
|
toJSON() {
|
|
const doc2 = {};
|
|
this.share.forEach((value, key) => {
|
|
doc2[key] = value.toJSON();
|
|
});
|
|
return doc2;
|
|
}
|
|
/**
|
|
* Emit `destroy` event and unregister all event handlers.
|
|
*/
|
|
destroy() {
|
|
this.isDestroyed = true;
|
|
from(this.subdocs).forEach((subdoc) => subdoc.destroy());
|
|
const item = this._item;
|
|
if (item !== null) {
|
|
this._item = null;
|
|
const content = (
|
|
/** @type {ContentDoc} */
|
|
item.content
|
|
);
|
|
content.doc = new _Doc({ guid: this.guid, ...content.opts, shouldLoad: false });
|
|
content.doc._item = item;
|
|
transact(
|
|
/** @type {any} */
|
|
item.parent.doc,
|
|
(transaction) => {
|
|
const doc2 = content.doc;
|
|
if (!item.deleted) {
|
|
transaction.subdocsAdded.add(doc2);
|
|
}
|
|
transaction.subdocsRemoved.add(this);
|
|
},
|
|
null,
|
|
true
|
|
);
|
|
}
|
|
this.emit("destroyed", [true]);
|
|
this.emit("destroy", [this]);
|
|
super.destroy();
|
|
}
|
|
};
|
|
var DSDecoderV1 = class {
|
|
/**
|
|
* @param {decoding.Decoder} decoder
|
|
*/
|
|
constructor(decoder) {
|
|
this.restDecoder = decoder;
|
|
}
|
|
resetDsCurVal() {
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
readDsClock() {
|
|
return readVarUint(this.restDecoder);
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
readDsLen() {
|
|
return readVarUint(this.restDecoder);
|
|
}
|
|
};
|
|
var UpdateDecoderV1 = class extends DSDecoderV1 {
|
|
/**
|
|
* @return {ID}
|
|
*/
|
|
readLeftID() {
|
|
return createID(readVarUint(this.restDecoder), readVarUint(this.restDecoder));
|
|
}
|
|
/**
|
|
* @return {ID}
|
|
*/
|
|
readRightID() {
|
|
return createID(readVarUint(this.restDecoder), readVarUint(this.restDecoder));
|
|
}
|
|
/**
|
|
* Read the next client id.
|
|
* Use this in favor of readID whenever possible to reduce the number of objects created.
|
|
*/
|
|
readClient() {
|
|
return readVarUint(this.restDecoder);
|
|
}
|
|
/**
|
|
* @return {number} info An unsigned 8-bit integer
|
|
*/
|
|
readInfo() {
|
|
return readUint8(this.restDecoder);
|
|
}
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
readString() {
|
|
return readVarString(this.restDecoder);
|
|
}
|
|
/**
|
|
* @return {boolean} isKey
|
|
*/
|
|
readParentInfo() {
|
|
return readVarUint(this.restDecoder) === 1;
|
|
}
|
|
/**
|
|
* @return {number} info An unsigned 8-bit integer
|
|
*/
|
|
readTypeRef() {
|
|
return readVarUint(this.restDecoder);
|
|
}
|
|
/**
|
|
* Write len of a struct - well suited for Opt RLE encoder.
|
|
*
|
|
* @return {number} len
|
|
*/
|
|
readLen() {
|
|
return readVarUint(this.restDecoder);
|
|
}
|
|
/**
|
|
* @return {any}
|
|
*/
|
|
readAny() {
|
|
return readAny(this.restDecoder);
|
|
}
|
|
/**
|
|
* @return {Uint8Array}
|
|
*/
|
|
readBuf() {
|
|
return copyUint8Array(readVarUint8Array(this.restDecoder));
|
|
}
|
|
/**
|
|
* Legacy implementation uses JSON parse. We use any-decoding in v2.
|
|
*
|
|
* @return {any}
|
|
*/
|
|
readJSON() {
|
|
return JSON.parse(readVarString(this.restDecoder));
|
|
}
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
readKey() {
|
|
return readVarString(this.restDecoder);
|
|
}
|
|
};
|
|
var DSDecoderV2 = class {
|
|
/**
|
|
* @param {decoding.Decoder} decoder
|
|
*/
|
|
constructor(decoder) {
|
|
this.dsCurrVal = 0;
|
|
this.restDecoder = decoder;
|
|
}
|
|
resetDsCurVal() {
|
|
this.dsCurrVal = 0;
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
readDsClock() {
|
|
this.dsCurrVal += readVarUint(this.restDecoder);
|
|
return this.dsCurrVal;
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
readDsLen() {
|
|
const diff = readVarUint(this.restDecoder) + 1;
|
|
this.dsCurrVal += diff;
|
|
return diff;
|
|
}
|
|
};
|
|
var UpdateDecoderV2 = class extends DSDecoderV2 {
|
|
/**
|
|
* @param {decoding.Decoder} decoder
|
|
*/
|
|
constructor(decoder) {
|
|
super(decoder);
|
|
this.keys = [];
|
|
readVarUint(decoder);
|
|
this.keyClockDecoder = new IntDiffOptRleDecoder(readVarUint8Array(decoder));
|
|
this.clientDecoder = new UintOptRleDecoder(readVarUint8Array(decoder));
|
|
this.leftClockDecoder = new IntDiffOptRleDecoder(readVarUint8Array(decoder));
|
|
this.rightClockDecoder = new IntDiffOptRleDecoder(readVarUint8Array(decoder));
|
|
this.infoDecoder = new RleDecoder(readVarUint8Array(decoder), readUint8);
|
|
this.stringDecoder = new StringDecoder(readVarUint8Array(decoder));
|
|
this.parentInfoDecoder = new RleDecoder(readVarUint8Array(decoder), readUint8);
|
|
this.typeRefDecoder = new UintOptRleDecoder(readVarUint8Array(decoder));
|
|
this.lenDecoder = new UintOptRleDecoder(readVarUint8Array(decoder));
|
|
}
|
|
/**
|
|
* @return {ID}
|
|
*/
|
|
readLeftID() {
|
|
return new ID(this.clientDecoder.read(), this.leftClockDecoder.read());
|
|
}
|
|
/**
|
|
* @return {ID}
|
|
*/
|
|
readRightID() {
|
|
return new ID(this.clientDecoder.read(), this.rightClockDecoder.read());
|
|
}
|
|
/**
|
|
* Read the next client id.
|
|
* Use this in favor of readID whenever possible to reduce the number of objects created.
|
|
*/
|
|
readClient() {
|
|
return this.clientDecoder.read();
|
|
}
|
|
/**
|
|
* @return {number} info An unsigned 8-bit integer
|
|
*/
|
|
readInfo() {
|
|
return (
|
|
/** @type {number} */
|
|
this.infoDecoder.read()
|
|
);
|
|
}
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
readString() {
|
|
return this.stringDecoder.read();
|
|
}
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
readParentInfo() {
|
|
return this.parentInfoDecoder.read() === 1;
|
|
}
|
|
/**
|
|
* @return {number} An unsigned 8-bit integer
|
|
*/
|
|
readTypeRef() {
|
|
return this.typeRefDecoder.read();
|
|
}
|
|
/**
|
|
* Write len of a struct - well suited for Opt RLE encoder.
|
|
*
|
|
* @return {number}
|
|
*/
|
|
readLen() {
|
|
return this.lenDecoder.read();
|
|
}
|
|
/**
|
|
* @return {any}
|
|
*/
|
|
readAny() {
|
|
return readAny(this.restDecoder);
|
|
}
|
|
/**
|
|
* @return {Uint8Array}
|
|
*/
|
|
readBuf() {
|
|
return readVarUint8Array(this.restDecoder);
|
|
}
|
|
/**
|
|
* This is mainly here for legacy purposes.
|
|
*
|
|
* Initial we incoded objects using JSON. Now we use the much faster lib0/any-encoder. This method mainly exists for legacy purposes for the v1 encoder.
|
|
*
|
|
* @return {any}
|
|
*/
|
|
readJSON() {
|
|
return readAny(this.restDecoder);
|
|
}
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
readKey() {
|
|
const keyClock = this.keyClockDecoder.read();
|
|
if (keyClock < this.keys.length) {
|
|
return this.keys[keyClock];
|
|
} else {
|
|
const key = this.stringDecoder.read();
|
|
this.keys.push(key);
|
|
return key;
|
|
}
|
|
}
|
|
};
|
|
var DSEncoderV1 = class {
|
|
constructor() {
|
|
this.restEncoder = createEncoder();
|
|
}
|
|
toUint8Array() {
|
|
return toUint8Array(this.restEncoder);
|
|
}
|
|
resetDsCurVal() {
|
|
}
|
|
/**
|
|
* @param {number} clock
|
|
*/
|
|
writeDsClock(clock) {
|
|
writeVarUint(this.restEncoder, clock);
|
|
}
|
|
/**
|
|
* @param {number} len
|
|
*/
|
|
writeDsLen(len) {
|
|
writeVarUint(this.restEncoder, len);
|
|
}
|
|
};
|
|
var UpdateEncoderV1 = class extends DSEncoderV1 {
|
|
/**
|
|
* @param {ID} id
|
|
*/
|
|
writeLeftID(id2) {
|
|
writeVarUint(this.restEncoder, id2.client);
|
|
writeVarUint(this.restEncoder, id2.clock);
|
|
}
|
|
/**
|
|
* @param {ID} id
|
|
*/
|
|
writeRightID(id2) {
|
|
writeVarUint(this.restEncoder, id2.client);
|
|
writeVarUint(this.restEncoder, id2.clock);
|
|
}
|
|
/**
|
|
* Use writeClient and writeClock instead of writeID if possible.
|
|
* @param {number} client
|
|
*/
|
|
writeClient(client) {
|
|
writeVarUint(this.restEncoder, client);
|
|
}
|
|
/**
|
|
* @param {number} info An unsigned 8-bit integer
|
|
*/
|
|
writeInfo(info) {
|
|
writeUint8(this.restEncoder, info);
|
|
}
|
|
/**
|
|
* @param {string} s
|
|
*/
|
|
writeString(s) {
|
|
writeVarString(this.restEncoder, s);
|
|
}
|
|
/**
|
|
* @param {boolean} isYKey
|
|
*/
|
|
writeParentInfo(isYKey) {
|
|
writeVarUint(this.restEncoder, isYKey ? 1 : 0);
|
|
}
|
|
/**
|
|
* @param {number} info An unsigned 8-bit integer
|
|
*/
|
|
writeTypeRef(info) {
|
|
writeVarUint(this.restEncoder, info);
|
|
}
|
|
/**
|
|
* Write len of a struct - well suited for Opt RLE encoder.
|
|
*
|
|
* @param {number} len
|
|
*/
|
|
writeLen(len) {
|
|
writeVarUint(this.restEncoder, len);
|
|
}
|
|
/**
|
|
* @param {any} any
|
|
*/
|
|
writeAny(any2) {
|
|
writeAny(this.restEncoder, any2);
|
|
}
|
|
/**
|
|
* @param {Uint8Array} buf
|
|
*/
|
|
writeBuf(buf) {
|
|
writeVarUint8Array(this.restEncoder, buf);
|
|
}
|
|
/**
|
|
* @param {any} embed
|
|
*/
|
|
writeJSON(embed) {
|
|
writeVarString(this.restEncoder, JSON.stringify(embed));
|
|
}
|
|
/**
|
|
* @param {string} key
|
|
*/
|
|
writeKey(key) {
|
|
writeVarString(this.restEncoder, key);
|
|
}
|
|
};
|
|
var DSEncoderV2 = class {
|
|
constructor() {
|
|
this.restEncoder = createEncoder();
|
|
this.dsCurrVal = 0;
|
|
}
|
|
toUint8Array() {
|
|
return toUint8Array(this.restEncoder);
|
|
}
|
|
resetDsCurVal() {
|
|
this.dsCurrVal = 0;
|
|
}
|
|
/**
|
|
* @param {number} clock
|
|
*/
|
|
writeDsClock(clock) {
|
|
const diff = clock - this.dsCurrVal;
|
|
this.dsCurrVal = clock;
|
|
writeVarUint(this.restEncoder, diff);
|
|
}
|
|
/**
|
|
* @param {number} len
|
|
*/
|
|
writeDsLen(len) {
|
|
if (len === 0) {
|
|
unexpectedCase();
|
|
}
|
|
writeVarUint(this.restEncoder, len - 1);
|
|
this.dsCurrVal += len;
|
|
}
|
|
};
|
|
var UpdateEncoderV2 = class extends DSEncoderV2 {
|
|
constructor() {
|
|
super();
|
|
this.keyMap = /* @__PURE__ */ new Map();
|
|
this.keyClock = 0;
|
|
this.keyClockEncoder = new IntDiffOptRleEncoder();
|
|
this.clientEncoder = new UintOptRleEncoder();
|
|
this.leftClockEncoder = new IntDiffOptRleEncoder();
|
|
this.rightClockEncoder = new IntDiffOptRleEncoder();
|
|
this.infoEncoder = new RleEncoder(writeUint8);
|
|
this.stringEncoder = new StringEncoder();
|
|
this.parentInfoEncoder = new RleEncoder(writeUint8);
|
|
this.typeRefEncoder = new UintOptRleEncoder();
|
|
this.lenEncoder = new UintOptRleEncoder();
|
|
}
|
|
toUint8Array() {
|
|
const encoder = createEncoder();
|
|
writeVarUint(encoder, 0);
|
|
writeVarUint8Array(encoder, this.keyClockEncoder.toUint8Array());
|
|
writeVarUint8Array(encoder, this.clientEncoder.toUint8Array());
|
|
writeVarUint8Array(encoder, this.leftClockEncoder.toUint8Array());
|
|
writeVarUint8Array(encoder, this.rightClockEncoder.toUint8Array());
|
|
writeVarUint8Array(encoder, toUint8Array(this.infoEncoder));
|
|
writeVarUint8Array(encoder, this.stringEncoder.toUint8Array());
|
|
writeVarUint8Array(encoder, toUint8Array(this.parentInfoEncoder));
|
|
writeVarUint8Array(encoder, this.typeRefEncoder.toUint8Array());
|
|
writeVarUint8Array(encoder, this.lenEncoder.toUint8Array());
|
|
writeUint8Array(encoder, toUint8Array(this.restEncoder));
|
|
return toUint8Array(encoder);
|
|
}
|
|
/**
|
|
* @param {ID} id
|
|
*/
|
|
writeLeftID(id2) {
|
|
this.clientEncoder.write(id2.client);
|
|
this.leftClockEncoder.write(id2.clock);
|
|
}
|
|
/**
|
|
* @param {ID} id
|
|
*/
|
|
writeRightID(id2) {
|
|
this.clientEncoder.write(id2.client);
|
|
this.rightClockEncoder.write(id2.clock);
|
|
}
|
|
/**
|
|
* @param {number} client
|
|
*/
|
|
writeClient(client) {
|
|
this.clientEncoder.write(client);
|
|
}
|
|
/**
|
|
* @param {number} info An unsigned 8-bit integer
|
|
*/
|
|
writeInfo(info) {
|
|
this.infoEncoder.write(info);
|
|
}
|
|
/**
|
|
* @param {string} s
|
|
*/
|
|
writeString(s) {
|
|
this.stringEncoder.write(s);
|
|
}
|
|
/**
|
|
* @param {boolean} isYKey
|
|
*/
|
|
writeParentInfo(isYKey) {
|
|
this.parentInfoEncoder.write(isYKey ? 1 : 0);
|
|
}
|
|
/**
|
|
* @param {number} info An unsigned 8-bit integer
|
|
*/
|
|
writeTypeRef(info) {
|
|
this.typeRefEncoder.write(info);
|
|
}
|
|
/**
|
|
* Write len of a struct - well suited for Opt RLE encoder.
|
|
*
|
|
* @param {number} len
|
|
*/
|
|
writeLen(len) {
|
|
this.lenEncoder.write(len);
|
|
}
|
|
/**
|
|
* @param {any} any
|
|
*/
|
|
writeAny(any2) {
|
|
writeAny(this.restEncoder, any2);
|
|
}
|
|
/**
|
|
* @param {Uint8Array} buf
|
|
*/
|
|
writeBuf(buf) {
|
|
writeVarUint8Array(this.restEncoder, buf);
|
|
}
|
|
/**
|
|
* This is mainly here for legacy purposes.
|
|
*
|
|
* Initial we incoded objects using JSON. Now we use the much faster lib0/any-encoder. This method mainly exists for legacy purposes for the v1 encoder.
|
|
*
|
|
* @param {any} embed
|
|
*/
|
|
writeJSON(embed) {
|
|
writeAny(this.restEncoder, embed);
|
|
}
|
|
/**
|
|
* Property keys are often reused. For example, in y-prosemirror the key `bold` might
|
|
* occur very often. For a 3d application, the key `position` might occur very often.
|
|
*
|
|
* We cache these keys in a Map and refer to them via a unique number.
|
|
*
|
|
* @param {string} key
|
|
*/
|
|
writeKey(key) {
|
|
const clock = this.keyMap.get(key);
|
|
if (clock === void 0) {
|
|
this.keyClockEncoder.write(this.keyClock++);
|
|
this.stringEncoder.write(key);
|
|
} else {
|
|
this.keyClockEncoder.write(clock);
|
|
}
|
|
}
|
|
};
|
|
var writeStructs = (encoder, structs, client, clock) => {
|
|
clock = max(clock, structs[0].id.clock);
|
|
const startNewStructs = findIndexSS(structs, clock);
|
|
writeVarUint(encoder.restEncoder, structs.length - startNewStructs);
|
|
encoder.writeClient(client);
|
|
writeVarUint(encoder.restEncoder, clock);
|
|
const firstStruct = structs[startNewStructs];
|
|
firstStruct.write(encoder, clock - firstStruct.id.clock);
|
|
for (let i = startNewStructs + 1; i < structs.length; i++) {
|
|
structs[i].write(encoder, 0);
|
|
}
|
|
};
|
|
var writeClientsStructs = (encoder, store2, _sm) => {
|
|
const sm = /* @__PURE__ */ new Map();
|
|
_sm.forEach((clock, client) => {
|
|
if (getState(store2, client) > clock) {
|
|
sm.set(client, clock);
|
|
}
|
|
});
|
|
getStateVector(store2).forEach((_clock, client) => {
|
|
if (!_sm.has(client)) {
|
|
sm.set(client, 0);
|
|
}
|
|
});
|
|
writeVarUint(encoder.restEncoder, sm.size);
|
|
from(sm.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => {
|
|
writeStructs(
|
|
encoder,
|
|
/** @type {Array<GC|Item>} */
|
|
store2.clients.get(client),
|
|
client,
|
|
clock
|
|
);
|
|
});
|
|
};
|
|
var readClientsStructRefs = (decoder, doc2) => {
|
|
const clientRefs = create();
|
|
const numOfStateUpdates = readVarUint(decoder.restDecoder);
|
|
for (let i = 0; i < numOfStateUpdates; i++) {
|
|
const numberOfStructs = readVarUint(decoder.restDecoder);
|
|
const refs = new Array(numberOfStructs);
|
|
const client = decoder.readClient();
|
|
let clock = readVarUint(decoder.restDecoder);
|
|
clientRefs.set(client, { i: 0, refs });
|
|
for (let i2 = 0; i2 < numberOfStructs; i2++) {
|
|
const info = decoder.readInfo();
|
|
switch (BITS5 & info) {
|
|
case 0: {
|
|
const len = decoder.readLen();
|
|
refs[i2] = new GC(createID(client, clock), len);
|
|
clock += len;
|
|
break;
|
|
}
|
|
case 10: {
|
|
const len = readVarUint(decoder.restDecoder);
|
|
refs[i2] = new Skip(createID(client, clock), len);
|
|
clock += len;
|
|
break;
|
|
}
|
|
default: {
|
|
const cantCopyParentInfo = (info & (BIT7 | BIT8)) === 0;
|
|
const struct = new Item(
|
|
createID(client, clock),
|
|
null,
|
|
// left
|
|
(info & BIT8) === BIT8 ? decoder.readLeftID() : null,
|
|
// origin
|
|
null,
|
|
// right
|
|
(info & BIT7) === BIT7 ? decoder.readRightID() : null,
|
|
// right origin
|
|
cantCopyParentInfo ? decoder.readParentInfo() ? doc2.get(decoder.readString()) : decoder.readLeftID() : null,
|
|
// parent
|
|
cantCopyParentInfo && (info & BIT6) === BIT6 ? decoder.readString() : null,
|
|
// parentSub
|
|
readItemContent(decoder, info)
|
|
// item content
|
|
);
|
|
refs[i2] = struct;
|
|
clock += struct.length;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return clientRefs;
|
|
};
|
|
var integrateStructs = (transaction, store2, clientsStructRefs) => {
|
|
const stack = [];
|
|
let clientsStructRefsIds = from(clientsStructRefs.keys()).sort((a, b) => a - b);
|
|
if (clientsStructRefsIds.length === 0) {
|
|
return null;
|
|
}
|
|
const getNextStructTarget = () => {
|
|
if (clientsStructRefsIds.length === 0) {
|
|
return null;
|
|
}
|
|
let nextStructsTarget = (
|
|
/** @type {{i:number,refs:Array<GC|Item>}} */
|
|
clientsStructRefs.get(clientsStructRefsIds[clientsStructRefsIds.length - 1])
|
|
);
|
|
while (nextStructsTarget.refs.length === nextStructsTarget.i) {
|
|
clientsStructRefsIds.pop();
|
|
if (clientsStructRefsIds.length > 0) {
|
|
nextStructsTarget = /** @type {{i:number,refs:Array<GC|Item>}} */
|
|
clientsStructRefs.get(clientsStructRefsIds[clientsStructRefsIds.length - 1]);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
return nextStructsTarget;
|
|
};
|
|
let curStructsTarget = getNextStructTarget();
|
|
if (curStructsTarget === null) {
|
|
return null;
|
|
}
|
|
const restStructs = new StructStore();
|
|
const missingSV = /* @__PURE__ */ new Map();
|
|
const updateMissingSv = (client, clock) => {
|
|
const mclock = missingSV.get(client);
|
|
if (mclock == null || mclock > clock) {
|
|
missingSV.set(client, clock);
|
|
}
|
|
};
|
|
let stackHead = (
|
|
/** @type {any} */
|
|
curStructsTarget.refs[
|
|
/** @type {any} */
|
|
curStructsTarget.i++
|
|
]
|
|
);
|
|
const state = /* @__PURE__ */ new Map();
|
|
const addStackToRestSS = () => {
|
|
for (const item of stack) {
|
|
const client = item.id.client;
|
|
const inapplicableItems = clientsStructRefs.get(client);
|
|
if (inapplicableItems) {
|
|
inapplicableItems.i--;
|
|
restStructs.clients.set(client, inapplicableItems.refs.slice(inapplicableItems.i));
|
|
clientsStructRefs.delete(client);
|
|
inapplicableItems.i = 0;
|
|
inapplicableItems.refs = [];
|
|
} else {
|
|
restStructs.clients.set(client, [item]);
|
|
}
|
|
clientsStructRefsIds = clientsStructRefsIds.filter((c) => c !== client);
|
|
}
|
|
stack.length = 0;
|
|
};
|
|
while (true) {
|
|
if (stackHead.constructor !== Skip) {
|
|
const localClock = setIfUndefined(state, stackHead.id.client, () => getState(store2, stackHead.id.client));
|
|
const offset = localClock - stackHead.id.clock;
|
|
if (offset < 0) {
|
|
stack.push(stackHead);
|
|
updateMissingSv(stackHead.id.client, stackHead.id.clock - 1);
|
|
addStackToRestSS();
|
|
} else {
|
|
const missing = stackHead.getMissing(transaction, store2);
|
|
if (missing !== null) {
|
|
stack.push(stackHead);
|
|
const structRefs = clientsStructRefs.get(
|
|
/** @type {number} */
|
|
missing
|
|
) || { refs: [], i: 0 };
|
|
if (structRefs.refs.length === structRefs.i) {
|
|
updateMissingSv(
|
|
/** @type {number} */
|
|
missing,
|
|
getState(store2, missing)
|
|
);
|
|
addStackToRestSS();
|
|
} else {
|
|
stackHead = structRefs.refs[structRefs.i++];
|
|
continue;
|
|
}
|
|
} else if (offset === 0 || offset < stackHead.length) {
|
|
stackHead.integrate(transaction, offset);
|
|
state.set(stackHead.id.client, stackHead.id.clock + stackHead.length);
|
|
}
|
|
}
|
|
}
|
|
if (stack.length > 0) {
|
|
stackHead = /** @type {GC|Item} */
|
|
stack.pop();
|
|
} else if (curStructsTarget !== null && curStructsTarget.i < curStructsTarget.refs.length) {
|
|
stackHead = /** @type {GC|Item} */
|
|
curStructsTarget.refs[curStructsTarget.i++];
|
|
} else {
|
|
curStructsTarget = getNextStructTarget();
|
|
if (curStructsTarget === null) {
|
|
break;
|
|
} else {
|
|
stackHead = /** @type {GC|Item} */
|
|
curStructsTarget.refs[curStructsTarget.i++];
|
|
}
|
|
}
|
|
}
|
|
if (restStructs.clients.size > 0) {
|
|
const encoder = new UpdateEncoderV2();
|
|
writeClientsStructs(encoder, restStructs, /* @__PURE__ */ new Map());
|
|
writeVarUint(encoder.restEncoder, 0);
|
|
return { missing: missingSV, update: encoder.toUint8Array() };
|
|
}
|
|
return null;
|
|
};
|
|
var writeStructsFromTransaction = (encoder, transaction) => writeClientsStructs(encoder, transaction.doc.store, transaction.beforeState);
|
|
var readUpdateV2 = (decoder, ydoc, transactionOrigin, structDecoder = new UpdateDecoderV2(decoder)) => transact(ydoc, (transaction) => {
|
|
transaction.local = false;
|
|
let retry = false;
|
|
const doc2 = transaction.doc;
|
|
const store2 = doc2.store;
|
|
const ss = readClientsStructRefs(structDecoder, doc2);
|
|
const restStructs = integrateStructs(transaction, store2, ss);
|
|
const pending = store2.pendingStructs;
|
|
if (pending) {
|
|
for (const [client, clock] of pending.missing) {
|
|
if (clock < getState(store2, client)) {
|
|
retry = true;
|
|
break;
|
|
}
|
|
}
|
|
if (restStructs) {
|
|
for (const [client, clock] of restStructs.missing) {
|
|
const mclock = pending.missing.get(client);
|
|
if (mclock == null || mclock > clock) {
|
|
pending.missing.set(client, clock);
|
|
}
|
|
}
|
|
pending.update = mergeUpdatesV2([pending.update, restStructs.update]);
|
|
}
|
|
} else {
|
|
store2.pendingStructs = restStructs;
|
|
}
|
|
const dsRest = readAndApplyDeleteSet(structDecoder, transaction, store2);
|
|
if (store2.pendingDs) {
|
|
const pendingDSUpdate = new UpdateDecoderV2(createDecoder(store2.pendingDs));
|
|
readVarUint(pendingDSUpdate.restDecoder);
|
|
const dsRest2 = readAndApplyDeleteSet(pendingDSUpdate, transaction, store2);
|
|
if (dsRest && dsRest2) {
|
|
store2.pendingDs = mergeUpdatesV2([dsRest, dsRest2]);
|
|
} else {
|
|
store2.pendingDs = dsRest || dsRest2;
|
|
}
|
|
} else {
|
|
store2.pendingDs = dsRest;
|
|
}
|
|
if (retry) {
|
|
const update = (
|
|
/** @type {{update: Uint8Array}} */
|
|
store2.pendingStructs.update
|
|
);
|
|
store2.pendingStructs = null;
|
|
applyUpdateV2(transaction.doc, update);
|
|
}
|
|
}, transactionOrigin, false);
|
|
var readUpdate = (decoder, ydoc, transactionOrigin) => readUpdateV2(decoder, ydoc, transactionOrigin, new UpdateDecoderV1(decoder));
|
|
var applyUpdateV2 = (ydoc, update, transactionOrigin, YDecoder = UpdateDecoderV2) => {
|
|
const decoder = createDecoder(update);
|
|
readUpdateV2(decoder, ydoc, transactionOrigin, new YDecoder(decoder));
|
|
};
|
|
var applyUpdate = (ydoc, update, transactionOrigin) => applyUpdateV2(ydoc, update, transactionOrigin, UpdateDecoderV1);
|
|
var writeStateAsUpdate = (encoder, doc2, targetStateVector = /* @__PURE__ */ new Map()) => {
|
|
writeClientsStructs(encoder, doc2.store, targetStateVector);
|
|
writeDeleteSet(encoder, createDeleteSetFromStructStore(doc2.store));
|
|
};
|
|
var encodeStateAsUpdateV2 = (doc2, encodedTargetStateVector = new Uint8Array([0]), encoder = new UpdateEncoderV2()) => {
|
|
const targetStateVector = decodeStateVector(encodedTargetStateVector);
|
|
writeStateAsUpdate(encoder, doc2, targetStateVector);
|
|
const updates = [encoder.toUint8Array()];
|
|
if (doc2.store.pendingDs) {
|
|
updates.push(doc2.store.pendingDs);
|
|
}
|
|
if (doc2.store.pendingStructs) {
|
|
updates.push(diffUpdateV2(doc2.store.pendingStructs.update, encodedTargetStateVector));
|
|
}
|
|
if (updates.length > 1) {
|
|
if (encoder.constructor === UpdateEncoderV1) {
|
|
return mergeUpdates(updates.map((update, i) => i === 0 ? update : convertUpdateFormatV2ToV1(update)));
|
|
} else if (encoder.constructor === UpdateEncoderV2) {
|
|
return mergeUpdatesV2(updates);
|
|
}
|
|
}
|
|
return updates[0];
|
|
};
|
|
var encodeStateAsUpdate = (doc2, encodedTargetStateVector) => encodeStateAsUpdateV2(doc2, encodedTargetStateVector, new UpdateEncoderV1());
|
|
var readStateVector = (decoder) => {
|
|
const ss = /* @__PURE__ */ new Map();
|
|
const ssLength = readVarUint(decoder.restDecoder);
|
|
for (let i = 0; i < ssLength; i++) {
|
|
const client = readVarUint(decoder.restDecoder);
|
|
const clock = readVarUint(decoder.restDecoder);
|
|
ss.set(client, clock);
|
|
}
|
|
return ss;
|
|
};
|
|
var decodeStateVector = (decodedState) => readStateVector(new DSDecoderV1(createDecoder(decodedState)));
|
|
var writeStateVector = (encoder, sv) => {
|
|
writeVarUint(encoder.restEncoder, sv.size);
|
|
from(sv.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => {
|
|
writeVarUint(encoder.restEncoder, client);
|
|
writeVarUint(encoder.restEncoder, clock);
|
|
});
|
|
return encoder;
|
|
};
|
|
var writeDocumentStateVector = (encoder, doc2) => writeStateVector(encoder, getStateVector(doc2.store));
|
|
var encodeStateVectorV2 = (doc2, encoder = new DSEncoderV2()) => {
|
|
if (doc2 instanceof Map) {
|
|
writeStateVector(encoder, doc2);
|
|
} else {
|
|
writeDocumentStateVector(encoder, doc2);
|
|
}
|
|
return encoder.toUint8Array();
|
|
};
|
|
var encodeStateVector = (doc2) => encodeStateVectorV2(doc2, new DSEncoderV1());
|
|
var EventHandler = class {
|
|
constructor() {
|
|
this.l = [];
|
|
}
|
|
};
|
|
var createEventHandler = () => new EventHandler();
|
|
var addEventHandlerListener = (eventHandler, f) => eventHandler.l.push(f);
|
|
var removeEventHandlerListener = (eventHandler, f) => {
|
|
const l = eventHandler.l;
|
|
const len = l.length;
|
|
eventHandler.l = l.filter((g) => f !== g);
|
|
if (len === eventHandler.l.length) {
|
|
console.error("[yjs] Tried to remove event handler that doesn't exist.");
|
|
}
|
|
};
|
|
var callEventHandlerListeners = (eventHandler, arg0, arg1) => callAll(eventHandler.l, [arg0, arg1]);
|
|
var ID = class {
|
|
/**
|
|
* @param {number} client client id
|
|
* @param {number} clock unique per client id, continuous number
|
|
*/
|
|
constructor(client, clock) {
|
|
this.client = client;
|
|
this.clock = clock;
|
|
}
|
|
};
|
|
var compareIDs = (a, b) => a === b || a !== null && b !== null && a.client === b.client && a.clock === b.clock;
|
|
var createID = (client, clock) => new ID(client, clock);
|
|
var writeID = (encoder, id2) => {
|
|
writeVarUint(encoder, id2.client);
|
|
writeVarUint(encoder, id2.clock);
|
|
};
|
|
var readID = (decoder) => createID(readVarUint(decoder), readVarUint(decoder));
|
|
var findRootTypeKey = (type) => {
|
|
for (const [key, value] of type.doc.share.entries()) {
|
|
if (value === type) {
|
|
return key;
|
|
}
|
|
}
|
|
throw unexpectedCase();
|
|
};
|
|
var isParentOf = (parent, child) => {
|
|
while (child !== null) {
|
|
if (child.parent === parent) {
|
|
return true;
|
|
}
|
|
child = /** @type {AbstractType<any>} */
|
|
child.parent._item;
|
|
}
|
|
return false;
|
|
};
|
|
var logType = (type) => {
|
|
const res = [];
|
|
let n = type._start;
|
|
while (n) {
|
|
res.push(n);
|
|
n = n.right;
|
|
}
|
|
console.log("Children: ", res);
|
|
console.log("Children content: ", res.filter((m) => !m.deleted).map((m) => m.content));
|
|
};
|
|
var PermanentUserData = class {
|
|
/**
|
|
* @param {Doc} doc
|
|
* @param {YMap<any>} [storeType]
|
|
*/
|
|
constructor(doc2, storeType = doc2.getMap("users")) {
|
|
const dss = /* @__PURE__ */ new Map();
|
|
this.yusers = storeType;
|
|
this.doc = doc2;
|
|
this.clients = /* @__PURE__ */ new Map();
|
|
this.dss = dss;
|
|
const initUser = (user, userDescription) => {
|
|
const ds = user.get("ds");
|
|
const ids = user.get("ids");
|
|
const addClientId = (
|
|
/** @param {number} clientid */
|
|
(clientid) => this.clients.set(clientid, userDescription)
|
|
);
|
|
ds.observe(
|
|
/** @param {YArrayEvent<any>} event */
|
|
(event) => {
|
|
event.changes.added.forEach((item) => {
|
|
item.content.getContent().forEach((encodedDs) => {
|
|
if (encodedDs instanceof Uint8Array) {
|
|
this.dss.set(userDescription, mergeDeleteSets([this.dss.get(userDescription) || createDeleteSet(), readDeleteSet(new DSDecoderV1(createDecoder(encodedDs)))]));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
);
|
|
this.dss.set(userDescription, mergeDeleteSets(ds.map((encodedDs) => readDeleteSet(new DSDecoderV1(createDecoder(encodedDs))))));
|
|
ids.observe(
|
|
/** @param {YArrayEvent<any>} event */
|
|
(event) => event.changes.added.forEach((item) => item.content.getContent().forEach(addClientId))
|
|
);
|
|
ids.forEach(addClientId);
|
|
};
|
|
storeType.observe((event) => {
|
|
event.keysChanged.forEach(
|
|
(userDescription) => initUser(storeType.get(userDescription), userDescription)
|
|
);
|
|
});
|
|
storeType.forEach(initUser);
|
|
}
|
|
/**
|
|
* @param {Doc} doc
|
|
* @param {number} clientid
|
|
* @param {string} userDescription
|
|
* @param {Object} conf
|
|
* @param {function(Transaction, DeleteSet):boolean} [conf.filter]
|
|
*/
|
|
setUserMapping(doc2, clientid, userDescription, { filter = () => true } = {}) {
|
|
const users2 = this.yusers;
|
|
let user = users2.get(userDescription);
|
|
if (!user) {
|
|
user = new YMap();
|
|
user.set("ids", new YArray());
|
|
user.set("ds", new YArray());
|
|
users2.set(userDescription, user);
|
|
}
|
|
user.get("ids").push([clientid]);
|
|
users2.observe((_event) => {
|
|
setTimeout(() => {
|
|
const userOverwrite = users2.get(userDescription);
|
|
if (userOverwrite !== user) {
|
|
user = userOverwrite;
|
|
this.clients.forEach((_userDescription, clientid2) => {
|
|
if (userDescription === _userDescription) {
|
|
user.get("ids").push([clientid2]);
|
|
}
|
|
});
|
|
const encoder = new DSEncoderV1();
|
|
const ds = this.dss.get(userDescription);
|
|
if (ds) {
|
|
writeDeleteSet(encoder, ds);
|
|
user.get("ds").push([encoder.toUint8Array()]);
|
|
}
|
|
}
|
|
}, 0);
|
|
});
|
|
doc2.on(
|
|
"afterTransaction",
|
|
/** @param {Transaction} transaction */
|
|
(transaction) => {
|
|
setTimeout(() => {
|
|
const yds = user.get("ds");
|
|
const ds = transaction.deleteSet;
|
|
if (transaction.local && ds.clients.size > 0 && filter(transaction, ds)) {
|
|
const encoder = new DSEncoderV1();
|
|
writeDeleteSet(encoder, ds);
|
|
yds.push([encoder.toUint8Array()]);
|
|
}
|
|
});
|
|
}
|
|
);
|
|
}
|
|
/**
|
|
* @param {number} clientid
|
|
* @return {any}
|
|
*/
|
|
getUserByClientId(clientid) {
|
|
return this.clients.get(clientid) || null;
|
|
}
|
|
/**
|
|
* @param {ID} id
|
|
* @return {string | null}
|
|
*/
|
|
getUserByDeletedId(id2) {
|
|
for (const [userDescription, ds] of this.dss.entries()) {
|
|
if (isDeleted(ds, id2)) {
|
|
return userDescription;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
var RelativePosition = class {
|
|
/**
|
|
* @param {ID|null} type
|
|
* @param {string|null} tname
|
|
* @param {ID|null} item
|
|
* @param {number} assoc
|
|
*/
|
|
constructor(type, tname, item, assoc = 0) {
|
|
this.type = type;
|
|
this.tname = tname;
|
|
this.item = item;
|
|
this.assoc = assoc;
|
|
}
|
|
};
|
|
var relativePositionToJSON = (rpos) => {
|
|
const json = {};
|
|
if (rpos.type) {
|
|
json.type = rpos.type;
|
|
}
|
|
if (rpos.tname) {
|
|
json.tname = rpos.tname;
|
|
}
|
|
if (rpos.item) {
|
|
json.item = rpos.item;
|
|
}
|
|
if (rpos.assoc != null) {
|
|
json.assoc = rpos.assoc;
|
|
}
|
|
return json;
|
|
};
|
|
var createRelativePositionFromJSON = (json) => new RelativePosition(json.type == null ? null : createID(json.type.client, json.type.clock), json.tname ?? null, json.item == null ? null : createID(json.item.client, json.item.clock), json.assoc == null ? 0 : json.assoc);
|
|
var AbsolutePosition = class {
|
|
/**
|
|
* @param {AbstractType<any>} type
|
|
* @param {number} index
|
|
* @param {number} [assoc]
|
|
*/
|
|
constructor(type, index, assoc = 0) {
|
|
this.type = type;
|
|
this.index = index;
|
|
this.assoc = assoc;
|
|
}
|
|
};
|
|
var createAbsolutePosition = (type, index, assoc = 0) => new AbsolutePosition(type, index, assoc);
|
|
var createRelativePosition = (type, item, assoc) => {
|
|
let typeid = null;
|
|
let tname = null;
|
|
if (type._item === null) {
|
|
tname = findRootTypeKey(type);
|
|
} else {
|
|
typeid = createID(type._item.id.client, type._item.id.clock);
|
|
}
|
|
return new RelativePosition(typeid, tname, item, assoc);
|
|
};
|
|
var createRelativePositionFromTypeIndex = (type, index, assoc = 0) => {
|
|
let t = type._start;
|
|
if (assoc < 0) {
|
|
if (index === 0) {
|
|
return createRelativePosition(type, null, assoc);
|
|
}
|
|
index--;
|
|
}
|
|
while (t !== null) {
|
|
if (!t.deleted && t.countable) {
|
|
if (t.length > index) {
|
|
return createRelativePosition(type, createID(t.id.client, t.id.clock + index), assoc);
|
|
}
|
|
index -= t.length;
|
|
}
|
|
if (t.right === null && assoc < 0) {
|
|
return createRelativePosition(type, t.lastId, assoc);
|
|
}
|
|
t = t.right;
|
|
}
|
|
return createRelativePosition(type, null, assoc);
|
|
};
|
|
var writeRelativePosition = (encoder, rpos) => {
|
|
const { type, tname, item, assoc } = rpos;
|
|
if (item !== null) {
|
|
writeVarUint(encoder, 0);
|
|
writeID(encoder, item);
|
|
} else if (tname !== null) {
|
|
writeUint8(encoder, 1);
|
|
writeVarString(encoder, tname);
|
|
} else if (type !== null) {
|
|
writeUint8(encoder, 2);
|
|
writeID(encoder, type);
|
|
} else {
|
|
throw unexpectedCase();
|
|
}
|
|
writeVarInt(encoder, assoc);
|
|
return encoder;
|
|
};
|
|
var encodeRelativePosition = (rpos) => {
|
|
const encoder = createEncoder();
|
|
writeRelativePosition(encoder, rpos);
|
|
return toUint8Array(encoder);
|
|
};
|
|
var readRelativePosition = (decoder) => {
|
|
let type = null;
|
|
let tname = null;
|
|
let itemID = null;
|
|
switch (readVarUint(decoder)) {
|
|
case 0:
|
|
itemID = readID(decoder);
|
|
break;
|
|
case 1:
|
|
tname = readVarString(decoder);
|
|
break;
|
|
case 2: {
|
|
type = readID(decoder);
|
|
}
|
|
}
|
|
const assoc = hasContent(decoder) ? readVarInt(decoder) : 0;
|
|
return new RelativePosition(type, tname, itemID, assoc);
|
|
};
|
|
var decodeRelativePosition = (uint8Array) => readRelativePosition(createDecoder(uint8Array));
|
|
var getItemWithOffset = (store2, id2) => {
|
|
const item = getItem(store2, id2);
|
|
const diff = id2.clock - item.id.clock;
|
|
return {
|
|
item,
|
|
diff
|
|
};
|
|
};
|
|
var createAbsolutePositionFromRelativePosition = (rpos, doc2, followUndoneDeletions = true) => {
|
|
const store2 = doc2.store;
|
|
const rightID = rpos.item;
|
|
const typeID = rpos.type;
|
|
const tname = rpos.tname;
|
|
const assoc = rpos.assoc;
|
|
let type = null;
|
|
let index = 0;
|
|
if (rightID !== null) {
|
|
if (getState(store2, rightID.client) <= rightID.clock) {
|
|
return null;
|
|
}
|
|
const res = followUndoneDeletions ? followRedone(store2, rightID) : getItemWithOffset(store2, rightID);
|
|
const right = res.item;
|
|
if (!(right instanceof Item)) {
|
|
return null;
|
|
}
|
|
type = /** @type {AbstractType<any>} */
|
|
right.parent;
|
|
if (type._item === null || !type._item.deleted) {
|
|
index = right.deleted || !right.countable ? 0 : res.diff + (assoc >= 0 ? 0 : 1);
|
|
let n = right.left;
|
|
while (n !== null) {
|
|
if (!n.deleted && n.countable) {
|
|
index += n.length;
|
|
}
|
|
n = n.left;
|
|
}
|
|
}
|
|
} else {
|
|
if (tname !== null) {
|
|
type = doc2.get(tname);
|
|
} else if (typeID !== null) {
|
|
if (getState(store2, typeID.client) <= typeID.clock) {
|
|
return null;
|
|
}
|
|
const { item } = followUndoneDeletions ? followRedone(store2, typeID) : { item: getItem(store2, typeID) };
|
|
if (item instanceof Item && item.content instanceof ContentType) {
|
|
type = item.content.type;
|
|
} else {
|
|
return null;
|
|
}
|
|
} else {
|
|
throw unexpectedCase();
|
|
}
|
|
if (assoc >= 0) {
|
|
index = type._length;
|
|
} else {
|
|
index = 0;
|
|
}
|
|
}
|
|
return createAbsolutePosition(type, index, rpos.assoc);
|
|
};
|
|
var compareRelativePositions = (a, b) => a === b || a !== null && b !== null && a.tname === b.tname && compareIDs(a.item, b.item) && compareIDs(a.type, b.type) && a.assoc === b.assoc;
|
|
var Snapshot = class {
|
|
/**
|
|
* @param {DeleteSet} ds
|
|
* @param {Map<number,number>} sv state map
|
|
*/
|
|
constructor(ds, sv) {
|
|
this.ds = ds;
|
|
this.sv = sv;
|
|
}
|
|
};
|
|
var equalSnapshots = (snap1, snap2) => {
|
|
const ds1 = snap1.ds.clients;
|
|
const ds2 = snap2.ds.clients;
|
|
const sv1 = snap1.sv;
|
|
const sv2 = snap2.sv;
|
|
if (sv1.size !== sv2.size || ds1.size !== ds2.size) {
|
|
return false;
|
|
}
|
|
for (const [key, value] of sv1.entries()) {
|
|
if (sv2.get(key) !== value) {
|
|
return false;
|
|
}
|
|
}
|
|
for (const [client, dsitems1] of ds1.entries()) {
|
|
const dsitems2 = ds2.get(client) || [];
|
|
if (dsitems1.length !== dsitems2.length) {
|
|
return false;
|
|
}
|
|
for (let i = 0; i < dsitems1.length; i++) {
|
|
const dsitem1 = dsitems1[i];
|
|
const dsitem2 = dsitems2[i];
|
|
if (dsitem1.clock !== dsitem2.clock || dsitem1.len !== dsitem2.len) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
var encodeSnapshotV2 = (snapshot2, encoder = new DSEncoderV2()) => {
|
|
writeDeleteSet(encoder, snapshot2.ds);
|
|
writeStateVector(encoder, snapshot2.sv);
|
|
return encoder.toUint8Array();
|
|
};
|
|
var encodeSnapshot = (snapshot2) => encodeSnapshotV2(snapshot2, new DSEncoderV1());
|
|
var decodeSnapshotV2 = (buf, decoder = new DSDecoderV2(createDecoder(buf))) => {
|
|
return new Snapshot(readDeleteSet(decoder), readStateVector(decoder));
|
|
};
|
|
var decodeSnapshot = (buf) => decodeSnapshotV2(buf, new DSDecoderV1(createDecoder(buf)));
|
|
var createSnapshot = (ds, sm) => new Snapshot(ds, sm);
|
|
var emptySnapshot = createSnapshot(createDeleteSet(), /* @__PURE__ */ new Map());
|
|
var snapshot = (doc2) => createSnapshot(createDeleteSetFromStructStore(doc2.store), getStateVector(doc2.store));
|
|
var isVisible = (item, snapshot2) => snapshot2 === void 0 ? !item.deleted : snapshot2.sv.has(item.id.client) && (snapshot2.sv.get(item.id.client) || 0) > item.id.clock && !isDeleted(snapshot2.ds, item.id);
|
|
var splitSnapshotAffectedStructs = (transaction, snapshot2) => {
|
|
const meta = setIfUndefined(transaction.meta, splitSnapshotAffectedStructs, create2);
|
|
const store2 = transaction.doc.store;
|
|
if (!meta.has(snapshot2)) {
|
|
snapshot2.sv.forEach((clock, client) => {
|
|
if (clock < getState(store2, client)) {
|
|
getItemCleanStart(transaction, createID(client, clock));
|
|
}
|
|
});
|
|
iterateDeletedStructs(transaction, snapshot2.ds, (_item) => {
|
|
});
|
|
meta.add(snapshot2);
|
|
}
|
|
};
|
|
var createDocFromSnapshot = (originDoc, snapshot2, newDoc = new Doc()) => {
|
|
if (originDoc.gc) {
|
|
throw new Error("Garbage-collection must be disabled in `originDoc`!");
|
|
}
|
|
const { sv, ds } = snapshot2;
|
|
const encoder = new UpdateEncoderV2();
|
|
originDoc.transact((transaction) => {
|
|
let size2 = 0;
|
|
sv.forEach((clock) => {
|
|
if (clock > 0) {
|
|
size2++;
|
|
}
|
|
});
|
|
writeVarUint(encoder.restEncoder, size2);
|
|
for (const [client, clock] of sv) {
|
|
if (clock === 0) {
|
|
continue;
|
|
}
|
|
if (clock < getState(originDoc.store, client)) {
|
|
getItemCleanStart(transaction, createID(client, clock));
|
|
}
|
|
const structs = originDoc.store.clients.get(client) || [];
|
|
const lastStructIndex = findIndexSS(structs, clock - 1);
|
|
writeVarUint(encoder.restEncoder, lastStructIndex + 1);
|
|
encoder.writeClient(client);
|
|
writeVarUint(encoder.restEncoder, 0);
|
|
for (let i = 0; i <= lastStructIndex; i++) {
|
|
structs[i].write(encoder, 0);
|
|
}
|
|
}
|
|
writeDeleteSet(encoder, ds);
|
|
});
|
|
applyUpdateV2(newDoc, encoder.toUint8Array(), "snapshot");
|
|
return newDoc;
|
|
};
|
|
var snapshotContainsUpdateV2 = (snapshot2, update, YDecoder = UpdateDecoderV2) => {
|
|
const updateDecoder = new YDecoder(createDecoder(update));
|
|
const lazyDecoder = new LazyStructReader(updateDecoder, false);
|
|
for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
|
|
if ((snapshot2.sv.get(curr.id.client) || 0) < curr.id.clock + curr.length) {
|
|
return false;
|
|
}
|
|
}
|
|
const mergedDS = mergeDeleteSets([snapshot2.ds, readDeleteSet(updateDecoder)]);
|
|
return equalDeleteSets(snapshot2.ds, mergedDS);
|
|
};
|
|
var snapshotContainsUpdate = (snapshot2, update) => snapshotContainsUpdateV2(snapshot2, update, UpdateDecoderV1);
|
|
var StructStore = class {
|
|
constructor() {
|
|
this.clients = /* @__PURE__ */ new Map();
|
|
this.pendingStructs = null;
|
|
this.pendingDs = null;
|
|
}
|
|
};
|
|
var getStateVector = (store2) => {
|
|
const sm = /* @__PURE__ */ new Map();
|
|
store2.clients.forEach((structs, client) => {
|
|
const struct = structs[structs.length - 1];
|
|
sm.set(client, struct.id.clock + struct.length);
|
|
});
|
|
return sm;
|
|
};
|
|
var getState = (store2, client) => {
|
|
const structs = store2.clients.get(client);
|
|
if (structs === void 0) {
|
|
return 0;
|
|
}
|
|
const lastStruct = structs[structs.length - 1];
|
|
return lastStruct.id.clock + lastStruct.length;
|
|
};
|
|
var addStruct = (store2, struct) => {
|
|
let structs = store2.clients.get(struct.id.client);
|
|
if (structs === void 0) {
|
|
structs = [];
|
|
store2.clients.set(struct.id.client, structs);
|
|
} else {
|
|
const lastStruct = structs[structs.length - 1];
|
|
if (lastStruct.id.clock + lastStruct.length !== struct.id.clock) {
|
|
throw unexpectedCase();
|
|
}
|
|
}
|
|
structs.push(struct);
|
|
};
|
|
var findIndexSS = (structs, clock) => {
|
|
let left = 0;
|
|
let right = structs.length - 1;
|
|
let mid = structs[right];
|
|
let midclock = mid.id.clock;
|
|
if (midclock === clock) {
|
|
return right;
|
|
}
|
|
let midindex = floor(clock / (midclock + mid.length - 1) * right);
|
|
while (left <= right) {
|
|
mid = structs[midindex];
|
|
midclock = mid.id.clock;
|
|
if (midclock <= clock) {
|
|
if (clock < midclock + mid.length) {
|
|
return midindex;
|
|
}
|
|
left = midindex + 1;
|
|
} else {
|
|
right = midindex - 1;
|
|
}
|
|
midindex = floor((left + right) / 2);
|
|
}
|
|
throw unexpectedCase();
|
|
};
|
|
var find = (store2, id2) => {
|
|
const structs = store2.clients.get(id2.client);
|
|
return structs[findIndexSS(structs, id2.clock)];
|
|
};
|
|
var getItem = (
|
|
/** @type {function(StructStore,ID):Item} */
|
|
find
|
|
);
|
|
var findIndexCleanStart = (transaction, structs, clock) => {
|
|
const index = findIndexSS(structs, clock);
|
|
const struct = structs[index];
|
|
if (struct.id.clock < clock && struct instanceof Item) {
|
|
structs.splice(index + 1, 0, splitItem(transaction, struct, clock - struct.id.clock));
|
|
return index + 1;
|
|
}
|
|
return index;
|
|
};
|
|
var getItemCleanStart = (transaction, id2) => {
|
|
const structs = (
|
|
/** @type {Array<Item>} */
|
|
transaction.doc.store.clients.get(id2.client)
|
|
);
|
|
return structs[findIndexCleanStart(transaction, structs, id2.clock)];
|
|
};
|
|
var getItemCleanEnd = (transaction, store2, id2) => {
|
|
const structs = store2.clients.get(id2.client);
|
|
const index = findIndexSS(structs, id2.clock);
|
|
const struct = structs[index];
|
|
if (id2.clock !== struct.id.clock + struct.length - 1 && struct.constructor !== GC) {
|
|
structs.splice(index + 1, 0, splitItem(transaction, struct, id2.clock - struct.id.clock + 1));
|
|
}
|
|
return struct;
|
|
};
|
|
var replaceStruct = (store2, struct, newStruct) => {
|
|
const structs = (
|
|
/** @type {Array<GC|Item>} */
|
|
store2.clients.get(struct.id.client)
|
|
);
|
|
structs[findIndexSS(structs, struct.id.clock)] = newStruct;
|
|
};
|
|
var iterateStructs = (transaction, structs, clockStart, len, f) => {
|
|
if (len === 0) {
|
|
return;
|
|
}
|
|
const clockEnd = clockStart + len;
|
|
let index = findIndexCleanStart(transaction, structs, clockStart);
|
|
let struct;
|
|
do {
|
|
struct = structs[index++];
|
|
if (clockEnd < struct.id.clock + struct.length) {
|
|
findIndexCleanStart(transaction, structs, clockEnd);
|
|
}
|
|
f(struct);
|
|
} while (index < structs.length && structs[index].id.clock < clockEnd);
|
|
};
|
|
var Transaction = class {
|
|
/**
|
|
* @param {Doc} doc
|
|
* @param {any} origin
|
|
* @param {boolean} local
|
|
*/
|
|
constructor(doc2, origin2, local) {
|
|
this.doc = doc2;
|
|
this.deleteSet = new DeleteSet();
|
|
this.beforeState = getStateVector(doc2.store);
|
|
this.afterState = /* @__PURE__ */ new Map();
|
|
this.changed = /* @__PURE__ */ new Map();
|
|
this.changedParentTypes = /* @__PURE__ */ new Map();
|
|
this._mergeStructs = [];
|
|
this.origin = origin2;
|
|
this.meta = /* @__PURE__ */ new Map();
|
|
this.local = local;
|
|
this.subdocsAdded = /* @__PURE__ */ new Set();
|
|
this.subdocsRemoved = /* @__PURE__ */ new Set();
|
|
this.subdocsLoaded = /* @__PURE__ */ new Set();
|
|
this._needFormattingCleanup = false;
|
|
}
|
|
};
|
|
var writeUpdateMessageFromTransaction = (encoder, transaction) => {
|
|
if (transaction.deleteSet.clients.size === 0 && !any(transaction.afterState, (clock, client) => transaction.beforeState.get(client) !== clock)) {
|
|
return false;
|
|
}
|
|
sortAndMergeDeleteSet(transaction.deleteSet);
|
|
writeStructsFromTransaction(encoder, transaction);
|
|
writeDeleteSet(encoder, transaction.deleteSet);
|
|
return true;
|
|
};
|
|
var addChangedTypeToTransaction = (transaction, type, parentSub) => {
|
|
const item = type._item;
|
|
if (item === null || item.id.clock < (transaction.beforeState.get(item.id.client) || 0) && !item.deleted) {
|
|
setIfUndefined(transaction.changed, type, create2).add(parentSub);
|
|
}
|
|
};
|
|
var tryToMergeWithLefts = (structs, pos) => {
|
|
let right = structs[pos];
|
|
let left = structs[pos - 1];
|
|
let i = pos;
|
|
for (; i > 0; right = left, left = structs[--i - 1]) {
|
|
if (left.deleted === right.deleted && left.constructor === right.constructor) {
|
|
if (left.mergeWith(right)) {
|
|
if (right instanceof Item && right.parentSub !== null && /** @type {AbstractType<any>} */
|
|
right.parent._map.get(right.parentSub) === right) {
|
|
right.parent._map.set(
|
|
right.parentSub,
|
|
/** @type {Item} */
|
|
left
|
|
);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
const merged = pos - i;
|
|
if (merged) {
|
|
structs.splice(pos + 1 - merged, merged);
|
|
}
|
|
return merged;
|
|
};
|
|
var tryGcDeleteSet = (ds, store2, gcFilter) => {
|
|
for (const [client, deleteItems] of ds.clients.entries()) {
|
|
const structs = (
|
|
/** @type {Array<GC|Item>} */
|
|
store2.clients.get(client)
|
|
);
|
|
for (let di = deleteItems.length - 1; di >= 0; di--) {
|
|
const deleteItem = deleteItems[di];
|
|
const endDeleteItemClock = deleteItem.clock + deleteItem.len;
|
|
for (let si = findIndexSS(structs, deleteItem.clock), struct = structs[si]; si < structs.length && struct.id.clock < endDeleteItemClock; struct = structs[++si]) {
|
|
const struct2 = structs[si];
|
|
if (deleteItem.clock + deleteItem.len <= struct2.id.clock) {
|
|
break;
|
|
}
|
|
if (struct2 instanceof Item && struct2.deleted && !struct2.keep && gcFilter(struct2)) {
|
|
struct2.gc(store2, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var tryMergeDeleteSet = (ds, store2) => {
|
|
ds.clients.forEach((deleteItems, client) => {
|
|
const structs = (
|
|
/** @type {Array<GC|Item>} */
|
|
store2.clients.get(client)
|
|
);
|
|
for (let di = deleteItems.length - 1; di >= 0; di--) {
|
|
const deleteItem = deleteItems[di];
|
|
const mostRightIndexToCheck = min(structs.length - 1, 1 + findIndexSS(structs, deleteItem.clock + deleteItem.len - 1));
|
|
for (let si = mostRightIndexToCheck, struct = structs[si]; si > 0 && struct.id.clock >= deleteItem.clock; struct = structs[si]) {
|
|
si -= 1 + tryToMergeWithLefts(structs, si);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
var tryGc = (ds, store2, gcFilter) => {
|
|
tryGcDeleteSet(ds, store2, gcFilter);
|
|
tryMergeDeleteSet(ds, store2);
|
|
};
|
|
var cleanupTransactions = (transactionCleanups, i) => {
|
|
if (i < transactionCleanups.length) {
|
|
const transaction = transactionCleanups[i];
|
|
const doc2 = transaction.doc;
|
|
const store2 = doc2.store;
|
|
const ds = transaction.deleteSet;
|
|
const mergeStructs = transaction._mergeStructs;
|
|
try {
|
|
sortAndMergeDeleteSet(ds);
|
|
transaction.afterState = getStateVector(transaction.doc.store);
|
|
doc2.emit("beforeObserverCalls", [transaction, doc2]);
|
|
const fs = [];
|
|
transaction.changed.forEach(
|
|
(subs, itemtype) => fs.push(() => {
|
|
if (itemtype._item === null || !itemtype._item.deleted) {
|
|
itemtype._callObserver(transaction, subs);
|
|
}
|
|
})
|
|
);
|
|
fs.push(() => {
|
|
transaction.changedParentTypes.forEach((events, type) => {
|
|
if (type._dEH.l.length > 0 && (type._item === null || !type._item.deleted)) {
|
|
events = events.filter(
|
|
(event) => event.target._item === null || !event.target._item.deleted
|
|
);
|
|
events.forEach((event) => {
|
|
event.currentTarget = type;
|
|
event._path = null;
|
|
});
|
|
events.sort((event1, event2) => event1.path.length - event2.path.length);
|
|
fs.push(() => {
|
|
callEventHandlerListeners(type._dEH, events, transaction);
|
|
});
|
|
}
|
|
});
|
|
fs.push(() => doc2.emit("afterTransaction", [transaction, doc2]));
|
|
fs.push(() => {
|
|
if (transaction._needFormattingCleanup) {
|
|
cleanupYTextAfterTransaction(transaction);
|
|
}
|
|
});
|
|
});
|
|
callAll(fs, []);
|
|
} finally {
|
|
if (doc2.gc) {
|
|
tryGcDeleteSet(ds, store2, doc2.gcFilter);
|
|
}
|
|
tryMergeDeleteSet(ds, store2);
|
|
transaction.afterState.forEach((clock, client) => {
|
|
const beforeClock = transaction.beforeState.get(client) || 0;
|
|
if (beforeClock !== clock) {
|
|
const structs = (
|
|
/** @type {Array<GC|Item>} */
|
|
store2.clients.get(client)
|
|
);
|
|
const firstChangePos = max(findIndexSS(structs, beforeClock), 1);
|
|
for (let i2 = structs.length - 1; i2 >= firstChangePos; ) {
|
|
i2 -= 1 + tryToMergeWithLefts(structs, i2);
|
|
}
|
|
}
|
|
});
|
|
for (let i2 = mergeStructs.length - 1; i2 >= 0; i2--) {
|
|
const { client, clock } = mergeStructs[i2].id;
|
|
const structs = (
|
|
/** @type {Array<GC|Item>} */
|
|
store2.clients.get(client)
|
|
);
|
|
const replacedStructPos = findIndexSS(structs, clock);
|
|
if (replacedStructPos + 1 < structs.length) {
|
|
if (tryToMergeWithLefts(structs, replacedStructPos + 1) > 1) {
|
|
continue;
|
|
}
|
|
}
|
|
if (replacedStructPos > 0) {
|
|
tryToMergeWithLefts(structs, replacedStructPos);
|
|
}
|
|
}
|
|
if (!transaction.local && transaction.afterState.get(doc2.clientID) !== transaction.beforeState.get(doc2.clientID)) {
|
|
print(ORANGE, BOLD, "[yjs] ", UNBOLD, RED, "Changed the client-id because another client seems to be using it.");
|
|
doc2.clientID = generateNewClientId();
|
|
}
|
|
doc2.emit("afterTransactionCleanup", [transaction, doc2]);
|
|
if (doc2._observers.has("update")) {
|
|
const encoder = new UpdateEncoderV1();
|
|
const hasContent2 = writeUpdateMessageFromTransaction(encoder, transaction);
|
|
if (hasContent2) {
|
|
doc2.emit("update", [encoder.toUint8Array(), transaction.origin, doc2, transaction]);
|
|
}
|
|
}
|
|
if (doc2._observers.has("updateV2")) {
|
|
const encoder = new UpdateEncoderV2();
|
|
const hasContent2 = writeUpdateMessageFromTransaction(encoder, transaction);
|
|
if (hasContent2) {
|
|
doc2.emit("updateV2", [encoder.toUint8Array(), transaction.origin, doc2, transaction]);
|
|
}
|
|
}
|
|
const { subdocsAdded, subdocsLoaded, subdocsRemoved } = transaction;
|
|
if (subdocsAdded.size > 0 || subdocsRemoved.size > 0 || subdocsLoaded.size > 0) {
|
|
subdocsAdded.forEach((subdoc) => {
|
|
subdoc.clientID = doc2.clientID;
|
|
if (subdoc.collectionid == null) {
|
|
subdoc.collectionid = doc2.collectionid;
|
|
}
|
|
doc2.subdocs.add(subdoc);
|
|
});
|
|
subdocsRemoved.forEach((subdoc) => doc2.subdocs.delete(subdoc));
|
|
doc2.emit("subdocs", [{ loaded: subdocsLoaded, added: subdocsAdded, removed: subdocsRemoved }, doc2, transaction]);
|
|
subdocsRemoved.forEach((subdoc) => subdoc.destroy());
|
|
}
|
|
if (transactionCleanups.length <= i + 1) {
|
|
doc2._transactionCleanups = [];
|
|
doc2.emit("afterAllTransactions", [doc2, transactionCleanups]);
|
|
} else {
|
|
cleanupTransactions(transactionCleanups, i + 1);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var transact = (doc2, f, origin2 = null, local = true) => {
|
|
const transactionCleanups = doc2._transactionCleanups;
|
|
let initialCall = false;
|
|
let result = null;
|
|
if (doc2._transaction === null) {
|
|
initialCall = true;
|
|
doc2._transaction = new Transaction(doc2, origin2, local);
|
|
transactionCleanups.push(doc2._transaction);
|
|
if (transactionCleanups.length === 1) {
|
|
doc2.emit("beforeAllTransactions", [doc2]);
|
|
}
|
|
doc2.emit("beforeTransaction", [doc2._transaction, doc2]);
|
|
}
|
|
try {
|
|
result = f(doc2._transaction);
|
|
} finally {
|
|
if (initialCall) {
|
|
const finishCleanup = doc2._transaction === transactionCleanups[0];
|
|
doc2._transaction = null;
|
|
if (finishCleanup) {
|
|
cleanupTransactions(transactionCleanups, 0);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
var StackItem = class {
|
|
/**
|
|
* @param {DeleteSet} deletions
|
|
* @param {DeleteSet} insertions
|
|
*/
|
|
constructor(deletions, insertions) {
|
|
this.insertions = insertions;
|
|
this.deletions = deletions;
|
|
this.meta = /* @__PURE__ */ new Map();
|
|
}
|
|
};
|
|
var clearUndoManagerStackItem = (tr, um, stackItem) => {
|
|
iterateDeletedStructs(tr, stackItem.deletions, (item) => {
|
|
if (item instanceof Item && um.scope.some((type) => type === tr.doc || isParentOf(
|
|
/** @type {AbstractType<any>} */
|
|
type,
|
|
item
|
|
))) {
|
|
keepItem(item, false);
|
|
}
|
|
});
|
|
};
|
|
var popStackItem = (undoManager2, stack, eventType) => {
|
|
let _tr = null;
|
|
const doc2 = undoManager2.doc;
|
|
const scope = undoManager2.scope;
|
|
transact(doc2, (transaction) => {
|
|
while (stack.length > 0 && undoManager2.currStackItem === null) {
|
|
const store2 = doc2.store;
|
|
const stackItem = (
|
|
/** @type {StackItem} */
|
|
stack.pop()
|
|
);
|
|
const itemsToRedo = /* @__PURE__ */ new Set();
|
|
const itemsToDelete = [];
|
|
let performedChange = false;
|
|
iterateDeletedStructs(transaction, stackItem.insertions, (struct) => {
|
|
if (struct instanceof Item) {
|
|
if (struct.redone !== null) {
|
|
let { item, diff } = followRedone(store2, struct.id);
|
|
if (diff > 0) {
|
|
item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + diff));
|
|
}
|
|
struct = item;
|
|
}
|
|
if (!struct.deleted && scope.some((type) => type === transaction.doc || isParentOf(
|
|
/** @type {AbstractType<any>} */
|
|
type,
|
|
/** @type {Item} */
|
|
struct
|
|
))) {
|
|
itemsToDelete.push(struct);
|
|
}
|
|
}
|
|
});
|
|
iterateDeletedStructs(transaction, stackItem.deletions, (struct) => {
|
|
if (struct instanceof Item && scope.some((type) => type === transaction.doc || isParentOf(
|
|
/** @type {AbstractType<any>} */
|
|
type,
|
|
struct
|
|
)) && // Never redo structs in stackItem.insertions because they were created and deleted in the same capture interval.
|
|
!isDeleted(stackItem.insertions, struct.id)) {
|
|
itemsToRedo.add(struct);
|
|
}
|
|
});
|
|
itemsToRedo.forEach((struct) => {
|
|
performedChange = redoItem(transaction, struct, itemsToRedo, stackItem.insertions, undoManager2.ignoreRemoteMapChanges, undoManager2) !== null || performedChange;
|
|
});
|
|
for (let i = itemsToDelete.length - 1; i >= 0; i--) {
|
|
const item = itemsToDelete[i];
|
|
if (undoManager2.deleteFilter(item)) {
|
|
item.delete(transaction);
|
|
performedChange = true;
|
|
}
|
|
}
|
|
undoManager2.currStackItem = performedChange ? stackItem : null;
|
|
}
|
|
transaction.changed.forEach((subProps, type) => {
|
|
if (subProps.has(null) && type._searchMarker) {
|
|
type._searchMarker.length = 0;
|
|
}
|
|
});
|
|
_tr = transaction;
|
|
}, undoManager2);
|
|
const res = undoManager2.currStackItem;
|
|
if (res != null) {
|
|
const changedParentTypes = _tr.changedParentTypes;
|
|
undoManager2.emit("stack-item-popped", [{ stackItem: res, type: eventType, changedParentTypes, origin: undoManager2 }, undoManager2]);
|
|
undoManager2.currStackItem = null;
|
|
}
|
|
return res;
|
|
};
|
|
var UndoManager = class extends ObservableV2 {
|
|
/**
|
|
* @param {Doc|AbstractType<any>|Array<AbstractType<any>>} typeScope Limits the scope of the UndoManager. If this is set to a ydoc instance, all changes on that ydoc will be undone. If set to a specific type, only changes on that type or its children will be undone. Also accepts an array of types.
|
|
* @param {UndoManagerOptions} options
|
|
*/
|
|
constructor(typeScope, {
|
|
captureTimeout = 500,
|
|
captureTransaction = (_tr) => true,
|
|
deleteFilter = () => true,
|
|
trackedOrigins = /* @__PURE__ */ new Set([null]),
|
|
ignoreRemoteMapChanges = false,
|
|
doc: doc2 = (
|
|
/** @type {Doc} */
|
|
isArray(typeScope) ? typeScope[0].doc : typeScope instanceof Doc ? typeScope : typeScope.doc
|
|
)
|
|
} = {}) {
|
|
super();
|
|
this.scope = [];
|
|
this.doc = doc2;
|
|
this.addToScope(typeScope);
|
|
this.deleteFilter = deleteFilter;
|
|
trackedOrigins.add(this);
|
|
this.trackedOrigins = trackedOrigins;
|
|
this.captureTransaction = captureTransaction;
|
|
this.undoStack = [];
|
|
this.redoStack = [];
|
|
this.undoing = false;
|
|
this.redoing = false;
|
|
this.currStackItem = null;
|
|
this.lastChange = 0;
|
|
this.ignoreRemoteMapChanges = ignoreRemoteMapChanges;
|
|
this.captureTimeout = captureTimeout;
|
|
this.afterTransactionHandler = (transaction) => {
|
|
if (!this.captureTransaction(transaction) || !this.scope.some((type) => transaction.changedParentTypes.has(
|
|
/** @type {AbstractType<any>} */
|
|
type
|
|
) || type === this.doc) || !this.trackedOrigins.has(transaction.origin) && (!transaction.origin || !this.trackedOrigins.has(transaction.origin.constructor))) {
|
|
return;
|
|
}
|
|
const undoing = this.undoing;
|
|
const redoing = this.redoing;
|
|
const stack = undoing ? this.redoStack : this.undoStack;
|
|
if (undoing) {
|
|
this.stopCapturing();
|
|
} else if (!redoing) {
|
|
this.clear(false, true);
|
|
}
|
|
const insertions = new DeleteSet();
|
|
transaction.afterState.forEach((endClock, client) => {
|
|
const startClock = transaction.beforeState.get(client) || 0;
|
|
const len = endClock - startClock;
|
|
if (len > 0) {
|
|
addToDeleteSet(insertions, client, startClock, len);
|
|
}
|
|
});
|
|
const now = getUnixTime();
|
|
let didAdd = false;
|
|
if (this.lastChange > 0 && now - this.lastChange < this.captureTimeout && stack.length > 0 && !undoing && !redoing) {
|
|
const lastOp = stack[stack.length - 1];
|
|
lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet]);
|
|
lastOp.insertions = mergeDeleteSets([lastOp.insertions, insertions]);
|
|
} else {
|
|
stack.push(new StackItem(transaction.deleteSet, insertions));
|
|
didAdd = true;
|
|
}
|
|
if (!undoing && !redoing) {
|
|
this.lastChange = now;
|
|
}
|
|
iterateDeletedStructs(
|
|
transaction,
|
|
transaction.deleteSet,
|
|
/** @param {Item|GC} item */
|
|
(item) => {
|
|
if (item instanceof Item && this.scope.some((type) => type === transaction.doc || isParentOf(
|
|
/** @type {AbstractType<any>} */
|
|
type,
|
|
item
|
|
))) {
|
|
keepItem(item, true);
|
|
}
|
|
}
|
|
);
|
|
const changeEvent = [{ stackItem: stack[stack.length - 1], origin: transaction.origin, type: undoing ? "redo" : "undo", changedParentTypes: transaction.changedParentTypes }, this];
|
|
if (didAdd) {
|
|
this.emit("stack-item-added", changeEvent);
|
|
} else {
|
|
this.emit("stack-item-updated", changeEvent);
|
|
}
|
|
};
|
|
this.doc.on("afterTransaction", this.afterTransactionHandler);
|
|
this.doc.on("destroy", () => {
|
|
this.destroy();
|
|
});
|
|
}
|
|
/**
|
|
* Extend the scope.
|
|
*
|
|
* @param {Array<AbstractType<any> | Doc> | AbstractType<any> | Doc} ytypes
|
|
*/
|
|
addToScope(ytypes) {
|
|
const tmpSet = new Set(this.scope);
|
|
ytypes = isArray(ytypes) ? ytypes : [ytypes];
|
|
ytypes.forEach((ytype) => {
|
|
if (!tmpSet.has(ytype)) {
|
|
tmpSet.add(ytype);
|
|
if (ytype instanceof AbstractType ? ytype.doc !== this.doc : ytype !== this.doc) warn("[yjs#509] Not same Y.Doc");
|
|
this.scope.push(ytype);
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* @param {any} origin
|
|
*/
|
|
addTrackedOrigin(origin2) {
|
|
this.trackedOrigins.add(origin2);
|
|
}
|
|
/**
|
|
* @param {any} origin
|
|
*/
|
|
removeTrackedOrigin(origin2) {
|
|
this.trackedOrigins.delete(origin2);
|
|
}
|
|
clear(clearUndoStack = true, clearRedoStack = true) {
|
|
if (clearUndoStack && this.canUndo() || clearRedoStack && this.canRedo()) {
|
|
this.doc.transact((tr) => {
|
|
if (clearUndoStack) {
|
|
this.undoStack.forEach((item) => clearUndoManagerStackItem(tr, this, item));
|
|
this.undoStack = [];
|
|
}
|
|
if (clearRedoStack) {
|
|
this.redoStack.forEach((item) => clearUndoManagerStackItem(tr, this, item));
|
|
this.redoStack = [];
|
|
}
|
|
this.emit("stack-cleared", [{ undoStackCleared: clearUndoStack, redoStackCleared: clearRedoStack }]);
|
|
});
|
|
}
|
|
}
|
|
/**
|
|
* UndoManager merges Undo-StackItem if they are created within time-gap
|
|
* smaller than `options.captureTimeout`. Call `um.stopCapturing()` so that the next
|
|
* StackItem won't be merged.
|
|
*
|
|
*
|
|
* @example
|
|
* // without stopCapturing
|
|
* ytext.insert(0, 'a')
|
|
* ytext.insert(1, 'b')
|
|
* um.undo()
|
|
* ytext.toString() // => '' (note that 'ab' was removed)
|
|
* // with stopCapturing
|
|
* ytext.insert(0, 'a')
|
|
* um.stopCapturing()
|
|
* ytext.insert(0, 'b')
|
|
* um.undo()
|
|
* ytext.toString() // => 'a' (note that only 'b' was removed)
|
|
*
|
|
*/
|
|
stopCapturing() {
|
|
this.lastChange = 0;
|
|
}
|
|
/**
|
|
* Undo last changes on type.
|
|
*
|
|
* @return {StackItem?} Returns StackItem if a change was applied
|
|
*/
|
|
undo() {
|
|
this.undoing = true;
|
|
let res;
|
|
try {
|
|
res = popStackItem(this, this.undoStack, "undo");
|
|
} finally {
|
|
this.undoing = false;
|
|
}
|
|
return res;
|
|
}
|
|
/**
|
|
* Redo last undo operation.
|
|
*
|
|
* @return {StackItem?} Returns StackItem if a change was applied
|
|
*/
|
|
redo() {
|
|
this.redoing = true;
|
|
let res;
|
|
try {
|
|
res = popStackItem(this, this.redoStack, "redo");
|
|
} finally {
|
|
this.redoing = false;
|
|
}
|
|
return res;
|
|
}
|
|
/**
|
|
* Are undo steps available?
|
|
*
|
|
* @return {boolean} `true` if undo is possible
|
|
*/
|
|
canUndo() {
|
|
return this.undoStack.length > 0;
|
|
}
|
|
/**
|
|
* Are redo steps available?
|
|
*
|
|
* @return {boolean} `true` if redo is possible
|
|
*/
|
|
canRedo() {
|
|
return this.redoStack.length > 0;
|
|
}
|
|
destroy() {
|
|
this.trackedOrigins.delete(this);
|
|
this.doc.off("afterTransaction", this.afterTransactionHandler);
|
|
super.destroy();
|
|
}
|
|
};
|
|
function* lazyStructReaderGenerator(decoder) {
|
|
const numOfStateUpdates = readVarUint(decoder.restDecoder);
|
|
for (let i = 0; i < numOfStateUpdates; i++) {
|
|
const numberOfStructs = readVarUint(decoder.restDecoder);
|
|
const client = decoder.readClient();
|
|
let clock = readVarUint(decoder.restDecoder);
|
|
for (let i2 = 0; i2 < numberOfStructs; i2++) {
|
|
const info = decoder.readInfo();
|
|
if (info === 10) {
|
|
const len = readVarUint(decoder.restDecoder);
|
|
yield new Skip(createID(client, clock), len);
|
|
clock += len;
|
|
} else if ((BITS5 & info) !== 0) {
|
|
const cantCopyParentInfo = (info & (BIT7 | BIT8)) === 0;
|
|
const struct = new Item(
|
|
createID(client, clock),
|
|
null,
|
|
// left
|
|
(info & BIT8) === BIT8 ? decoder.readLeftID() : null,
|
|
// origin
|
|
null,
|
|
// right
|
|
(info & BIT7) === BIT7 ? decoder.readRightID() : null,
|
|
// right origin
|
|
// @ts-ignore Force writing a string here.
|
|
cantCopyParentInfo ? decoder.readParentInfo() ? decoder.readString() : decoder.readLeftID() : null,
|
|
// parent
|
|
cantCopyParentInfo && (info & BIT6) === BIT6 ? decoder.readString() : null,
|
|
// parentSub
|
|
readItemContent(decoder, info)
|
|
// item content
|
|
);
|
|
yield struct;
|
|
clock += struct.length;
|
|
} else {
|
|
const len = decoder.readLen();
|
|
yield new GC(createID(client, clock), len);
|
|
clock += len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var LazyStructReader = class {
|
|
/**
|
|
* @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
|
|
* @param {boolean} filterSkips
|
|
*/
|
|
constructor(decoder, filterSkips) {
|
|
this.gen = lazyStructReaderGenerator(decoder);
|
|
this.curr = null;
|
|
this.done = false;
|
|
this.filterSkips = filterSkips;
|
|
this.next();
|
|
}
|
|
/**
|
|
* @return {Item | GC | Skip |null}
|
|
*/
|
|
next() {
|
|
do {
|
|
this.curr = this.gen.next().value || null;
|
|
} while (this.filterSkips && this.curr !== null && this.curr.constructor === Skip);
|
|
return this.curr;
|
|
}
|
|
};
|
|
var logUpdate = (update) => logUpdateV2(update, UpdateDecoderV1);
|
|
var logUpdateV2 = (update, YDecoder = UpdateDecoderV2) => {
|
|
const structs = [];
|
|
const updateDecoder = new YDecoder(createDecoder(update));
|
|
const lazyDecoder = new LazyStructReader(updateDecoder, false);
|
|
for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
|
|
structs.push(curr);
|
|
}
|
|
print("Structs: ", structs);
|
|
const ds = readDeleteSet(updateDecoder);
|
|
print("DeleteSet: ", ds);
|
|
};
|
|
var decodeUpdate = (update) => decodeUpdateV2(update, UpdateDecoderV1);
|
|
var decodeUpdateV2 = (update, YDecoder = UpdateDecoderV2) => {
|
|
const structs = [];
|
|
const updateDecoder = new YDecoder(createDecoder(update));
|
|
const lazyDecoder = new LazyStructReader(updateDecoder, false);
|
|
for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
|
|
structs.push(curr);
|
|
}
|
|
return {
|
|
structs,
|
|
ds: readDeleteSet(updateDecoder)
|
|
};
|
|
};
|
|
var LazyStructWriter = class {
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
*/
|
|
constructor(encoder) {
|
|
this.currClient = 0;
|
|
this.startClock = 0;
|
|
this.written = 0;
|
|
this.encoder = encoder;
|
|
this.clientStructs = [];
|
|
}
|
|
};
|
|
var mergeUpdates = (updates) => mergeUpdatesV2(updates, UpdateDecoderV1, UpdateEncoderV1);
|
|
var encodeStateVectorFromUpdateV2 = (update, YEncoder = DSEncoderV2, YDecoder = UpdateDecoderV2) => {
|
|
const encoder = new YEncoder();
|
|
const updateDecoder = new LazyStructReader(new YDecoder(createDecoder(update)), false);
|
|
let curr = updateDecoder.curr;
|
|
if (curr !== null) {
|
|
let size2 = 0;
|
|
let currClient = curr.id.client;
|
|
let stopCounting = curr.id.clock !== 0;
|
|
let currClock = stopCounting ? 0 : curr.id.clock + curr.length;
|
|
for (; curr !== null; curr = updateDecoder.next()) {
|
|
if (currClient !== curr.id.client) {
|
|
if (currClock !== 0) {
|
|
size2++;
|
|
writeVarUint(encoder.restEncoder, currClient);
|
|
writeVarUint(encoder.restEncoder, currClock);
|
|
}
|
|
currClient = curr.id.client;
|
|
currClock = 0;
|
|
stopCounting = curr.id.clock !== 0;
|
|
}
|
|
if (curr.constructor === Skip) {
|
|
stopCounting = true;
|
|
}
|
|
if (!stopCounting) {
|
|
currClock = curr.id.clock + curr.length;
|
|
}
|
|
}
|
|
if (currClock !== 0) {
|
|
size2++;
|
|
writeVarUint(encoder.restEncoder, currClient);
|
|
writeVarUint(encoder.restEncoder, currClock);
|
|
}
|
|
const enc = createEncoder();
|
|
writeVarUint(enc, size2);
|
|
writeBinaryEncoder(enc, encoder.restEncoder);
|
|
encoder.restEncoder = enc;
|
|
return encoder.toUint8Array();
|
|
} else {
|
|
writeVarUint(encoder.restEncoder, 0);
|
|
return encoder.toUint8Array();
|
|
}
|
|
};
|
|
var encodeStateVectorFromUpdate = (update) => encodeStateVectorFromUpdateV2(update, DSEncoderV1, UpdateDecoderV1);
|
|
var parseUpdateMetaV2 = (update, YDecoder = UpdateDecoderV2) => {
|
|
const from2 = /* @__PURE__ */ new Map();
|
|
const to = /* @__PURE__ */ new Map();
|
|
const updateDecoder = new LazyStructReader(new YDecoder(createDecoder(update)), false);
|
|
let curr = updateDecoder.curr;
|
|
if (curr !== null) {
|
|
let currClient = curr.id.client;
|
|
let currClock = curr.id.clock;
|
|
from2.set(currClient, currClock);
|
|
for (; curr !== null; curr = updateDecoder.next()) {
|
|
if (currClient !== curr.id.client) {
|
|
to.set(currClient, currClock);
|
|
from2.set(curr.id.client, curr.id.clock);
|
|
currClient = curr.id.client;
|
|
}
|
|
currClock = curr.id.clock + curr.length;
|
|
}
|
|
to.set(currClient, currClock);
|
|
}
|
|
return { from: from2, to };
|
|
};
|
|
var parseUpdateMeta = (update) => parseUpdateMetaV2(update, UpdateDecoderV1);
|
|
var sliceStruct = (left, diff) => {
|
|
if (left.constructor === GC) {
|
|
const { client, clock } = left.id;
|
|
return new GC(createID(client, clock + diff), left.length - diff);
|
|
} else if (left.constructor === Skip) {
|
|
const { client, clock } = left.id;
|
|
return new Skip(createID(client, clock + diff), left.length - diff);
|
|
} else {
|
|
const leftItem = (
|
|
/** @type {Item} */
|
|
left
|
|
);
|
|
const { client, clock } = leftItem.id;
|
|
return new Item(
|
|
createID(client, clock + diff),
|
|
null,
|
|
createID(client, clock + diff - 1),
|
|
null,
|
|
leftItem.rightOrigin,
|
|
leftItem.parent,
|
|
leftItem.parentSub,
|
|
leftItem.content.splice(diff)
|
|
);
|
|
}
|
|
};
|
|
var mergeUpdatesV2 = (updates, YDecoder = UpdateDecoderV2, YEncoder = UpdateEncoderV2) => {
|
|
if (updates.length === 1) {
|
|
return updates[0];
|
|
}
|
|
const updateDecoders = updates.map((update) => new YDecoder(createDecoder(update)));
|
|
let lazyStructDecoders = updateDecoders.map((decoder) => new LazyStructReader(decoder, true));
|
|
let currWrite = null;
|
|
const updateEncoder = new YEncoder();
|
|
const lazyStructEncoder = new LazyStructWriter(updateEncoder);
|
|
while (true) {
|
|
lazyStructDecoders = lazyStructDecoders.filter((dec) => dec.curr !== null);
|
|
lazyStructDecoders.sort(
|
|
/** @type {function(any,any):number} */
|
|
(dec1, dec2) => {
|
|
if (dec1.curr.id.client === dec2.curr.id.client) {
|
|
const clockDiff = dec1.curr.id.clock - dec2.curr.id.clock;
|
|
if (clockDiff === 0) {
|
|
return dec1.curr.constructor === dec2.curr.constructor ? 0 : dec1.curr.constructor === Skip ? 1 : -1;
|
|
} else {
|
|
return clockDiff;
|
|
}
|
|
} else {
|
|
return dec2.curr.id.client - dec1.curr.id.client;
|
|
}
|
|
}
|
|
);
|
|
if (lazyStructDecoders.length === 0) {
|
|
break;
|
|
}
|
|
const currDecoder = lazyStructDecoders[0];
|
|
const firstClient = (
|
|
/** @type {Item | GC} */
|
|
currDecoder.curr.id.client
|
|
);
|
|
if (currWrite !== null) {
|
|
let curr = (
|
|
/** @type {Item | GC | null} */
|
|
currDecoder.curr
|
|
);
|
|
let iterated = false;
|
|
while (curr !== null && curr.id.clock + curr.length <= currWrite.struct.id.clock + currWrite.struct.length && curr.id.client >= currWrite.struct.id.client) {
|
|
curr = currDecoder.next();
|
|
iterated = true;
|
|
}
|
|
if (curr === null || // current decoder is empty
|
|
curr.id.client !== firstClient || // check whether there is another decoder that has has updates from `firstClient`
|
|
iterated && curr.id.clock > currWrite.struct.id.clock + currWrite.struct.length) {
|
|
continue;
|
|
}
|
|
if (firstClient !== currWrite.struct.id.client) {
|
|
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset);
|
|
currWrite = { struct: curr, offset: 0 };
|
|
currDecoder.next();
|
|
} else {
|
|
if (currWrite.struct.id.clock + currWrite.struct.length < curr.id.clock) {
|
|
if (currWrite.struct.constructor === Skip) {
|
|
currWrite.struct.length = curr.id.clock + curr.length - currWrite.struct.id.clock;
|
|
} else {
|
|
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset);
|
|
const diff = curr.id.clock - currWrite.struct.id.clock - currWrite.struct.length;
|
|
const struct = new Skip(createID(firstClient, currWrite.struct.id.clock + currWrite.struct.length), diff);
|
|
currWrite = { struct, offset: 0 };
|
|
}
|
|
} else {
|
|
const diff = currWrite.struct.id.clock + currWrite.struct.length - curr.id.clock;
|
|
if (diff > 0) {
|
|
if (currWrite.struct.constructor === Skip) {
|
|
currWrite.struct.length -= diff;
|
|
} else {
|
|
curr = sliceStruct(curr, diff);
|
|
}
|
|
}
|
|
if (!currWrite.struct.mergeWith(
|
|
/** @type {any} */
|
|
curr
|
|
)) {
|
|
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset);
|
|
currWrite = { struct: curr, offset: 0 };
|
|
currDecoder.next();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
currWrite = { struct: (
|
|
/** @type {Item | GC} */
|
|
currDecoder.curr
|
|
), offset: 0 };
|
|
currDecoder.next();
|
|
}
|
|
for (let next = currDecoder.curr; next !== null && next.id.client === firstClient && next.id.clock === currWrite.struct.id.clock + currWrite.struct.length && next.constructor !== Skip; next = currDecoder.next()) {
|
|
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset);
|
|
currWrite = { struct: next, offset: 0 };
|
|
}
|
|
}
|
|
if (currWrite !== null) {
|
|
writeStructToLazyStructWriter(lazyStructEncoder, currWrite.struct, currWrite.offset);
|
|
currWrite = null;
|
|
}
|
|
finishLazyStructWriting(lazyStructEncoder);
|
|
const dss = updateDecoders.map((decoder) => readDeleteSet(decoder));
|
|
const ds = mergeDeleteSets(dss);
|
|
writeDeleteSet(updateEncoder, ds);
|
|
return updateEncoder.toUint8Array();
|
|
};
|
|
var diffUpdateV2 = (update, sv, YDecoder = UpdateDecoderV2, YEncoder = UpdateEncoderV2) => {
|
|
const state = decodeStateVector(sv);
|
|
const encoder = new YEncoder();
|
|
const lazyStructWriter = new LazyStructWriter(encoder);
|
|
const decoder = new YDecoder(createDecoder(update));
|
|
const reader = new LazyStructReader(decoder, false);
|
|
while (reader.curr) {
|
|
const curr = reader.curr;
|
|
const currClient = curr.id.client;
|
|
const svClock = state.get(currClient) || 0;
|
|
if (reader.curr.constructor === Skip) {
|
|
reader.next();
|
|
continue;
|
|
}
|
|
if (curr.id.clock + curr.length > svClock) {
|
|
writeStructToLazyStructWriter(lazyStructWriter, curr, max(svClock - curr.id.clock, 0));
|
|
reader.next();
|
|
while (reader.curr && reader.curr.id.client === currClient) {
|
|
writeStructToLazyStructWriter(lazyStructWriter, reader.curr, 0);
|
|
reader.next();
|
|
}
|
|
} else {
|
|
while (reader.curr && reader.curr.id.client === currClient && reader.curr.id.clock + reader.curr.length <= svClock) {
|
|
reader.next();
|
|
}
|
|
}
|
|
}
|
|
finishLazyStructWriting(lazyStructWriter);
|
|
const ds = readDeleteSet(decoder);
|
|
writeDeleteSet(encoder, ds);
|
|
return encoder.toUint8Array();
|
|
};
|
|
var diffUpdate = (update, sv) => diffUpdateV2(update, sv, UpdateDecoderV1, UpdateEncoderV1);
|
|
var flushLazyStructWriter = (lazyWriter) => {
|
|
if (lazyWriter.written > 0) {
|
|
lazyWriter.clientStructs.push({ written: lazyWriter.written, restEncoder: toUint8Array(lazyWriter.encoder.restEncoder) });
|
|
lazyWriter.encoder.restEncoder = createEncoder();
|
|
lazyWriter.written = 0;
|
|
}
|
|
};
|
|
var writeStructToLazyStructWriter = (lazyWriter, struct, offset) => {
|
|
if (lazyWriter.written > 0 && lazyWriter.currClient !== struct.id.client) {
|
|
flushLazyStructWriter(lazyWriter);
|
|
}
|
|
if (lazyWriter.written === 0) {
|
|
lazyWriter.currClient = struct.id.client;
|
|
lazyWriter.encoder.writeClient(struct.id.client);
|
|
writeVarUint(lazyWriter.encoder.restEncoder, struct.id.clock + offset);
|
|
}
|
|
struct.write(lazyWriter.encoder, offset);
|
|
lazyWriter.written++;
|
|
};
|
|
var finishLazyStructWriting = (lazyWriter) => {
|
|
flushLazyStructWriter(lazyWriter);
|
|
const restEncoder = lazyWriter.encoder.restEncoder;
|
|
writeVarUint(restEncoder, lazyWriter.clientStructs.length);
|
|
for (let i = 0; i < lazyWriter.clientStructs.length; i++) {
|
|
const partStructs = lazyWriter.clientStructs[i];
|
|
writeVarUint(restEncoder, partStructs.written);
|
|
writeUint8Array(restEncoder, partStructs.restEncoder);
|
|
}
|
|
};
|
|
var convertUpdateFormat = (update, blockTransformer, YDecoder, YEncoder) => {
|
|
const updateDecoder = new YDecoder(createDecoder(update));
|
|
const lazyDecoder = new LazyStructReader(updateDecoder, false);
|
|
const updateEncoder = new YEncoder();
|
|
const lazyWriter = new LazyStructWriter(updateEncoder);
|
|
for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) {
|
|
writeStructToLazyStructWriter(lazyWriter, blockTransformer(curr), 0);
|
|
}
|
|
finishLazyStructWriting(lazyWriter);
|
|
const ds = readDeleteSet(updateDecoder);
|
|
writeDeleteSet(updateEncoder, ds);
|
|
return updateEncoder.toUint8Array();
|
|
};
|
|
var createObfuscator = ({ formatting = true, subdocs = true, yxml = true } = {}) => {
|
|
let i = 0;
|
|
const mapKeyCache = create();
|
|
const nodeNameCache = create();
|
|
const formattingKeyCache = create();
|
|
const formattingValueCache = create();
|
|
formattingValueCache.set(null, null);
|
|
return (block) => {
|
|
switch (block.constructor) {
|
|
case GC:
|
|
case Skip:
|
|
return block;
|
|
case Item: {
|
|
const item = (
|
|
/** @type {Item} */
|
|
block
|
|
);
|
|
const content = item.content;
|
|
switch (content.constructor) {
|
|
case ContentDeleted:
|
|
break;
|
|
case ContentType: {
|
|
if (yxml) {
|
|
const type = (
|
|
/** @type {ContentType} */
|
|
content.type
|
|
);
|
|
if (type instanceof YXmlElement) {
|
|
type.nodeName = setIfUndefined(nodeNameCache, type.nodeName, () => "node-" + i);
|
|
}
|
|
if (type instanceof YXmlHook) {
|
|
type.hookName = setIfUndefined(nodeNameCache, type.hookName, () => "hook-" + i);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ContentAny: {
|
|
const c = (
|
|
/** @type {ContentAny} */
|
|
content
|
|
);
|
|
c.arr = c.arr.map(() => i);
|
|
break;
|
|
}
|
|
case ContentBinary: {
|
|
const c = (
|
|
/** @type {ContentBinary} */
|
|
content
|
|
);
|
|
c.content = new Uint8Array([i]);
|
|
break;
|
|
}
|
|
case ContentDoc: {
|
|
const c = (
|
|
/** @type {ContentDoc} */
|
|
content
|
|
);
|
|
if (subdocs) {
|
|
c.opts = {};
|
|
c.doc.guid = i + "";
|
|
}
|
|
break;
|
|
}
|
|
case ContentEmbed: {
|
|
const c = (
|
|
/** @type {ContentEmbed} */
|
|
content
|
|
);
|
|
c.embed = {};
|
|
break;
|
|
}
|
|
case ContentFormat: {
|
|
const c = (
|
|
/** @type {ContentFormat} */
|
|
content
|
|
);
|
|
if (formatting) {
|
|
c.key = setIfUndefined(formattingKeyCache, c.key, () => i + "");
|
|
c.value = setIfUndefined(formattingValueCache, c.value, () => ({ i }));
|
|
}
|
|
break;
|
|
}
|
|
case ContentJSON: {
|
|
const c = (
|
|
/** @type {ContentJSON} */
|
|
content
|
|
);
|
|
c.arr = c.arr.map(() => i);
|
|
break;
|
|
}
|
|
case ContentString: {
|
|
const c = (
|
|
/** @type {ContentString} */
|
|
content
|
|
);
|
|
c.str = repeat(i % 10 + "", c.str.length);
|
|
break;
|
|
}
|
|
default:
|
|
unexpectedCase();
|
|
}
|
|
if (item.parentSub) {
|
|
item.parentSub = setIfUndefined(mapKeyCache, item.parentSub, () => i + "");
|
|
}
|
|
i++;
|
|
return block;
|
|
}
|
|
default:
|
|
unexpectedCase();
|
|
}
|
|
};
|
|
};
|
|
var obfuscateUpdate = (update, opts) => convertUpdateFormat(update, createObfuscator(opts), UpdateDecoderV1, UpdateEncoderV1);
|
|
var obfuscateUpdateV2 = (update, opts) => convertUpdateFormat(update, createObfuscator(opts), UpdateDecoderV2, UpdateEncoderV2);
|
|
var convertUpdateFormatV1ToV2 = (update) => convertUpdateFormat(update, id, UpdateDecoderV1, UpdateEncoderV2);
|
|
var convertUpdateFormatV2ToV1 = (update) => convertUpdateFormat(update, id, UpdateDecoderV2, UpdateEncoderV1);
|
|
var errorComputeChanges = "You must not compute changes after the event-handler fired.";
|
|
var YEvent = class {
|
|
/**
|
|
* @param {T} target The changed type.
|
|
* @param {Transaction} transaction
|
|
*/
|
|
constructor(target, transaction) {
|
|
this.target = target;
|
|
this.currentTarget = target;
|
|
this.transaction = transaction;
|
|
this._changes = null;
|
|
this._keys = null;
|
|
this._delta = null;
|
|
this._path = null;
|
|
}
|
|
/**
|
|
* Computes the path from `y` to the changed type.
|
|
*
|
|
* @todo v14 should standardize on path: Array<{parent, index}> because that is easier to work with.
|
|
*
|
|
* The following property holds:
|
|
* @example
|
|
* let type = y
|
|
* event.path.forEach(dir => {
|
|
* type = type.get(dir)
|
|
* })
|
|
* type === event.target // => true
|
|
*/
|
|
get path() {
|
|
return this._path || (this._path = getPathTo(this.currentTarget, this.target));
|
|
}
|
|
/**
|
|
* Check if a struct is deleted by this event.
|
|
*
|
|
* In contrast to change.deleted, this method also returns true if the struct was added and then deleted.
|
|
*
|
|
* @param {AbstractStruct} struct
|
|
* @return {boolean}
|
|
*/
|
|
deletes(struct) {
|
|
return isDeleted(this.transaction.deleteSet, struct.id);
|
|
}
|
|
/**
|
|
* @type {Map<string, { action: 'add' | 'update' | 'delete', oldValue: any }>}
|
|
*/
|
|
get keys() {
|
|
if (this._keys === null) {
|
|
if (this.transaction.doc._transactionCleanups.length === 0) {
|
|
throw create3(errorComputeChanges);
|
|
}
|
|
const keys2 = /* @__PURE__ */ new Map();
|
|
const target = this.target;
|
|
const changed = (
|
|
/** @type Set<string|null> */
|
|
this.transaction.changed.get(target)
|
|
);
|
|
changed.forEach((key) => {
|
|
if (key !== null) {
|
|
const item = (
|
|
/** @type {Item} */
|
|
target._map.get(key)
|
|
);
|
|
let action;
|
|
let oldValue;
|
|
if (this.adds(item)) {
|
|
let prev = item.left;
|
|
while (prev !== null && this.adds(prev)) {
|
|
prev = prev.left;
|
|
}
|
|
if (this.deletes(item)) {
|
|
if (prev !== null && this.deletes(prev)) {
|
|
action = "delete";
|
|
oldValue = last(prev.content.getContent());
|
|
} else {
|
|
return;
|
|
}
|
|
} else {
|
|
if (prev !== null && this.deletes(prev)) {
|
|
action = "update";
|
|
oldValue = last(prev.content.getContent());
|
|
} else {
|
|
action = "add";
|
|
oldValue = void 0;
|
|
}
|
|
}
|
|
} else {
|
|
if (this.deletes(item)) {
|
|
action = "delete";
|
|
oldValue = last(
|
|
/** @type {Item} */
|
|
item.content.getContent()
|
|
);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
keys2.set(key, { action, oldValue });
|
|
}
|
|
});
|
|
this._keys = keys2;
|
|
}
|
|
return this._keys;
|
|
}
|
|
/**
|
|
* This is a computed property. Note that this can only be safely computed during the
|
|
* event call. Computing this property after other changes happened might result in
|
|
* unexpected behavior (incorrect computation of deltas). A safe way to collect changes
|
|
* is to store the `changes` or the `delta` object. Avoid storing the `transaction` object.
|
|
*
|
|
* @type {Array<{insert?: string | Array<any> | object | AbstractType<any>, retain?: number, delete?: number, attributes?: Object<string, any>}>}
|
|
*/
|
|
get delta() {
|
|
return this.changes.delta;
|
|
}
|
|
/**
|
|
* Check if a struct is added by this event.
|
|
*
|
|
* In contrast to change.deleted, this method also returns true if the struct was added and then deleted.
|
|
*
|
|
* @param {AbstractStruct} struct
|
|
* @return {boolean}
|
|
*/
|
|
adds(struct) {
|
|
return struct.id.clock >= (this.transaction.beforeState.get(struct.id.client) || 0);
|
|
}
|
|
/**
|
|
* This is a computed property. Note that this can only be safely computed during the
|
|
* event call. Computing this property after other changes happened might result in
|
|
* unexpected behavior (incorrect computation of deltas). A safe way to collect changes
|
|
* is to store the `changes` or the `delta` object. Avoid storing the `transaction` object.
|
|
*
|
|
* @type {{added:Set<Item>,deleted:Set<Item>,keys:Map<string,{action:'add'|'update'|'delete',oldValue:any}>,delta:Array<{insert?:Array<any>|string, delete?:number, retain?:number}>}}
|
|
*/
|
|
get changes() {
|
|
let changes = this._changes;
|
|
if (changes === null) {
|
|
if (this.transaction.doc._transactionCleanups.length === 0) {
|
|
throw create3(errorComputeChanges);
|
|
}
|
|
const target = this.target;
|
|
const added = create2();
|
|
const deleted = create2();
|
|
const delta = [];
|
|
changes = {
|
|
added,
|
|
deleted,
|
|
delta,
|
|
keys: this.keys
|
|
};
|
|
const changed = (
|
|
/** @type Set<string|null> */
|
|
this.transaction.changed.get(target)
|
|
);
|
|
if (changed.has(null)) {
|
|
let lastOp = null;
|
|
const packOp = () => {
|
|
if (lastOp) {
|
|
delta.push(lastOp);
|
|
}
|
|
};
|
|
for (let item = target._start; item !== null; item = item.right) {
|
|
if (item.deleted) {
|
|
if (this.deletes(item) && !this.adds(item)) {
|
|
if (lastOp === null || lastOp.delete === void 0) {
|
|
packOp();
|
|
lastOp = { delete: 0 };
|
|
}
|
|
lastOp.delete += item.length;
|
|
deleted.add(item);
|
|
}
|
|
} else {
|
|
if (this.adds(item)) {
|
|
if (lastOp === null || lastOp.insert === void 0) {
|
|
packOp();
|
|
lastOp = { insert: [] };
|
|
}
|
|
lastOp.insert = lastOp.insert.concat(item.content.getContent());
|
|
added.add(item);
|
|
} else {
|
|
if (lastOp === null || lastOp.retain === void 0) {
|
|
packOp();
|
|
lastOp = { retain: 0 };
|
|
}
|
|
lastOp.retain += item.length;
|
|
}
|
|
}
|
|
}
|
|
if (lastOp !== null && lastOp.retain === void 0) {
|
|
packOp();
|
|
}
|
|
}
|
|
this._changes = changes;
|
|
}
|
|
return (
|
|
/** @type {any} */
|
|
changes
|
|
);
|
|
}
|
|
};
|
|
var getPathTo = (parent, child) => {
|
|
const path = [];
|
|
while (child._item !== null && child !== parent) {
|
|
if (child._item.parentSub !== null) {
|
|
path.unshift(child._item.parentSub);
|
|
} else {
|
|
let i = 0;
|
|
let c = (
|
|
/** @type {AbstractType<any>} */
|
|
child._item.parent._start
|
|
);
|
|
while (c !== child._item && c !== null) {
|
|
if (!c.deleted && c.countable) {
|
|
i += c.length;
|
|
}
|
|
c = c.right;
|
|
}
|
|
path.unshift(i);
|
|
}
|
|
child = /** @type {AbstractType<any>} */
|
|
child._item.parent;
|
|
}
|
|
return path;
|
|
};
|
|
var warnPrematureAccess = () => {
|
|
warn("Invalid access: Add Yjs type to a document before reading data.");
|
|
};
|
|
var maxSearchMarker = 80;
|
|
var globalSearchMarkerTimestamp = 0;
|
|
var ArraySearchMarker = class {
|
|
/**
|
|
* @param {Item} p
|
|
* @param {number} index
|
|
*/
|
|
constructor(p, index) {
|
|
p.marker = true;
|
|
this.p = p;
|
|
this.index = index;
|
|
this.timestamp = globalSearchMarkerTimestamp++;
|
|
}
|
|
};
|
|
var refreshMarkerTimestamp = (marker) => {
|
|
marker.timestamp = globalSearchMarkerTimestamp++;
|
|
};
|
|
var overwriteMarker = (marker, p, index) => {
|
|
marker.p.marker = false;
|
|
marker.p = p;
|
|
p.marker = true;
|
|
marker.index = index;
|
|
marker.timestamp = globalSearchMarkerTimestamp++;
|
|
};
|
|
var markPosition = (searchMarker, p, index) => {
|
|
if (searchMarker.length >= maxSearchMarker) {
|
|
const marker = searchMarker.reduce((a, b) => a.timestamp < b.timestamp ? a : b);
|
|
overwriteMarker(marker, p, index);
|
|
return marker;
|
|
} else {
|
|
const pm = new ArraySearchMarker(p, index);
|
|
searchMarker.push(pm);
|
|
return pm;
|
|
}
|
|
};
|
|
var findMarker = (yarray, index) => {
|
|
if (yarray._start === null || index === 0 || yarray._searchMarker === null) {
|
|
return null;
|
|
}
|
|
const marker = yarray._searchMarker.length === 0 ? null : yarray._searchMarker.reduce((a, b) => abs(index - a.index) < abs(index - b.index) ? a : b);
|
|
let p = yarray._start;
|
|
let pindex = 0;
|
|
if (marker !== null) {
|
|
p = marker.p;
|
|
pindex = marker.index;
|
|
refreshMarkerTimestamp(marker);
|
|
}
|
|
while (p.right !== null && pindex < index) {
|
|
if (!p.deleted && p.countable) {
|
|
if (index < pindex + p.length) {
|
|
break;
|
|
}
|
|
pindex += p.length;
|
|
}
|
|
p = p.right;
|
|
}
|
|
while (p.left !== null && pindex > index) {
|
|
p = p.left;
|
|
if (!p.deleted && p.countable) {
|
|
pindex -= p.length;
|
|
}
|
|
}
|
|
while (p.left !== null && p.left.id.client === p.id.client && p.left.id.clock + p.left.length === p.id.clock) {
|
|
p = p.left;
|
|
if (!p.deleted && p.countable) {
|
|
pindex -= p.length;
|
|
}
|
|
}
|
|
if (marker !== null && abs(marker.index - pindex) < /** @type {YText|YArray<any>} */
|
|
p.parent.length / maxSearchMarker) {
|
|
overwriteMarker(marker, p, pindex);
|
|
return marker;
|
|
} else {
|
|
return markPosition(yarray._searchMarker, p, pindex);
|
|
}
|
|
};
|
|
var updateMarkerChanges = (searchMarker, index, len) => {
|
|
for (let i = searchMarker.length - 1; i >= 0; i--) {
|
|
const m = searchMarker[i];
|
|
if (len > 0) {
|
|
let p = m.p;
|
|
p.marker = false;
|
|
while (p && (p.deleted || !p.countable)) {
|
|
p = p.left;
|
|
if (p && !p.deleted && p.countable) {
|
|
m.index -= p.length;
|
|
}
|
|
}
|
|
if (p === null || p.marker === true) {
|
|
searchMarker.splice(i, 1);
|
|
continue;
|
|
}
|
|
m.p = p;
|
|
p.marker = true;
|
|
}
|
|
if (index < m.index || len > 0 && index === m.index) {
|
|
m.index = max(index, m.index + len);
|
|
}
|
|
}
|
|
};
|
|
var getTypeChildren = (t) => {
|
|
t.doc ?? warnPrematureAccess();
|
|
let s = t._start;
|
|
const arr = [];
|
|
while (s) {
|
|
arr.push(s);
|
|
s = s.right;
|
|
}
|
|
return arr;
|
|
};
|
|
var callTypeObservers = (type, transaction, event) => {
|
|
const changedType = type;
|
|
const changedParentTypes = transaction.changedParentTypes;
|
|
while (true) {
|
|
setIfUndefined(changedParentTypes, type, () => []).push(event);
|
|
if (type._item === null) {
|
|
break;
|
|
}
|
|
type = /** @type {AbstractType<any>} */
|
|
type._item.parent;
|
|
}
|
|
callEventHandlerListeners(changedType._eH, event, transaction);
|
|
};
|
|
var AbstractType = class {
|
|
constructor() {
|
|
this._item = null;
|
|
this._map = /* @__PURE__ */ new Map();
|
|
this._start = null;
|
|
this.doc = null;
|
|
this._length = 0;
|
|
this._eH = createEventHandler();
|
|
this._dEH = createEventHandler();
|
|
this._searchMarker = null;
|
|
}
|
|
/**
|
|
* @return {AbstractType<any>|null}
|
|
*/
|
|
get parent() {
|
|
return this._item ? (
|
|
/** @type {AbstractType<any>} */
|
|
this._item.parent
|
|
) : null;
|
|
}
|
|
/**
|
|
* Integrate this type into the Yjs instance.
|
|
*
|
|
* * Save this struct in the os
|
|
* * This type is sent to other client
|
|
* * Observer functions are fired
|
|
*
|
|
* @param {Doc} y The Yjs instance
|
|
* @param {Item|null} item
|
|
*/
|
|
_integrate(y, item) {
|
|
this.doc = y;
|
|
this._item = item;
|
|
}
|
|
/**
|
|
* @return {AbstractType<EventType>}
|
|
*/
|
|
_copy() {
|
|
throw methodUnimplemented();
|
|
}
|
|
/**
|
|
* Makes a copy of this data type that can be included somewhere else.
|
|
*
|
|
* Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
|
|
*
|
|
* @return {AbstractType<EventType>}
|
|
*/
|
|
clone() {
|
|
throw methodUnimplemented();
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} _encoder
|
|
*/
|
|
_write(_encoder) {
|
|
}
|
|
/**
|
|
* The first non-deleted item
|
|
*/
|
|
get _first() {
|
|
let n = this._start;
|
|
while (n !== null && n.deleted) {
|
|
n = n.right;
|
|
}
|
|
return n;
|
|
}
|
|
/**
|
|
* Creates YEvent and calls all type observers.
|
|
* Must be implemented by each type.
|
|
*
|
|
* @param {Transaction} transaction
|
|
* @param {Set<null|string>} _parentSubs Keys changed on this type. `null` if list was modified.
|
|
*/
|
|
_callObserver(transaction, _parentSubs) {
|
|
if (!transaction.local && this._searchMarker) {
|
|
this._searchMarker.length = 0;
|
|
}
|
|
}
|
|
/**
|
|
* Observe all events that are created on this type.
|
|
*
|
|
* @param {function(EventType, Transaction):void} f Observer function
|
|
*/
|
|
observe(f) {
|
|
addEventHandlerListener(this._eH, f);
|
|
}
|
|
/**
|
|
* Observe all events that are created by this type and its children.
|
|
*
|
|
* @param {function(Array<YEvent<any>>,Transaction):void} f Observer function
|
|
*/
|
|
observeDeep(f) {
|
|
addEventHandlerListener(this._dEH, f);
|
|
}
|
|
/**
|
|
* Unregister an observer function.
|
|
*
|
|
* @param {function(EventType,Transaction):void} f Observer function
|
|
*/
|
|
unobserve(f) {
|
|
removeEventHandlerListener(this._eH, f);
|
|
}
|
|
/**
|
|
* Unregister an observer function.
|
|
*
|
|
* @param {function(Array<YEvent<any>>,Transaction):void} f Observer function
|
|
*/
|
|
unobserveDeep(f) {
|
|
removeEventHandlerListener(this._dEH, f);
|
|
}
|
|
/**
|
|
* @abstract
|
|
* @return {any}
|
|
*/
|
|
toJSON() {
|
|
}
|
|
};
|
|
var typeListSlice = (type, start, end) => {
|
|
type.doc ?? warnPrematureAccess();
|
|
if (start < 0) {
|
|
start = type._length + start;
|
|
}
|
|
if (end < 0) {
|
|
end = type._length + end;
|
|
}
|
|
let len = end - start;
|
|
const cs = [];
|
|
let n = type._start;
|
|
while (n !== null && len > 0) {
|
|
if (n.countable && !n.deleted) {
|
|
const c = n.content.getContent();
|
|
if (c.length <= start) {
|
|
start -= c.length;
|
|
} else {
|
|
for (let i = start; i < c.length && len > 0; i++) {
|
|
cs.push(c[i]);
|
|
len--;
|
|
}
|
|
start = 0;
|
|
}
|
|
}
|
|
n = n.right;
|
|
}
|
|
return cs;
|
|
};
|
|
var typeListToArray = (type) => {
|
|
type.doc ?? warnPrematureAccess();
|
|
const cs = [];
|
|
let n = type._start;
|
|
while (n !== null) {
|
|
if (n.countable && !n.deleted) {
|
|
const c = n.content.getContent();
|
|
for (let i = 0; i < c.length; i++) {
|
|
cs.push(c[i]);
|
|
}
|
|
}
|
|
n = n.right;
|
|
}
|
|
return cs;
|
|
};
|
|
var typeListToArraySnapshot = (type, snapshot2) => {
|
|
const cs = [];
|
|
let n = type._start;
|
|
while (n !== null) {
|
|
if (n.countable && isVisible(n, snapshot2)) {
|
|
const c = n.content.getContent();
|
|
for (let i = 0; i < c.length; i++) {
|
|
cs.push(c[i]);
|
|
}
|
|
}
|
|
n = n.right;
|
|
}
|
|
return cs;
|
|
};
|
|
var typeListForEach = (type, f) => {
|
|
let index = 0;
|
|
let n = type._start;
|
|
type.doc ?? warnPrematureAccess();
|
|
while (n !== null) {
|
|
if (n.countable && !n.deleted) {
|
|
const c = n.content.getContent();
|
|
for (let i = 0; i < c.length; i++) {
|
|
f(c[i], index++, type);
|
|
}
|
|
}
|
|
n = n.right;
|
|
}
|
|
};
|
|
var typeListMap = (type, f) => {
|
|
const result = [];
|
|
typeListForEach(type, (c, i) => {
|
|
result.push(f(c, i, type));
|
|
});
|
|
return result;
|
|
};
|
|
var typeListCreateIterator = (type) => {
|
|
let n = type._start;
|
|
let currentContent = null;
|
|
let currentContentIndex = 0;
|
|
return {
|
|
[Symbol.iterator]() {
|
|
return this;
|
|
},
|
|
next: () => {
|
|
if (currentContent === null) {
|
|
while (n !== null && n.deleted) {
|
|
n = n.right;
|
|
}
|
|
if (n === null) {
|
|
return {
|
|
done: true,
|
|
value: void 0
|
|
};
|
|
}
|
|
currentContent = n.content.getContent();
|
|
currentContentIndex = 0;
|
|
n = n.right;
|
|
}
|
|
const value = currentContent[currentContentIndex++];
|
|
if (currentContent.length <= currentContentIndex) {
|
|
currentContent = null;
|
|
}
|
|
return {
|
|
done: false,
|
|
value
|
|
};
|
|
}
|
|
};
|
|
};
|
|
var typeListGet = (type, index) => {
|
|
type.doc ?? warnPrematureAccess();
|
|
const marker = findMarker(type, index);
|
|
let n = type._start;
|
|
if (marker !== null) {
|
|
n = marker.p;
|
|
index -= marker.index;
|
|
}
|
|
for (; n !== null; n = n.right) {
|
|
if (!n.deleted && n.countable) {
|
|
if (index < n.length) {
|
|
return n.content.getContent()[index];
|
|
}
|
|
index -= n.length;
|
|
}
|
|
}
|
|
};
|
|
var typeListInsertGenericsAfter = (transaction, parent, referenceItem, content) => {
|
|
let left = referenceItem;
|
|
const doc2 = transaction.doc;
|
|
const ownClientId = doc2.clientID;
|
|
const store2 = doc2.store;
|
|
const right = referenceItem === null ? parent._start : referenceItem.right;
|
|
let jsonContent = [];
|
|
const packJsonContent = () => {
|
|
if (jsonContent.length > 0) {
|
|
left = new Item(createID(ownClientId, getState(store2, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentAny(jsonContent));
|
|
left.integrate(transaction, 0);
|
|
jsonContent = [];
|
|
}
|
|
};
|
|
content.forEach((c) => {
|
|
if (c === null) {
|
|
jsonContent.push(c);
|
|
} else {
|
|
switch (c.constructor) {
|
|
case Number:
|
|
case Object:
|
|
case Boolean:
|
|
case Array:
|
|
case String:
|
|
jsonContent.push(c);
|
|
break;
|
|
default:
|
|
packJsonContent();
|
|
switch (c.constructor) {
|
|
case Uint8Array:
|
|
case ArrayBuffer:
|
|
left = new Item(createID(ownClientId, getState(store2, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentBinary(new Uint8Array(
|
|
/** @type {Uint8Array} */
|
|
c
|
|
)));
|
|
left.integrate(transaction, 0);
|
|
break;
|
|
case Doc:
|
|
left = new Item(createID(ownClientId, getState(store2, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentDoc(
|
|
/** @type {Doc} */
|
|
c
|
|
));
|
|
left.integrate(transaction, 0);
|
|
break;
|
|
default:
|
|
if (c instanceof AbstractType) {
|
|
left = new Item(createID(ownClientId, getState(store2, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentType(c));
|
|
left.integrate(transaction, 0);
|
|
} else {
|
|
throw new Error("Unexpected content type in insert operation");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
packJsonContent();
|
|
};
|
|
var lengthExceeded = () => create3("Length exceeded!");
|
|
var typeListInsertGenerics = (transaction, parent, index, content) => {
|
|
if (index > parent._length) {
|
|
throw lengthExceeded();
|
|
}
|
|
if (index === 0) {
|
|
if (parent._searchMarker) {
|
|
updateMarkerChanges(parent._searchMarker, index, content.length);
|
|
}
|
|
return typeListInsertGenericsAfter(transaction, parent, null, content);
|
|
}
|
|
const startIndex = index;
|
|
const marker = findMarker(parent, index);
|
|
let n = parent._start;
|
|
if (marker !== null) {
|
|
n = marker.p;
|
|
index -= marker.index;
|
|
if (index === 0) {
|
|
n = n.prev;
|
|
index += n && n.countable && !n.deleted ? n.length : 0;
|
|
}
|
|
}
|
|
for (; n !== null; n = n.right) {
|
|
if (!n.deleted && n.countable) {
|
|
if (index <= n.length) {
|
|
if (index < n.length) {
|
|
getItemCleanStart(transaction, createID(n.id.client, n.id.clock + index));
|
|
}
|
|
break;
|
|
}
|
|
index -= n.length;
|
|
}
|
|
}
|
|
if (parent._searchMarker) {
|
|
updateMarkerChanges(parent._searchMarker, startIndex, content.length);
|
|
}
|
|
return typeListInsertGenericsAfter(transaction, parent, n, content);
|
|
};
|
|
var typeListPushGenerics = (transaction, parent, content) => {
|
|
const marker = (parent._searchMarker || []).reduce((maxMarker, currMarker) => currMarker.index > maxMarker.index ? currMarker : maxMarker, { index: 0, p: parent._start });
|
|
let n = marker.p;
|
|
if (n) {
|
|
while (n.right) {
|
|
n = n.right;
|
|
}
|
|
}
|
|
return typeListInsertGenericsAfter(transaction, parent, n, content);
|
|
};
|
|
var typeListDelete = (transaction, parent, index, length3) => {
|
|
if (length3 === 0) {
|
|
return;
|
|
}
|
|
const startIndex = index;
|
|
const startLength = length3;
|
|
const marker = findMarker(parent, index);
|
|
let n = parent._start;
|
|
if (marker !== null) {
|
|
n = marker.p;
|
|
index -= marker.index;
|
|
}
|
|
for (; n !== null && index > 0; n = n.right) {
|
|
if (!n.deleted && n.countable) {
|
|
if (index < n.length) {
|
|
getItemCleanStart(transaction, createID(n.id.client, n.id.clock + index));
|
|
}
|
|
index -= n.length;
|
|
}
|
|
}
|
|
while (length3 > 0 && n !== null) {
|
|
if (!n.deleted) {
|
|
if (length3 < n.length) {
|
|
getItemCleanStart(transaction, createID(n.id.client, n.id.clock + length3));
|
|
}
|
|
n.delete(transaction);
|
|
length3 -= n.length;
|
|
}
|
|
n = n.right;
|
|
}
|
|
if (length3 > 0) {
|
|
throw lengthExceeded();
|
|
}
|
|
if (parent._searchMarker) {
|
|
updateMarkerChanges(
|
|
parent._searchMarker,
|
|
startIndex,
|
|
-startLength + length3
|
|
/* in case we remove the above exception */
|
|
);
|
|
}
|
|
};
|
|
var typeMapDelete = (transaction, parent, key) => {
|
|
const c = parent._map.get(key);
|
|
if (c !== void 0) {
|
|
c.delete(transaction);
|
|
}
|
|
};
|
|
var typeMapSet = (transaction, parent, key, value) => {
|
|
const left = parent._map.get(key) || null;
|
|
const doc2 = transaction.doc;
|
|
const ownClientId = doc2.clientID;
|
|
let content;
|
|
if (value == null) {
|
|
content = new ContentAny([value]);
|
|
} else {
|
|
switch (value.constructor) {
|
|
case Number:
|
|
case Object:
|
|
case Boolean:
|
|
case Array:
|
|
case String:
|
|
case Date:
|
|
case BigInt:
|
|
content = new ContentAny([value]);
|
|
break;
|
|
case Uint8Array:
|
|
content = new ContentBinary(
|
|
/** @type {Uint8Array} */
|
|
value
|
|
);
|
|
break;
|
|
case Doc:
|
|
content = new ContentDoc(
|
|
/** @type {Doc} */
|
|
value
|
|
);
|
|
break;
|
|
default:
|
|
if (value instanceof AbstractType) {
|
|
content = new ContentType(value);
|
|
} else {
|
|
throw new Error("Unexpected content type");
|
|
}
|
|
}
|
|
}
|
|
new Item(createID(ownClientId, getState(doc2.store, ownClientId)), left, left && left.lastId, null, null, parent, key, content).integrate(transaction, 0);
|
|
};
|
|
var typeMapGet = (parent, key) => {
|
|
parent.doc ?? warnPrematureAccess();
|
|
const val = parent._map.get(key);
|
|
return val !== void 0 && !val.deleted ? val.content.getContent()[val.length - 1] : void 0;
|
|
};
|
|
var typeMapGetAll = (parent) => {
|
|
const res = {};
|
|
parent.doc ?? warnPrematureAccess();
|
|
parent._map.forEach((value, key) => {
|
|
if (!value.deleted) {
|
|
res[key] = value.content.getContent()[value.length - 1];
|
|
}
|
|
});
|
|
return res;
|
|
};
|
|
var typeMapHas = (parent, key) => {
|
|
parent.doc ?? warnPrematureAccess();
|
|
const val = parent._map.get(key);
|
|
return val !== void 0 && !val.deleted;
|
|
};
|
|
var typeMapGetSnapshot = (parent, key, snapshot2) => {
|
|
let v = parent._map.get(key) || null;
|
|
while (v !== null && (!snapshot2.sv.has(v.id.client) || v.id.clock >= (snapshot2.sv.get(v.id.client) || 0))) {
|
|
v = v.left;
|
|
}
|
|
return v !== null && isVisible(v, snapshot2) ? v.content.getContent()[v.length - 1] : void 0;
|
|
};
|
|
var typeMapGetAllSnapshot = (parent, snapshot2) => {
|
|
const res = {};
|
|
parent._map.forEach((value, key) => {
|
|
let v = value;
|
|
while (v !== null && (!snapshot2.sv.has(v.id.client) || v.id.clock >= (snapshot2.sv.get(v.id.client) || 0))) {
|
|
v = v.left;
|
|
}
|
|
if (v !== null && isVisible(v, snapshot2)) {
|
|
res[key] = v.content.getContent()[v.length - 1];
|
|
}
|
|
});
|
|
return res;
|
|
};
|
|
var createMapIterator = (type) => {
|
|
type.doc ?? warnPrematureAccess();
|
|
return iteratorFilter(
|
|
type._map.entries(),
|
|
/** @param {any} entry */
|
|
(entry) => !entry[1].deleted
|
|
);
|
|
};
|
|
var YArrayEvent = class extends YEvent {
|
|
};
|
|
var YArray = class _YArray extends AbstractType {
|
|
constructor() {
|
|
super();
|
|
this._prelimContent = [];
|
|
this._searchMarker = [];
|
|
}
|
|
/**
|
|
* Construct a new YArray containing the specified items.
|
|
* @template {Object<string,any>|Array<any>|number|null|string|Uint8Array} T
|
|
* @param {Array<T>} items
|
|
* @return {YArray<T>}
|
|
*/
|
|
static from(items2) {
|
|
const a = new _YArray();
|
|
a.push(items2);
|
|
return a;
|
|
}
|
|
/**
|
|
* Integrate this type into the Yjs instance.
|
|
*
|
|
* * Save this struct in the os
|
|
* * This type is sent to other client
|
|
* * Observer functions are fired
|
|
*
|
|
* @param {Doc} y The Yjs instance
|
|
* @param {Item} item
|
|
*/
|
|
_integrate(y, item) {
|
|
super._integrate(y, item);
|
|
this.insert(
|
|
0,
|
|
/** @type {Array<any>} */
|
|
this._prelimContent
|
|
);
|
|
this._prelimContent = null;
|
|
}
|
|
/**
|
|
* @return {YArray<T>}
|
|
*/
|
|
_copy() {
|
|
return new _YArray();
|
|
}
|
|
/**
|
|
* Makes a copy of this data type that can be included somewhere else.
|
|
*
|
|
* Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
|
|
*
|
|
* @return {YArray<T>}
|
|
*/
|
|
clone() {
|
|
const arr = new _YArray();
|
|
arr.insert(0, this.toArray().map(
|
|
(el) => el instanceof AbstractType ? (
|
|
/** @type {typeof el} */
|
|
el.clone()
|
|
) : el
|
|
));
|
|
return arr;
|
|
}
|
|
get length() {
|
|
this.doc ?? warnPrematureAccess();
|
|
return this._length;
|
|
}
|
|
/**
|
|
* Creates YArrayEvent and calls observers.
|
|
*
|
|
* @param {Transaction} transaction
|
|
* @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
|
|
*/
|
|
_callObserver(transaction, parentSubs) {
|
|
super._callObserver(transaction, parentSubs);
|
|
callTypeObservers(this, transaction, new YArrayEvent(this, transaction));
|
|
}
|
|
/**
|
|
* Inserts new content at an index.
|
|
*
|
|
* Important: This function expects an array of content. Not just a content
|
|
* object. The reason for this "weirdness" is that inserting several elements
|
|
* is very efficient when it is done as a single operation.
|
|
*
|
|
* @example
|
|
* // Insert character 'a' at position 0
|
|
* yarray.insert(0, ['a'])
|
|
* // Insert numbers 1, 2 at position 1
|
|
* yarray.insert(1, [1, 2])
|
|
*
|
|
* @param {number} index The index to insert content at.
|
|
* @param {Array<T>} content The array of content
|
|
*/
|
|
insert(index, content) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
typeListInsertGenerics(
|
|
transaction,
|
|
this,
|
|
index,
|
|
/** @type {any} */
|
|
content
|
|
);
|
|
});
|
|
} else {
|
|
this._prelimContent.splice(index, 0, ...content);
|
|
}
|
|
}
|
|
/**
|
|
* Appends content to this YArray.
|
|
*
|
|
* @param {Array<T>} content Array of content to append.
|
|
*
|
|
* @todo Use the following implementation in all types.
|
|
*/
|
|
push(content) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
typeListPushGenerics(
|
|
transaction,
|
|
this,
|
|
/** @type {any} */
|
|
content
|
|
);
|
|
});
|
|
} else {
|
|
this._prelimContent.push(...content);
|
|
}
|
|
}
|
|
/**
|
|
* Prepends content to this YArray.
|
|
*
|
|
* @param {Array<T>} content Array of content to prepend.
|
|
*/
|
|
unshift(content) {
|
|
this.insert(0, content);
|
|
}
|
|
/**
|
|
* Deletes elements starting from an index.
|
|
*
|
|
* @param {number} index Index at which to start deleting elements
|
|
* @param {number} length The number of elements to remove. Defaults to 1.
|
|
*/
|
|
delete(index, length3 = 1) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
typeListDelete(transaction, this, index, length3);
|
|
});
|
|
} else {
|
|
this._prelimContent.splice(index, length3);
|
|
}
|
|
}
|
|
/**
|
|
* Returns the i-th element from a YArray.
|
|
*
|
|
* @param {number} index The index of the element to return from the YArray
|
|
* @return {T}
|
|
*/
|
|
get(index) {
|
|
return typeListGet(this, index);
|
|
}
|
|
/**
|
|
* Transforms this YArray to a JavaScript Array.
|
|
*
|
|
* @return {Array<T>}
|
|
*/
|
|
toArray() {
|
|
return typeListToArray(this);
|
|
}
|
|
/**
|
|
* Returns a portion of this YArray into a JavaScript Array selected
|
|
* from start to end (end not included).
|
|
*
|
|
* @param {number} [start]
|
|
* @param {number} [end]
|
|
* @return {Array<T>}
|
|
*/
|
|
slice(start = 0, end = this.length) {
|
|
return typeListSlice(this, start, end);
|
|
}
|
|
/**
|
|
* Transforms this Shared Type to a JSON object.
|
|
*
|
|
* @return {Array<any>}
|
|
*/
|
|
toJSON() {
|
|
return this.map((c) => c instanceof AbstractType ? c.toJSON() : c);
|
|
}
|
|
/**
|
|
* Returns an Array with the result of calling a provided function on every
|
|
* element of this YArray.
|
|
*
|
|
* @template M
|
|
* @param {function(T,number,YArray<T>):M} f Function that produces an element of the new Array
|
|
* @return {Array<M>} A new array with each element being the result of the
|
|
* callback function
|
|
*/
|
|
map(f) {
|
|
return typeListMap(
|
|
this,
|
|
/** @type {any} */
|
|
f
|
|
);
|
|
}
|
|
/**
|
|
* Executes a provided function once on every element of this YArray.
|
|
*
|
|
* @param {function(T,number,YArray<T>):void} f A function to execute on every element of this YArray.
|
|
*/
|
|
forEach(f) {
|
|
typeListForEach(this, f);
|
|
}
|
|
/**
|
|
* @return {IterableIterator<T>}
|
|
*/
|
|
[Symbol.iterator]() {
|
|
return typeListCreateIterator(this);
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
*/
|
|
_write(encoder) {
|
|
encoder.writeTypeRef(YArrayRefID);
|
|
}
|
|
};
|
|
var readYArray = (_decoder) => new YArray();
|
|
var YMapEvent = class extends YEvent {
|
|
/**
|
|
* @param {YMap<T>} ymap The YArray that changed.
|
|
* @param {Transaction} transaction
|
|
* @param {Set<any>} subs The keys that changed.
|
|
*/
|
|
constructor(ymap, transaction, subs) {
|
|
super(ymap, transaction);
|
|
this.keysChanged = subs;
|
|
}
|
|
};
|
|
var YMap = class _YMap extends AbstractType {
|
|
/**
|
|
*
|
|
* @param {Iterable<readonly [string, any]>=} entries - an optional iterable to initialize the YMap
|
|
*/
|
|
constructor(entries) {
|
|
super();
|
|
this._prelimContent = null;
|
|
if (entries === void 0) {
|
|
this._prelimContent = /* @__PURE__ */ new Map();
|
|
} else {
|
|
this._prelimContent = new Map(entries);
|
|
}
|
|
}
|
|
/**
|
|
* Integrate this type into the Yjs instance.
|
|
*
|
|
* * Save this struct in the os
|
|
* * This type is sent to other client
|
|
* * Observer functions are fired
|
|
*
|
|
* @param {Doc} y The Yjs instance
|
|
* @param {Item} item
|
|
*/
|
|
_integrate(y, item) {
|
|
super._integrate(y, item);
|
|
this._prelimContent.forEach((value, key) => {
|
|
this.set(key, value);
|
|
});
|
|
this._prelimContent = null;
|
|
}
|
|
/**
|
|
* @return {YMap<MapType>}
|
|
*/
|
|
_copy() {
|
|
return new _YMap();
|
|
}
|
|
/**
|
|
* Makes a copy of this data type that can be included somewhere else.
|
|
*
|
|
* Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
|
|
*
|
|
* @return {YMap<MapType>}
|
|
*/
|
|
clone() {
|
|
const map2 = new _YMap();
|
|
this.forEach((value, key) => {
|
|
map2.set(key, value instanceof AbstractType ? (
|
|
/** @type {typeof value} */
|
|
value.clone()
|
|
) : value);
|
|
});
|
|
return map2;
|
|
}
|
|
/**
|
|
* Creates YMapEvent and calls observers.
|
|
*
|
|
* @param {Transaction} transaction
|
|
* @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
|
|
*/
|
|
_callObserver(transaction, parentSubs) {
|
|
callTypeObservers(this, transaction, new YMapEvent(this, transaction, parentSubs));
|
|
}
|
|
/**
|
|
* Transforms this Shared Type to a JSON object.
|
|
*
|
|
* @return {Object<string,any>}
|
|
*/
|
|
toJSON() {
|
|
this.doc ?? warnPrematureAccess();
|
|
const map2 = {};
|
|
this._map.forEach((item, key) => {
|
|
if (!item.deleted) {
|
|
const v = item.content.getContent()[item.length - 1];
|
|
map2[key] = v instanceof AbstractType ? v.toJSON() : v;
|
|
}
|
|
});
|
|
return map2;
|
|
}
|
|
/**
|
|
* Returns the size of the YMap (count of key/value pairs)
|
|
*
|
|
* @return {number}
|
|
*/
|
|
get size() {
|
|
return [...createMapIterator(this)].length;
|
|
}
|
|
/**
|
|
* Returns the keys for each element in the YMap Type.
|
|
*
|
|
* @return {IterableIterator<string>}
|
|
*/
|
|
keys() {
|
|
return iteratorMap(
|
|
createMapIterator(this),
|
|
/** @param {any} v */
|
|
(v) => v[0]
|
|
);
|
|
}
|
|
/**
|
|
* Returns the values for each element in the YMap Type.
|
|
*
|
|
* @return {IterableIterator<MapType>}
|
|
*/
|
|
values() {
|
|
return iteratorMap(
|
|
createMapIterator(this),
|
|
/** @param {any} v */
|
|
(v) => v[1].content.getContent()[v[1].length - 1]
|
|
);
|
|
}
|
|
/**
|
|
* Returns an Iterator of [key, value] pairs
|
|
*
|
|
* @return {IterableIterator<[string, MapType]>}
|
|
*/
|
|
entries() {
|
|
return iteratorMap(
|
|
createMapIterator(this),
|
|
/** @param {any} v */
|
|
(v) => (
|
|
/** @type {any} */
|
|
[v[0], v[1].content.getContent()[v[1].length - 1]]
|
|
)
|
|
);
|
|
}
|
|
/**
|
|
* Executes a provided function on once on every key-value pair.
|
|
*
|
|
* @param {function(MapType,string,YMap<MapType>):void} f A function to execute on every element of this YArray.
|
|
*/
|
|
forEach(f) {
|
|
this.doc ?? warnPrematureAccess();
|
|
this._map.forEach((item, key) => {
|
|
if (!item.deleted) {
|
|
f(item.content.getContent()[item.length - 1], key, this);
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Returns an Iterator of [key, value] pairs
|
|
*
|
|
* @return {IterableIterator<[string, MapType]>}
|
|
*/
|
|
[Symbol.iterator]() {
|
|
return this.entries();
|
|
}
|
|
/**
|
|
* Remove a specified element from this YMap.
|
|
*
|
|
* @param {string} key The key of the element to remove.
|
|
*/
|
|
delete(key) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
typeMapDelete(transaction, this, key);
|
|
});
|
|
} else {
|
|
this._prelimContent.delete(key);
|
|
}
|
|
}
|
|
/**
|
|
* Adds or updates an element with a specified key and value.
|
|
* @template {MapType} VAL
|
|
*
|
|
* @param {string} key The key of the element to add to this YMap
|
|
* @param {VAL} value The value of the element to add
|
|
* @return {VAL}
|
|
*/
|
|
set(key, value) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
typeMapSet(
|
|
transaction,
|
|
this,
|
|
key,
|
|
/** @type {any} */
|
|
value
|
|
);
|
|
});
|
|
} else {
|
|
this._prelimContent.set(key, value);
|
|
}
|
|
return value;
|
|
}
|
|
/**
|
|
* Returns a specified element from this YMap.
|
|
*
|
|
* @param {string} key
|
|
* @return {MapType|undefined}
|
|
*/
|
|
get(key) {
|
|
return (
|
|
/** @type {any} */
|
|
typeMapGet(this, key)
|
|
);
|
|
}
|
|
/**
|
|
* Returns a boolean indicating whether the specified key exists or not.
|
|
*
|
|
* @param {string} key The key to test.
|
|
* @return {boolean}
|
|
*/
|
|
has(key) {
|
|
return typeMapHas(this, key);
|
|
}
|
|
/**
|
|
* Removes all elements from this YMap.
|
|
*/
|
|
clear() {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
this.forEach(function(_value, key, map2) {
|
|
typeMapDelete(transaction, map2, key);
|
|
});
|
|
});
|
|
} else {
|
|
this._prelimContent.clear();
|
|
}
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
*/
|
|
_write(encoder) {
|
|
encoder.writeTypeRef(YMapRefID);
|
|
}
|
|
};
|
|
var readYMap = (_decoder) => new YMap();
|
|
var equalAttrs = (a, b) => a === b || typeof a === "object" && typeof b === "object" && a && b && equalFlat(a, b);
|
|
var ItemTextListPosition = class {
|
|
/**
|
|
* @param {Item|null} left
|
|
* @param {Item|null} right
|
|
* @param {number} index
|
|
* @param {Map<string,any>} currentAttributes
|
|
*/
|
|
constructor(left, right, index, currentAttributes) {
|
|
this.left = left;
|
|
this.right = right;
|
|
this.index = index;
|
|
this.currentAttributes = currentAttributes;
|
|
}
|
|
/**
|
|
* Only call this if you know that this.right is defined
|
|
*/
|
|
forward() {
|
|
if (this.right === null) {
|
|
unexpectedCase();
|
|
}
|
|
switch (this.right.content.constructor) {
|
|
case ContentFormat:
|
|
if (!this.right.deleted) {
|
|
updateCurrentAttributes(
|
|
this.currentAttributes,
|
|
/** @type {ContentFormat} */
|
|
this.right.content
|
|
);
|
|
}
|
|
break;
|
|
default:
|
|
if (!this.right.deleted) {
|
|
this.index += this.right.length;
|
|
}
|
|
break;
|
|
}
|
|
this.left = this.right;
|
|
this.right = this.right.right;
|
|
}
|
|
};
|
|
var findNextPosition = (transaction, pos, count) => {
|
|
while (pos.right !== null && count > 0) {
|
|
switch (pos.right.content.constructor) {
|
|
case ContentFormat:
|
|
if (!pos.right.deleted) {
|
|
updateCurrentAttributes(
|
|
pos.currentAttributes,
|
|
/** @type {ContentFormat} */
|
|
pos.right.content
|
|
);
|
|
}
|
|
break;
|
|
default:
|
|
if (!pos.right.deleted) {
|
|
if (count < pos.right.length) {
|
|
getItemCleanStart(transaction, createID(pos.right.id.client, pos.right.id.clock + count));
|
|
}
|
|
pos.index += pos.right.length;
|
|
count -= pos.right.length;
|
|
}
|
|
break;
|
|
}
|
|
pos.left = pos.right;
|
|
pos.right = pos.right.right;
|
|
}
|
|
return pos;
|
|
};
|
|
var findPosition = (transaction, parent, index, useSearchMarker) => {
|
|
const currentAttributes = /* @__PURE__ */ new Map();
|
|
const marker = useSearchMarker ? findMarker(parent, index) : null;
|
|
if (marker) {
|
|
const pos = new ItemTextListPosition(marker.p.left, marker.p, marker.index, currentAttributes);
|
|
return findNextPosition(transaction, pos, index - marker.index);
|
|
} else {
|
|
const pos = new ItemTextListPosition(null, parent._start, 0, currentAttributes);
|
|
return findNextPosition(transaction, pos, index);
|
|
}
|
|
};
|
|
var insertNegatedAttributes = (transaction, parent, currPos, negatedAttributes) => {
|
|
while (currPos.right !== null && (currPos.right.deleted === true || currPos.right.content.constructor === ContentFormat && equalAttrs(
|
|
negatedAttributes.get(
|
|
/** @type {ContentFormat} */
|
|
currPos.right.content.key
|
|
),
|
|
/** @type {ContentFormat} */
|
|
currPos.right.content.value
|
|
))) {
|
|
if (!currPos.right.deleted) {
|
|
negatedAttributes.delete(
|
|
/** @type {ContentFormat} */
|
|
currPos.right.content.key
|
|
);
|
|
}
|
|
currPos.forward();
|
|
}
|
|
const doc2 = transaction.doc;
|
|
const ownClientId = doc2.clientID;
|
|
negatedAttributes.forEach((val, key) => {
|
|
const left = currPos.left;
|
|
const right = currPos.right;
|
|
const nextFormat = new Item(createID(ownClientId, getState(doc2.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentFormat(key, val));
|
|
nextFormat.integrate(transaction, 0);
|
|
currPos.right = nextFormat;
|
|
currPos.forward();
|
|
});
|
|
};
|
|
var updateCurrentAttributes = (currentAttributes, format) => {
|
|
const { key, value } = format;
|
|
if (value === null) {
|
|
currentAttributes.delete(key);
|
|
} else {
|
|
currentAttributes.set(key, value);
|
|
}
|
|
};
|
|
var minimizeAttributeChanges = (currPos, attributes) => {
|
|
while (true) {
|
|
if (currPos.right === null) {
|
|
break;
|
|
} else if (currPos.right.deleted || currPos.right.content.constructor === ContentFormat && equalAttrs(
|
|
attributes[
|
|
/** @type {ContentFormat} */
|
|
currPos.right.content.key
|
|
] ?? null,
|
|
/** @type {ContentFormat} */
|
|
currPos.right.content.value
|
|
)) ;
|
|
else {
|
|
break;
|
|
}
|
|
currPos.forward();
|
|
}
|
|
};
|
|
var insertAttributes = (transaction, parent, currPos, attributes) => {
|
|
const doc2 = transaction.doc;
|
|
const ownClientId = doc2.clientID;
|
|
const negatedAttributes = /* @__PURE__ */ new Map();
|
|
for (const key in attributes) {
|
|
const val = attributes[key];
|
|
const currentVal = currPos.currentAttributes.get(key) ?? null;
|
|
if (!equalAttrs(currentVal, val)) {
|
|
negatedAttributes.set(key, currentVal);
|
|
const { left, right } = currPos;
|
|
currPos.right = new Item(createID(ownClientId, getState(doc2.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentFormat(key, val));
|
|
currPos.right.integrate(transaction, 0);
|
|
currPos.forward();
|
|
}
|
|
}
|
|
return negatedAttributes;
|
|
};
|
|
var insertText = (transaction, parent, currPos, text2, attributes) => {
|
|
currPos.currentAttributes.forEach((_val, key) => {
|
|
if (attributes[key] === void 0) {
|
|
attributes[key] = null;
|
|
}
|
|
});
|
|
const doc2 = transaction.doc;
|
|
const ownClientId = doc2.clientID;
|
|
minimizeAttributeChanges(currPos, attributes);
|
|
const negatedAttributes = insertAttributes(transaction, parent, currPos, attributes);
|
|
const content = text2.constructor === String ? new ContentString(
|
|
/** @type {string} */
|
|
text2
|
|
) : text2 instanceof AbstractType ? new ContentType(text2) : new ContentEmbed(text2);
|
|
let { left, right, index } = currPos;
|
|
if (parent._searchMarker) {
|
|
updateMarkerChanges(parent._searchMarker, currPos.index, content.getLength());
|
|
}
|
|
right = new Item(createID(ownClientId, getState(doc2.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, content);
|
|
right.integrate(transaction, 0);
|
|
currPos.right = right;
|
|
currPos.index = index;
|
|
currPos.forward();
|
|
insertNegatedAttributes(transaction, parent, currPos, negatedAttributes);
|
|
};
|
|
var formatText = (transaction, parent, currPos, length3, attributes) => {
|
|
const doc2 = transaction.doc;
|
|
const ownClientId = doc2.clientID;
|
|
minimizeAttributeChanges(currPos, attributes);
|
|
const negatedAttributes = insertAttributes(transaction, parent, currPos, attributes);
|
|
iterationLoop: while (currPos.right !== null && (length3 > 0 || negatedAttributes.size > 0 && (currPos.right.deleted || currPos.right.content.constructor === ContentFormat))) {
|
|
if (!currPos.right.deleted) {
|
|
switch (currPos.right.content.constructor) {
|
|
case ContentFormat: {
|
|
const { key, value } = (
|
|
/** @type {ContentFormat} */
|
|
currPos.right.content
|
|
);
|
|
const attr = attributes[key];
|
|
if (attr !== void 0) {
|
|
if (equalAttrs(attr, value)) {
|
|
negatedAttributes.delete(key);
|
|
} else {
|
|
if (length3 === 0) {
|
|
break iterationLoop;
|
|
}
|
|
negatedAttributes.set(key, value);
|
|
}
|
|
currPos.right.delete(transaction);
|
|
} else {
|
|
currPos.currentAttributes.set(key, value);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
if (length3 < currPos.right.length) {
|
|
getItemCleanStart(transaction, createID(currPos.right.id.client, currPos.right.id.clock + length3));
|
|
}
|
|
length3 -= currPos.right.length;
|
|
break;
|
|
}
|
|
}
|
|
currPos.forward();
|
|
}
|
|
if (length3 > 0) {
|
|
let newlines = "";
|
|
for (; length3 > 0; length3--) {
|
|
newlines += "\n";
|
|
}
|
|
currPos.right = new Item(createID(ownClientId, getState(doc2.store, ownClientId)), currPos.left, currPos.left && currPos.left.lastId, currPos.right, currPos.right && currPos.right.id, parent, null, new ContentString(newlines));
|
|
currPos.right.integrate(transaction, 0);
|
|
currPos.forward();
|
|
}
|
|
insertNegatedAttributes(transaction, parent, currPos, negatedAttributes);
|
|
};
|
|
var cleanupFormattingGap = (transaction, start, curr, startAttributes, currAttributes) => {
|
|
let end = start;
|
|
const endFormats = create();
|
|
while (end && (!end.countable || end.deleted)) {
|
|
if (!end.deleted && end.content.constructor === ContentFormat) {
|
|
const cf = (
|
|
/** @type {ContentFormat} */
|
|
end.content
|
|
);
|
|
endFormats.set(cf.key, cf);
|
|
}
|
|
end = end.right;
|
|
}
|
|
let cleanups = 0;
|
|
let reachedCurr = false;
|
|
while (start !== end) {
|
|
if (curr === start) {
|
|
reachedCurr = true;
|
|
}
|
|
if (!start.deleted) {
|
|
const content = start.content;
|
|
switch (content.constructor) {
|
|
case ContentFormat: {
|
|
const { key, value } = (
|
|
/** @type {ContentFormat} */
|
|
content
|
|
);
|
|
const startAttrValue = startAttributes.get(key) ?? null;
|
|
if (endFormats.get(key) !== content || startAttrValue === value) {
|
|
start.delete(transaction);
|
|
cleanups++;
|
|
if (!reachedCurr && (currAttributes.get(key) ?? null) === value && startAttrValue !== value) {
|
|
if (startAttrValue === null) {
|
|
currAttributes.delete(key);
|
|
} else {
|
|
currAttributes.set(key, startAttrValue);
|
|
}
|
|
}
|
|
}
|
|
if (!reachedCurr && !start.deleted) {
|
|
updateCurrentAttributes(
|
|
currAttributes,
|
|
/** @type {ContentFormat} */
|
|
content
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
start = /** @type {Item} */
|
|
start.right;
|
|
}
|
|
return cleanups;
|
|
};
|
|
var cleanupContextlessFormattingGap = (transaction, item) => {
|
|
while (item && item.right && (item.right.deleted || !item.right.countable)) {
|
|
item = item.right;
|
|
}
|
|
const attrs = /* @__PURE__ */ new Set();
|
|
while (item && (item.deleted || !item.countable)) {
|
|
if (!item.deleted && item.content.constructor === ContentFormat) {
|
|
const key = (
|
|
/** @type {ContentFormat} */
|
|
item.content.key
|
|
);
|
|
if (attrs.has(key)) {
|
|
item.delete(transaction);
|
|
} else {
|
|
attrs.add(key);
|
|
}
|
|
}
|
|
item = item.left;
|
|
}
|
|
};
|
|
var cleanupYTextFormatting = (type) => {
|
|
let res = 0;
|
|
transact(
|
|
/** @type {Doc} */
|
|
type.doc,
|
|
(transaction) => {
|
|
let start = (
|
|
/** @type {Item} */
|
|
type._start
|
|
);
|
|
let end = type._start;
|
|
let startAttributes = create();
|
|
const currentAttributes = copy(startAttributes);
|
|
while (end) {
|
|
if (end.deleted === false) {
|
|
switch (end.content.constructor) {
|
|
case ContentFormat:
|
|
updateCurrentAttributes(
|
|
currentAttributes,
|
|
/** @type {ContentFormat} */
|
|
end.content
|
|
);
|
|
break;
|
|
default:
|
|
res += cleanupFormattingGap(transaction, start, end, startAttributes, currentAttributes);
|
|
startAttributes = copy(currentAttributes);
|
|
start = end;
|
|
break;
|
|
}
|
|
}
|
|
end = end.right;
|
|
}
|
|
}
|
|
);
|
|
return res;
|
|
};
|
|
var cleanupYTextAfterTransaction = (transaction) => {
|
|
const needFullCleanup = /* @__PURE__ */ new Set();
|
|
const doc2 = transaction.doc;
|
|
for (const [client, afterClock] of transaction.afterState.entries()) {
|
|
const clock = transaction.beforeState.get(client) || 0;
|
|
if (afterClock === clock) {
|
|
continue;
|
|
}
|
|
iterateStructs(
|
|
transaction,
|
|
/** @type {Array<Item|GC>} */
|
|
doc2.store.clients.get(client),
|
|
clock,
|
|
afterClock,
|
|
(item) => {
|
|
if (!item.deleted && /** @type {Item} */
|
|
item.content.constructor === ContentFormat && item.constructor !== GC) {
|
|
needFullCleanup.add(
|
|
/** @type {any} */
|
|
item.parent
|
|
);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
transact(doc2, (t) => {
|
|
iterateDeletedStructs(transaction, transaction.deleteSet, (item) => {
|
|
if (item instanceof GC || !/** @type {YText} */
|
|
item.parent._hasFormatting || needFullCleanup.has(
|
|
/** @type {YText} */
|
|
item.parent
|
|
)) {
|
|
return;
|
|
}
|
|
const parent = (
|
|
/** @type {YText} */
|
|
item.parent
|
|
);
|
|
if (item.content.constructor === ContentFormat) {
|
|
needFullCleanup.add(parent);
|
|
} else {
|
|
cleanupContextlessFormattingGap(t, item);
|
|
}
|
|
});
|
|
for (const yText of needFullCleanup) {
|
|
cleanupYTextFormatting(yText);
|
|
}
|
|
});
|
|
};
|
|
var deleteText = (transaction, currPos, length3) => {
|
|
const startLength = length3;
|
|
const startAttrs = copy(currPos.currentAttributes);
|
|
const start = currPos.right;
|
|
while (length3 > 0 && currPos.right !== null) {
|
|
if (currPos.right.deleted === false) {
|
|
switch (currPos.right.content.constructor) {
|
|
case ContentType:
|
|
case ContentEmbed:
|
|
case ContentString:
|
|
if (length3 < currPos.right.length) {
|
|
getItemCleanStart(transaction, createID(currPos.right.id.client, currPos.right.id.clock + length3));
|
|
}
|
|
length3 -= currPos.right.length;
|
|
currPos.right.delete(transaction);
|
|
break;
|
|
}
|
|
}
|
|
currPos.forward();
|
|
}
|
|
if (start) {
|
|
cleanupFormattingGap(transaction, start, currPos.right, startAttrs, currPos.currentAttributes);
|
|
}
|
|
const parent = (
|
|
/** @type {AbstractType<any>} */
|
|
/** @type {Item} */
|
|
(currPos.left || currPos.right).parent
|
|
);
|
|
if (parent._searchMarker) {
|
|
updateMarkerChanges(parent._searchMarker, currPos.index, -startLength + length3);
|
|
}
|
|
return currPos;
|
|
};
|
|
var YTextEvent = class extends YEvent {
|
|
/**
|
|
* @param {YText} ytext
|
|
* @param {Transaction} transaction
|
|
* @param {Set<any>} subs The keys that changed
|
|
*/
|
|
constructor(ytext, transaction, subs) {
|
|
super(ytext, transaction);
|
|
this.childListChanged = false;
|
|
this.keysChanged = /* @__PURE__ */ new Set();
|
|
subs.forEach((sub) => {
|
|
if (sub === null) {
|
|
this.childListChanged = true;
|
|
} else {
|
|
this.keysChanged.add(sub);
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* @type {{added:Set<Item>,deleted:Set<Item>,keys:Map<string,{action:'add'|'update'|'delete',oldValue:any}>,delta:Array<{insert?:Array<any>|string, delete?:number, retain?:number}>}}
|
|
*/
|
|
get changes() {
|
|
if (this._changes === null) {
|
|
const changes = {
|
|
keys: this.keys,
|
|
delta: this.delta,
|
|
added: /* @__PURE__ */ new Set(),
|
|
deleted: /* @__PURE__ */ new Set()
|
|
};
|
|
this._changes = changes;
|
|
}
|
|
return (
|
|
/** @type {any} */
|
|
this._changes
|
|
);
|
|
}
|
|
/**
|
|
* Compute the changes in the delta format.
|
|
* A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document.
|
|
*
|
|
* @type {Array<{insert?:string|object|AbstractType<any>, delete?:number, retain?:number, attributes?: Object<string,any>}>}
|
|
*
|
|
* @public
|
|
*/
|
|
get delta() {
|
|
if (this._delta === null) {
|
|
const y = (
|
|
/** @type {Doc} */
|
|
this.target.doc
|
|
);
|
|
const delta = [];
|
|
transact(y, (transaction) => {
|
|
const currentAttributes = /* @__PURE__ */ new Map();
|
|
const oldAttributes = /* @__PURE__ */ new Map();
|
|
let item = this.target._start;
|
|
let action = null;
|
|
const attributes = {};
|
|
let insert2 = "";
|
|
let retain = 0;
|
|
let deleteLen = 0;
|
|
const addOp = () => {
|
|
if (action !== null) {
|
|
let op = null;
|
|
switch (action) {
|
|
case "delete":
|
|
if (deleteLen > 0) {
|
|
op = { delete: deleteLen };
|
|
}
|
|
deleteLen = 0;
|
|
break;
|
|
case "insert":
|
|
if (typeof insert2 === "object" || insert2.length > 0) {
|
|
op = { insert: insert2 };
|
|
if (currentAttributes.size > 0) {
|
|
op.attributes = {};
|
|
currentAttributes.forEach((value, key) => {
|
|
if (value !== null) {
|
|
op.attributes[key] = value;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
insert2 = "";
|
|
break;
|
|
case "retain":
|
|
if (retain > 0) {
|
|
op = { retain };
|
|
if (!isEmpty(attributes)) {
|
|
op.attributes = assign({}, attributes);
|
|
}
|
|
}
|
|
retain = 0;
|
|
break;
|
|
}
|
|
if (op) delta.push(op);
|
|
action = null;
|
|
}
|
|
};
|
|
while (item !== null) {
|
|
switch (item.content.constructor) {
|
|
case ContentType:
|
|
case ContentEmbed:
|
|
if (this.adds(item)) {
|
|
if (!this.deletes(item)) {
|
|
addOp();
|
|
action = "insert";
|
|
insert2 = item.content.getContent()[0];
|
|
addOp();
|
|
}
|
|
} else if (this.deletes(item)) {
|
|
if (action !== "delete") {
|
|
addOp();
|
|
action = "delete";
|
|
}
|
|
deleteLen += 1;
|
|
} else if (!item.deleted) {
|
|
if (action !== "retain") {
|
|
addOp();
|
|
action = "retain";
|
|
}
|
|
retain += 1;
|
|
}
|
|
break;
|
|
case ContentString:
|
|
if (this.adds(item)) {
|
|
if (!this.deletes(item)) {
|
|
if (action !== "insert") {
|
|
addOp();
|
|
action = "insert";
|
|
}
|
|
insert2 += /** @type {ContentString} */
|
|
item.content.str;
|
|
}
|
|
} else if (this.deletes(item)) {
|
|
if (action !== "delete") {
|
|
addOp();
|
|
action = "delete";
|
|
}
|
|
deleteLen += item.length;
|
|
} else if (!item.deleted) {
|
|
if (action !== "retain") {
|
|
addOp();
|
|
action = "retain";
|
|
}
|
|
retain += item.length;
|
|
}
|
|
break;
|
|
case ContentFormat: {
|
|
const { key, value } = (
|
|
/** @type {ContentFormat} */
|
|
item.content
|
|
);
|
|
if (this.adds(item)) {
|
|
if (!this.deletes(item)) {
|
|
const curVal = currentAttributes.get(key) ?? null;
|
|
if (!equalAttrs(curVal, value)) {
|
|
if (action === "retain") {
|
|
addOp();
|
|
}
|
|
if (equalAttrs(value, oldAttributes.get(key) ?? null)) {
|
|
delete attributes[key];
|
|
} else {
|
|
attributes[key] = value;
|
|
}
|
|
} else if (value !== null) {
|
|
item.delete(transaction);
|
|
}
|
|
}
|
|
} else if (this.deletes(item)) {
|
|
oldAttributes.set(key, value);
|
|
const curVal = currentAttributes.get(key) ?? null;
|
|
if (!equalAttrs(curVal, value)) {
|
|
if (action === "retain") {
|
|
addOp();
|
|
}
|
|
attributes[key] = curVal;
|
|
}
|
|
} else if (!item.deleted) {
|
|
oldAttributes.set(key, value);
|
|
const attr = attributes[key];
|
|
if (attr !== void 0) {
|
|
if (!equalAttrs(attr, value)) {
|
|
if (action === "retain") {
|
|
addOp();
|
|
}
|
|
if (value === null) {
|
|
delete attributes[key];
|
|
} else {
|
|
attributes[key] = value;
|
|
}
|
|
} else if (attr !== null) {
|
|
item.delete(transaction);
|
|
}
|
|
}
|
|
}
|
|
if (!item.deleted) {
|
|
if (action === "insert") {
|
|
addOp();
|
|
}
|
|
updateCurrentAttributes(
|
|
currentAttributes,
|
|
/** @type {ContentFormat} */
|
|
item.content
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
item = item.right;
|
|
}
|
|
addOp();
|
|
while (delta.length > 0) {
|
|
const lastOp = delta[delta.length - 1];
|
|
if (lastOp.retain !== void 0 && lastOp.attributes === void 0) {
|
|
delta.pop();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
this._delta = delta;
|
|
}
|
|
return (
|
|
/** @type {any} */
|
|
this._delta
|
|
);
|
|
}
|
|
};
|
|
var YText = class _YText extends AbstractType {
|
|
/**
|
|
* @param {String} [string] The initial value of the YText.
|
|
*/
|
|
constructor(string) {
|
|
super();
|
|
this._pending = string !== void 0 ? [() => this.insert(0, string)] : [];
|
|
this._searchMarker = [];
|
|
this._hasFormatting = false;
|
|
}
|
|
/**
|
|
* Number of characters of this text type.
|
|
*
|
|
* @type {number}
|
|
*/
|
|
get length() {
|
|
this.doc ?? warnPrematureAccess();
|
|
return this._length;
|
|
}
|
|
/**
|
|
* @param {Doc} y
|
|
* @param {Item} item
|
|
*/
|
|
_integrate(y, item) {
|
|
super._integrate(y, item);
|
|
try {
|
|
this._pending.forEach((f) => f());
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
this._pending = null;
|
|
}
|
|
_copy() {
|
|
return new _YText();
|
|
}
|
|
/**
|
|
* Makes a copy of this data type that can be included somewhere else.
|
|
*
|
|
* Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
|
|
*
|
|
* @return {YText}
|
|
*/
|
|
clone() {
|
|
const text2 = new _YText();
|
|
text2.applyDelta(this.toDelta());
|
|
return text2;
|
|
}
|
|
/**
|
|
* Creates YTextEvent and calls observers.
|
|
*
|
|
* @param {Transaction} transaction
|
|
* @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
|
|
*/
|
|
_callObserver(transaction, parentSubs) {
|
|
super._callObserver(transaction, parentSubs);
|
|
const event = new YTextEvent(this, transaction, parentSubs);
|
|
callTypeObservers(this, transaction, event);
|
|
if (!transaction.local && this._hasFormatting) {
|
|
transaction._needFormattingCleanup = true;
|
|
}
|
|
}
|
|
/**
|
|
* Returns the unformatted string representation of this YText type.
|
|
*
|
|
* @public
|
|
*/
|
|
toString() {
|
|
this.doc ?? warnPrematureAccess();
|
|
let str = "";
|
|
let n = this._start;
|
|
while (n !== null) {
|
|
if (!n.deleted && n.countable && n.content.constructor === ContentString) {
|
|
str += /** @type {ContentString} */
|
|
n.content.str;
|
|
}
|
|
n = n.right;
|
|
}
|
|
return str;
|
|
}
|
|
/**
|
|
* Returns the unformatted string representation of this YText type.
|
|
*
|
|
* @return {string}
|
|
* @public
|
|
*/
|
|
toJSON() {
|
|
return this.toString();
|
|
}
|
|
/**
|
|
* Apply a {@link Delta} on this shared YText type.
|
|
*
|
|
* @param {Array<any>} delta The changes to apply on this element.
|
|
* @param {object} opts
|
|
* @param {boolean} [opts.sanitize] Sanitize input delta. Removes ending newlines if set to true.
|
|
*
|
|
*
|
|
* @public
|
|
*/
|
|
applyDelta(delta, { sanitize = true } = {}) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
const currPos = new ItemTextListPosition(null, this._start, 0, /* @__PURE__ */ new Map());
|
|
for (let i = 0; i < delta.length; i++) {
|
|
const op = delta[i];
|
|
if (op.insert !== void 0) {
|
|
const ins = !sanitize && typeof op.insert === "string" && i === delta.length - 1 && currPos.right === null && op.insert.slice(-1) === "\n" ? op.insert.slice(0, -1) : op.insert;
|
|
if (typeof ins !== "string" || ins.length > 0) {
|
|
insertText(transaction, this, currPos, ins, op.attributes || {});
|
|
}
|
|
} else if (op.retain !== void 0) {
|
|
formatText(transaction, this, currPos, op.retain, op.attributes || {});
|
|
} else if (op.delete !== void 0) {
|
|
deleteText(transaction, currPos, op.delete);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
this._pending.push(() => this.applyDelta(delta));
|
|
}
|
|
}
|
|
/**
|
|
* Returns the Delta representation of this YText type.
|
|
*
|
|
* @param {Snapshot} [snapshot]
|
|
* @param {Snapshot} [prevSnapshot]
|
|
* @param {function('removed' | 'added', ID):any} [computeYChange]
|
|
* @return {any} The Delta representation of this type.
|
|
*
|
|
* @public
|
|
*/
|
|
toDelta(snapshot2, prevSnapshot, computeYChange) {
|
|
this.doc ?? warnPrematureAccess();
|
|
const ops = [];
|
|
const currentAttributes = /* @__PURE__ */ new Map();
|
|
const doc2 = (
|
|
/** @type {Doc} */
|
|
this.doc
|
|
);
|
|
let str = "";
|
|
let n = this._start;
|
|
function packStr() {
|
|
if (str.length > 0) {
|
|
const attributes = {};
|
|
let addAttributes = false;
|
|
currentAttributes.forEach((value, key) => {
|
|
addAttributes = true;
|
|
attributes[key] = value;
|
|
});
|
|
const op = { insert: str };
|
|
if (addAttributes) {
|
|
op.attributes = attributes;
|
|
}
|
|
ops.push(op);
|
|
str = "";
|
|
}
|
|
}
|
|
const computeDelta = () => {
|
|
while (n !== null) {
|
|
if (isVisible(n, snapshot2) || prevSnapshot !== void 0 && isVisible(n, prevSnapshot)) {
|
|
switch (n.content.constructor) {
|
|
case ContentString: {
|
|
const cur = currentAttributes.get("ychange");
|
|
if (snapshot2 !== void 0 && !isVisible(n, snapshot2)) {
|
|
if (cur === void 0 || cur.user !== n.id.client || cur.type !== "removed") {
|
|
packStr();
|
|
currentAttributes.set("ychange", computeYChange ? computeYChange("removed", n.id) : { type: "removed" });
|
|
}
|
|
} else if (prevSnapshot !== void 0 && !isVisible(n, prevSnapshot)) {
|
|
if (cur === void 0 || cur.user !== n.id.client || cur.type !== "added") {
|
|
packStr();
|
|
currentAttributes.set("ychange", computeYChange ? computeYChange("added", n.id) : { type: "added" });
|
|
}
|
|
} else if (cur !== void 0) {
|
|
packStr();
|
|
currentAttributes.delete("ychange");
|
|
}
|
|
str += /** @type {ContentString} */
|
|
n.content.str;
|
|
break;
|
|
}
|
|
case ContentType:
|
|
case ContentEmbed: {
|
|
packStr();
|
|
const op = {
|
|
insert: n.content.getContent()[0]
|
|
};
|
|
if (currentAttributes.size > 0) {
|
|
const attrs = (
|
|
/** @type {Object<string,any>} */
|
|
{}
|
|
);
|
|
op.attributes = attrs;
|
|
currentAttributes.forEach((value, key) => {
|
|
attrs[key] = value;
|
|
});
|
|
}
|
|
ops.push(op);
|
|
break;
|
|
}
|
|
case ContentFormat:
|
|
if (isVisible(n, snapshot2)) {
|
|
packStr();
|
|
updateCurrentAttributes(
|
|
currentAttributes,
|
|
/** @type {ContentFormat} */
|
|
n.content
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
n = n.right;
|
|
}
|
|
packStr();
|
|
};
|
|
if (snapshot2 || prevSnapshot) {
|
|
transact(doc2, (transaction) => {
|
|
if (snapshot2) {
|
|
splitSnapshotAffectedStructs(transaction, snapshot2);
|
|
}
|
|
if (prevSnapshot) {
|
|
splitSnapshotAffectedStructs(transaction, prevSnapshot);
|
|
}
|
|
computeDelta();
|
|
}, "cleanup");
|
|
} else {
|
|
computeDelta();
|
|
}
|
|
return ops;
|
|
}
|
|
/**
|
|
* Insert text at a given index.
|
|
*
|
|
* @param {number} index The index at which to start inserting.
|
|
* @param {String} text The text to insert at the specified position.
|
|
* @param {TextAttributes} [attributes] Optionally define some formatting
|
|
* information to apply on the inserted
|
|
* Text.
|
|
* @public
|
|
*/
|
|
insert(index, text2, attributes) {
|
|
if (text2.length <= 0) {
|
|
return;
|
|
}
|
|
const y = this.doc;
|
|
if (y !== null) {
|
|
transact(y, (transaction) => {
|
|
const pos = findPosition(transaction, this, index, !attributes);
|
|
if (!attributes) {
|
|
attributes = {};
|
|
pos.currentAttributes.forEach((v, k) => {
|
|
attributes[k] = v;
|
|
});
|
|
}
|
|
insertText(transaction, this, pos, text2, attributes);
|
|
});
|
|
} else {
|
|
this._pending.push(() => this.insert(index, text2, attributes));
|
|
}
|
|
}
|
|
/**
|
|
* Inserts an embed at a index.
|
|
*
|
|
* @param {number} index The index to insert the embed at.
|
|
* @param {Object | AbstractType<any>} embed The Object that represents the embed.
|
|
* @param {TextAttributes} [attributes] Attribute information to apply on the
|
|
* embed
|
|
*
|
|
* @public
|
|
*/
|
|
insertEmbed(index, embed, attributes) {
|
|
const y = this.doc;
|
|
if (y !== null) {
|
|
transact(y, (transaction) => {
|
|
const pos = findPosition(transaction, this, index, !attributes);
|
|
insertText(transaction, this, pos, embed, attributes || {});
|
|
});
|
|
} else {
|
|
this._pending.push(() => this.insertEmbed(index, embed, attributes || {}));
|
|
}
|
|
}
|
|
/**
|
|
* Deletes text starting from an index.
|
|
*
|
|
* @param {number} index Index at which to start deleting.
|
|
* @param {number} length The number of characters to remove. Defaults to 1.
|
|
*
|
|
* @public
|
|
*/
|
|
delete(index, length3) {
|
|
if (length3 === 0) {
|
|
return;
|
|
}
|
|
const y = this.doc;
|
|
if (y !== null) {
|
|
transact(y, (transaction) => {
|
|
deleteText(transaction, findPosition(transaction, this, index, true), length3);
|
|
});
|
|
} else {
|
|
this._pending.push(() => this.delete(index, length3));
|
|
}
|
|
}
|
|
/**
|
|
* Assigns properties to a range of text.
|
|
*
|
|
* @param {number} index The position where to start formatting.
|
|
* @param {number} length The amount of characters to assign properties to.
|
|
* @param {TextAttributes} attributes Attribute information to apply on the
|
|
* text.
|
|
*
|
|
* @public
|
|
*/
|
|
format(index, length3, attributes) {
|
|
if (length3 === 0) {
|
|
return;
|
|
}
|
|
const y = this.doc;
|
|
if (y !== null) {
|
|
transact(y, (transaction) => {
|
|
const pos = findPosition(transaction, this, index, false);
|
|
if (pos.right === null) {
|
|
return;
|
|
}
|
|
formatText(transaction, this, pos, length3, attributes);
|
|
});
|
|
} else {
|
|
this._pending.push(() => this.format(index, length3, attributes));
|
|
}
|
|
}
|
|
/**
|
|
* Removes an attribute.
|
|
*
|
|
* @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
|
|
*
|
|
* @param {String} attributeName The attribute name that is to be removed.
|
|
*
|
|
* @public
|
|
*/
|
|
removeAttribute(attributeName) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
typeMapDelete(transaction, this, attributeName);
|
|
});
|
|
} else {
|
|
this._pending.push(() => this.removeAttribute(attributeName));
|
|
}
|
|
}
|
|
/**
|
|
* Sets or updates an attribute.
|
|
*
|
|
* @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
|
|
*
|
|
* @param {String} attributeName The attribute name that is to be set.
|
|
* @param {any} attributeValue The attribute value that is to be set.
|
|
*
|
|
* @public
|
|
*/
|
|
setAttribute(attributeName, attributeValue) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
typeMapSet(transaction, this, attributeName, attributeValue);
|
|
});
|
|
} else {
|
|
this._pending.push(() => this.setAttribute(attributeName, attributeValue));
|
|
}
|
|
}
|
|
/**
|
|
* Returns an attribute value that belongs to the attribute name.
|
|
*
|
|
* @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
|
|
*
|
|
* @param {String} attributeName The attribute name that identifies the
|
|
* queried value.
|
|
* @return {any} The queried attribute value.
|
|
*
|
|
* @public
|
|
*/
|
|
getAttribute(attributeName) {
|
|
return (
|
|
/** @type {any} */
|
|
typeMapGet(this, attributeName)
|
|
);
|
|
}
|
|
/**
|
|
* Returns all attribute name/value pairs in a JSON Object.
|
|
*
|
|
* @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks.
|
|
*
|
|
* @return {Object<string, any>} A JSON Object that describes the attributes.
|
|
*
|
|
* @public
|
|
*/
|
|
getAttributes() {
|
|
return typeMapGetAll(this);
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
*/
|
|
_write(encoder) {
|
|
encoder.writeTypeRef(YTextRefID);
|
|
}
|
|
};
|
|
var readYText = (_decoder) => new YText();
|
|
var YXmlTreeWalker = class {
|
|
/**
|
|
* @param {YXmlFragment | YXmlElement} root
|
|
* @param {function(AbstractType<any>):boolean} [f]
|
|
*/
|
|
constructor(root, f = () => true) {
|
|
this._filter = f;
|
|
this._root = root;
|
|
this._currentNode = /** @type {Item} */
|
|
root._start;
|
|
this._firstCall = true;
|
|
root.doc ?? warnPrematureAccess();
|
|
}
|
|
[Symbol.iterator]() {
|
|
return this;
|
|
}
|
|
/**
|
|
* Get the next node.
|
|
*
|
|
* @return {IteratorResult<YXmlElement|YXmlText|YXmlHook>} The next node.
|
|
*
|
|
* @public
|
|
*/
|
|
next() {
|
|
let n = this._currentNode;
|
|
let type = n && n.content && /** @type {any} */
|
|
n.content.type;
|
|
if (n !== null && (!this._firstCall || n.deleted || !this._filter(type))) {
|
|
do {
|
|
type = /** @type {any} */
|
|
n.content.type;
|
|
if (!n.deleted && (type.constructor === YXmlElement || type.constructor === YXmlFragment) && type._start !== null) {
|
|
n = type._start;
|
|
} else {
|
|
while (n !== null) {
|
|
const nxt = n.next;
|
|
if (nxt !== null) {
|
|
n = nxt;
|
|
break;
|
|
} else if (n.parent === this._root) {
|
|
n = null;
|
|
} else {
|
|
n = /** @type {AbstractType<any>} */
|
|
n.parent._item;
|
|
}
|
|
}
|
|
}
|
|
} while (n !== null && (n.deleted || !this._filter(
|
|
/** @type {ContentType} */
|
|
n.content.type
|
|
)));
|
|
}
|
|
this._firstCall = false;
|
|
if (n === null) {
|
|
return { value: void 0, done: true };
|
|
}
|
|
this._currentNode = n;
|
|
return { value: (
|
|
/** @type {any} */
|
|
n.content.type
|
|
), done: false };
|
|
}
|
|
};
|
|
var YXmlFragment = class _YXmlFragment extends AbstractType {
|
|
constructor() {
|
|
super();
|
|
this._prelimContent = [];
|
|
}
|
|
/**
|
|
* @type {YXmlElement|YXmlText|null}
|
|
*/
|
|
get firstChild() {
|
|
const first = this._first;
|
|
return first ? first.content.getContent()[0] : null;
|
|
}
|
|
/**
|
|
* Integrate this type into the Yjs instance.
|
|
*
|
|
* * Save this struct in the os
|
|
* * This type is sent to other client
|
|
* * Observer functions are fired
|
|
*
|
|
* @param {Doc} y The Yjs instance
|
|
* @param {Item} item
|
|
*/
|
|
_integrate(y, item) {
|
|
super._integrate(y, item);
|
|
this.insert(
|
|
0,
|
|
/** @type {Array<any>} */
|
|
this._prelimContent
|
|
);
|
|
this._prelimContent = null;
|
|
}
|
|
_copy() {
|
|
return new _YXmlFragment();
|
|
}
|
|
/**
|
|
* Makes a copy of this data type that can be included somewhere else.
|
|
*
|
|
* Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
|
|
*
|
|
* @return {YXmlFragment}
|
|
*/
|
|
clone() {
|
|
const el = new _YXmlFragment();
|
|
el.insert(0, this.toArray().map((item) => item instanceof AbstractType ? item.clone() : item));
|
|
return el;
|
|
}
|
|
get length() {
|
|
this.doc ?? warnPrematureAccess();
|
|
return this._prelimContent === null ? this._length : this._prelimContent.length;
|
|
}
|
|
/**
|
|
* Create a subtree of childNodes.
|
|
*
|
|
* @example
|
|
* const walker = elem.createTreeWalker(dom => dom.nodeName === 'div')
|
|
* for (let node in walker) {
|
|
* // `node` is a div node
|
|
* nop(node)
|
|
* }
|
|
*
|
|
* @param {function(AbstractType<any>):boolean} filter Function that is called on each child element and
|
|
* returns a Boolean indicating whether the child
|
|
* is to be included in the subtree.
|
|
* @return {YXmlTreeWalker} A subtree and a position within it.
|
|
*
|
|
* @public
|
|
*/
|
|
createTreeWalker(filter) {
|
|
return new YXmlTreeWalker(this, filter);
|
|
}
|
|
/**
|
|
* Returns the first YXmlElement that matches the query.
|
|
* Similar to DOM's {@link querySelector}.
|
|
*
|
|
* Query support:
|
|
* - tagname
|
|
* TODO:
|
|
* - id
|
|
* - attribute
|
|
*
|
|
* @param {CSS_Selector} query The query on the children.
|
|
* @return {YXmlElement|YXmlText|YXmlHook|null} The first element that matches the query or null.
|
|
*
|
|
* @public
|
|
*/
|
|
querySelector(query) {
|
|
query = query.toUpperCase();
|
|
const iterator = new YXmlTreeWalker(this, (element2) => element2.nodeName && element2.nodeName.toUpperCase() === query);
|
|
const next = iterator.next();
|
|
if (next.done) {
|
|
return null;
|
|
} else {
|
|
return next.value;
|
|
}
|
|
}
|
|
/**
|
|
* Returns all YXmlElements that match the query.
|
|
* Similar to Dom's {@link querySelectorAll}.
|
|
*
|
|
* @todo Does not yet support all queries. Currently only query by tagName.
|
|
*
|
|
* @param {CSS_Selector} query The query on the children
|
|
* @return {Array<YXmlElement|YXmlText|YXmlHook|null>} The elements that match this query.
|
|
*
|
|
* @public
|
|
*/
|
|
querySelectorAll(query) {
|
|
query = query.toUpperCase();
|
|
return from(new YXmlTreeWalker(this, (element2) => element2.nodeName && element2.nodeName.toUpperCase() === query));
|
|
}
|
|
/**
|
|
* Creates YXmlEvent and calls observers.
|
|
*
|
|
* @param {Transaction} transaction
|
|
* @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
|
|
*/
|
|
_callObserver(transaction, parentSubs) {
|
|
callTypeObservers(this, transaction, new YXmlEvent(this, parentSubs, transaction));
|
|
}
|
|
/**
|
|
* Get the string representation of all the children of this YXmlFragment.
|
|
*
|
|
* @return {string} The string representation of all children.
|
|
*/
|
|
toString() {
|
|
return typeListMap(this, (xml) => xml.toString()).join("");
|
|
}
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
toJSON() {
|
|
return this.toString();
|
|
}
|
|
/**
|
|
* Creates a Dom Element that mirrors this YXmlElement.
|
|
*
|
|
* @param {Document} [_document=document] The document object (you must define
|
|
* this when calling this method in
|
|
* nodejs)
|
|
* @param {Object<string, any>} [hooks={}] Optional property to customize how hooks
|
|
* are presented in the DOM
|
|
* @param {any} [binding] You should not set this property. This is
|
|
* used if DomBinding wants to create a
|
|
* association to the created DOM type.
|
|
* @return {Node} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
|
|
*
|
|
* @public
|
|
*/
|
|
toDOM(_document = document, hooks = {}, binding) {
|
|
const fragment = _document.createDocumentFragment();
|
|
if (binding !== void 0) {
|
|
binding._createAssociation(fragment, this);
|
|
}
|
|
typeListForEach(this, (xmlType) => {
|
|
fragment.insertBefore(xmlType.toDOM(_document, hooks, binding), null);
|
|
});
|
|
return fragment;
|
|
}
|
|
/**
|
|
* Inserts new content at an index.
|
|
*
|
|
* @example
|
|
* // Insert character 'a' at position 0
|
|
* xml.insert(0, [new Y.XmlText('text')])
|
|
*
|
|
* @param {number} index The index to insert content at
|
|
* @param {Array<YXmlElement|YXmlText>} content The array of content
|
|
*/
|
|
insert(index, content) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
typeListInsertGenerics(transaction, this, index, content);
|
|
});
|
|
} else {
|
|
this._prelimContent.splice(index, 0, ...content);
|
|
}
|
|
}
|
|
/**
|
|
* Inserts new content at an index.
|
|
*
|
|
* @example
|
|
* // Insert character 'a' at position 0
|
|
* xml.insert(0, [new Y.XmlText('text')])
|
|
*
|
|
* @param {null|Item|YXmlElement|YXmlText} ref The index to insert content at
|
|
* @param {Array<YXmlElement|YXmlText>} content The array of content
|
|
*/
|
|
insertAfter(ref, content) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
const refItem = ref && ref instanceof AbstractType ? ref._item : ref;
|
|
typeListInsertGenericsAfter(transaction, this, refItem, content);
|
|
});
|
|
} else {
|
|
const pc = (
|
|
/** @type {Array<any>} */
|
|
this._prelimContent
|
|
);
|
|
const index = ref === null ? 0 : pc.findIndex((el) => el === ref) + 1;
|
|
if (index === 0 && ref !== null) {
|
|
throw create3("Reference item not found");
|
|
}
|
|
pc.splice(index, 0, ...content);
|
|
}
|
|
}
|
|
/**
|
|
* Deletes elements starting from an index.
|
|
*
|
|
* @param {number} index Index at which to start deleting elements
|
|
* @param {number} [length=1] The number of elements to remove. Defaults to 1.
|
|
*/
|
|
delete(index, length3 = 1) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
typeListDelete(transaction, this, index, length3);
|
|
});
|
|
} else {
|
|
this._prelimContent.splice(index, length3);
|
|
}
|
|
}
|
|
/**
|
|
* Transforms this YArray to a JavaScript Array.
|
|
*
|
|
* @return {Array<YXmlElement|YXmlText|YXmlHook>}
|
|
*/
|
|
toArray() {
|
|
return typeListToArray(this);
|
|
}
|
|
/**
|
|
* Appends content to this YArray.
|
|
*
|
|
* @param {Array<YXmlElement|YXmlText>} content Array of content to append.
|
|
*/
|
|
push(content) {
|
|
this.insert(this.length, content);
|
|
}
|
|
/**
|
|
* Prepends content to this YArray.
|
|
*
|
|
* @param {Array<YXmlElement|YXmlText>} content Array of content to prepend.
|
|
*/
|
|
unshift(content) {
|
|
this.insert(0, content);
|
|
}
|
|
/**
|
|
* Returns the i-th element from a YArray.
|
|
*
|
|
* @param {number} index The index of the element to return from the YArray
|
|
* @return {YXmlElement|YXmlText}
|
|
*/
|
|
get(index) {
|
|
return typeListGet(this, index);
|
|
}
|
|
/**
|
|
* Returns a portion of this YXmlFragment into a JavaScript Array selected
|
|
* from start to end (end not included).
|
|
*
|
|
* @param {number} [start]
|
|
* @param {number} [end]
|
|
* @return {Array<YXmlElement|YXmlText>}
|
|
*/
|
|
slice(start = 0, end = this.length) {
|
|
return typeListSlice(this, start, end);
|
|
}
|
|
/**
|
|
* Executes a provided function on once on every child element.
|
|
*
|
|
* @param {function(YXmlElement|YXmlText,number, typeof self):void} f A function to execute on every element of this YArray.
|
|
*/
|
|
forEach(f) {
|
|
typeListForEach(this, f);
|
|
}
|
|
/**
|
|
* Transform the properties of this type to binary and write it to an
|
|
* BinaryEncoder.
|
|
*
|
|
* This is called when this Item is sent to a remote peer.
|
|
*
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
|
|
*/
|
|
_write(encoder) {
|
|
encoder.writeTypeRef(YXmlFragmentRefID);
|
|
}
|
|
};
|
|
var readYXmlFragment = (_decoder) => new YXmlFragment();
|
|
var YXmlElement = class _YXmlElement extends YXmlFragment {
|
|
constructor(nodeName = "UNDEFINED") {
|
|
super();
|
|
this.nodeName = nodeName;
|
|
this._prelimAttrs = /* @__PURE__ */ new Map();
|
|
}
|
|
/**
|
|
* @type {YXmlElement|YXmlText|null}
|
|
*/
|
|
get nextSibling() {
|
|
const n = this._item ? this._item.next : null;
|
|
return n ? (
|
|
/** @type {YXmlElement|YXmlText} */
|
|
/** @type {ContentType} */
|
|
n.content.type
|
|
) : null;
|
|
}
|
|
/**
|
|
* @type {YXmlElement|YXmlText|null}
|
|
*/
|
|
get prevSibling() {
|
|
const n = this._item ? this._item.prev : null;
|
|
return n ? (
|
|
/** @type {YXmlElement|YXmlText} */
|
|
/** @type {ContentType} */
|
|
n.content.type
|
|
) : null;
|
|
}
|
|
/**
|
|
* Integrate this type into the Yjs instance.
|
|
*
|
|
* * Save this struct in the os
|
|
* * This type is sent to other client
|
|
* * Observer functions are fired
|
|
*
|
|
* @param {Doc} y The Yjs instance
|
|
* @param {Item} item
|
|
*/
|
|
_integrate(y, item) {
|
|
super._integrate(y, item);
|
|
/** @type {Map<string, any>} */
|
|
this._prelimAttrs.forEach((value, key) => {
|
|
this.setAttribute(key, value);
|
|
});
|
|
this._prelimAttrs = null;
|
|
}
|
|
/**
|
|
* Creates an Item with the same effect as this Item (without position effect)
|
|
*
|
|
* @return {YXmlElement}
|
|
*/
|
|
_copy() {
|
|
return new _YXmlElement(this.nodeName);
|
|
}
|
|
/**
|
|
* Makes a copy of this data type that can be included somewhere else.
|
|
*
|
|
* Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
|
|
*
|
|
* @return {YXmlElement<KV>}
|
|
*/
|
|
clone() {
|
|
const el = new _YXmlElement(this.nodeName);
|
|
const attrs = this.getAttributes();
|
|
forEach(attrs, (value, key) => {
|
|
el.setAttribute(
|
|
key,
|
|
/** @type {any} */
|
|
value
|
|
);
|
|
});
|
|
el.insert(0, this.toArray().map((v) => v instanceof AbstractType ? v.clone() : v));
|
|
return el;
|
|
}
|
|
/**
|
|
* Returns the XML serialization of this YXmlElement.
|
|
* The attributes are ordered by attribute-name, so you can easily use this
|
|
* method to compare YXmlElements
|
|
*
|
|
* @return {string} The string representation of this type.
|
|
*
|
|
* @public
|
|
*/
|
|
toString() {
|
|
const attrs = this.getAttributes();
|
|
const stringBuilder = [];
|
|
const keys2 = [];
|
|
for (const key in attrs) {
|
|
keys2.push(key);
|
|
}
|
|
keys2.sort();
|
|
const keysLen = keys2.length;
|
|
for (let i = 0; i < keysLen; i++) {
|
|
const key = keys2[i];
|
|
stringBuilder.push(key + '="' + attrs[key] + '"');
|
|
}
|
|
const nodeName = this.nodeName.toLocaleLowerCase();
|
|
const attrsString = stringBuilder.length > 0 ? " " + stringBuilder.join(" ") : "";
|
|
return `<${nodeName}${attrsString}>${super.toString()}</${nodeName}>`;
|
|
}
|
|
/**
|
|
* Removes an attribute from this YXmlElement.
|
|
*
|
|
* @param {string} attributeName The attribute name that is to be removed.
|
|
*
|
|
* @public
|
|
*/
|
|
removeAttribute(attributeName) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
typeMapDelete(transaction, this, attributeName);
|
|
});
|
|
} else {
|
|
this._prelimAttrs.delete(attributeName);
|
|
}
|
|
}
|
|
/**
|
|
* Sets or updates an attribute.
|
|
*
|
|
* @template {keyof KV & string} KEY
|
|
*
|
|
* @param {KEY} attributeName The attribute name that is to be set.
|
|
* @param {KV[KEY]} attributeValue The attribute value that is to be set.
|
|
*
|
|
* @public
|
|
*/
|
|
setAttribute(attributeName, attributeValue) {
|
|
if (this.doc !== null) {
|
|
transact(this.doc, (transaction) => {
|
|
typeMapSet(transaction, this, attributeName, attributeValue);
|
|
});
|
|
} else {
|
|
this._prelimAttrs.set(attributeName, attributeValue);
|
|
}
|
|
}
|
|
/**
|
|
* Returns an attribute value that belongs to the attribute name.
|
|
*
|
|
* @template {keyof KV & string} KEY
|
|
*
|
|
* @param {KEY} attributeName The attribute name that identifies the
|
|
* queried value.
|
|
* @return {KV[KEY]|undefined} The queried attribute value.
|
|
*
|
|
* @public
|
|
*/
|
|
getAttribute(attributeName) {
|
|
return (
|
|
/** @type {any} */
|
|
typeMapGet(this, attributeName)
|
|
);
|
|
}
|
|
/**
|
|
* Returns whether an attribute exists
|
|
*
|
|
* @param {string} attributeName The attribute name to check for existence.
|
|
* @return {boolean} whether the attribute exists.
|
|
*
|
|
* @public
|
|
*/
|
|
hasAttribute(attributeName) {
|
|
return (
|
|
/** @type {any} */
|
|
typeMapHas(this, attributeName)
|
|
);
|
|
}
|
|
/**
|
|
* Returns all attribute name/value pairs in a JSON Object.
|
|
*
|
|
* @param {Snapshot} [snapshot]
|
|
* @return {{ [Key in Extract<keyof KV,string>]?: KV[Key]}} A JSON Object that describes the attributes.
|
|
*
|
|
* @public
|
|
*/
|
|
getAttributes(snapshot2) {
|
|
return (
|
|
/** @type {any} */
|
|
snapshot2 ? typeMapGetAllSnapshot(this, snapshot2) : typeMapGetAll(this)
|
|
);
|
|
}
|
|
/**
|
|
* Creates a Dom Element that mirrors this YXmlElement.
|
|
*
|
|
* @param {Document} [_document=document] The document object (you must define
|
|
* this when calling this method in
|
|
* nodejs)
|
|
* @param {Object<string, any>} [hooks={}] Optional property to customize how hooks
|
|
* are presented in the DOM
|
|
* @param {any} [binding] You should not set this property. This is
|
|
* used if DomBinding wants to create a
|
|
* association to the created DOM type.
|
|
* @return {Node} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
|
|
*
|
|
* @public
|
|
*/
|
|
toDOM(_document = document, hooks = {}, binding) {
|
|
const dom = _document.createElement(this.nodeName);
|
|
const attrs = this.getAttributes();
|
|
for (const key in attrs) {
|
|
const value = attrs[key];
|
|
if (typeof value === "string") {
|
|
dom.setAttribute(key, value);
|
|
}
|
|
}
|
|
typeListForEach(this, (yxml) => {
|
|
dom.appendChild(yxml.toDOM(_document, hooks, binding));
|
|
});
|
|
if (binding !== void 0) {
|
|
binding._createAssociation(dom, this);
|
|
}
|
|
return dom;
|
|
}
|
|
/**
|
|
* Transform the properties of this type to binary and write it to an
|
|
* BinaryEncoder.
|
|
*
|
|
* This is called when this Item is sent to a remote peer.
|
|
*
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
|
|
*/
|
|
_write(encoder) {
|
|
encoder.writeTypeRef(YXmlElementRefID);
|
|
encoder.writeKey(this.nodeName);
|
|
}
|
|
};
|
|
var readYXmlElement = (decoder) => new YXmlElement(decoder.readKey());
|
|
var YXmlEvent = class extends YEvent {
|
|
/**
|
|
* @param {YXmlElement|YXmlText|YXmlFragment} target The target on which the event is created.
|
|
* @param {Set<string|null>} subs The set of changed attributes. `null` is included if the
|
|
* child list changed.
|
|
* @param {Transaction} transaction The transaction instance with which the
|
|
* change was created.
|
|
*/
|
|
constructor(target, subs, transaction) {
|
|
super(target, transaction);
|
|
this.childListChanged = false;
|
|
this.attributesChanged = /* @__PURE__ */ new Set();
|
|
subs.forEach((sub) => {
|
|
if (sub === null) {
|
|
this.childListChanged = true;
|
|
} else {
|
|
this.attributesChanged.add(sub);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
var YXmlHook = class _YXmlHook extends YMap {
|
|
/**
|
|
* @param {string} hookName nodeName of the Dom Node.
|
|
*/
|
|
constructor(hookName) {
|
|
super();
|
|
this.hookName = hookName;
|
|
}
|
|
/**
|
|
* Creates an Item with the same effect as this Item (without position effect)
|
|
*/
|
|
_copy() {
|
|
return new _YXmlHook(this.hookName);
|
|
}
|
|
/**
|
|
* Makes a copy of this data type that can be included somewhere else.
|
|
*
|
|
* Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
|
|
*
|
|
* @return {YXmlHook}
|
|
*/
|
|
clone() {
|
|
const el = new _YXmlHook(this.hookName);
|
|
this.forEach((value, key) => {
|
|
el.set(key, value);
|
|
});
|
|
return el;
|
|
}
|
|
/**
|
|
* Creates a Dom Element that mirrors this YXmlElement.
|
|
*
|
|
* @param {Document} [_document=document] The document object (you must define
|
|
* this when calling this method in
|
|
* nodejs)
|
|
* @param {Object.<string, any>} [hooks] Optional property to customize how hooks
|
|
* are presented in the DOM
|
|
* @param {any} [binding] You should not set this property. This is
|
|
* used if DomBinding wants to create a
|
|
* association to the created DOM type
|
|
* @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
|
|
*
|
|
* @public
|
|
*/
|
|
toDOM(_document = document, hooks = {}, binding) {
|
|
const hook = hooks[this.hookName];
|
|
let dom;
|
|
if (hook !== void 0) {
|
|
dom = hook.createDom(this);
|
|
} else {
|
|
dom = document.createElement(this.hookName);
|
|
}
|
|
dom.setAttribute("data-yjs-hook", this.hookName);
|
|
if (binding !== void 0) {
|
|
binding._createAssociation(dom, this);
|
|
}
|
|
return dom;
|
|
}
|
|
/**
|
|
* Transform the properties of this type to binary and write it to an
|
|
* BinaryEncoder.
|
|
*
|
|
* This is called when this Item is sent to a remote peer.
|
|
*
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
|
|
*/
|
|
_write(encoder) {
|
|
encoder.writeTypeRef(YXmlHookRefID);
|
|
encoder.writeKey(this.hookName);
|
|
}
|
|
};
|
|
var readYXmlHook = (decoder) => new YXmlHook(decoder.readKey());
|
|
var YXmlText = class _YXmlText extends YText {
|
|
/**
|
|
* @type {YXmlElement|YXmlText|null}
|
|
*/
|
|
get nextSibling() {
|
|
const n = this._item ? this._item.next : null;
|
|
return n ? (
|
|
/** @type {YXmlElement|YXmlText} */
|
|
/** @type {ContentType} */
|
|
n.content.type
|
|
) : null;
|
|
}
|
|
/**
|
|
* @type {YXmlElement|YXmlText|null}
|
|
*/
|
|
get prevSibling() {
|
|
const n = this._item ? this._item.prev : null;
|
|
return n ? (
|
|
/** @type {YXmlElement|YXmlText} */
|
|
/** @type {ContentType} */
|
|
n.content.type
|
|
) : null;
|
|
}
|
|
_copy() {
|
|
return new _YXmlText();
|
|
}
|
|
/**
|
|
* Makes a copy of this data type that can be included somewhere else.
|
|
*
|
|
* Note that the content is only readable _after_ it has been included somewhere in the Ydoc.
|
|
*
|
|
* @return {YXmlText}
|
|
*/
|
|
clone() {
|
|
const text2 = new _YXmlText();
|
|
text2.applyDelta(this.toDelta());
|
|
return text2;
|
|
}
|
|
/**
|
|
* Creates a Dom Element that mirrors this YXmlText.
|
|
*
|
|
* @param {Document} [_document=document] The document object (you must define
|
|
* this when calling this method in
|
|
* nodejs)
|
|
* @param {Object<string, any>} [hooks] Optional property to customize how hooks
|
|
* are presented in the DOM
|
|
* @param {any} [binding] You should not set this property. This is
|
|
* used if DomBinding wants to create a
|
|
* association to the created DOM type.
|
|
* @return {Text} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
|
|
*
|
|
* @public
|
|
*/
|
|
toDOM(_document = document, hooks, binding) {
|
|
const dom = _document.createTextNode(this.toString());
|
|
if (binding !== void 0) {
|
|
binding._createAssociation(dom, this);
|
|
}
|
|
return dom;
|
|
}
|
|
toString() {
|
|
return this.toDelta().map((delta) => {
|
|
const nestedNodes = [];
|
|
for (const nodeName in delta.attributes) {
|
|
const attrs = [];
|
|
for (const key in delta.attributes[nodeName]) {
|
|
attrs.push({ key, value: delta.attributes[nodeName][key] });
|
|
}
|
|
attrs.sort((a, b) => a.key < b.key ? -1 : 1);
|
|
nestedNodes.push({ nodeName, attrs });
|
|
}
|
|
nestedNodes.sort((a, b) => a.nodeName < b.nodeName ? -1 : 1);
|
|
let str = "";
|
|
for (let i = 0; i < nestedNodes.length; i++) {
|
|
const node = nestedNodes[i];
|
|
str += `<${node.nodeName}`;
|
|
for (let j = 0; j < node.attrs.length; j++) {
|
|
const attr = node.attrs[j];
|
|
str += ` ${attr.key}="${attr.value}"`;
|
|
}
|
|
str += ">";
|
|
}
|
|
str += delta.insert;
|
|
for (let i = nestedNodes.length - 1; i >= 0; i--) {
|
|
str += `</${nestedNodes[i].nodeName}>`;
|
|
}
|
|
return str;
|
|
}).join("");
|
|
}
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
toJSON() {
|
|
return this.toString();
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
*/
|
|
_write(encoder) {
|
|
encoder.writeTypeRef(YXmlTextRefID);
|
|
}
|
|
};
|
|
var readYXmlText = (decoder) => new YXmlText();
|
|
var AbstractStruct = class {
|
|
/**
|
|
* @param {ID} id
|
|
* @param {number} length
|
|
*/
|
|
constructor(id2, length3) {
|
|
this.id = id2;
|
|
this.length = length3;
|
|
}
|
|
/**
|
|
* @type {boolean}
|
|
*/
|
|
get deleted() {
|
|
throw methodUnimplemented();
|
|
}
|
|
/**
|
|
* Merge this struct with the item to the right.
|
|
* This method is already assuming that `this.id.clock + this.length === this.id.clock`.
|
|
* Also this method does *not* remove right from StructStore!
|
|
* @param {AbstractStruct} right
|
|
* @return {boolean} whether this merged with right
|
|
*/
|
|
mergeWith(right) {
|
|
return false;
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
|
|
* @param {number} offset
|
|
* @param {number} encodingRef
|
|
*/
|
|
write(encoder, offset, encodingRef) {
|
|
throw methodUnimplemented();
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {number} offset
|
|
*/
|
|
integrate(transaction, offset) {
|
|
throw methodUnimplemented();
|
|
}
|
|
};
|
|
var structGCRefNumber = 0;
|
|
var GC = class extends AbstractStruct {
|
|
get deleted() {
|
|
return true;
|
|
}
|
|
delete() {
|
|
}
|
|
/**
|
|
* @param {GC} right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(right) {
|
|
if (this.constructor !== right.constructor) {
|
|
return false;
|
|
}
|
|
this.length += right.length;
|
|
return true;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {number} offset
|
|
*/
|
|
integrate(transaction, offset) {
|
|
if (offset > 0) {
|
|
this.id.clock += offset;
|
|
this.length -= offset;
|
|
}
|
|
addStruct(transaction.doc.store, this);
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
encoder.writeInfo(structGCRefNumber);
|
|
encoder.writeLen(this.length - offset);
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {StructStore} store
|
|
* @return {null | number}
|
|
*/
|
|
getMissing(transaction, store2) {
|
|
return null;
|
|
}
|
|
};
|
|
var ContentBinary = class _ContentBinary {
|
|
/**
|
|
* @param {Uint8Array} content
|
|
*/
|
|
constructor(content) {
|
|
this.content = content;
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getLength() {
|
|
return 1;
|
|
}
|
|
/**
|
|
* @return {Array<any>}
|
|
*/
|
|
getContent() {
|
|
return [this.content];
|
|
}
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isCountable() {
|
|
return true;
|
|
}
|
|
/**
|
|
* @return {ContentBinary}
|
|
*/
|
|
copy() {
|
|
return new _ContentBinary(this.content);
|
|
}
|
|
/**
|
|
* @param {number} offset
|
|
* @return {ContentBinary}
|
|
*/
|
|
splice(offset) {
|
|
throw methodUnimplemented();
|
|
}
|
|
/**
|
|
* @param {ContentBinary} right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(right) {
|
|
return false;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {Item} item
|
|
*/
|
|
integrate(transaction, item) {
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
*/
|
|
delete(transaction) {
|
|
}
|
|
/**
|
|
* @param {StructStore} store
|
|
*/
|
|
gc(store2) {
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
encoder.writeBuf(this.content);
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getRef() {
|
|
return 3;
|
|
}
|
|
};
|
|
var readContentBinary = (decoder) => new ContentBinary(decoder.readBuf());
|
|
var ContentDeleted = class _ContentDeleted {
|
|
/**
|
|
* @param {number} len
|
|
*/
|
|
constructor(len) {
|
|
this.len = len;
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getLength() {
|
|
return this.len;
|
|
}
|
|
/**
|
|
* @return {Array<any>}
|
|
*/
|
|
getContent() {
|
|
return [];
|
|
}
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isCountable() {
|
|
return false;
|
|
}
|
|
/**
|
|
* @return {ContentDeleted}
|
|
*/
|
|
copy() {
|
|
return new _ContentDeleted(this.len);
|
|
}
|
|
/**
|
|
* @param {number} offset
|
|
* @return {ContentDeleted}
|
|
*/
|
|
splice(offset) {
|
|
const right = new _ContentDeleted(this.len - offset);
|
|
this.len = offset;
|
|
return right;
|
|
}
|
|
/**
|
|
* @param {ContentDeleted} right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(right) {
|
|
this.len += right.len;
|
|
return true;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {Item} item
|
|
*/
|
|
integrate(transaction, item) {
|
|
addToDeleteSet(transaction.deleteSet, item.id.client, item.id.clock, this.len);
|
|
item.markDeleted();
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
*/
|
|
delete(transaction) {
|
|
}
|
|
/**
|
|
* @param {StructStore} store
|
|
*/
|
|
gc(store2) {
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
encoder.writeLen(this.len - offset);
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getRef() {
|
|
return 1;
|
|
}
|
|
};
|
|
var readContentDeleted = (decoder) => new ContentDeleted(decoder.readLen());
|
|
var createDocFromOpts = (guid, opts) => new Doc({ guid, ...opts, shouldLoad: opts.shouldLoad || opts.autoLoad || false });
|
|
var ContentDoc = class _ContentDoc {
|
|
/**
|
|
* @param {Doc} doc
|
|
*/
|
|
constructor(doc2) {
|
|
if (doc2._item) {
|
|
console.error("This document was already integrated as a sub-document. You should create a second instance instead with the same guid.");
|
|
}
|
|
this.doc = doc2;
|
|
const opts = {};
|
|
this.opts = opts;
|
|
if (!doc2.gc) {
|
|
opts.gc = false;
|
|
}
|
|
if (doc2.autoLoad) {
|
|
opts.autoLoad = true;
|
|
}
|
|
if (doc2.meta !== null) {
|
|
opts.meta = doc2.meta;
|
|
}
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getLength() {
|
|
return 1;
|
|
}
|
|
/**
|
|
* @return {Array<any>}
|
|
*/
|
|
getContent() {
|
|
return [this.doc];
|
|
}
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isCountable() {
|
|
return true;
|
|
}
|
|
/**
|
|
* @return {ContentDoc}
|
|
*/
|
|
copy() {
|
|
return new _ContentDoc(createDocFromOpts(this.doc.guid, this.opts));
|
|
}
|
|
/**
|
|
* @param {number} offset
|
|
* @return {ContentDoc}
|
|
*/
|
|
splice(offset) {
|
|
throw methodUnimplemented();
|
|
}
|
|
/**
|
|
* @param {ContentDoc} right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(right) {
|
|
return false;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {Item} item
|
|
*/
|
|
integrate(transaction, item) {
|
|
this.doc._item = item;
|
|
transaction.subdocsAdded.add(this.doc);
|
|
if (this.doc.shouldLoad) {
|
|
transaction.subdocsLoaded.add(this.doc);
|
|
}
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
*/
|
|
delete(transaction) {
|
|
if (transaction.subdocsAdded.has(this.doc)) {
|
|
transaction.subdocsAdded.delete(this.doc);
|
|
} else {
|
|
transaction.subdocsRemoved.add(this.doc);
|
|
}
|
|
}
|
|
/**
|
|
* @param {StructStore} store
|
|
*/
|
|
gc(store2) {
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
encoder.writeString(this.doc.guid);
|
|
encoder.writeAny(this.opts);
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getRef() {
|
|
return 9;
|
|
}
|
|
};
|
|
var readContentDoc = (decoder) => new ContentDoc(createDocFromOpts(decoder.readString(), decoder.readAny()));
|
|
var ContentEmbed = class _ContentEmbed {
|
|
/**
|
|
* @param {Object} embed
|
|
*/
|
|
constructor(embed) {
|
|
this.embed = embed;
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getLength() {
|
|
return 1;
|
|
}
|
|
/**
|
|
* @return {Array<any>}
|
|
*/
|
|
getContent() {
|
|
return [this.embed];
|
|
}
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isCountable() {
|
|
return true;
|
|
}
|
|
/**
|
|
* @return {ContentEmbed}
|
|
*/
|
|
copy() {
|
|
return new _ContentEmbed(this.embed);
|
|
}
|
|
/**
|
|
* @param {number} offset
|
|
* @return {ContentEmbed}
|
|
*/
|
|
splice(offset) {
|
|
throw methodUnimplemented();
|
|
}
|
|
/**
|
|
* @param {ContentEmbed} right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(right) {
|
|
return false;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {Item} item
|
|
*/
|
|
integrate(transaction, item) {
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
*/
|
|
delete(transaction) {
|
|
}
|
|
/**
|
|
* @param {StructStore} store
|
|
*/
|
|
gc(store2) {
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
encoder.writeJSON(this.embed);
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getRef() {
|
|
return 5;
|
|
}
|
|
};
|
|
var readContentEmbed = (decoder) => new ContentEmbed(decoder.readJSON());
|
|
var ContentFormat = class _ContentFormat {
|
|
/**
|
|
* @param {string} key
|
|
* @param {Object} value
|
|
*/
|
|
constructor(key, value) {
|
|
this.key = key;
|
|
this.value = value;
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getLength() {
|
|
return 1;
|
|
}
|
|
/**
|
|
* @return {Array<any>}
|
|
*/
|
|
getContent() {
|
|
return [];
|
|
}
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isCountable() {
|
|
return false;
|
|
}
|
|
/**
|
|
* @return {ContentFormat}
|
|
*/
|
|
copy() {
|
|
return new _ContentFormat(this.key, this.value);
|
|
}
|
|
/**
|
|
* @param {number} _offset
|
|
* @return {ContentFormat}
|
|
*/
|
|
splice(_offset) {
|
|
throw methodUnimplemented();
|
|
}
|
|
/**
|
|
* @param {ContentFormat} _right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(_right) {
|
|
return false;
|
|
}
|
|
/**
|
|
* @param {Transaction} _transaction
|
|
* @param {Item} item
|
|
*/
|
|
integrate(_transaction, item) {
|
|
const p = (
|
|
/** @type {YText} */
|
|
item.parent
|
|
);
|
|
p._searchMarker = null;
|
|
p._hasFormatting = true;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
*/
|
|
delete(transaction) {
|
|
}
|
|
/**
|
|
* @param {StructStore} store
|
|
*/
|
|
gc(store2) {
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
encoder.writeKey(this.key);
|
|
encoder.writeJSON(this.value);
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getRef() {
|
|
return 6;
|
|
}
|
|
};
|
|
var readContentFormat = (decoder) => new ContentFormat(decoder.readKey(), decoder.readJSON());
|
|
var ContentJSON = class _ContentJSON {
|
|
/**
|
|
* @param {Array<any>} arr
|
|
*/
|
|
constructor(arr) {
|
|
this.arr = arr;
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getLength() {
|
|
return this.arr.length;
|
|
}
|
|
/**
|
|
* @return {Array<any>}
|
|
*/
|
|
getContent() {
|
|
return this.arr;
|
|
}
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isCountable() {
|
|
return true;
|
|
}
|
|
/**
|
|
* @return {ContentJSON}
|
|
*/
|
|
copy() {
|
|
return new _ContentJSON(this.arr);
|
|
}
|
|
/**
|
|
* @param {number} offset
|
|
* @return {ContentJSON}
|
|
*/
|
|
splice(offset) {
|
|
const right = new _ContentJSON(this.arr.slice(offset));
|
|
this.arr = this.arr.slice(0, offset);
|
|
return right;
|
|
}
|
|
/**
|
|
* @param {ContentJSON} right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(right) {
|
|
this.arr = this.arr.concat(right.arr);
|
|
return true;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {Item} item
|
|
*/
|
|
integrate(transaction, item) {
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
*/
|
|
delete(transaction) {
|
|
}
|
|
/**
|
|
* @param {StructStore} store
|
|
*/
|
|
gc(store2) {
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
const len = this.arr.length;
|
|
encoder.writeLen(len - offset);
|
|
for (let i = offset; i < len; i++) {
|
|
const c = this.arr[i];
|
|
encoder.writeString(c === void 0 ? "undefined" : JSON.stringify(c));
|
|
}
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getRef() {
|
|
return 2;
|
|
}
|
|
};
|
|
var readContentJSON = (decoder) => {
|
|
const len = decoder.readLen();
|
|
const cs = [];
|
|
for (let i = 0; i < len; i++) {
|
|
const c = decoder.readString();
|
|
if (c === "undefined") {
|
|
cs.push(void 0);
|
|
} else {
|
|
cs.push(JSON.parse(c));
|
|
}
|
|
}
|
|
return new ContentJSON(cs);
|
|
};
|
|
var isDevMode = getVariable("node_env") === "development";
|
|
var ContentAny = class _ContentAny {
|
|
/**
|
|
* @param {Array<any>} arr
|
|
*/
|
|
constructor(arr) {
|
|
this.arr = arr;
|
|
isDevMode && deepFreeze(arr);
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getLength() {
|
|
return this.arr.length;
|
|
}
|
|
/**
|
|
* @return {Array<any>}
|
|
*/
|
|
getContent() {
|
|
return this.arr;
|
|
}
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isCountable() {
|
|
return true;
|
|
}
|
|
/**
|
|
* @return {ContentAny}
|
|
*/
|
|
copy() {
|
|
return new _ContentAny(this.arr);
|
|
}
|
|
/**
|
|
* @param {number} offset
|
|
* @return {ContentAny}
|
|
*/
|
|
splice(offset) {
|
|
const right = new _ContentAny(this.arr.slice(offset));
|
|
this.arr = this.arr.slice(0, offset);
|
|
return right;
|
|
}
|
|
/**
|
|
* @param {ContentAny} right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(right) {
|
|
this.arr = this.arr.concat(right.arr);
|
|
return true;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {Item} item
|
|
*/
|
|
integrate(transaction, item) {
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
*/
|
|
delete(transaction) {
|
|
}
|
|
/**
|
|
* @param {StructStore} store
|
|
*/
|
|
gc(store2) {
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
const len = this.arr.length;
|
|
encoder.writeLen(len - offset);
|
|
for (let i = offset; i < len; i++) {
|
|
const c = this.arr[i];
|
|
encoder.writeAny(c);
|
|
}
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getRef() {
|
|
return 8;
|
|
}
|
|
};
|
|
var readContentAny = (decoder) => {
|
|
const len = decoder.readLen();
|
|
const cs = [];
|
|
for (let i = 0; i < len; i++) {
|
|
cs.push(decoder.readAny());
|
|
}
|
|
return new ContentAny(cs);
|
|
};
|
|
var ContentString = class _ContentString {
|
|
/**
|
|
* @param {string} str
|
|
*/
|
|
constructor(str) {
|
|
this.str = str;
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getLength() {
|
|
return this.str.length;
|
|
}
|
|
/**
|
|
* @return {Array<any>}
|
|
*/
|
|
getContent() {
|
|
return this.str.split("");
|
|
}
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isCountable() {
|
|
return true;
|
|
}
|
|
/**
|
|
* @return {ContentString}
|
|
*/
|
|
copy() {
|
|
return new _ContentString(this.str);
|
|
}
|
|
/**
|
|
* @param {number} offset
|
|
* @return {ContentString}
|
|
*/
|
|
splice(offset) {
|
|
const right = new _ContentString(this.str.slice(offset));
|
|
this.str = this.str.slice(0, offset);
|
|
const firstCharCode = this.str.charCodeAt(offset - 1);
|
|
if (firstCharCode >= 55296 && firstCharCode <= 56319) {
|
|
this.str = this.str.slice(0, offset - 1) + "\uFFFD";
|
|
right.str = "\uFFFD" + right.str.slice(1);
|
|
}
|
|
return right;
|
|
}
|
|
/**
|
|
* @param {ContentString} right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(right) {
|
|
this.str += right.str;
|
|
return true;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {Item} item
|
|
*/
|
|
integrate(transaction, item) {
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
*/
|
|
delete(transaction) {
|
|
}
|
|
/**
|
|
* @param {StructStore} store
|
|
*/
|
|
gc(store2) {
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
encoder.writeString(offset === 0 ? this.str : this.str.slice(offset));
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getRef() {
|
|
return 4;
|
|
}
|
|
};
|
|
var readContentString = (decoder) => new ContentString(decoder.readString());
|
|
var typeRefs = [
|
|
readYArray,
|
|
readYMap,
|
|
readYText,
|
|
readYXmlElement,
|
|
readYXmlFragment,
|
|
readYXmlHook,
|
|
readYXmlText
|
|
];
|
|
var YArrayRefID = 0;
|
|
var YMapRefID = 1;
|
|
var YTextRefID = 2;
|
|
var YXmlElementRefID = 3;
|
|
var YXmlFragmentRefID = 4;
|
|
var YXmlHookRefID = 5;
|
|
var YXmlTextRefID = 6;
|
|
var ContentType = class _ContentType {
|
|
/**
|
|
* @param {AbstractType<any>} type
|
|
*/
|
|
constructor(type) {
|
|
this.type = type;
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getLength() {
|
|
return 1;
|
|
}
|
|
/**
|
|
* @return {Array<any>}
|
|
*/
|
|
getContent() {
|
|
return [this.type];
|
|
}
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isCountable() {
|
|
return true;
|
|
}
|
|
/**
|
|
* @return {ContentType}
|
|
*/
|
|
copy() {
|
|
return new _ContentType(this.type._copy());
|
|
}
|
|
/**
|
|
* @param {number} offset
|
|
* @return {ContentType}
|
|
*/
|
|
splice(offset) {
|
|
throw methodUnimplemented();
|
|
}
|
|
/**
|
|
* @param {ContentType} right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(right) {
|
|
return false;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {Item} item
|
|
*/
|
|
integrate(transaction, item) {
|
|
this.type._integrate(transaction.doc, item);
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
*/
|
|
delete(transaction) {
|
|
let item = this.type._start;
|
|
while (item !== null) {
|
|
if (!item.deleted) {
|
|
item.delete(transaction);
|
|
} else if (item.id.clock < (transaction.beforeState.get(item.id.client) || 0)) {
|
|
transaction._mergeStructs.push(item);
|
|
}
|
|
item = item.right;
|
|
}
|
|
this.type._map.forEach((item2) => {
|
|
if (!item2.deleted) {
|
|
item2.delete(transaction);
|
|
} else if (item2.id.clock < (transaction.beforeState.get(item2.id.client) || 0)) {
|
|
transaction._mergeStructs.push(item2);
|
|
}
|
|
});
|
|
transaction.changed.delete(this.type);
|
|
}
|
|
/**
|
|
* @param {StructStore} store
|
|
*/
|
|
gc(store2) {
|
|
let item = this.type._start;
|
|
while (item !== null) {
|
|
item.gc(store2, true);
|
|
item = item.right;
|
|
}
|
|
this.type._start = null;
|
|
this.type._map.forEach(
|
|
/** @param {Item | null} item */
|
|
(item2) => {
|
|
while (item2 !== null) {
|
|
item2.gc(store2, true);
|
|
item2 = item2.left;
|
|
}
|
|
}
|
|
);
|
|
this.type._map = /* @__PURE__ */ new Map();
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
this.type._write(encoder);
|
|
}
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
getRef() {
|
|
return 7;
|
|
}
|
|
};
|
|
var readContentType = (decoder) => new ContentType(typeRefs[decoder.readTypeRef()](decoder));
|
|
var followRedone = (store2, id2) => {
|
|
let nextID = id2;
|
|
let diff = 0;
|
|
let item;
|
|
do {
|
|
if (diff > 0) {
|
|
nextID = createID(nextID.client, nextID.clock + diff);
|
|
}
|
|
item = getItem(store2, nextID);
|
|
diff = nextID.clock - item.id.clock;
|
|
nextID = item.redone;
|
|
} while (nextID !== null && item instanceof Item);
|
|
return {
|
|
item,
|
|
diff
|
|
};
|
|
};
|
|
var keepItem = (item, keep) => {
|
|
while (item !== null && item.keep !== keep) {
|
|
item.keep = keep;
|
|
item = /** @type {AbstractType<any>} */
|
|
item.parent._item;
|
|
}
|
|
};
|
|
var splitItem = (transaction, leftItem, diff) => {
|
|
const { client, clock } = leftItem.id;
|
|
const rightItem = new Item(
|
|
createID(client, clock + diff),
|
|
leftItem,
|
|
createID(client, clock + diff - 1),
|
|
leftItem.right,
|
|
leftItem.rightOrigin,
|
|
leftItem.parent,
|
|
leftItem.parentSub,
|
|
leftItem.content.splice(diff)
|
|
);
|
|
if (leftItem.deleted) {
|
|
rightItem.markDeleted();
|
|
}
|
|
if (leftItem.keep) {
|
|
rightItem.keep = true;
|
|
}
|
|
if (leftItem.redone !== null) {
|
|
rightItem.redone = createID(leftItem.redone.client, leftItem.redone.clock + diff);
|
|
}
|
|
leftItem.right = rightItem;
|
|
if (rightItem.right !== null) {
|
|
rightItem.right.left = rightItem;
|
|
}
|
|
transaction._mergeStructs.push(rightItem);
|
|
if (rightItem.parentSub !== null && rightItem.right === null) {
|
|
rightItem.parent._map.set(rightItem.parentSub, rightItem);
|
|
}
|
|
leftItem.length = diff;
|
|
return rightItem;
|
|
};
|
|
var isDeletedByUndoStack = (stack, id2) => some(
|
|
stack,
|
|
/** @param {StackItem} s */
|
|
(s) => isDeleted(s.deletions, id2)
|
|
);
|
|
var redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) => {
|
|
const doc2 = transaction.doc;
|
|
const store2 = doc2.store;
|
|
const ownClientID = doc2.clientID;
|
|
const redone = item.redone;
|
|
if (redone !== null) {
|
|
return getItemCleanStart(transaction, redone);
|
|
}
|
|
let parentItem = (
|
|
/** @type {AbstractType<any>} */
|
|
item.parent._item
|
|
);
|
|
let left = null;
|
|
let right;
|
|
if (parentItem !== null && parentItem.deleted === true) {
|
|
if (parentItem.redone === null && (!redoitems.has(parentItem) || redoItem(transaction, parentItem, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) === null)) {
|
|
return null;
|
|
}
|
|
while (parentItem.redone !== null) {
|
|
parentItem = getItemCleanStart(transaction, parentItem.redone);
|
|
}
|
|
}
|
|
const parentType = parentItem === null ? (
|
|
/** @type {AbstractType<any>} */
|
|
item.parent
|
|
) : (
|
|
/** @type {ContentType} */
|
|
parentItem.content.type
|
|
);
|
|
if (item.parentSub === null) {
|
|
left = item.left;
|
|
right = item;
|
|
while (left !== null) {
|
|
let leftTrace = left;
|
|
while (leftTrace !== null && /** @type {AbstractType<any>} */
|
|
leftTrace.parent._item !== parentItem) {
|
|
leftTrace = leftTrace.redone === null ? null : getItemCleanStart(transaction, leftTrace.redone);
|
|
}
|
|
if (leftTrace !== null && /** @type {AbstractType<any>} */
|
|
leftTrace.parent._item === parentItem) {
|
|
left = leftTrace;
|
|
break;
|
|
}
|
|
left = left.left;
|
|
}
|
|
while (right !== null) {
|
|
let rightTrace = right;
|
|
while (rightTrace !== null && /** @type {AbstractType<any>} */
|
|
rightTrace.parent._item !== parentItem) {
|
|
rightTrace = rightTrace.redone === null ? null : getItemCleanStart(transaction, rightTrace.redone);
|
|
}
|
|
if (rightTrace !== null && /** @type {AbstractType<any>} */
|
|
rightTrace.parent._item === parentItem) {
|
|
right = rightTrace;
|
|
break;
|
|
}
|
|
right = right.right;
|
|
}
|
|
} else {
|
|
right = null;
|
|
if (item.right && !ignoreRemoteMapChanges) {
|
|
left = item;
|
|
while (left !== null && left.right !== null && (left.right.redone || isDeleted(itemsToDelete, left.right.id) || isDeletedByUndoStack(um.undoStack, left.right.id) || isDeletedByUndoStack(um.redoStack, left.right.id))) {
|
|
left = left.right;
|
|
while (left.redone) left = getItemCleanStart(transaction, left.redone);
|
|
}
|
|
if (left && left.right !== null) {
|
|
return null;
|
|
}
|
|
} else {
|
|
left = parentType._map.get(item.parentSub) || null;
|
|
}
|
|
}
|
|
const nextClock = getState(store2, ownClientID);
|
|
const nextId = createID(ownClientID, nextClock);
|
|
const redoneItem = new Item(
|
|
nextId,
|
|
left,
|
|
left && left.lastId,
|
|
right,
|
|
right && right.id,
|
|
parentType,
|
|
item.parentSub,
|
|
item.content.copy()
|
|
);
|
|
item.redone = nextId;
|
|
keepItem(redoneItem, true);
|
|
redoneItem.integrate(transaction, 0);
|
|
return redoneItem;
|
|
};
|
|
var Item = class _Item extends AbstractStruct {
|
|
/**
|
|
* @param {ID} id
|
|
* @param {Item | null} left
|
|
* @param {ID | null} origin
|
|
* @param {Item | null} right
|
|
* @param {ID | null} rightOrigin
|
|
* @param {AbstractType<any>|ID|null} parent Is a type if integrated, is null if it is possible to copy parent from left or right, is ID before integration to search for it.
|
|
* @param {string | null} parentSub
|
|
* @param {AbstractContent} content
|
|
*/
|
|
constructor(id2, left, origin2, right, rightOrigin, parent, parentSub, content) {
|
|
super(id2, content.getLength());
|
|
this.origin = origin2;
|
|
this.left = left;
|
|
this.right = right;
|
|
this.rightOrigin = rightOrigin;
|
|
this.parent = parent;
|
|
this.parentSub = parentSub;
|
|
this.redone = null;
|
|
this.content = content;
|
|
this.info = this.content.isCountable() ? BIT2 : 0;
|
|
}
|
|
/**
|
|
* This is used to mark the item as an indexed fast-search marker
|
|
*
|
|
* @type {boolean}
|
|
*/
|
|
set marker(isMarked) {
|
|
if ((this.info & BIT4) > 0 !== isMarked) {
|
|
this.info ^= BIT4;
|
|
}
|
|
}
|
|
get marker() {
|
|
return (this.info & BIT4) > 0;
|
|
}
|
|
/**
|
|
* If true, do not garbage collect this Item.
|
|
*/
|
|
get keep() {
|
|
return (this.info & BIT1) > 0;
|
|
}
|
|
set keep(doKeep) {
|
|
if (this.keep !== doKeep) {
|
|
this.info ^= BIT1;
|
|
}
|
|
}
|
|
get countable() {
|
|
return (this.info & BIT2) > 0;
|
|
}
|
|
/**
|
|
* Whether this item was deleted or not.
|
|
* @type {Boolean}
|
|
*/
|
|
get deleted() {
|
|
return (this.info & BIT3) > 0;
|
|
}
|
|
set deleted(doDelete) {
|
|
if (this.deleted !== doDelete) {
|
|
this.info ^= BIT3;
|
|
}
|
|
}
|
|
markDeleted() {
|
|
this.info |= BIT3;
|
|
}
|
|
/**
|
|
* Return the creator clientID of the missing op or define missing items and return null.
|
|
*
|
|
* @param {Transaction} transaction
|
|
* @param {StructStore} store
|
|
* @return {null | number}
|
|
*/
|
|
getMissing(transaction, store2) {
|
|
if (this.origin && this.origin.client !== this.id.client && this.origin.clock >= getState(store2, this.origin.client)) {
|
|
return this.origin.client;
|
|
}
|
|
if (this.rightOrigin && this.rightOrigin.client !== this.id.client && this.rightOrigin.clock >= getState(store2, this.rightOrigin.client)) {
|
|
return this.rightOrigin.client;
|
|
}
|
|
if (this.parent && this.parent.constructor === ID && this.id.client !== this.parent.client && this.parent.clock >= getState(store2, this.parent.client)) {
|
|
return this.parent.client;
|
|
}
|
|
if (this.origin) {
|
|
this.left = getItemCleanEnd(transaction, store2, this.origin);
|
|
this.origin = this.left.lastId;
|
|
}
|
|
if (this.rightOrigin) {
|
|
this.right = getItemCleanStart(transaction, this.rightOrigin);
|
|
this.rightOrigin = this.right.id;
|
|
}
|
|
if (this.left && this.left.constructor === GC || this.right && this.right.constructor === GC) {
|
|
this.parent = null;
|
|
} else if (!this.parent) {
|
|
if (this.left && this.left.constructor === _Item) {
|
|
this.parent = this.left.parent;
|
|
this.parentSub = this.left.parentSub;
|
|
} else if (this.right && this.right.constructor === _Item) {
|
|
this.parent = this.right.parent;
|
|
this.parentSub = this.right.parentSub;
|
|
}
|
|
} else if (this.parent.constructor === ID) {
|
|
const parentItem = getItem(store2, this.parent);
|
|
if (parentItem.constructor === GC) {
|
|
this.parent = null;
|
|
} else {
|
|
this.parent = /** @type {ContentType} */
|
|
parentItem.content.type;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {number} offset
|
|
*/
|
|
integrate(transaction, offset) {
|
|
if (offset > 0) {
|
|
this.id.clock += offset;
|
|
this.left = getItemCleanEnd(transaction, transaction.doc.store, createID(this.id.client, this.id.clock - 1));
|
|
this.origin = this.left.lastId;
|
|
this.content = this.content.splice(offset);
|
|
this.length -= offset;
|
|
}
|
|
if (this.parent) {
|
|
if (!this.left && (!this.right || this.right.left !== null) || this.left && this.left.right !== this.right) {
|
|
let left = this.left;
|
|
let o;
|
|
if (left !== null) {
|
|
o = left.right;
|
|
} else if (this.parentSub !== null) {
|
|
o = /** @type {AbstractType<any>} */
|
|
this.parent._map.get(this.parentSub) || null;
|
|
while (o !== null && o.left !== null) {
|
|
o = o.left;
|
|
}
|
|
} else {
|
|
o = /** @type {AbstractType<any>} */
|
|
this.parent._start;
|
|
}
|
|
const conflictingItems = /* @__PURE__ */ new Set();
|
|
const itemsBeforeOrigin = /* @__PURE__ */ new Set();
|
|
while (o !== null && o !== this.right) {
|
|
itemsBeforeOrigin.add(o);
|
|
conflictingItems.add(o);
|
|
if (compareIDs(this.origin, o.origin)) {
|
|
if (o.id.client < this.id.client) {
|
|
left = o;
|
|
conflictingItems.clear();
|
|
} else if (compareIDs(this.rightOrigin, o.rightOrigin)) {
|
|
break;
|
|
}
|
|
} else if (o.origin !== null && itemsBeforeOrigin.has(getItem(transaction.doc.store, o.origin))) {
|
|
if (!conflictingItems.has(getItem(transaction.doc.store, o.origin))) {
|
|
left = o;
|
|
conflictingItems.clear();
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
o = o.right;
|
|
}
|
|
this.left = left;
|
|
}
|
|
if (this.left !== null) {
|
|
const right = this.left.right;
|
|
this.right = right;
|
|
this.left.right = this;
|
|
} else {
|
|
let r;
|
|
if (this.parentSub !== null) {
|
|
r = /** @type {AbstractType<any>} */
|
|
this.parent._map.get(this.parentSub) || null;
|
|
while (r !== null && r.left !== null) {
|
|
r = r.left;
|
|
}
|
|
} else {
|
|
r = /** @type {AbstractType<any>} */
|
|
this.parent._start;
|
|
this.parent._start = this;
|
|
}
|
|
this.right = r;
|
|
}
|
|
if (this.right !== null) {
|
|
this.right.left = this;
|
|
} else if (this.parentSub !== null) {
|
|
this.parent._map.set(this.parentSub, this);
|
|
if (this.left !== null) {
|
|
this.left.delete(transaction);
|
|
}
|
|
}
|
|
if (this.parentSub === null && this.countable && !this.deleted) {
|
|
this.parent._length += this.length;
|
|
}
|
|
addStruct(transaction.doc.store, this);
|
|
this.content.integrate(transaction, this);
|
|
addChangedTypeToTransaction(
|
|
transaction,
|
|
/** @type {AbstractType<any>} */
|
|
this.parent,
|
|
this.parentSub
|
|
);
|
|
if (
|
|
/** @type {AbstractType<any>} */
|
|
this.parent._item !== null && /** @type {AbstractType<any>} */
|
|
this.parent._item.deleted || this.parentSub !== null && this.right !== null
|
|
) {
|
|
this.delete(transaction);
|
|
}
|
|
} else {
|
|
new GC(this.id, this.length).integrate(transaction, 0);
|
|
}
|
|
}
|
|
/**
|
|
* Returns the next non-deleted item
|
|
*/
|
|
get next() {
|
|
let n = this.right;
|
|
while (n !== null && n.deleted) {
|
|
n = n.right;
|
|
}
|
|
return n;
|
|
}
|
|
/**
|
|
* Returns the previous non-deleted item
|
|
*/
|
|
get prev() {
|
|
let n = this.left;
|
|
while (n !== null && n.deleted) {
|
|
n = n.left;
|
|
}
|
|
return n;
|
|
}
|
|
/**
|
|
* Computes the last content address of this Item.
|
|
*/
|
|
get lastId() {
|
|
return this.length === 1 ? this.id : createID(this.id.client, this.id.clock + this.length - 1);
|
|
}
|
|
/**
|
|
* Try to merge two items
|
|
*
|
|
* @param {Item} right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(right) {
|
|
if (this.constructor === right.constructor && compareIDs(right.origin, this.lastId) && this.right === right && compareIDs(this.rightOrigin, right.rightOrigin) && this.id.client === right.id.client && this.id.clock + this.length === right.id.clock && this.deleted === right.deleted && this.redone === null && right.redone === null && this.content.constructor === right.content.constructor && this.content.mergeWith(right.content)) {
|
|
const searchMarker = (
|
|
/** @type {AbstractType<any>} */
|
|
this.parent._searchMarker
|
|
);
|
|
if (searchMarker) {
|
|
searchMarker.forEach((marker) => {
|
|
if (marker.p === right) {
|
|
marker.p = this;
|
|
if (!this.deleted && this.countable) {
|
|
marker.index -= this.length;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if (right.keep) {
|
|
this.keep = true;
|
|
}
|
|
this.right = right.right;
|
|
if (this.right !== null) {
|
|
this.right.left = this;
|
|
}
|
|
this.length += right.length;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* Mark this Item as deleted.
|
|
*
|
|
* @param {Transaction} transaction
|
|
*/
|
|
delete(transaction) {
|
|
if (!this.deleted) {
|
|
const parent = (
|
|
/** @type {AbstractType<any>} */
|
|
this.parent
|
|
);
|
|
if (this.countable && this.parentSub === null) {
|
|
parent._length -= this.length;
|
|
}
|
|
this.markDeleted();
|
|
addToDeleteSet(transaction.deleteSet, this.id.client, this.id.clock, this.length);
|
|
addChangedTypeToTransaction(transaction, parent, this.parentSub);
|
|
this.content.delete(transaction);
|
|
}
|
|
}
|
|
/**
|
|
* @param {StructStore} store
|
|
* @param {boolean} parentGCd
|
|
*/
|
|
gc(store2, parentGCd) {
|
|
if (!this.deleted) {
|
|
throw unexpectedCase();
|
|
}
|
|
this.content.gc(store2);
|
|
if (parentGCd) {
|
|
replaceStruct(store2, this, new GC(this.id, this.length));
|
|
} else {
|
|
this.content = new ContentDeleted(this.length);
|
|
}
|
|
}
|
|
/**
|
|
* Transform the properties of this type to binary and write it to an
|
|
* BinaryEncoder.
|
|
*
|
|
* This is called when this Item is sent to a remote peer.
|
|
*
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to.
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
const origin2 = offset > 0 ? createID(this.id.client, this.id.clock + offset - 1) : this.origin;
|
|
const rightOrigin = this.rightOrigin;
|
|
const parentSub = this.parentSub;
|
|
const info = this.content.getRef() & BITS5 | (origin2 === null ? 0 : BIT8) | // origin is defined
|
|
(rightOrigin === null ? 0 : BIT7) | // right origin is defined
|
|
(parentSub === null ? 0 : BIT6);
|
|
encoder.writeInfo(info);
|
|
if (origin2 !== null) {
|
|
encoder.writeLeftID(origin2);
|
|
}
|
|
if (rightOrigin !== null) {
|
|
encoder.writeRightID(rightOrigin);
|
|
}
|
|
if (origin2 === null && rightOrigin === null) {
|
|
const parent = (
|
|
/** @type {AbstractType<any>} */
|
|
this.parent
|
|
);
|
|
if (parent._item !== void 0) {
|
|
const parentItem = parent._item;
|
|
if (parentItem === null) {
|
|
const ykey = findRootTypeKey(parent);
|
|
encoder.writeParentInfo(true);
|
|
encoder.writeString(ykey);
|
|
} else {
|
|
encoder.writeParentInfo(false);
|
|
encoder.writeLeftID(parentItem.id);
|
|
}
|
|
} else if (parent.constructor === String) {
|
|
encoder.writeParentInfo(true);
|
|
encoder.writeString(parent);
|
|
} else if (parent.constructor === ID) {
|
|
encoder.writeParentInfo(false);
|
|
encoder.writeLeftID(parent);
|
|
} else {
|
|
unexpectedCase();
|
|
}
|
|
if (parentSub !== null) {
|
|
encoder.writeString(parentSub);
|
|
}
|
|
}
|
|
this.content.write(encoder, offset);
|
|
}
|
|
};
|
|
var readItemContent = (decoder, info) => contentRefs[info & BITS5](decoder);
|
|
var contentRefs = [
|
|
() => {
|
|
unexpectedCase();
|
|
},
|
|
// GC is not ItemContent
|
|
readContentDeleted,
|
|
// 1
|
|
readContentJSON,
|
|
// 2
|
|
readContentBinary,
|
|
// 3
|
|
readContentString,
|
|
// 4
|
|
readContentEmbed,
|
|
// 5
|
|
readContentFormat,
|
|
// 6
|
|
readContentType,
|
|
// 7
|
|
readContentAny,
|
|
// 8
|
|
readContentDoc,
|
|
// 9
|
|
() => {
|
|
unexpectedCase();
|
|
}
|
|
// 10 - Skip is not ItemContent
|
|
];
|
|
var structSkipRefNumber = 10;
|
|
var Skip = class extends AbstractStruct {
|
|
get deleted() {
|
|
return true;
|
|
}
|
|
delete() {
|
|
}
|
|
/**
|
|
* @param {Skip} right
|
|
* @return {boolean}
|
|
*/
|
|
mergeWith(right) {
|
|
if (this.constructor !== right.constructor) {
|
|
return false;
|
|
}
|
|
this.length += right.length;
|
|
return true;
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {number} offset
|
|
*/
|
|
integrate(transaction, offset) {
|
|
unexpectedCase();
|
|
}
|
|
/**
|
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
* @param {number} offset
|
|
*/
|
|
write(encoder, offset) {
|
|
encoder.writeInfo(structSkipRefNumber);
|
|
writeVarUint(encoder.restEncoder, this.length - offset);
|
|
}
|
|
/**
|
|
* @param {Transaction} transaction
|
|
* @param {StructStore} store
|
|
* @return {null | number}
|
|
*/
|
|
getMissing(transaction, store2) {
|
|
return null;
|
|
}
|
|
};
|
|
var glo = (
|
|
/** @type {any} */
|
|
typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}
|
|
);
|
|
var importIdentifier = "__ $YJS$ __";
|
|
if (glo[importIdentifier] === true) {
|
|
console.error("Yjs was already imported. This breaks constructor checks and will lead to issues! - https://github.com/yjs/yjs/issues/438");
|
|
}
|
|
glo[importIdentifier] = true;
|
|
|
|
// packages/sync/node_modules/y-protocols/awareness.js
|
|
var outdatedTimeout = 3e4;
|
|
var Awareness = class extends Observable {
|
|
/**
|
|
* @param {Y.Doc} doc
|
|
*/
|
|
constructor(doc2) {
|
|
super();
|
|
this.doc = doc2;
|
|
this.clientID = doc2.clientID;
|
|
this.states = /* @__PURE__ */ new Map();
|
|
this.meta = /* @__PURE__ */ new Map();
|
|
this._checkInterval = /** @type {any} */
|
|
setInterval(() => {
|
|
const now = getUnixTime();
|
|
if (this.getLocalState() !== null && outdatedTimeout / 2 <= now - /** @type {{lastUpdated:number}} */
|
|
this.meta.get(this.clientID).lastUpdated) {
|
|
this.setLocalState(this.getLocalState());
|
|
}
|
|
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");
|
|
}
|
|
}, floor(outdatedTimeout / 10));
|
|
doc2.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 === void 0 ? 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: 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 (!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;
|
|
}
|
|
};
|
|
var removeAwarenessStates = (awareness, clients, origin2) => {
|
|
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: getUnixTime()
|
|
});
|
|
}
|
|
removed.push(clientID);
|
|
}
|
|
}
|
|
if (removed.length > 0) {
|
|
awareness.emit("change", [{ added: [], updated: [], removed }, origin2]);
|
|
awareness.emit("update", [{ added: [], updated: [], removed }, origin2]);
|
|
}
|
|
};
|
|
|
|
// packages/sync/build-module/config.mjs
|
|
var CRDT_DOC_VERSION = 1;
|
|
var CRDT_DOC_META_PERSISTENCE_KEY = "fromPersistence";
|
|
var CRDT_RECORD_MAP_KEY = "document";
|
|
var CRDT_STATE_MAP_KEY = "state";
|
|
var CRDT_STATE_MAP_SAVED_AT_KEY = "savedAt";
|
|
var CRDT_STATE_MAP_SAVED_BY_KEY = "savedBy";
|
|
var CRDT_STATE_MAP_VERSION_KEY = "version";
|
|
var LOCAL_EDITOR_ORIGIN = "gutenberg";
|
|
var LOCAL_SYNC_MANAGER_ORIGIN = "syncManager";
|
|
var LOCAL_UNDO_IGNORED_ORIGIN = "gutenberg-undo-ignored";
|
|
|
|
// packages/sync/build-module/errors.mjs
|
|
var ConnectionErrorCode = /* @__PURE__ */ ((ConnectionErrorCode22) => {
|
|
ConnectionErrorCode22["AUTHENTICATION_FAILED"] = "authentication-failed";
|
|
ConnectionErrorCode22["CONNECTION_EXPIRED"] = "connection-expired";
|
|
ConnectionErrorCode22["CONNECTION_LIMIT_EXCEEDED"] = "connection-limit-exceeded";
|
|
ConnectionErrorCode22["DOCUMENT_SIZE_LIMIT_EXCEEDED"] = "document-size-limit-exceeded";
|
|
ConnectionErrorCode22["UNKNOWN_ERROR"] = "unknown-error";
|
|
return ConnectionErrorCode22;
|
|
})(ConnectionErrorCode || {});
|
|
var ConnectionError = class extends Error {
|
|
constructor(code = "unknown-error", message) {
|
|
super(message);
|
|
this.code = code;
|
|
this.name = "ConnectionError";
|
|
}
|
|
};
|
|
|
|
// packages/sync/build-module/lock-unlock.mjs
|
|
var import_private_apis = __toESM(require_private_apis(), 1);
|
|
var { lock, unlock } = (0, import_private_apis.__dangerousOptInToUnstableAPIsOnlyForCoreModules)(
|
|
"I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.",
|
|
"@wordpress/sync"
|
|
);
|
|
|
|
// packages/sync/build-module/performance.mjs
|
|
function logPerformanceTiming(fn) {
|
|
return function(...args2) {
|
|
const start = performance.now();
|
|
const result = fn.apply(this, args2);
|
|
const end = performance.now();
|
|
console.log(
|
|
`[SyncManager][performance]: ${fn.name} took ${(end - start).toFixed(2)} ms`
|
|
);
|
|
return result;
|
|
};
|
|
}
|
|
function passThru(fn) {
|
|
return ((...args2) => fn(...args2));
|
|
}
|
|
function yieldToEventLoop(fn) {
|
|
return function(...args2) {
|
|
setTimeout(() => {
|
|
fn.apply(this, args2);
|
|
}, 0);
|
|
};
|
|
}
|
|
|
|
// packages/sync/build-module/providers/index.mjs
|
|
var import_hooks3 = __toESM(require_hooks(), 1);
|
|
|
|
// packages/sync/build-module/providers/http-polling/polling-manager.mjs
|
|
var import_hooks2 = __toESM(require_hooks(), 1);
|
|
|
|
// packages/sync/node_modules/y-protocols/sync.js
|
|
var messageYjsSyncStep1 = 0;
|
|
var messageYjsSyncStep2 = 1;
|
|
var messageYjsUpdate = 2;
|
|
var writeSyncStep1 = (encoder, doc2) => {
|
|
writeVarUint(encoder, messageYjsSyncStep1);
|
|
const sv = encodeStateVector(doc2);
|
|
writeVarUint8Array(encoder, sv);
|
|
};
|
|
var writeSyncStep2 = (encoder, doc2, encodedStateVector) => {
|
|
writeVarUint(encoder, messageYjsSyncStep2);
|
|
writeVarUint8Array(encoder, encodeStateAsUpdate(doc2, encodedStateVector));
|
|
};
|
|
var readSyncStep1 = (decoder, encoder, doc2) => writeSyncStep2(encoder, doc2, readVarUint8Array(decoder));
|
|
var readSyncStep2 = (decoder, doc2, transactionOrigin, errorHandler) => {
|
|
try {
|
|
applyUpdate(doc2, readVarUint8Array(decoder), transactionOrigin);
|
|
} catch (error) {
|
|
if (errorHandler != null) errorHandler(
|
|
/** @type {Error} */
|
|
error
|
|
);
|
|
console.error("Caught error while handling a Yjs update", error);
|
|
}
|
|
};
|
|
var readUpdate2 = readSyncStep2;
|
|
var readSyncMessage = (decoder, encoder, doc2, transactionOrigin, errorHandler) => {
|
|
const messageType = readVarUint(decoder);
|
|
switch (messageType) {
|
|
case messageYjsSyncStep1:
|
|
readSyncStep1(decoder, encoder, doc2);
|
|
break;
|
|
case messageYjsSyncStep2:
|
|
readSyncStep2(decoder, doc2, transactionOrigin, errorHandler);
|
|
break;
|
|
case messageYjsUpdate:
|
|
readUpdate2(decoder, doc2, transactionOrigin, errorHandler);
|
|
break;
|
|
default:
|
|
throw new Error("Unknown message type");
|
|
}
|
|
return messageType;
|
|
};
|
|
|
|
// packages/sync/build-module/providers/http-polling/config.mjs
|
|
var import_hooks = __toESM(require_hooks(), 1);
|
|
var DEFAULT_CLIENT_LIMIT_PER_ROOM = 3;
|
|
var ERROR_RETRY_DELAYS_SOLO_MS = [
|
|
2e3,
|
|
4e3,
|
|
8e3,
|
|
12e3
|
|
// Solo: 26s total retry time solo before dialog
|
|
];
|
|
var ERROR_RETRY_DELAYS_WITH_COLLABORATORS_MS = [
|
|
1e3,
|
|
2e3,
|
|
4e3,
|
|
8e3
|
|
// With collaborators: 15s total retry time before dialog
|
|
];
|
|
var DISCONNECT_DIALOG_RETRY_MS = 3e4;
|
|
var MANUAL_RETRY_INTERVAL_MS = 15e3;
|
|
var MAX_UPDATE_SIZE_IN_BYTES = 1 * 1024 * 1024;
|
|
var MAX_ROOMS_PER_REQUEST = 50;
|
|
var POLLING_INTERVAL_IN_MS = (0, import_hooks.applyFilters)(
|
|
"sync.pollingManager.pollingInterval",
|
|
4e3
|
|
// 4 seconds
|
|
);
|
|
var POLLING_INTERVAL_WITH_COLLABORATORS_IN_MS = (0, import_hooks.applyFilters)(
|
|
"sync.pollingManager.pollingIntervalWithCollaborators",
|
|
1e3
|
|
// 1 second
|
|
);
|
|
var POLLING_INTERVAL_BACKGROUND_TAB_IN_MS = 25 * 1e3;
|
|
|
|
// packages/sync/build-module/providers/http-polling/types.mjs
|
|
var SyncUpdateType = /* @__PURE__ */ ((SyncUpdateType2) => {
|
|
SyncUpdateType2["COMPACTION"] = "compaction";
|
|
SyncUpdateType2["SYNC_STEP_1"] = "sync_step1";
|
|
SyncUpdateType2["SYNC_STEP_2"] = "sync_step2";
|
|
SyncUpdateType2["UPDATE"] = "update";
|
|
return SyncUpdateType2;
|
|
})(SyncUpdateType || {});
|
|
|
|
// packages/sync/build-module/providers/http-polling/utils.mjs
|
|
var import_api_fetch = __toESM(require_api_fetch(), 1);
|
|
var SYNC_API_PATH = "/wp-sync/v1/updates";
|
|
function uint8ArrayToBase64(data) {
|
|
let binary = "";
|
|
const len = data.byteLength;
|
|
for (let i = 0; i < len; i++) {
|
|
binary += String.fromCharCode(data[i]);
|
|
}
|
|
return globalThis.btoa(binary);
|
|
}
|
|
function base64ToUint8Array(base64) {
|
|
const binaryString = globalThis.atob(base64);
|
|
const len = binaryString.length;
|
|
const bytes = new Uint8Array(len);
|
|
for (let i = 0; i < len; i++) {
|
|
bytes[i] = binaryString.charCodeAt(i);
|
|
}
|
|
return bytes;
|
|
}
|
|
function createSyncUpdate(data, type) {
|
|
return {
|
|
data: uint8ArrayToBase64(data),
|
|
type
|
|
};
|
|
}
|
|
function createUpdateQueue(initial = [], paused = true) {
|
|
let isPaused = paused;
|
|
const updates = [...initial];
|
|
return {
|
|
add(update) {
|
|
updates.push(update);
|
|
},
|
|
addBulk(bulkUpdates) {
|
|
if (0 === bulkUpdates.length) {
|
|
return;
|
|
}
|
|
updates.push(...bulkUpdates);
|
|
},
|
|
clear() {
|
|
updates.splice(0, updates.length);
|
|
},
|
|
get() {
|
|
if (isPaused) {
|
|
return [];
|
|
}
|
|
return updates.splice(0, updates.length);
|
|
},
|
|
pause() {
|
|
isPaused = true;
|
|
},
|
|
restore(restoredUpdates) {
|
|
const filtered = restoredUpdates.filter(
|
|
(u) => u.type !== SyncUpdateType.COMPACTION
|
|
);
|
|
if (0 === filtered.length) {
|
|
return;
|
|
}
|
|
updates.unshift(...filtered);
|
|
},
|
|
resume() {
|
|
isPaused = false;
|
|
},
|
|
size() {
|
|
return updates.length;
|
|
}
|
|
};
|
|
}
|
|
async function postSyncUpdate(payload) {
|
|
const response = await (0, import_api_fetch.default)({
|
|
body: JSON.stringify(payload),
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
method: "POST",
|
|
parse: false,
|
|
path: SYNC_API_PATH
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(
|
|
`Sync update failed with status ${response.status}`
|
|
);
|
|
}
|
|
return await response.json();
|
|
}
|
|
function postSyncUpdateNonBlocking(payload) {
|
|
if (payload.rooms.length === 0) {
|
|
return;
|
|
}
|
|
(0, import_api_fetch.default)({
|
|
body: JSON.stringify(payload),
|
|
headers: { "Content-Type": "application/json" },
|
|
keepalive: true,
|
|
method: "POST",
|
|
parse: false,
|
|
path: SYNC_API_PATH
|
|
}).catch(() => {
|
|
});
|
|
}
|
|
function intValueOrDefault(value, defaultValue) {
|
|
const intValue = parseInt(String(value), 10);
|
|
return isNaN(intValue) ? defaultValue : intValue;
|
|
}
|
|
function rotateWindow(items2, offset, size2) {
|
|
if (items2.length === 0) {
|
|
return { window: [], nextOffset: 0 };
|
|
}
|
|
const start = (offset % items2.length + items2.length) % items2.length;
|
|
const wrapped = [...items2.slice(start), ...items2.slice(0, start)];
|
|
return {
|
|
window: wrapped.slice(0, Math.max(0, size2)),
|
|
nextOffset: (start + Math.max(0, size2)) % items2.length
|
|
};
|
|
}
|
|
|
|
// packages/sync/build-module/providers/http-polling/polling-manager.mjs
|
|
var POLLING_MANAGER_ORIGIN = "polling-manager";
|
|
function isForbiddenError(error) {
|
|
return error?.data?.status === 403;
|
|
}
|
|
function identifyForbiddenRoom(error, rooms) {
|
|
const message = typeof error.message === "string" ? error.message : "";
|
|
const sortedRooms = [...rooms].sort((a, b) => b.length - a.length);
|
|
for (const room of sortedRooms) {
|
|
if (message.includes(room)) {
|
|
return room;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function handleForbiddenError(error, requestedRooms) {
|
|
const forbiddenRoom = identifyForbiddenRoom(
|
|
error,
|
|
requestedRooms.map((r) => r.room)
|
|
);
|
|
if (forbiddenRoom) {
|
|
const state = roomStates.get(forbiddenRoom);
|
|
if (state) {
|
|
state.log(
|
|
"Permission denied, unregistering room",
|
|
{ error },
|
|
"error",
|
|
true
|
|
// force
|
|
);
|
|
unregisterRoom(forbiddenRoom, { sendDisconnectSignal: false });
|
|
}
|
|
for (const room of requestedRooms) {
|
|
if (room.room === forbiddenRoom || !roomStates.has(room.room)) {
|
|
continue;
|
|
}
|
|
const remainingState = roomStates.get(room.room);
|
|
if (room.updates.length > 0) {
|
|
remainingState.updateQueue.restore(room.updates);
|
|
}
|
|
}
|
|
} else {
|
|
const rooms = [...roomStates.keys()];
|
|
for (const room of rooms) {
|
|
const state = roomStates.get(room);
|
|
if (state) {
|
|
state.log(
|
|
"Permission denied, unregistering room",
|
|
{ error },
|
|
"error",
|
|
true
|
|
// force
|
|
);
|
|
unregisterRoom(room, { sendDisconnectSignal: false });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var roomStates = /* @__PURE__ */ new Map();
|
|
function createDeprecatedCompactionUpdate(updates) {
|
|
const mergeable = updates.filter(
|
|
(u) => [SyncUpdateType.COMPACTION, SyncUpdateType.UPDATE].includes(
|
|
u.type
|
|
)
|
|
).map((u) => base64ToUint8Array(u.data));
|
|
return createSyncUpdate(
|
|
mergeUpdatesV2(mergeable),
|
|
SyncUpdateType.COMPACTION
|
|
);
|
|
}
|
|
function createSyncStep1Update(doc2) {
|
|
const encoder = createEncoder();
|
|
writeSyncStep1(encoder, doc2);
|
|
return createSyncUpdate(
|
|
toUint8Array(encoder),
|
|
SyncUpdateType.SYNC_STEP_1
|
|
);
|
|
}
|
|
function createSyncStep2Update(doc2, step1) {
|
|
const decoder = createDecoder(step1);
|
|
const encoder = createEncoder();
|
|
readSyncMessage(
|
|
decoder,
|
|
encoder,
|
|
doc2,
|
|
POLLING_MANAGER_ORIGIN
|
|
);
|
|
return createSyncUpdate(
|
|
toUint8Array(encoder),
|
|
SyncUpdateType.SYNC_STEP_2
|
|
);
|
|
}
|
|
function processAwarenessUpdate(state, awareness) {
|
|
const currentStates = awareness.getStates();
|
|
const added = /* @__PURE__ */ new Set();
|
|
const updated = /* @__PURE__ */ new Set();
|
|
const removed = new Set(
|
|
Array.from(currentStates.keys()).filter(
|
|
(clientId) => !state[clientId]
|
|
)
|
|
);
|
|
Object.entries(state).forEach(([clientIdString, awarenessState]) => {
|
|
const clientId = Number(clientIdString);
|
|
if (clientId === awareness.clientID) {
|
|
return;
|
|
}
|
|
if (null === awarenessState) {
|
|
currentStates.delete(clientId);
|
|
removed.add(clientId);
|
|
return;
|
|
}
|
|
if (!currentStates.has(clientId)) {
|
|
currentStates.set(clientId, awarenessState);
|
|
added.add(clientId);
|
|
return;
|
|
}
|
|
const currentState = currentStates.get(clientId);
|
|
if (JSON.stringify(currentState) !== JSON.stringify(awarenessState)) {
|
|
currentStates.set(clientId, awarenessState);
|
|
updated.add(clientId);
|
|
}
|
|
});
|
|
if (added.size + updated.size > 0) {
|
|
awareness.emit("change", [
|
|
{
|
|
added: Array.from(added),
|
|
updated: Array.from(updated),
|
|
// Left blank on purpose, as the removal of clients is handled in the if condition below.
|
|
removed: []
|
|
}
|
|
]);
|
|
}
|
|
if (removed.size > 0) {
|
|
removeAwarenessStates(
|
|
awareness,
|
|
Array.from(removed),
|
|
POLLING_MANAGER_ORIGIN
|
|
);
|
|
}
|
|
}
|
|
function processDocUpdate(update, doc2, onSync) {
|
|
const data = base64ToUint8Array(update.data);
|
|
switch (update.type) {
|
|
case SyncUpdateType.SYNC_STEP_1: {
|
|
return createSyncStep2Update(doc2, data);
|
|
}
|
|
case SyncUpdateType.SYNC_STEP_2: {
|
|
const decoder = createDecoder(data);
|
|
const encoder = createEncoder();
|
|
readSyncMessage(
|
|
decoder,
|
|
encoder,
|
|
doc2,
|
|
POLLING_MANAGER_ORIGIN
|
|
);
|
|
onSync();
|
|
return;
|
|
}
|
|
case SyncUpdateType.COMPACTION:
|
|
case SyncUpdateType.UPDATE: {
|
|
applyUpdateV2(doc2, data, POLLING_MANAGER_ORIGIN);
|
|
}
|
|
}
|
|
}
|
|
function checkConnectionLimit(awareness, roomState) {
|
|
if (!roomState.isPrimaryRoom || hasCheckedConnectionLimit) {
|
|
return false;
|
|
}
|
|
hasCheckedConnectionLimit = true;
|
|
const maxClientsPerRoom = (0, import_hooks2.applyFilters)(
|
|
"sync.pollingProvider.maxClientsPerRoom",
|
|
DEFAULT_CLIENT_LIMIT_PER_ROOM,
|
|
roomState.room
|
|
);
|
|
const clientCount = Object.keys(awareness).length;
|
|
const validatedLimit = intValueOrDefault(
|
|
maxClientsPerRoom,
|
|
DEFAULT_CLIENT_LIMIT_PER_ROOM
|
|
);
|
|
if (clientCount > validatedLimit) {
|
|
roomState.log("Connection limit exceeded", {
|
|
clientCount,
|
|
maxClientsPerRoom: validatedLimit,
|
|
room: roomState.room
|
|
});
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
var areListenersRegistered = false;
|
|
var consecutiveFailures = 0;
|
|
var hasCheckedConnectionLimit = false;
|
|
var isManualRetry = false;
|
|
var hasCollaborators = false;
|
|
var isActiveBrowser = "visible" === document.visibilityState;
|
|
var isPolling = false;
|
|
var isUnloadPending = false;
|
|
var pollInterval = POLLING_INTERVAL_IN_MS;
|
|
var pollingTimeoutId = null;
|
|
var roomOverflowOffset = 0;
|
|
function handleBeforeUnload() {
|
|
isUnloadPending = true;
|
|
}
|
|
function handlePageHide() {
|
|
const rooms = Array.from(roomStates.entries()).map(
|
|
([room, state]) => ({
|
|
after: 0,
|
|
awareness: null,
|
|
client_id: state.clientId,
|
|
room,
|
|
updates: []
|
|
})
|
|
);
|
|
for (let i = 0; i < rooms.length; i += MAX_ROOMS_PER_REQUEST) {
|
|
postSyncUpdateNonBlocking({
|
|
rooms: rooms.slice(i, i + MAX_ROOMS_PER_REQUEST)
|
|
});
|
|
}
|
|
}
|
|
function handleVisibilityChange() {
|
|
const wasActive = isActiveBrowser;
|
|
isActiveBrowser = document.visibilityState === "visible";
|
|
if (isActiveBrowser && !wasActive) {
|
|
if (pollingTimeoutId) {
|
|
clearTimeout(pollingTimeoutId);
|
|
pollingTimeoutId = null;
|
|
poll();
|
|
}
|
|
}
|
|
}
|
|
function selectRoomsForRequest() {
|
|
const allRooms = Array.from(roomStates.values());
|
|
if (allRooms.length <= MAX_ROOMS_PER_REQUEST) {
|
|
return allRooms;
|
|
}
|
|
const primaryRoom = allRooms.find((state) => state.isPrimaryRoom);
|
|
const overflowRooms = allRooms.filter((state) => state !== primaryRoom);
|
|
const overflowSlotsPerRequest = MAX_ROOMS_PER_REQUEST - (primaryRoom ? 1 : 0);
|
|
const { window: overflowSlice, nextOffset } = rotateWindow(
|
|
overflowRooms,
|
|
roomOverflowOffset,
|
|
overflowSlotsPerRequest
|
|
);
|
|
roomOverflowOffset = nextOffset;
|
|
if (primaryRoom) {
|
|
return [primaryRoom, ...overflowSlice];
|
|
}
|
|
return overflowSlice;
|
|
}
|
|
function poll() {
|
|
isPolling = true;
|
|
pollingTimeoutId = null;
|
|
async function start() {
|
|
if (0 === roomStates.size) {
|
|
isPolling = false;
|
|
return;
|
|
}
|
|
isUnloadPending = false;
|
|
const roomsInRequest = selectRoomsForRequest();
|
|
const payload = {
|
|
rooms: roomsInRequest.map((state) => ({
|
|
after: state.endCursor ?? 0,
|
|
awareness: state.localAwarenessState,
|
|
client_id: state.clientId,
|
|
room: state.room,
|
|
updates: state.updateQueue.get()
|
|
}))
|
|
};
|
|
roomsInRequest.forEach((state) => {
|
|
state.onStatusChange({ status: "connecting" });
|
|
});
|
|
try {
|
|
const { rooms } = await postSyncUpdate(payload);
|
|
consecutiveFailures = 0;
|
|
isManualRetry = false;
|
|
roomsInRequest.forEach((state) => {
|
|
if (roomStates.get(state.room) !== state) {
|
|
return;
|
|
}
|
|
state.onStatusChange({ status: "connected" });
|
|
});
|
|
hasCollaborators = false;
|
|
rooms.forEach((room) => {
|
|
if (!roomStates.has(room.room)) {
|
|
return;
|
|
}
|
|
const roomState = roomStates.get(room.room);
|
|
roomState.endCursor = room.end_cursor;
|
|
if (checkConnectionLimit(room.awareness, roomState)) {
|
|
roomState.onStatusChange({
|
|
status: "disconnected",
|
|
error: new ConnectionError(
|
|
ConnectionErrorCode.CONNECTION_LIMIT_EXCEEDED,
|
|
"Connection limit exceeded"
|
|
)
|
|
});
|
|
unregisterRoom(room.room);
|
|
return;
|
|
}
|
|
roomState.processAwarenessUpdate(room.awareness);
|
|
if (roomState.isPrimaryRoom && Object.keys(room.awareness).length > 1) {
|
|
hasCollaborators = true;
|
|
roomStates.forEach((state) => {
|
|
state.updateQueue.resume();
|
|
});
|
|
}
|
|
const responseUpdates = [];
|
|
for (const update of room.updates) {
|
|
try {
|
|
const response = roomState.processDocUpdate(update);
|
|
if (response) {
|
|
responseUpdates.push(response);
|
|
}
|
|
} catch (error) {
|
|
roomState.log(
|
|
"Failed to apply sync update",
|
|
{ error, update },
|
|
"error",
|
|
true
|
|
// force
|
|
);
|
|
}
|
|
}
|
|
roomState.updateQueue.addBulk(responseUpdates);
|
|
if (room.should_compact) {
|
|
roomState.log("Server requested compaction update");
|
|
roomState.updateQueue.clear();
|
|
roomState.updateQueue.add(
|
|
roomState.createCompactionUpdate()
|
|
);
|
|
} else if (room.compaction_request) {
|
|
roomState.log("Server requested (old) compaction update");
|
|
roomState.updateQueue.add(
|
|
createDeprecatedCompactionUpdate(
|
|
room.compaction_request
|
|
)
|
|
);
|
|
}
|
|
});
|
|
if (isActiveBrowser && hasCollaborators) {
|
|
pollInterval = POLLING_INTERVAL_WITH_COLLABORATORS_IN_MS;
|
|
} else if (isActiveBrowser) {
|
|
pollInterval = POLLING_INTERVAL_IN_MS;
|
|
} else {
|
|
pollInterval = POLLING_INTERVAL_BACKGROUND_TAB_IN_MS;
|
|
}
|
|
} catch (error) {
|
|
if (isForbiddenError(error)) {
|
|
handleForbiddenError(error, payload.rooms);
|
|
if (roomStates.size === 0) {
|
|
isPolling = false;
|
|
return;
|
|
}
|
|
} else {
|
|
consecutiveFailures++;
|
|
const retrySchedule = hasCollaborators ? ERROR_RETRY_DELAYS_WITH_COLLABORATORS_MS : ERROR_RETRY_DELAYS_SOLO_MS;
|
|
if (consecutiveFailures <= retrySchedule.length) {
|
|
pollInterval = retrySchedule[consecutiveFailures - 1];
|
|
} else {
|
|
pollInterval = DISCONNECT_DIALOG_RETRY_MS;
|
|
}
|
|
if (isManualRetry) {
|
|
pollInterval = MANUAL_RETRY_INTERVAL_MS;
|
|
isManualRetry = false;
|
|
}
|
|
for (const room of payload.rooms) {
|
|
if (!roomStates.has(room.room)) {
|
|
continue;
|
|
}
|
|
const state = roomStates.get(room.room);
|
|
if (room.updates.length > 0 && state.endCursor > 0) {
|
|
state.updateQueue.clear();
|
|
state.updateQueue.add(state.createCompactionUpdate());
|
|
} else if (room.updates.length > 0) {
|
|
state.updateQueue.restore(room.updates);
|
|
}
|
|
state.log(
|
|
"Error posting sync update, will retry with backoff",
|
|
{ error, nextPoll: pollInterval },
|
|
"error",
|
|
true
|
|
// force
|
|
);
|
|
}
|
|
if (!isUnloadPending) {
|
|
const backgroundRetriesFailed = consecutiveFailures > retrySchedule.length;
|
|
roomsInRequest.forEach((state) => {
|
|
if (roomStates.get(state.room) !== state) {
|
|
return;
|
|
}
|
|
state.onStatusChange({
|
|
status: "disconnected",
|
|
canManuallyRetry: true,
|
|
consecutiveFailures,
|
|
backgroundRetriesFailed,
|
|
willAutoRetryInMs: pollInterval
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
pollingTimeoutId = setTimeout(poll, pollInterval);
|
|
}
|
|
void start();
|
|
}
|
|
function registerRoom({
|
|
room,
|
|
doc: doc2,
|
|
awareness,
|
|
log,
|
|
onSync,
|
|
onStatusChange
|
|
}) {
|
|
if (roomStates.has(room)) {
|
|
return;
|
|
}
|
|
const updateQueue = createUpdateQueue([createSyncStep1Update(doc2)]);
|
|
const isPrimaryRoom = 0 === roomStates.size;
|
|
function onAwarenessUpdate() {
|
|
roomState.localAwarenessState = awareness.getLocalState() ?? {};
|
|
}
|
|
function onDocUpdate(update, origin2) {
|
|
if (POLLING_MANAGER_ORIGIN === origin2) {
|
|
return;
|
|
}
|
|
if (update.byteLength > MAX_UPDATE_SIZE_IN_BYTES) {
|
|
const state = roomStates.get(room);
|
|
if (!state) {
|
|
return;
|
|
}
|
|
state.log("Document size limit exceeded", {
|
|
maxUpdateSizeInBytes: MAX_UPDATE_SIZE_IN_BYTES,
|
|
updateSizeInBytes: update.byteLength
|
|
});
|
|
state.onStatusChange({
|
|
status: "disconnected",
|
|
error: new ConnectionError(
|
|
ConnectionErrorCode.DOCUMENT_SIZE_LIMIT_EXCEEDED,
|
|
"Document size limit exceeded"
|
|
)
|
|
});
|
|
unregisterRoom(room);
|
|
}
|
|
updateQueue.add(createSyncUpdate(update, SyncUpdateType.UPDATE));
|
|
}
|
|
function unregister() {
|
|
doc2.off("updateV2", onDocUpdate);
|
|
awareness.off("change", onAwarenessUpdate);
|
|
updateQueue.clear();
|
|
}
|
|
const roomState = {
|
|
clientId: doc2.clientID,
|
|
createCompactionUpdate: () => createSyncUpdate(
|
|
encodeStateAsUpdateV2(doc2),
|
|
SyncUpdateType.COMPACTION
|
|
),
|
|
endCursor: 0,
|
|
isPrimaryRoom,
|
|
localAwarenessState: awareness.getLocalState() ?? {},
|
|
log,
|
|
onStatusChange,
|
|
processAwarenessUpdate: (state) => processAwarenessUpdate(state, awareness),
|
|
processDocUpdate: (update) => processDocUpdate(update, doc2, onSync),
|
|
room,
|
|
unregister,
|
|
updateQueue
|
|
};
|
|
doc2.on("updateV2", onDocUpdate);
|
|
awareness.on("change", onAwarenessUpdate);
|
|
roomStates.set(room, roomState);
|
|
if (!areListenersRegistered) {
|
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
window.addEventListener("pagehide", handlePageHide);
|
|
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
areListenersRegistered = true;
|
|
}
|
|
if (!isPolling) {
|
|
poll();
|
|
}
|
|
}
|
|
function unregisterRoom(room, { sendDisconnectSignal = true } = {}) {
|
|
const state = roomStates.get(room);
|
|
if (state) {
|
|
if (sendDisconnectSignal) {
|
|
const rooms = [
|
|
{
|
|
after: 0,
|
|
awareness: null,
|
|
client_id: state.clientId,
|
|
room,
|
|
updates: []
|
|
}
|
|
];
|
|
postSyncUpdateNonBlocking({ rooms });
|
|
}
|
|
state.unregister();
|
|
roomStates.delete(room);
|
|
}
|
|
if (0 === roomStates.size && areListenersRegistered) {
|
|
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
window.removeEventListener("pagehide", handlePageHide);
|
|
document.removeEventListener(
|
|
"visibilitychange",
|
|
handleVisibilityChange
|
|
);
|
|
areListenersRegistered = false;
|
|
hasCheckedConnectionLimit = false;
|
|
consecutiveFailures = 0;
|
|
roomOverflowOffset = 0;
|
|
}
|
|
}
|
|
function retryNow() {
|
|
isManualRetry = true;
|
|
if (pollingTimeoutId) {
|
|
clearTimeout(pollingTimeoutId);
|
|
pollingTimeoutId = null;
|
|
poll();
|
|
}
|
|
}
|
|
var pollingManager = {
|
|
registerRoom,
|
|
retryNow,
|
|
unregisterRoom
|
|
};
|
|
|
|
// packages/sync/build-module/providers/http-polling/http-polling-provider.mjs
|
|
var HttpPollingProvider = class extends ObservableV2 {
|
|
constructor(options) {
|
|
super();
|
|
this.options = options;
|
|
this.log("Initializing", { room: options.room });
|
|
this.awareness = options.awareness ?? new Awareness(options.ydoc);
|
|
this.connect();
|
|
}
|
|
awareness;
|
|
status = "disconnected";
|
|
synced = false;
|
|
/**
|
|
* Connect to the endpoint and initialize sync.
|
|
*/
|
|
connect() {
|
|
this.log("Connecting");
|
|
pollingManager.registerRoom({
|
|
room: this.options.room,
|
|
doc: this.options.ydoc,
|
|
awareness: this.awareness,
|
|
log: this.log,
|
|
onStatusChange: this.emitStatus,
|
|
onSync: this.onSync
|
|
});
|
|
}
|
|
/**
|
|
* Destroy the provider and cleanup resources.
|
|
*/
|
|
destroy() {
|
|
this.disconnect();
|
|
super.destroy();
|
|
}
|
|
/**
|
|
* Disconnect the provider and allow reconnection later.
|
|
*/
|
|
disconnect() {
|
|
this.log("Disconnecting");
|
|
pollingManager.unregisterRoom(this.options.room);
|
|
this.emitStatus({ status: "disconnected" });
|
|
}
|
|
/**
|
|
* Emit connection status, passing the full object through so that
|
|
* additional fields (e.g. `willAutoRetryInMs`) are preserved for consumers.
|
|
*
|
|
* @param connectionStatus The connection status object
|
|
*/
|
|
emitStatus = (connectionStatus) => {
|
|
const { status } = connectionStatus;
|
|
const error = status === "disconnected" ? connectionStatus.error : void 0;
|
|
if (this.status === status && !error) {
|
|
return;
|
|
}
|
|
if (status === "connecting" && this.status !== "disconnected") {
|
|
return;
|
|
}
|
|
this.log("Status change", { status, error });
|
|
this.status = status;
|
|
this.emit("status", [connectionStatus]);
|
|
};
|
|
/**
|
|
* Log debug messages if debugging is enabled.
|
|
*
|
|
* @param message The debug message
|
|
* @param debug Additional debug information
|
|
* @param errorLevel The console method to use for logging
|
|
* @param force Whether to force logging regardless of debug setting
|
|
*/
|
|
log = (message, debug = {}, errorLevel = "log", force = false) => {
|
|
if (!this.options.debug && !force) {
|
|
return;
|
|
}
|
|
const logFn = console[errorLevel] || console.log;
|
|
logFn(`[${this.constructor.name}]: ${message}`, {
|
|
room: this.options.room,
|
|
...debug
|
|
});
|
|
};
|
|
/**
|
|
* Handle synchronization events from the polling manager.
|
|
*/
|
|
onSync = () => {
|
|
if (!this.synced) {
|
|
this.synced = true;
|
|
this.log("Synced");
|
|
}
|
|
};
|
|
};
|
|
function createHttpPollingProvider() {
|
|
return async ({
|
|
awareness,
|
|
objectType,
|
|
objectId,
|
|
ydoc
|
|
}) => {
|
|
const room = objectId ? `${objectType}:${objectId}` : objectType;
|
|
const provider = new HttpPollingProvider({
|
|
awareness,
|
|
// debug: true,
|
|
room,
|
|
ydoc
|
|
});
|
|
return {
|
|
destroy: () => provider.destroy(),
|
|
// Adapter: ObservableV2.on is compatible with ProviderOn
|
|
// The callback receives data as the first parameter
|
|
on: (event, callback) => {
|
|
provider.on(event, callback);
|
|
}
|
|
};
|
|
};
|
|
}
|
|
|
|
// packages/sync/build-module/providers/index.mjs
|
|
var providerCreators = null;
|
|
function getDefaultProviderCreators() {
|
|
return [createHttpPollingProvider()];
|
|
}
|
|
function isProviderCreator(creator) {
|
|
return "function" === typeof creator;
|
|
}
|
|
function getProviderCreators() {
|
|
if (providerCreators) {
|
|
return providerCreators;
|
|
}
|
|
if (!window._wpCollaborationEnabled) {
|
|
return [];
|
|
}
|
|
const filteredProviderCreators = (0, import_hooks3.applyFilters)(
|
|
"sync.providers",
|
|
getDefaultProviderCreators()
|
|
);
|
|
if (!Array.isArray(filteredProviderCreators)) {
|
|
providerCreators = [];
|
|
return providerCreators;
|
|
}
|
|
providerCreators = filteredProviderCreators.filter(isProviderCreator);
|
|
return providerCreators;
|
|
}
|
|
|
|
// packages/sync/build-module/y-utilities/y-multidoc-undomanager.mjs
|
|
var popStackItem2 = (mum, type) => {
|
|
const stack = type === "undo" ? mum.undoStack : mum.redoStack;
|
|
while (stack.length > 0) {
|
|
const um = (
|
|
/** @type {Y.UndoManager} */
|
|
stack.pop()
|
|
);
|
|
const prevUmStack = type === "undo" ? um.undoStack : um.redoStack;
|
|
const stackItem = (
|
|
/** @type {any} */
|
|
prevUmStack.pop()
|
|
);
|
|
let actionPerformed = false;
|
|
if (type === "undo") {
|
|
um.undoStack = [stackItem];
|
|
actionPerformed = um.undo() !== null;
|
|
um.undoStack = prevUmStack;
|
|
} else {
|
|
um.redoStack = [stackItem];
|
|
actionPerformed = um.redo() !== null;
|
|
um.redoStack = prevUmStack;
|
|
}
|
|
if (actionPerformed) {
|
|
return stackItem;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
var YMultiDocUndoManager = class extends Observable {
|
|
/**
|
|
* @param {Y.AbstractType<any>|Array<Y.AbstractType<any>>} typeScope Accepts either a single type, or an array of types
|
|
* @param {ConstructorParameters<typeof Y.UndoManager>[1]} opts
|
|
*/
|
|
constructor(typeScope = [], opts = {}) {
|
|
super();
|
|
this.docs = /* @__PURE__ */ new Map();
|
|
this.trackedOrigins = opts.trackedOrigins || /* @__PURE__ */ new Set([null]);
|
|
opts.trackedOrigins = this.trackedOrigins;
|
|
this._defaultOpts = opts;
|
|
this.undoStack = [];
|
|
this.redoStack = [];
|
|
this.addToScope(typeScope);
|
|
}
|
|
/**
|
|
* @param {Array<Y.AbstractType<any>> | Y.AbstractType<any>} ytypes
|
|
*/
|
|
addToScope(ytypes) {
|
|
ytypes = isArray(ytypes) ? ytypes : [ytypes];
|
|
ytypes.forEach((ytype) => {
|
|
const ydoc = (
|
|
/** @type {Y.Doc} */
|
|
ytype.doc
|
|
);
|
|
const um = setIfUndefined(this.docs, ydoc, () => {
|
|
const um2 = new UndoManager([ytype], this._defaultOpts);
|
|
um2.on(
|
|
"stack-cleared",
|
|
/** @param {any} opts */
|
|
({
|
|
undoStackCleared,
|
|
redoStackCleared
|
|
}) => {
|
|
this.clear(undoStackCleared, redoStackCleared);
|
|
}
|
|
);
|
|
ydoc.on("destroy", () => {
|
|
this.docs.delete(ydoc);
|
|
this.undoStack = this.undoStack.filter(
|
|
(um3) => um3.doc !== ydoc
|
|
);
|
|
this.redoStack = this.redoStack.filter(
|
|
(um3) => um3.doc !== ydoc
|
|
);
|
|
});
|
|
um2.on(
|
|
"stack-item-added",
|
|
/** @param {any} change */
|
|
(change) => {
|
|
const stack = change.type === "undo" ? this.undoStack : this.redoStack;
|
|
stack.push(um2);
|
|
this.emit("stack-item-added", [
|
|
{ ...change, ydoc },
|
|
this
|
|
]);
|
|
}
|
|
);
|
|
um2.on(
|
|
"stack-item-updated",
|
|
/** @param {any} change */
|
|
(change) => {
|
|
this.emit("stack-item-updated", [
|
|
{ ...change, ydoc },
|
|
this
|
|
]);
|
|
}
|
|
);
|
|
um2.on(
|
|
"stack-item-popped",
|
|
/** @param {any} change */
|
|
(change) => {
|
|
this.emit("stack-item-popped", [
|
|
{ ...change, ydoc },
|
|
this
|
|
]);
|
|
}
|
|
);
|
|
return um2;
|
|
});
|
|
if (um.scope.every((yt) => yt !== ytype)) {
|
|
um.scope.push(ytype);
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* @param {any} origin
|
|
*/
|
|
/* c8 ignore next 3 */
|
|
addTrackedOrigin(origin2) {
|
|
this.trackedOrigins.add(origin2);
|
|
}
|
|
/**
|
|
* @param {any} origin
|
|
*/
|
|
/* c8 ignore next 3 */
|
|
removeTrackedOrigin(origin2) {
|
|
this.trackedOrigins.delete(origin2);
|
|
}
|
|
/**
|
|
* Undo last changes on type.
|
|
*
|
|
* @return {any?} Returns StackItem if a change was applied
|
|
*/
|
|
undo() {
|
|
return popStackItem2(this, "undo");
|
|
}
|
|
/**
|
|
* Redo last undo operation.
|
|
*
|
|
* @return {any?} Returns StackItem if a change was applied
|
|
*/
|
|
redo() {
|
|
return popStackItem2(this, "redo");
|
|
}
|
|
clear(clearUndoStack = true, clearRedoStack = true) {
|
|
if (clearUndoStack && this.canUndo() || clearRedoStack && this.canRedo()) {
|
|
this.docs.forEach((um) => {
|
|
clearUndoStack && (this.undoStack = []);
|
|
clearRedoStack && (this.redoStack = []);
|
|
um.clear(clearUndoStack, clearRedoStack);
|
|
});
|
|
this.emit("stack-cleared", [
|
|
{
|
|
undoStackCleared: clearUndoStack,
|
|
redoStackCleared: clearRedoStack
|
|
}
|
|
]);
|
|
}
|
|
}
|
|
/* c8 ignore next 5 */
|
|
stopCapturing() {
|
|
this.docs.forEach((um) => {
|
|
um.stopCapturing();
|
|
});
|
|
}
|
|
/**
|
|
* Are undo steps available?
|
|
*
|
|
* @return {boolean} `true` if undo is possible
|
|
*/
|
|
canUndo() {
|
|
return this.undoStack.length > 0;
|
|
}
|
|
/**
|
|
* Are redo steps available?
|
|
*
|
|
* @return {boolean} `true` if redo is possible
|
|
*/
|
|
canRedo() {
|
|
return this.redoStack.length > 0;
|
|
}
|
|
destroy() {
|
|
this.docs.forEach((um) => um.destroy());
|
|
super.destroy();
|
|
}
|
|
};
|
|
|
|
// packages/sync/build-module/undo-manager.mjs
|
|
function createUndoManager() {
|
|
const yUndoManager = new YMultiDocUndoManager([], {
|
|
// Throttle undo/redo captures after 500ms of inactivity.
|
|
// 500 was selected from subjective local UX testing, shorter timeouts
|
|
// may cause mid-word undo stack items.
|
|
captureTimeout: 500,
|
|
// Ensure that we only scope the undo/redo to the current editor.
|
|
// The yjs document's clientID is added once it's available.
|
|
trackedOrigins: /* @__PURE__ */ new Set([LOCAL_EDITOR_ORIGIN])
|
|
});
|
|
return {
|
|
/**
|
|
* Record changes into the history.
|
|
* Since Yjs automatically tracks changes, this method translates the WordPress
|
|
* HistoryRecord format into Yjs operations.
|
|
*
|
|
* @param _record A record of changes to record.
|
|
* @param _isStaged Whether to immediately create an undo point or not.
|
|
*/
|
|
addRecord(_record, _isStaged = false) {
|
|
},
|
|
/**
|
|
* Add a Yjs map to the scope of the undo manager.
|
|
*
|
|
* @param {Y.Map< any >} ymap The Yjs map to add to the scope.
|
|
* @param handlers
|
|
* @param handlers.addUndoMeta
|
|
* @param handlers.restoreUndoMeta
|
|
*/
|
|
addToScope(ymap, handlers) {
|
|
if (ymap.doc === null) {
|
|
return;
|
|
}
|
|
const ydoc = ymap.doc;
|
|
yUndoManager.addToScope(ymap);
|
|
const { addUndoMeta, restoreUndoMeta } = handlers;
|
|
yUndoManager.on("stack-item-added", (event) => {
|
|
addUndoMeta(ydoc, event.stackItem.meta);
|
|
});
|
|
yUndoManager.on("stack-item-popped", (event) => {
|
|
restoreUndoMeta(ydoc, event.stackItem.meta);
|
|
});
|
|
},
|
|
/**
|
|
* Undo the last recorded changes.
|
|
*
|
|
*/
|
|
undo() {
|
|
if (!yUndoManager.canUndo()) {
|
|
return;
|
|
}
|
|
yUndoManager.undo();
|
|
return [];
|
|
},
|
|
/**
|
|
* Redo the last undone changes.
|
|
*/
|
|
redo() {
|
|
if (!yUndoManager.canRedo()) {
|
|
return;
|
|
}
|
|
yUndoManager.redo();
|
|
return [];
|
|
},
|
|
/**
|
|
* Check if there are changes that can be undone.
|
|
*
|
|
* @return {boolean} Whether there are changes to undo.
|
|
*/
|
|
hasUndo() {
|
|
return yUndoManager.canUndo();
|
|
},
|
|
/**
|
|
* Check if there are changes that can be redone.
|
|
*
|
|
* @return {boolean} Whether there are changes to redo.
|
|
*/
|
|
hasRedo() {
|
|
return yUndoManager.canRedo();
|
|
},
|
|
/**
|
|
* Stop capturing changes into the current undo item.
|
|
* The next change will create a new undo item.
|
|
*/
|
|
stopCapturing() {
|
|
yUndoManager.stopCapturing();
|
|
}
|
|
};
|
|
}
|
|
|
|
// packages/sync/build-module/utils.mjs
|
|
function createYjsDoc(documentMeta = {}) {
|
|
const metaMap = new Map(
|
|
Object.entries(documentMeta)
|
|
);
|
|
return new Doc({ meta: metaMap });
|
|
}
|
|
function initializeYjsDoc(ydoc) {
|
|
const stateMap = ydoc.getMap(CRDT_STATE_MAP_KEY);
|
|
stateMap.set(CRDT_STATE_MAP_VERSION_KEY, CRDT_DOC_VERSION);
|
|
}
|
|
function markEntityAsSaved(ydoc) {
|
|
const recordMeta = ydoc.getMap(CRDT_STATE_MAP_KEY);
|
|
recordMeta.set(CRDT_STATE_MAP_SAVED_AT_KEY, Date.now());
|
|
recordMeta.set(CRDT_STATE_MAP_SAVED_BY_KEY, ydoc.clientID);
|
|
}
|
|
function pseudoRandomID() {
|
|
return Math.floor(Math.random() * 1e9);
|
|
}
|
|
function serializeCrdtDoc(crdtDoc) {
|
|
return JSON.stringify({
|
|
document: toBase64(encodeStateAsUpdateV2(crdtDoc)),
|
|
updateId: pseudoRandomID()
|
|
// helps with debugging
|
|
});
|
|
}
|
|
function deserializeCrdtDoc(serializedCrdtDoc) {
|
|
try {
|
|
const { document: document2 } = JSON.parse(serializedCrdtDoc);
|
|
const docMeta = {
|
|
[CRDT_DOC_META_PERSISTENCE_KEY]: true
|
|
};
|
|
const ydoc = createYjsDoc(docMeta);
|
|
const yupdate = fromBase64(document2);
|
|
applyUpdateV2(ydoc, yupdate);
|
|
ydoc.clientID = pseudoRandomID();
|
|
return ydoc;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// packages/sync/build-module/manager.mjs
|
|
function getEntityId(objectType, objectId) {
|
|
return `${objectType}_${objectId}`;
|
|
}
|
|
function createSyncManager(debug = false) {
|
|
const debugWrap = debug ? logPerformanceTiming : passThru;
|
|
const collectionStates = /* @__PURE__ */ new Map();
|
|
const entityStates = /* @__PURE__ */ new Map();
|
|
let undoManager2;
|
|
function log(component, message, entityId, context = {}) {
|
|
if (!debug) {
|
|
return;
|
|
}
|
|
console.log(`[SyncManager][${component}]: ${message}`, {
|
|
...context,
|
|
entityId
|
|
});
|
|
}
|
|
async function loadEntity(syncConfig, objectType, objectId, record, handlers) {
|
|
const providerCreators2 = getProviderCreators();
|
|
const entityId = getEntityId(objectType, objectId);
|
|
if (0 === providerCreators2.length) {
|
|
log("loadEntity", "no providers, skipping", entityId);
|
|
return;
|
|
}
|
|
if (entityStates.has(entityId)) {
|
|
log("loadEntity", "already loaded", entityId);
|
|
return;
|
|
}
|
|
if (false === syncConfig.shouldSync?.(objectType, objectId)) {
|
|
log("loadEntity", "shouldSync false, skipping", entityId);
|
|
return;
|
|
}
|
|
log("loadEntity", "loading", entityId);
|
|
handlers = {
|
|
addUndoMeta: debugWrap(handlers.addUndoMeta),
|
|
editRecord: debugWrap(handlers.editRecord),
|
|
getEditedRecord: debugWrap(handlers.getEditedRecord),
|
|
onStatusChange: debugWrap(handlers.onStatusChange),
|
|
persistCRDTDoc: debugWrap(handlers.persistCRDTDoc),
|
|
refetchRecord: debugWrap(handlers.refetchRecord),
|
|
restoreUndoMeta: debugWrap(handlers.restoreUndoMeta)
|
|
};
|
|
const ydoc = createYjsDoc({ objectType });
|
|
const recordMap = ydoc.getMap(CRDT_RECORD_MAP_KEY);
|
|
const stateMap = ydoc.getMap(CRDT_STATE_MAP_KEY);
|
|
const now = Date.now();
|
|
const unload = () => {
|
|
log("loadEntity", "unloading", entityId);
|
|
providerResults.forEach((result) => result.destroy());
|
|
handlers.onStatusChange(null);
|
|
recordMap.unobserveDeep(onRecordUpdate);
|
|
stateMap.unobserve(onStateMapUpdate);
|
|
ydoc.destroy();
|
|
entityStates.delete(entityId);
|
|
};
|
|
const awareness = syncConfig.createAwareness?.(ydoc, objectId);
|
|
const onRecordUpdate = (_events, transaction) => {
|
|
if (transaction.local && !(transaction.origin instanceof UndoManager)) {
|
|
return;
|
|
}
|
|
void internal.updateEntityRecord(objectType, objectId);
|
|
};
|
|
const onStateMapUpdate = (event, transaction) => {
|
|
if (transaction.local) {
|
|
return;
|
|
}
|
|
event.keysChanged.forEach((key) => {
|
|
switch (key) {
|
|
case CRDT_STATE_MAP_SAVED_AT_KEY:
|
|
const newValue = stateMap.get(CRDT_STATE_MAP_SAVED_AT_KEY);
|
|
if ("number" === typeof newValue && newValue > now) {
|
|
log("loadEntity", "refetching record", entityId);
|
|
void handlers.refetchRecord().catch(() => {
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
};
|
|
if (!undoManager2) {
|
|
undoManager2 = createUndoManager();
|
|
}
|
|
const { addUndoMeta, restoreUndoMeta } = handlers;
|
|
undoManager2.addToScope(recordMap, {
|
|
addUndoMeta,
|
|
restoreUndoMeta
|
|
});
|
|
const entityState = {
|
|
awareness,
|
|
handlers,
|
|
objectId,
|
|
objectType,
|
|
syncConfig,
|
|
unload,
|
|
ydoc
|
|
};
|
|
entityStates.set(entityId, entityState);
|
|
log("loadEntity", "connecting", entityId);
|
|
const providerResults = await Promise.all(
|
|
providerCreators2.map(async (create9) => {
|
|
const provider = await create9({
|
|
objectType,
|
|
objectId,
|
|
ydoc,
|
|
awareness
|
|
});
|
|
provider.on("status", handlers.onStatusChange);
|
|
return provider;
|
|
})
|
|
);
|
|
recordMap.observeDeep(onRecordUpdate);
|
|
stateMap.observe(onStateMapUpdate);
|
|
initializeYjsDoc(ydoc);
|
|
internal.applyPersistedCrdtDoc(objectType, objectId, record);
|
|
}
|
|
async function loadCollection(syncConfig, objectType, handlers) {
|
|
const providerCreators2 = getProviderCreators();
|
|
const entityId = getEntityId(objectType, null);
|
|
if (0 === providerCreators2.length) {
|
|
log("loadCollection", "no providers, skipping", entityId);
|
|
return;
|
|
}
|
|
if (collectionStates.has(objectType)) {
|
|
log("loadCollection", "already loaded", entityId);
|
|
return;
|
|
}
|
|
if (false === syncConfig.shouldSync?.(objectType, null)) {
|
|
log("loadCollection", "shouldSync false, skipping", entityId);
|
|
return;
|
|
}
|
|
log("loadCollection", "loading", entityId);
|
|
const ydoc = createYjsDoc({ collection: true, objectType });
|
|
const stateMap = ydoc.getMap(CRDT_STATE_MAP_KEY);
|
|
const now = Date.now();
|
|
const unload = () => {
|
|
log("loadCollection", "unloading", entityId);
|
|
providerResults.forEach((result) => result.destroy());
|
|
handlers.onStatusChange(null);
|
|
stateMap.unobserve(onStateMapUpdate);
|
|
ydoc.destroy();
|
|
collectionStates.delete(objectType);
|
|
};
|
|
const onStateMapUpdate = (event, transaction) => {
|
|
if (transaction.local) {
|
|
return;
|
|
}
|
|
event.keysChanged.forEach((key) => {
|
|
switch (key) {
|
|
case CRDT_STATE_MAP_SAVED_AT_KEY:
|
|
const newValue = stateMap.get(CRDT_STATE_MAP_SAVED_AT_KEY);
|
|
if ("number" === typeof newValue && newValue > now) {
|
|
void handlers.refetchRecords().catch(() => {
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
};
|
|
const awareness = syncConfig.createAwareness?.(ydoc);
|
|
const collectionState = {
|
|
awareness,
|
|
handlers,
|
|
syncConfig,
|
|
unload,
|
|
ydoc
|
|
};
|
|
collectionStates.set(objectType, collectionState);
|
|
log("loadCollection", "connecting", entityId);
|
|
const providerResults = await Promise.all(
|
|
providerCreators2.map(async (create9) => {
|
|
const provider = await create9({
|
|
awareness,
|
|
objectType,
|
|
objectId: null,
|
|
ydoc
|
|
});
|
|
provider.on("status", handlers.onStatusChange);
|
|
return provider;
|
|
})
|
|
);
|
|
stateMap.observe(onStateMapUpdate);
|
|
initializeYjsDoc(ydoc);
|
|
}
|
|
function unloadEntity(objectType, objectId) {
|
|
const entityId = getEntityId(objectType, objectId);
|
|
log("unloadEntity", "unloading", entityId);
|
|
entityStates.get(entityId)?.unload();
|
|
updateCRDTDoc(objectType, null, {}, origin, { isSave: true });
|
|
}
|
|
function getAwareness(objectType, objectId) {
|
|
const entityId = getEntityId(objectType, objectId);
|
|
const entityState = entityStates.get(entityId);
|
|
if (!entityState || !entityState.awareness) {
|
|
return void 0;
|
|
}
|
|
return entityState.awareness;
|
|
}
|
|
function _applyPersistedCrdtDoc(objectType, objectId, record) {
|
|
const entityId = getEntityId(objectType, objectId);
|
|
const entityState = entityStates.get(entityId);
|
|
if (!entityState) {
|
|
log("applyPersistedCrdtDoc", "no entity state", entityId);
|
|
return;
|
|
}
|
|
const {
|
|
handlers,
|
|
syncConfig: {
|
|
applyChangesToCRDTDoc,
|
|
getChangesFromCRDTDoc,
|
|
getPersistedCRDTDoc
|
|
},
|
|
ydoc: targetDoc
|
|
} = entityState;
|
|
const serialized = getPersistedCRDTDoc?.(record);
|
|
const tempDoc = serialized ? deserializeCrdtDoc(serialized) : null;
|
|
if (!tempDoc) {
|
|
log("applyPersistedCrdtDoc", "no persisted doc", entityId);
|
|
targetDoc.transact(() => {
|
|
applyChangesToCRDTDoc(targetDoc, record);
|
|
handlers.persistCRDTDoc();
|
|
}, LOCAL_SYNC_MANAGER_ORIGIN);
|
|
return;
|
|
}
|
|
const update = encodeStateAsUpdateV2(tempDoc);
|
|
applyUpdateV2(targetDoc, update);
|
|
const invalidations = getChangesFromCRDTDoc(tempDoc, record);
|
|
const invalidatedKeys = Object.keys(invalidations);
|
|
tempDoc.destroy();
|
|
if (0 === invalidatedKeys.length) {
|
|
log("applyPersistedCrdtDoc", "valid persisted doc", entityId);
|
|
return;
|
|
}
|
|
log("applyPersistedCrdtDoc", "invalidated keys", entityId, {
|
|
invalidatedKeys
|
|
});
|
|
const changes = invalidatedKeys.reduce(
|
|
(acc, key) => Object.assign(acc, {
|
|
[key]: record[key]
|
|
}),
|
|
{}
|
|
);
|
|
targetDoc.transact(() => {
|
|
applyChangesToCRDTDoc(targetDoc, changes);
|
|
handlers.persistCRDTDoc();
|
|
}, LOCAL_SYNC_MANAGER_ORIGIN);
|
|
}
|
|
function updateCRDTDoc(objectType, objectId, changes, origin2, options = {}) {
|
|
const { isSave = false, isNewUndoLevel = false } = options;
|
|
const entityId = getEntityId(objectType, objectId);
|
|
const entityState = entityStates.get(entityId);
|
|
const collectionState = collectionStates.get(objectType);
|
|
if (entityState) {
|
|
const { syncConfig, ydoc } = entityState;
|
|
if (isNewUndoLevel && undoManager2) {
|
|
undoManager2.stopCapturing?.();
|
|
}
|
|
ydoc.transact(() => {
|
|
log("updateCRDTDoc", "applying changes", entityId, {
|
|
changedKeys: Object.keys(changes)
|
|
});
|
|
syncConfig.applyChangesToCRDTDoc(ydoc, changes);
|
|
if (isSave) {
|
|
markEntityAsSaved(ydoc);
|
|
}
|
|
}, origin2);
|
|
}
|
|
if (collectionState && isSave) {
|
|
collectionState.ydoc.transact(() => {
|
|
markEntityAsSaved(collectionState.ydoc);
|
|
}, origin2);
|
|
}
|
|
}
|
|
async function _updateEntityRecord(objectType, objectId) {
|
|
const entityId = getEntityId(objectType, objectId);
|
|
const entityState = entityStates.get(entityId);
|
|
if (!entityState) {
|
|
log("updateEntityRecord", "no entity state", entityId);
|
|
return;
|
|
}
|
|
const { handlers, syncConfig, ydoc } = entityState;
|
|
const changes = syncConfig.getChangesFromCRDTDoc(
|
|
ydoc,
|
|
await handlers.getEditedRecord()
|
|
);
|
|
const changedKeys = Object.keys(changes);
|
|
if (0 === changedKeys.length) {
|
|
return;
|
|
}
|
|
log("updateEntityRecord", "changes", entityId, {
|
|
changedKeys
|
|
});
|
|
handlers.editRecord(changes);
|
|
}
|
|
async function createPersistedCRDTDoc(objectType, objectId) {
|
|
const entityId = getEntityId(objectType, objectId);
|
|
const entityState = entityStates.get(entityId);
|
|
if (!entityState?.ydoc) {
|
|
return null;
|
|
}
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
return serializeCrdtDoc(entityState.ydoc);
|
|
}
|
|
const internal = {
|
|
applyPersistedCrdtDoc: debugWrap(_applyPersistedCrdtDoc),
|
|
updateEntityRecord: debugWrap(_updateEntityRecord)
|
|
};
|
|
return {
|
|
createPersistedCRDTDoc: debugWrap(createPersistedCRDTDoc),
|
|
getAwareness,
|
|
load: debugWrap(loadEntity),
|
|
loadCollection: debugWrap(loadCollection),
|
|
// Use getter to ensure we always return the current value of `undoManager`.
|
|
get undoManager() {
|
|
return undoManager2;
|
|
},
|
|
unload: debugWrap(unloadEntity),
|
|
update: debugWrap(yieldToEventLoop(updateCRDTDoc))
|
|
};
|
|
}
|
|
|
|
// packages/sync/node_modules/diff/libesm/diff/base.js
|
|
var Diff = class {
|
|
diff(oldStr, newStr, options = {}) {
|
|
let callback;
|
|
if (typeof options === "function") {
|
|
callback = options;
|
|
options = {};
|
|
} else if ("callback" in options) {
|
|
callback = options.callback;
|
|
}
|
|
const oldString = this.castInput(oldStr, options);
|
|
const newString = this.castInput(newStr, options);
|
|
const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
|
|
const newTokens = this.removeEmpty(this.tokenize(newString, options));
|
|
return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
|
|
}
|
|
diffWithOptionsObj(oldTokens, newTokens, options, callback) {
|
|
var _a;
|
|
const done = (value) => {
|
|
value = this.postProcess(value, options);
|
|
if (callback) {
|
|
setTimeout(function() {
|
|
callback(value);
|
|
}, 0);
|
|
return void 0;
|
|
} else {
|
|
return value;
|
|
}
|
|
};
|
|
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
let editLength = 1;
|
|
let maxEditLength = newLen + oldLen;
|
|
if (options.maxEditLength != null) {
|
|
maxEditLength = Math.min(maxEditLength, options.maxEditLength);
|
|
}
|
|
const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
|
|
const abortAfterTimestamp = Date.now() + maxExecutionTime;
|
|
const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
|
|
let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
|
|
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
|
|
}
|
|
let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
|
|
const execEditLength = () => {
|
|
for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
let basePath;
|
|
const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
|
|
if (removePath) {
|
|
bestPath[diagonalPath - 1] = void 0;
|
|
}
|
|
let canAdd = false;
|
|
if (addPath) {
|
|
const addPathNewPos = addPath.oldPos - diagonalPath;
|
|
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
}
|
|
const canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
if (!canAdd && !canRemove) {
|
|
bestPath[diagonalPath] = void 0;
|
|
continue;
|
|
}
|
|
if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
|
|
basePath = this.addToPath(addPath, true, false, 0, options);
|
|
} else {
|
|
basePath = this.addToPath(removePath, false, true, 1, options);
|
|
}
|
|
newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
|
|
if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
|
|
} else {
|
|
bestPath[diagonalPath] = basePath;
|
|
if (basePath.oldPos + 1 >= oldLen) {
|
|
maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
|
|
}
|
|
if (newPos + 1 >= newLen) {
|
|
minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
|
|
}
|
|
}
|
|
}
|
|
editLength++;
|
|
};
|
|
if (callback) {
|
|
(function exec() {
|
|
setTimeout(function() {
|
|
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
|
|
return callback(void 0);
|
|
}
|
|
if (!execEditLength()) {
|
|
exec();
|
|
}
|
|
}, 0);
|
|
})();
|
|
} else {
|
|
while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
const ret = execEditLength();
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
addToPath(path, added, removed, oldPosInc, options) {
|
|
const last2 = path.lastComponent;
|
|
if (last2 && !options.oneChangePerToken && last2.added === added && last2.removed === removed) {
|
|
return {
|
|
oldPos: path.oldPos + oldPosInc,
|
|
lastComponent: { count: last2.count + 1, added, removed, previousComponent: last2.previousComponent }
|
|
};
|
|
} else {
|
|
return {
|
|
oldPos: path.oldPos + oldPosInc,
|
|
lastComponent: { count: 1, added, removed, previousComponent: last2 }
|
|
};
|
|
}
|
|
}
|
|
extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
|
|
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
|
|
newPos++;
|
|
oldPos++;
|
|
commonCount++;
|
|
if (options.oneChangePerToken) {
|
|
basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
}
|
|
}
|
|
if (commonCount && !options.oneChangePerToken) {
|
|
basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
}
|
|
basePath.oldPos = oldPos;
|
|
return newPos;
|
|
}
|
|
equals(left, right, options) {
|
|
if (options.comparator) {
|
|
return options.comparator(left, right);
|
|
} else {
|
|
return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
}
|
|
}
|
|
removeEmpty(array) {
|
|
const ret = [];
|
|
for (let i = 0; i < array.length; i++) {
|
|
if (array[i]) {
|
|
ret.push(array[i]);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
castInput(value, options) {
|
|
return value;
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
tokenize(value, options) {
|
|
return Array.from(value);
|
|
}
|
|
join(chars) {
|
|
return chars.join("");
|
|
}
|
|
postProcess(changeObjects, options) {
|
|
return changeObjects;
|
|
}
|
|
get useLongestToken() {
|
|
return false;
|
|
}
|
|
buildValues(lastComponent, newTokens, oldTokens) {
|
|
const components = [];
|
|
let nextComponent;
|
|
while (lastComponent) {
|
|
components.push(lastComponent);
|
|
nextComponent = lastComponent.previousComponent;
|
|
delete lastComponent.previousComponent;
|
|
lastComponent = nextComponent;
|
|
}
|
|
components.reverse();
|
|
const componentLen = components.length;
|
|
let componentPos = 0, newPos = 0, oldPos = 0;
|
|
for (; componentPos < componentLen; componentPos++) {
|
|
const component = components[componentPos];
|
|
if (!component.removed) {
|
|
if (!component.added && this.useLongestToken) {
|
|
let value = newTokens.slice(newPos, newPos + component.count);
|
|
value = value.map(function(value2, i) {
|
|
const oldValue = oldTokens[oldPos + i];
|
|
return oldValue.length > value2.length ? oldValue : value2;
|
|
});
|
|
component.value = this.join(value);
|
|
} else {
|
|
component.value = this.join(newTokens.slice(newPos, newPos + component.count));
|
|
}
|
|
newPos += component.count;
|
|
if (!component.added) {
|
|
oldPos += component.count;
|
|
}
|
|
} else {
|
|
component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
|
|
oldPos += component.count;
|
|
}
|
|
}
|
|
return components;
|
|
}
|
|
};
|
|
|
|
// packages/sync/node_modules/diff/libesm/diff/character.js
|
|
var CharacterDiff = class extends Diff {
|
|
};
|
|
var characterDiff = new CharacterDiff();
|
|
function diffChars(oldStr, newStr, options) {
|
|
return characterDiff.diff(oldStr, newStr, options);
|
|
}
|
|
|
|
// packages/sync/node_modules/diff/libesm/diff/line.js
|
|
var LineDiff = class extends Diff {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.tokenize = tokenize;
|
|
}
|
|
equals(left, right, options) {
|
|
if (options.ignoreWhitespace) {
|
|
if (!options.newlineIsToken || !left.includes("\n")) {
|
|
left = left.trim();
|
|
}
|
|
if (!options.newlineIsToken || !right.includes("\n")) {
|
|
right = right.trim();
|
|
}
|
|
} else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
|
|
if (left.endsWith("\n")) {
|
|
left = left.slice(0, -1);
|
|
}
|
|
if (right.endsWith("\n")) {
|
|
right = right.slice(0, -1);
|
|
}
|
|
}
|
|
return super.equals(left, right, options);
|
|
}
|
|
};
|
|
var lineDiff = new LineDiff();
|
|
function diffLines(oldStr, newStr, options) {
|
|
return lineDiff.diff(oldStr, newStr, options);
|
|
}
|
|
function tokenize(value, options) {
|
|
if (options.stripTrailingCr) {
|
|
value = value.replace(/\r\n/g, "\n");
|
|
}
|
|
const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
|
|
if (!linesAndNewlines[linesAndNewlines.length - 1]) {
|
|
linesAndNewlines.pop();
|
|
}
|
|
for (let i = 0; i < linesAndNewlines.length; i++) {
|
|
const line = linesAndNewlines[i];
|
|
if (i % 2 && !options.newlineIsToken) {
|
|
retLines[retLines.length - 1] += line;
|
|
} else {
|
|
retLines.push(line);
|
|
}
|
|
}
|
|
return retLines;
|
|
}
|
|
|
|
// packages/sync/build-module/quill-delta/Delta.mjs
|
|
var import_es63 = __toESM(require_es6(), 1);
|
|
|
|
// packages/sync/build-module/quill-delta/AttributeMap.mjs
|
|
var import_es62 = __toESM(require_es6(), 1);
|
|
function cloneDeep(value) {
|
|
return JSON.parse(JSON.stringify(value));
|
|
}
|
|
var AttributeMap;
|
|
((AttributeMap2) => {
|
|
function compose3(a = {}, b = {}, keepNull = false) {
|
|
if (typeof a !== "object") {
|
|
a = {};
|
|
}
|
|
if (typeof b !== "object") {
|
|
b = {};
|
|
}
|
|
let attributes = cloneDeep(b);
|
|
if (!keepNull) {
|
|
attributes = Object.keys(attributes).reduce(
|
|
(copy2, key) => {
|
|
if (attributes[key] !== null || attributes[key] !== void 0) {
|
|
copy2[key] = attributes[key];
|
|
}
|
|
return copy2;
|
|
},
|
|
{}
|
|
);
|
|
}
|
|
for (const key in a) {
|
|
if (a[key] !== void 0 && b[key] === void 0) {
|
|
attributes[key] = a[key];
|
|
}
|
|
}
|
|
return Object.keys(attributes).length > 0 ? attributes : void 0;
|
|
}
|
|
AttributeMap2.compose = compose3;
|
|
function diff(a = {}, b = {}) {
|
|
if (typeof a !== "object") {
|
|
a = {};
|
|
}
|
|
if (typeof b !== "object") {
|
|
b = {};
|
|
}
|
|
const attributes = Object.keys(a).concat(Object.keys(b)).reduce((attrs, key) => {
|
|
if (!(0, import_es62.default)(a[key], b[key])) {
|
|
attrs[key] = b[key] === void 0 ? null : b[key];
|
|
}
|
|
return attrs;
|
|
}, {});
|
|
return Object.keys(attributes).length > 0 ? attributes : void 0;
|
|
}
|
|
AttributeMap2.diff = diff;
|
|
function invert(attr = {}, base = {}) {
|
|
attr = attr || {};
|
|
const baseInverted = Object.keys(base).reduce(
|
|
(memo, key) => {
|
|
if (base[key] !== attr[key] && attr[key] !== void 0) {
|
|
memo[key] = base[key];
|
|
}
|
|
return memo;
|
|
},
|
|
{}
|
|
);
|
|
return Object.keys(attr).reduce((memo, key) => {
|
|
if (attr[key] !== base[key] && base[key] === void 0) {
|
|
memo[key] = null;
|
|
}
|
|
return memo;
|
|
}, baseInverted);
|
|
}
|
|
AttributeMap2.invert = invert;
|
|
function transform(a, b, priority = false) {
|
|
if (typeof a !== "object") {
|
|
return b;
|
|
}
|
|
if (typeof b !== "object") {
|
|
return void 0;
|
|
}
|
|
if (!priority) {
|
|
return b;
|
|
}
|
|
const attributes = Object.keys(b).reduce(
|
|
(attrs, key) => {
|
|
if (a[key] === void 0) {
|
|
attrs[key] = b[key];
|
|
}
|
|
return attrs;
|
|
},
|
|
{}
|
|
);
|
|
return Object.keys(attributes).length > 0 ? attributes : void 0;
|
|
}
|
|
AttributeMap2.transform = transform;
|
|
})(AttributeMap || (AttributeMap = {}));
|
|
var AttributeMap_default = AttributeMap;
|
|
|
|
// packages/sync/build-module/quill-delta/Op.mjs
|
|
var Op;
|
|
((Op2) => {
|
|
function length3(op) {
|
|
if (typeof op.delete === "number") {
|
|
return op.delete;
|
|
} else if (typeof op.retain === "number") {
|
|
return op.retain;
|
|
} else if (typeof op.retain === "object" && op.retain !== null) {
|
|
return 1;
|
|
}
|
|
return typeof op.insert === "string" ? op.insert.length : 1;
|
|
}
|
|
Op2.length = length3;
|
|
})(Op || (Op = {}));
|
|
var Op_default = Op;
|
|
|
|
// packages/sync/build-module/quill-delta/OpIterator.mjs
|
|
var Iterator2 = class {
|
|
ops;
|
|
index;
|
|
offset;
|
|
constructor(ops) {
|
|
this.ops = ops;
|
|
this.index = 0;
|
|
this.offset = 0;
|
|
}
|
|
hasNext() {
|
|
return this.peekLength() < Infinity;
|
|
}
|
|
next(length3) {
|
|
if (!length3) {
|
|
length3 = Infinity;
|
|
}
|
|
const nextOp = this.ops[this.index];
|
|
if (nextOp) {
|
|
const offset = this.offset;
|
|
const opLength = Op_default.length(nextOp);
|
|
if (length3 >= opLength - offset) {
|
|
length3 = opLength - offset;
|
|
this.index += 1;
|
|
this.offset = 0;
|
|
} else {
|
|
this.offset += length3;
|
|
}
|
|
if (typeof nextOp.delete === "number") {
|
|
return { delete: length3 };
|
|
}
|
|
const retOp = {};
|
|
if (nextOp.attributes) {
|
|
retOp.attributes = nextOp.attributes;
|
|
}
|
|
if (typeof nextOp.retain === "number") {
|
|
retOp.retain = length3;
|
|
} else if (typeof nextOp.retain === "object" && nextOp.retain !== null) {
|
|
retOp.retain = nextOp.retain;
|
|
} else if (typeof nextOp.insert === "string") {
|
|
retOp.insert = nextOp.insert.substr(offset, length3);
|
|
} else {
|
|
retOp.insert = nextOp.insert;
|
|
}
|
|
return retOp;
|
|
}
|
|
return { retain: Infinity };
|
|
}
|
|
peek() {
|
|
return this.ops[this.index];
|
|
}
|
|
peekLength() {
|
|
if (this.ops[this.index]) {
|
|
return Op_default.length(this.ops[this.index]) - this.offset;
|
|
}
|
|
return Infinity;
|
|
}
|
|
peekType() {
|
|
const op = this.ops[this.index];
|
|
if (op) {
|
|
if (typeof op.delete === "number") {
|
|
return "delete";
|
|
} else if (typeof op.retain === "number" || typeof op.retain === "object" && op.retain !== null) {
|
|
return "retain";
|
|
}
|
|
return "insert";
|
|
}
|
|
return "retain";
|
|
}
|
|
rest() {
|
|
if (!this.hasNext()) {
|
|
return [];
|
|
} else if (this.offset === 0) {
|
|
return this.ops.slice(this.index);
|
|
}
|
|
const offset = this.offset;
|
|
const index = this.index;
|
|
const next = this.next();
|
|
const rest = this.ops.slice(this.index);
|
|
this.offset = offset;
|
|
this.index = index;
|
|
return [next].concat(rest);
|
|
}
|
|
};
|
|
|
|
// packages/sync/build-module/quill-delta/Delta.mjs
|
|
function cloneDeep2(value) {
|
|
return JSON.parse(JSON.stringify(value));
|
|
}
|
|
var NULL_CHARACTER = String.fromCharCode(0);
|
|
var STRING_TOO_LARGE_THRESHOLD = 1e4;
|
|
function normalizeChangeCounts(changes) {
|
|
return changes.map((change) => ({
|
|
...change,
|
|
count: change.value.length
|
|
}));
|
|
}
|
|
var getEmbedTypeAndData = (a, b) => {
|
|
if (typeof a !== "object" || a === null) {
|
|
throw new Error(`cannot retain a ${typeof a}`);
|
|
}
|
|
if (typeof b !== "object" || b === null) {
|
|
throw new Error(`cannot retain a ${typeof b}`);
|
|
}
|
|
const embedType = Object.keys(a)[0];
|
|
if (!embedType || embedType !== Object.keys(b)[0]) {
|
|
throw new Error(
|
|
`embed types not matched: ${embedType} != ${Object.keys(b)[0]}`
|
|
);
|
|
}
|
|
return [embedType, a[embedType], b[embedType]];
|
|
};
|
|
var Delta = class _Delta {
|
|
static Op = Op_default;
|
|
static OpIterator = Iterator2;
|
|
static AttributeMap = AttributeMap_default;
|
|
static handlers = {};
|
|
static registerEmbed(embedType, handler) {
|
|
this.handlers[embedType] = handler;
|
|
}
|
|
static unregisterEmbed(embedType) {
|
|
delete this.handlers[embedType];
|
|
}
|
|
static getHandler(embedType) {
|
|
const handler = this.handlers[embedType];
|
|
if (!handler) {
|
|
throw new Error(`no handlers for embed type "${embedType}"`);
|
|
}
|
|
return handler;
|
|
}
|
|
ops;
|
|
constructor(ops) {
|
|
if (Array.isArray(ops)) {
|
|
this.ops = ops;
|
|
} else if (ops !== null && ops !== void 0 && Array.isArray(ops.ops)) {
|
|
this.ops = ops.ops;
|
|
} else {
|
|
this.ops = [];
|
|
}
|
|
}
|
|
insert(arg, attributes) {
|
|
const newOp = {};
|
|
if (typeof arg === "string" && arg.length === 0) {
|
|
return this;
|
|
}
|
|
newOp.insert = arg;
|
|
if (attributes !== null && attributes !== void 0 && typeof attributes === "object" && Object.keys(attributes).length > 0) {
|
|
newOp.attributes = attributes;
|
|
}
|
|
return this.push(newOp);
|
|
}
|
|
delete(length3) {
|
|
if (length3 <= 0) {
|
|
return this;
|
|
}
|
|
return this.push({ delete: length3 });
|
|
}
|
|
retain(length3, attributes) {
|
|
if (typeof length3 === "number" && length3 <= 0) {
|
|
return this;
|
|
}
|
|
const newOp = { retain: length3 };
|
|
if (attributes !== null && attributes !== void 0 && typeof attributes === "object" && Object.keys(attributes).length > 0) {
|
|
newOp.attributes = attributes;
|
|
}
|
|
return this.push(newOp);
|
|
}
|
|
push(newOp) {
|
|
let index = this.ops.length;
|
|
let lastOp = this.ops[index - 1];
|
|
newOp = cloneDeep2(newOp);
|
|
if (typeof lastOp === "object") {
|
|
if (typeof newOp.delete === "number" && typeof lastOp.delete === "number") {
|
|
this.ops[index - 1] = {
|
|
delete: lastOp.delete + newOp.delete
|
|
};
|
|
return this;
|
|
}
|
|
if (typeof lastOp.delete === "number" && newOp.insert !== null && newOp.insert !== void 0) {
|
|
index -= 1;
|
|
lastOp = this.ops[index - 1];
|
|
if (typeof lastOp !== "object") {
|
|
this.ops.unshift(newOp);
|
|
return this;
|
|
}
|
|
}
|
|
if ((0, import_es63.default)(newOp.attributes, lastOp.attributes)) {
|
|
if (typeof newOp.insert === "string" && typeof lastOp.insert === "string") {
|
|
this.ops[index - 1] = {
|
|
insert: lastOp.insert + newOp.insert
|
|
};
|
|
if (typeof newOp.attributes === "object") {
|
|
this.ops[index - 1].attributes = newOp.attributes;
|
|
}
|
|
return this;
|
|
} else if (typeof newOp.retain === "number" && typeof lastOp.retain === "number") {
|
|
this.ops[index - 1] = {
|
|
retain: lastOp.retain + newOp.retain
|
|
};
|
|
if (typeof newOp.attributes === "object") {
|
|
this.ops[index - 1].attributes = newOp.attributes;
|
|
}
|
|
return this;
|
|
}
|
|
}
|
|
}
|
|
if (index === this.ops.length) {
|
|
this.ops.push(newOp);
|
|
} else {
|
|
this.ops.splice(index, 0, newOp);
|
|
}
|
|
return this;
|
|
}
|
|
chop() {
|
|
const lastOp = this.ops[this.ops.length - 1];
|
|
if (lastOp && typeof lastOp.retain === "number" && !lastOp.attributes) {
|
|
this.ops.pop();
|
|
}
|
|
return this;
|
|
}
|
|
filter(predicate) {
|
|
return this.ops.filter(predicate);
|
|
}
|
|
forEach(predicate) {
|
|
this.ops.forEach(predicate);
|
|
}
|
|
map(predicate) {
|
|
return this.ops.map(predicate);
|
|
}
|
|
partition(predicate) {
|
|
const passed = [];
|
|
const failed = [];
|
|
this.forEach((op) => {
|
|
const target = predicate(op) ? passed : failed;
|
|
target.push(op);
|
|
});
|
|
return [passed, failed];
|
|
}
|
|
reduce(predicate, initialValue) {
|
|
return this.ops.reduce(predicate, initialValue);
|
|
}
|
|
changeLength() {
|
|
return this.reduce((length3, elem) => {
|
|
if (elem.insert) {
|
|
return length3 + Op_default.length(elem);
|
|
} else if (elem.delete) {
|
|
return length3 - elem.delete;
|
|
}
|
|
return length3;
|
|
}, 0);
|
|
}
|
|
length() {
|
|
return this.reduce((length3, elem) => {
|
|
return length3 + Op_default.length(elem);
|
|
}, 0);
|
|
}
|
|
slice(start = 0, end = Infinity) {
|
|
const ops = [];
|
|
const iter = new Iterator2(this.ops);
|
|
let index = 0;
|
|
while (index < end && iter.hasNext()) {
|
|
let nextOp;
|
|
if (index < start) {
|
|
nextOp = iter.next(start - index);
|
|
} else {
|
|
nextOp = iter.next(end - index);
|
|
ops.push(nextOp);
|
|
}
|
|
index += Op_default.length(nextOp);
|
|
}
|
|
return new _Delta(ops);
|
|
}
|
|
compose(other) {
|
|
const thisIter = new Iterator2(this.ops);
|
|
const otherIter = new Iterator2(other.ops);
|
|
const ops = [];
|
|
const firstOther = otherIter.peek();
|
|
if (firstOther !== null && firstOther !== void 0 && typeof firstOther.retain === "number" && (firstOther.attributes === null || firstOther.attributes === void 0)) {
|
|
let firstLeft = firstOther.retain;
|
|
while (thisIter.peekType() === "insert" && thisIter.peekLength() <= firstLeft) {
|
|
firstLeft -= thisIter.peekLength();
|
|
ops.push(thisIter.next());
|
|
}
|
|
if (firstOther.retain - firstLeft > 0) {
|
|
otherIter.next(firstOther.retain - firstLeft);
|
|
}
|
|
}
|
|
const delta = new _Delta(ops);
|
|
while (thisIter.hasNext() || otherIter.hasNext()) {
|
|
if (otherIter.peekType() === "insert") {
|
|
delta.push(otherIter.next());
|
|
} else if (thisIter.peekType() === "delete") {
|
|
delta.push(thisIter.next());
|
|
} else {
|
|
const length3 = Math.min(
|
|
thisIter.peekLength(),
|
|
otherIter.peekLength()
|
|
);
|
|
const thisOp = thisIter.next(length3);
|
|
const otherOp = otherIter.next(length3);
|
|
if (otherOp.retain) {
|
|
const newOp = {};
|
|
if (typeof thisOp.retain === "number") {
|
|
newOp.retain = typeof otherOp.retain === "number" ? length3 : otherOp.retain;
|
|
} else if (typeof otherOp.retain === "number") {
|
|
if (thisOp.retain === null || thisOp.retain === void 0) {
|
|
newOp.insert = thisOp.insert;
|
|
} else {
|
|
newOp.retain = thisOp.retain;
|
|
}
|
|
} else {
|
|
const action = thisOp.retain === null || thisOp.retain === void 0 ? "insert" : "retain";
|
|
const [embedType, thisData, otherData] = getEmbedTypeAndData(
|
|
thisOp[action],
|
|
otherOp.retain
|
|
);
|
|
const handler = _Delta.getHandler(embedType);
|
|
newOp[action] = {
|
|
[embedType]: handler.compose(
|
|
thisData,
|
|
otherData,
|
|
action === "retain"
|
|
)
|
|
};
|
|
}
|
|
const attributes = AttributeMap_default.compose(
|
|
thisOp.attributes,
|
|
otherOp.attributes,
|
|
typeof thisOp.retain === "number"
|
|
);
|
|
if (attributes) {
|
|
newOp.attributes = attributes;
|
|
}
|
|
delta.push(newOp);
|
|
if (!otherIter.hasNext() && (0, import_es63.default)(delta.ops[delta.ops.length - 1], newOp)) {
|
|
const rest = new _Delta(thisIter.rest());
|
|
return delta.concat(rest).chop();
|
|
}
|
|
} else if (typeof otherOp.delete === "number" && (typeof thisOp.retain === "number" || typeof thisOp.retain === "object" && thisOp.retain !== null)) {
|
|
delta.push(otherOp);
|
|
}
|
|
}
|
|
}
|
|
return delta.chop();
|
|
}
|
|
concat(other) {
|
|
const delta = new _Delta(this.ops.slice());
|
|
if (other.ops.length > 0) {
|
|
delta.push(other.ops[0]);
|
|
delta.ops = delta.ops.concat(other.ops.slice(1));
|
|
}
|
|
return delta;
|
|
}
|
|
diff(other) {
|
|
if (this.ops === other.ops) {
|
|
return new _Delta();
|
|
}
|
|
const strings = this.deltasToStrings(other);
|
|
const diffResult = normalizeChangeCounts(
|
|
diffChars(strings[0], strings[1])
|
|
);
|
|
const thisIter = new Iterator2(this.ops);
|
|
const otherIter = new Iterator2(other.ops);
|
|
const retDelta = this.convertChangesToDelta(
|
|
diffResult,
|
|
thisIter,
|
|
otherIter
|
|
);
|
|
return retDelta.chop();
|
|
}
|
|
eachLine(predicate, newline = "\n") {
|
|
const iter = new Iterator2(this.ops);
|
|
let line = new _Delta();
|
|
let i = 0;
|
|
while (iter.hasNext()) {
|
|
if (iter.peekType() !== "insert") {
|
|
return;
|
|
}
|
|
const thisOp = iter.peek();
|
|
const start = Op_default.length(thisOp) - iter.peekLength();
|
|
const index = typeof thisOp.insert === "string" ? thisOp.insert.indexOf(newline, start) - start : -1;
|
|
if (index < 0) {
|
|
line.push(iter.next());
|
|
} else if (index > 0) {
|
|
line.push(iter.next(index));
|
|
} else {
|
|
if (predicate(line, iter.next(1).attributes || {}, i) === false) {
|
|
return;
|
|
}
|
|
i += 1;
|
|
line = new _Delta();
|
|
}
|
|
}
|
|
if (line.length() > 0) {
|
|
predicate(line, {}, i);
|
|
}
|
|
}
|
|
invert(base) {
|
|
const inverted = new _Delta();
|
|
this.reduce((baseIndex, op) => {
|
|
if (op.insert) {
|
|
inverted.delete(Op_default.length(op));
|
|
} else if (typeof op.retain === "number" && (op.attributes === null || op.attributes === void 0)) {
|
|
inverted.retain(op.retain);
|
|
return baseIndex + op.retain;
|
|
} else if (op.delete || typeof op.retain === "number") {
|
|
const length3 = op.delete || op.retain;
|
|
const slice = base.slice(baseIndex, baseIndex + length3);
|
|
slice.forEach((baseOp) => {
|
|
if (op.delete) {
|
|
inverted.push(baseOp);
|
|
} else if (op.retain && op.attributes) {
|
|
inverted.retain(
|
|
Op_default.length(baseOp),
|
|
AttributeMap_default.invert(
|
|
op.attributes,
|
|
baseOp.attributes
|
|
)
|
|
);
|
|
}
|
|
});
|
|
return baseIndex + length3;
|
|
} else if (typeof op.retain === "object" && op.retain !== null) {
|
|
const slice = base.slice(baseIndex, baseIndex + 1);
|
|
const baseOp = new Iterator2(slice.ops).next();
|
|
const [embedType, opData, baseOpData] = getEmbedTypeAndData(
|
|
op.retain,
|
|
baseOp.insert
|
|
);
|
|
const handler = _Delta.getHandler(embedType);
|
|
inverted.retain(
|
|
{ [embedType]: handler.invert(opData, baseOpData) },
|
|
AttributeMap_default.invert(op.attributes, baseOp.attributes)
|
|
);
|
|
return baseIndex + 1;
|
|
}
|
|
return baseIndex;
|
|
}, 0);
|
|
return inverted.chop();
|
|
}
|
|
transform(arg, priority = false) {
|
|
priority = !!priority;
|
|
if (typeof arg === "number") {
|
|
return this.transformPosition(arg, priority);
|
|
}
|
|
const other = arg;
|
|
const thisIter = new Iterator2(this.ops);
|
|
const otherIter = new Iterator2(other.ops);
|
|
const delta = new _Delta();
|
|
while (thisIter.hasNext() || otherIter.hasNext()) {
|
|
if (thisIter.peekType() === "insert" && (priority || otherIter.peekType() !== "insert")) {
|
|
delta.retain(Op_default.length(thisIter.next()));
|
|
} else if (otherIter.peekType() === "insert") {
|
|
delta.push(otherIter.next());
|
|
} else {
|
|
const length3 = Math.min(
|
|
thisIter.peekLength(),
|
|
otherIter.peekLength()
|
|
);
|
|
const thisOp = thisIter.next(length3);
|
|
const otherOp = otherIter.next(length3);
|
|
if (thisOp.delete) {
|
|
continue;
|
|
} else if (otherOp.delete) {
|
|
delta.push(otherOp);
|
|
} else {
|
|
const thisData = thisOp.retain;
|
|
const otherData = otherOp.retain;
|
|
let transformedData = typeof otherData === "object" && otherData !== null ? otherData : length3;
|
|
if (typeof thisData === "object" && thisData !== null && typeof otherData === "object" && otherData !== null) {
|
|
const embedType = Object.keys(thisData)[0];
|
|
if (embedType === Object.keys(otherData)[0]) {
|
|
const handler = _Delta.getHandler(embedType);
|
|
if (handler) {
|
|
transformedData = {
|
|
[embedType]: handler.transform(
|
|
thisData[embedType],
|
|
otherData[embedType],
|
|
priority
|
|
)
|
|
};
|
|
}
|
|
}
|
|
}
|
|
delta.retain(
|
|
transformedData,
|
|
AttributeMap_default.transform(
|
|
thisOp.attributes,
|
|
otherOp.attributes,
|
|
priority
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
return delta.chop();
|
|
}
|
|
transformPosition(index, priority = false) {
|
|
priority = !!priority;
|
|
const thisIter = new Iterator2(this.ops);
|
|
let offset = 0;
|
|
while (thisIter.hasNext() && offset <= index) {
|
|
const length3 = thisIter.peekLength();
|
|
const nextType = thisIter.peekType();
|
|
thisIter.next();
|
|
if (nextType === "delete") {
|
|
index -= Math.min(length3, index - offset);
|
|
continue;
|
|
} else if (nextType === "insert" && (offset < index || !priority)) {
|
|
index += length3;
|
|
}
|
|
offset += length3;
|
|
}
|
|
return index;
|
|
}
|
|
/**
|
|
* Given a Delta and a cursor position, do a diff and attempt to adjust
|
|
* the diff to place insertions or deletions at the cursor position.
|
|
*
|
|
* @param other - The other Delta to diff against.
|
|
* @param cursorAfterChange - The cursor position index after the change.
|
|
* @return A Delta that attempts to place insertions or deletions at the cursor position.
|
|
*/
|
|
diffWithCursor(other, cursorAfterChange) {
|
|
if (this.ops === other.ops) {
|
|
return new _Delta();
|
|
}
|
|
const strings = this.deltasToStrings(other);
|
|
const maxStringLength = Math.max(
|
|
...strings.map((str) => str.length)
|
|
);
|
|
if (maxStringLength > STRING_TOO_LARGE_THRESHOLD) {
|
|
const diffResult = normalizeChangeCounts(
|
|
diffLines(strings[0], strings[1])
|
|
);
|
|
const thisIterLarge = new Iterator2(this.ops);
|
|
const otherIterLarge = new Iterator2(other.ops);
|
|
return this.convertChangesToDelta(
|
|
diffResult,
|
|
thisIterLarge,
|
|
otherIterLarge
|
|
).chop();
|
|
} else if (cursorAfterChange === null) {
|
|
return this.diff(other);
|
|
}
|
|
let diffs = normalizeChangeCounts(
|
|
diffChars(strings[0], strings[1])
|
|
);
|
|
let lastDiffPosition = 0;
|
|
const adjustedDiffs = [];
|
|
for (let i = 0; i < diffs.length; i++) {
|
|
const diff = diffs[i];
|
|
const segmentStart = lastDiffPosition;
|
|
const segmentEnd = lastDiffPosition + (diff.count ?? 0);
|
|
const isCursorInSegment = cursorAfterChange > segmentStart && cursorAfterChange <= segmentEnd;
|
|
const isUnchangedSegment = !diff.added && !diff.removed;
|
|
const isRemovalSegment = diff.removed && !diff.added;
|
|
const nextDiff = diffs[i + 1];
|
|
const isNextDiffAnInsert = nextDiff && nextDiff.added && !nextDiff.removed;
|
|
if (isUnchangedSegment && isCursorInSegment && isNextDiffAnInsert) {
|
|
const movedSegments = this.tryMoveInsertionToCursor(
|
|
diff,
|
|
nextDiff,
|
|
cursorAfterChange,
|
|
segmentStart
|
|
);
|
|
if (movedSegments) {
|
|
adjustedDiffs.push(...movedSegments);
|
|
i++;
|
|
lastDiffPosition = segmentEnd;
|
|
continue;
|
|
}
|
|
}
|
|
if (isRemovalSegment) {
|
|
const movedSegments = this.tryMoveDeletionToCursor(
|
|
diff,
|
|
adjustedDiffs,
|
|
cursorAfterChange,
|
|
lastDiffPosition
|
|
);
|
|
if (movedSegments) {
|
|
adjustedDiffs.pop();
|
|
adjustedDiffs.push(...movedSegments);
|
|
lastDiffPosition += diff.count ?? 0;
|
|
continue;
|
|
}
|
|
}
|
|
adjustedDiffs.push(diff);
|
|
if (!diff.added) {
|
|
lastDiffPosition += diff.count ?? 0;
|
|
}
|
|
}
|
|
diffs = adjustedDiffs;
|
|
const thisIter = new Iterator2(this.ops);
|
|
const otherIter = new Iterator2(other.ops);
|
|
const retDelta = this.convertChangesToDelta(
|
|
diffs,
|
|
thisIter,
|
|
otherIter
|
|
);
|
|
return retDelta.chop();
|
|
}
|
|
/**
|
|
* Try to move an insertion operation from after an unchanged segment to the cursor position within it.
|
|
* This is a "look-ahead" strategy.
|
|
*
|
|
* @param diff - The current unchanged diff segment.
|
|
* @param nextDiff - The next diff segment (expected to be an insertion).
|
|
* @param cursorAfterChange - The cursor position after the change.
|
|
* @param segmentStart - The start position of the current segment.
|
|
* @return An array of adjusted diff segments if the insertion was successfully moved, null otherwise.
|
|
*/
|
|
tryMoveInsertionToCursor(diff, nextDiff, cursorAfterChange, segmentStart) {
|
|
const nextDiffInsert = nextDiff.value;
|
|
const insertLength = nextDiffInsert.length;
|
|
const insertOffset = cursorAfterChange - segmentStart - insertLength;
|
|
const textAtCursor = diff.value.substring(
|
|
insertOffset,
|
|
insertOffset + nextDiffInsert.length
|
|
);
|
|
const isInsertMoveable = textAtCursor === nextDiffInsert;
|
|
if (!isInsertMoveable) {
|
|
return null;
|
|
}
|
|
const beforeCursor = diff.value.substring(0, insertOffset);
|
|
const afterCursor = diff.value.substring(insertOffset);
|
|
const result = [];
|
|
if (beforeCursor.length > 0) {
|
|
result.push({
|
|
value: beforeCursor,
|
|
count: beforeCursor.length,
|
|
added: false,
|
|
removed: false
|
|
});
|
|
}
|
|
result.push(nextDiff);
|
|
if (afterCursor.length > 0) {
|
|
result.push({
|
|
value: afterCursor,
|
|
count: afterCursor.length,
|
|
added: false,
|
|
removed: false
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
* Try to move a deletion operation to the cursor position by looking back at the previous unchanged segment.
|
|
* This is a "look-back" strategy.
|
|
*
|
|
* @param diff - The current deletion diff segment.
|
|
* @param adjustedDiffs - The array of previously processed diff segments.
|
|
* @param cursorAfterChange - The cursor position after the change.
|
|
* @param lastDiffPosition - The position in the document up to (but not including) the current diff.
|
|
* @return An array of adjusted diff segments if the deletion was successfully moved, null otherwise.
|
|
*/
|
|
tryMoveDeletionToCursor(diff, adjustedDiffs, cursorAfterChange, lastDiffPosition) {
|
|
const prevDiff = adjustedDiffs[adjustedDiffs.length - 1];
|
|
if (!prevDiff || prevDiff.added || prevDiff.removed) {
|
|
return null;
|
|
}
|
|
const prevSegmentStart = lastDiffPosition - (prevDiff.count ?? 0);
|
|
const prevSegmentEnd = lastDiffPosition;
|
|
if (cursorAfterChange < prevSegmentStart || cursorAfterChange >= prevSegmentEnd) {
|
|
return null;
|
|
}
|
|
const deletedChars = diff.value;
|
|
const deleteOffset = cursorAfterChange - prevSegmentStart;
|
|
const textAtCursor = prevDiff.value.substring(
|
|
deleteOffset,
|
|
deleteOffset + deletedChars.length
|
|
);
|
|
const canBePlacedHere = textAtCursor === deletedChars;
|
|
if (!canBePlacedHere) {
|
|
return null;
|
|
}
|
|
const beforeCursor = prevDiff.value.substring(0, deleteOffset);
|
|
const atAndAfterCursor = prevDiff.value.substring(deleteOffset);
|
|
const deletionLength = diff.count ?? 0;
|
|
const afterDeletion = atAndAfterCursor.substring(deletionLength);
|
|
const result = [];
|
|
if (beforeCursor.length > 0) {
|
|
result.push({
|
|
value: beforeCursor,
|
|
count: beforeCursor.length,
|
|
added: false,
|
|
removed: false
|
|
});
|
|
}
|
|
result.push(diff);
|
|
if (afterDeletion.length > 0) {
|
|
result.push({
|
|
value: afterDeletion,
|
|
count: afterDeletion.length,
|
|
added: false,
|
|
removed: false
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
* Convert two Deltas to string representations for diffing.
|
|
*
|
|
* @param other - The other Delta to convert.
|
|
* @return A tuple of [thisString, otherString].
|
|
*/
|
|
deltasToStrings(other) {
|
|
return [this, other].map((delta) => {
|
|
return delta.map((op) => {
|
|
if (op.insert !== null || op.insert !== void 0) {
|
|
return typeof op.insert === "string" ? op.insert : NULL_CHARACTER;
|
|
}
|
|
const prep = delta === other ? "on" : "with";
|
|
throw new Error(
|
|
"diff() called " + prep + " non-document"
|
|
);
|
|
}).join("");
|
|
});
|
|
}
|
|
/**
|
|
* Process diff changes and convert them to Delta operations.
|
|
*
|
|
* @param changes - The array of changes from the diff algorithm.
|
|
* @param thisIter - Iterator for this Delta's operations.
|
|
* @param otherIter - Iterator for the other Delta's operations.
|
|
* @return A Delta containing the processed diff operations.
|
|
*/
|
|
convertChangesToDelta(changes, thisIter, otherIter) {
|
|
const retDelta = new _Delta();
|
|
changes.forEach((component) => {
|
|
let length3 = component.count ?? 0;
|
|
while (length3 > 0) {
|
|
let opLength = 0;
|
|
if (component.added) {
|
|
opLength = Math.min(otherIter.peekLength(), length3);
|
|
retDelta.push(otherIter.next(opLength));
|
|
} else if (component.removed) {
|
|
opLength = Math.min(length3, thisIter.peekLength());
|
|
thisIter.next(opLength);
|
|
retDelta.delete(opLength);
|
|
} else {
|
|
opLength = Math.min(
|
|
thisIter.peekLength(),
|
|
otherIter.peekLength(),
|
|
length3
|
|
);
|
|
const thisOp = thisIter.next(opLength);
|
|
const otherOp = otherIter.next(opLength);
|
|
if ((0, import_es63.default)(thisOp.insert, otherOp.insert)) {
|
|
retDelta.retain(
|
|
opLength,
|
|
AttributeMap_default.diff(
|
|
thisOp.attributes,
|
|
otherOp.attributes
|
|
)
|
|
);
|
|
} else {
|
|
retDelta.push(otherOp).delete(opLength);
|
|
}
|
|
}
|
|
length3 -= opLength;
|
|
}
|
|
});
|
|
return retDelta;
|
|
}
|
|
};
|
|
var Delta_default = Delta;
|
|
|
|
// packages/sync/build-module/private-apis.mjs
|
|
var privateApis = {};
|
|
lock(privateApis, {
|
|
ConnectionErrorCode,
|
|
createSyncManager,
|
|
Delta: Delta_default,
|
|
CRDT_DOC_META_PERSISTENCE_KEY,
|
|
CRDT_RECORD_MAP_KEY,
|
|
LOCAL_EDITOR_ORIGIN,
|
|
LOCAL_UNDO_IGNORED_ORIGIN,
|
|
retrySyncConnection: () => pollingManager.retryNow()
|
|
});
|
|
|
|
// packages/core-data/build-module/awareness/post-editor-awareness.mjs
|
|
var import_block_editor3 = __toESM(require_block_editor(), 1);
|
|
|
|
// packages/core-data/build-module/awareness/base-awareness.mjs
|
|
var import_data2 = __toESM(require_data(), 1);
|
|
|
|
// packages/core-data/build-module/awareness/config.mjs
|
|
var AWARENESS_CURSOR_UPDATE_THROTTLE_IN_MS = 100;
|
|
var LOCAL_CURSOR_UPDATE_DEBOUNCE_IN_MS = 5;
|
|
var REMOVAL_DELAY_IN_MS = 5e3;
|
|
|
|
// packages/core-data/build-module/awareness/utils.mjs
|
|
function getBrowserName() {
|
|
const userAgent = window.navigator.userAgent;
|
|
let browserName = "Unknown";
|
|
if (userAgent.includes("Firefox")) {
|
|
browserName = "Firefox";
|
|
} else if (userAgent.includes("Edg")) {
|
|
browserName = "Microsoft Edge";
|
|
} else if (userAgent.includes("Chrome") && !userAgent.includes("Edg")) {
|
|
browserName = "Chrome";
|
|
} else if (userAgent.includes("Safari") && !userAgent.includes("Chrome")) {
|
|
browserName = "Safari";
|
|
} else if (userAgent.includes("MSIE") || userAgent.includes("Trident")) {
|
|
browserName = "Internet Explorer";
|
|
} else if (userAgent.includes("Opera") || userAgent.includes("OPR")) {
|
|
browserName = "Opera";
|
|
}
|
|
return browserName;
|
|
}
|
|
function areMapsEqual(map1, map2, comparatorFn) {
|
|
if (map1.size !== map2.size) {
|
|
return false;
|
|
}
|
|
for (const [key, value1] of map1.entries()) {
|
|
if (!map2.has(key)) {
|
|
return false;
|
|
}
|
|
if (!comparatorFn(value1, map2.get(key))) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
function areCollaboratorInfosEqual(collaboratorInfo1, collaboratorInfo2) {
|
|
if (!collaboratorInfo1 || !collaboratorInfo2) {
|
|
return collaboratorInfo1 === collaboratorInfo2;
|
|
}
|
|
if (Object.keys(collaboratorInfo1).length !== Object.keys(collaboratorInfo2).length) {
|
|
return false;
|
|
}
|
|
return Object.entries(collaboratorInfo1).every(([key, value]) => {
|
|
return value === collaboratorInfo2[key];
|
|
});
|
|
}
|
|
function generateCollaboratorInfo(currentCollaborator) {
|
|
const { avatar_urls, id: id2, name, slug } = currentCollaborator;
|
|
return {
|
|
avatar_urls,
|
|
// eslint-disable-line camelcase
|
|
browserType: getBrowserName(),
|
|
enteredAt: Date.now(),
|
|
id: id2,
|
|
name,
|
|
slug
|
|
};
|
|
}
|
|
function getRecordValue(obj, key) {
|
|
if ("object" === typeof obj && null !== obj && key in obj) {
|
|
return obj[key];
|
|
}
|
|
return null;
|
|
}
|
|
function getTypedKeys(obj) {
|
|
return Object.keys(obj);
|
|
}
|
|
|
|
// packages/core-data/build-module/awareness/typed-awareness.mjs
|
|
var TypedAwareness = class extends Awareness {
|
|
/**
|
|
* Get the states from an awareness document.
|
|
*/
|
|
getStates() {
|
|
return super.getStates();
|
|
}
|
|
/**
|
|
* Get a local state field from an awareness document.
|
|
* @param field
|
|
*/
|
|
getLocalStateField(field) {
|
|
const state = this.getLocalState();
|
|
return getRecordValue(state, field);
|
|
}
|
|
/**
|
|
* Set a local state field on an awareness document.
|
|
* @param field
|
|
* @param value
|
|
*/
|
|
setLocalStateField(field, value) {
|
|
super.setLocalStateField(field, value);
|
|
}
|
|
};
|
|
|
|
// packages/core-data/build-module/awareness/awareness-state.mjs
|
|
var AwarenessWithEqualityChecks = class extends TypedAwareness {
|
|
/** OVERRIDDEN METHODS */
|
|
/**
|
|
* Set a local state field on an awareness document. Calling this method may
|
|
* trigger rerenders of any subscribed components.
|
|
*
|
|
* Equality checks are provided by the abstract `equalityFieldChecks` property.
|
|
* @param field - The field to set.
|
|
* @param value - The value to set.
|
|
*/
|
|
setLocalStateField(field, value) {
|
|
if (this.isFieldEqual(
|
|
field,
|
|
value,
|
|
this.getLocalStateField(field) ?? void 0
|
|
)) {
|
|
return;
|
|
}
|
|
super.setLocalStateField(field, value);
|
|
}
|
|
/** CUSTOM METHODS */
|
|
/**
|
|
* Determine if a field value has changed using the provided equality checks.
|
|
* @param field - The field to check.
|
|
* @param value1 - The first value to compare.
|
|
* @param value2 - The second value to compare.
|
|
*/
|
|
isFieldEqual(field, value1, value2) {
|
|
if (["clientId", "isConnected", "isMe"].includes(field)) {
|
|
return value1 === value2;
|
|
}
|
|
if (field in this.equalityFieldChecks) {
|
|
const fn = this.equalityFieldChecks[field];
|
|
return fn(value1, value2);
|
|
}
|
|
throw new Error(
|
|
`No equality check implemented for awareness state field "${field.toString()}".`
|
|
);
|
|
}
|
|
/**
|
|
* Determine if two states are equal by comparing each field using the
|
|
* provided equality checks.
|
|
* @param state1 - The first state to compare.
|
|
* @param state2 - The second state to compare.
|
|
*/
|
|
isStateEqual(state1, state2) {
|
|
return [
|
|
.../* @__PURE__ */ new Set([
|
|
...getTypedKeys(state1),
|
|
...getTypedKeys(state2)
|
|
])
|
|
].every((field) => {
|
|
const value1 = state1[field];
|
|
const value2 = state2[field];
|
|
return this.isFieldEqual(field, value1, value2);
|
|
});
|
|
}
|
|
};
|
|
var AwarenessState = class extends AwarenessWithEqualityChecks {
|
|
/** CUSTOM PROPERTIES */
|
|
/**
|
|
* Whether the setUp method has been called, to avoid running it multiple
|
|
* times.
|
|
*/
|
|
hasSetupRun = false;
|
|
/**
|
|
* We keep track of all seen states during the current session for two reasons:
|
|
*
|
|
* 1. So that we can represent recently disconnected collaborators in our UI, even
|
|
* after they have been removed from the awareness document.
|
|
* 2. So that we can provide debug information about all collaborators seen during
|
|
* the session.
|
|
*/
|
|
disconnectedCollaborators = /* @__PURE__ */ new Set();
|
|
seenStates = /* @__PURE__ */ new Map();
|
|
/**
|
|
* Hold a snapshot of the previous awareness state allows us to compare the
|
|
* state values and avoid unnecessary updates to subscribers.
|
|
*/
|
|
previousSnapshot = /* @__PURE__ */ new Map();
|
|
stateSubscriptions = [];
|
|
/**
|
|
* In some cases, we may want to throttle setting local state fields to avoid
|
|
* overwhelming the awareness document with rapid updates. At the same time, we
|
|
* want to ensure that when we read our own state locally, we get the latest
|
|
* value -- even if it hasn't yet been set on the awareness instance.
|
|
*/
|
|
myThrottledState = {};
|
|
throttleTimeouts = /* @__PURE__ */ new Map();
|
|
/** CUSTOM METHODS */
|
|
/**
|
|
* Set up the awareness state. This method is idempotent and will only run
|
|
* once. Subclasses should override `onSetUp()` instead of this method to
|
|
* add their own setup logic.
|
|
*
|
|
* This is defined as a readonly arrow function property to prevent
|
|
* subclasses from overriding it.
|
|
*/
|
|
setUp = () => {
|
|
if (this.hasSetupRun) {
|
|
return;
|
|
}
|
|
this.hasSetupRun = true;
|
|
this.onSetUp();
|
|
this.on(
|
|
"change",
|
|
({ added, removed, updated }) => {
|
|
[...added, ...updated].forEach((id2) => {
|
|
this.disconnectedCollaborators.delete(id2);
|
|
});
|
|
removed.forEach((id2) => {
|
|
this.disconnectedCollaborators.add(id2);
|
|
setTimeout(() => {
|
|
this.disconnectedCollaborators.delete(id2);
|
|
this.updateSubscribers(
|
|
true
|
|
/* force update */
|
|
);
|
|
}, REMOVAL_DELAY_IN_MS);
|
|
});
|
|
this.updateSubscribers();
|
|
}
|
|
);
|
|
};
|
|
/**
|
|
* Get the most recent state from the last processed change event.
|
|
*
|
|
* @return An array of EnhancedState< State >.
|
|
*/
|
|
getCurrentState() {
|
|
return Array.from(this.previousSnapshot.values());
|
|
}
|
|
/**
|
|
* Get all seen states in this session to enable debug reporting.
|
|
*/
|
|
getSeenStates() {
|
|
return this.seenStates;
|
|
}
|
|
/**
|
|
* Allow external code to subscribe to awareness state changes.
|
|
* @param callback - The callback to subscribe to.
|
|
*/
|
|
onStateChange(callback) {
|
|
this.stateSubscriptions.push(callback);
|
|
return () => {
|
|
this.stateSubscriptions = this.stateSubscriptions.filter(
|
|
(cb) => cb !== callback
|
|
);
|
|
};
|
|
}
|
|
/**
|
|
* Set a local state field on an awareness document with throttle. See caveats
|
|
* of this.setLocalStateField.
|
|
* @param field - The field to set.
|
|
* @param value - The value to set.
|
|
* @param wait - The wait time in milliseconds.
|
|
*/
|
|
setThrottledLocalStateField(field, value, wait) {
|
|
this.setLocalStateField(field, value);
|
|
this.throttleTimeouts.set(
|
|
field,
|
|
setTimeout(() => {
|
|
this.throttleTimeouts.delete(field);
|
|
if (this.myThrottledState[field]) {
|
|
this.setLocalStateField(
|
|
field,
|
|
this.myThrottledState[field]
|
|
);
|
|
delete this.myThrottledState[field];
|
|
}
|
|
}, wait)
|
|
);
|
|
}
|
|
/**
|
|
* Set the current collaborator's connection status as awareness state.
|
|
* @param isConnected - The connection status.
|
|
*/
|
|
setConnectionStatus(isConnected) {
|
|
if (isConnected) {
|
|
this.disconnectedCollaborators.delete(this.clientID);
|
|
} else {
|
|
this.disconnectedCollaborators.add(this.clientID);
|
|
}
|
|
this.updateSubscribers(
|
|
true
|
|
/* force update */
|
|
);
|
|
}
|
|
/**
|
|
* Update all subscribed listeners with the latest awareness state.
|
|
* @param forceUpdate - Whether to force an update.
|
|
*/
|
|
updateSubscribers(forceUpdate = false) {
|
|
if (!this.stateSubscriptions.length) {
|
|
return;
|
|
}
|
|
const states = this.getStates();
|
|
this.seenStates = new Map([
|
|
...this.seenStates.entries(),
|
|
...states.entries()
|
|
]);
|
|
const updatedStates = new Map(
|
|
[...this.disconnectedCollaborators, ...states.keys()].filter((clientId) => {
|
|
return Object.keys(this.seenStates.get(clientId) ?? {}).length > 0;
|
|
}).map((clientId) => {
|
|
const rawState = this.seenStates.get(clientId);
|
|
const isConnected = !this.disconnectedCollaborators.has(clientId);
|
|
const isMe = clientId === this.clientID;
|
|
const myState = isMe ? this.myThrottledState : {};
|
|
const state = {
|
|
...rawState,
|
|
...myState,
|
|
clientId,
|
|
isConnected,
|
|
isMe
|
|
};
|
|
return [clientId, state];
|
|
})
|
|
);
|
|
if (!forceUpdate) {
|
|
if (areMapsEqual(
|
|
this.previousSnapshot,
|
|
updatedStates,
|
|
this.isStateEqual.bind(this)
|
|
)) {
|
|
return;
|
|
}
|
|
}
|
|
this.previousSnapshot = updatedStates;
|
|
this.stateSubscriptions.forEach((callback) => {
|
|
callback(Array.from(updatedStates.values()));
|
|
});
|
|
}
|
|
};
|
|
|
|
// packages/core-data/build-module/name.mjs
|
|
var STORE_NAME = "core";
|
|
|
|
// packages/core-data/build-module/awareness/base-awareness.mjs
|
|
var BaseAwarenessState = class extends AwarenessState {
|
|
onSetUp() {
|
|
void this.setCurrentCollaboratorInfo();
|
|
}
|
|
/**
|
|
* Set the current collaborator info in the local state.
|
|
*/
|
|
async setCurrentCollaboratorInfo() {
|
|
const currentUser2 = await (0, import_data2.resolveSelect)(STORE_NAME).getCurrentUser();
|
|
const collaboratorInfo = generateCollaboratorInfo(currentUser2);
|
|
this.setLocalStateField("collaboratorInfo", collaboratorInfo);
|
|
}
|
|
};
|
|
var baseEqualityFieldChecks = {
|
|
collaboratorInfo: areCollaboratorInfosEqual
|
|
};
|
|
var BaseAwareness = class extends BaseAwarenessState {
|
|
equalityFieldChecks = baseEqualityFieldChecks;
|
|
};
|
|
|
|
// packages/core-data/build-module/awareness/block-lookup.mjs
|
|
var import_data3 = __toESM(require_data(), 1);
|
|
var import_block_editor = __toESM(require_block_editor(), 1);
|
|
function getBlockPathInYdoc(yType) {
|
|
const path = [];
|
|
let current = yType;
|
|
while (current) {
|
|
const parentArray = current.parent;
|
|
if (!parentArray || !(parentArray instanceof yjs_exports.Array)) {
|
|
return null;
|
|
}
|
|
let index = -1;
|
|
for (let i = 0; i < parentArray.length; i++) {
|
|
if (parentArray.get(i) === current) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
if (index === -1) {
|
|
return null;
|
|
}
|
|
path.unshift(index);
|
|
const grandparent = parentArray.parent;
|
|
if (grandparent instanceof yjs_exports.Map && grandparent.get("clientId") !== void 0) {
|
|
current = grandparent;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
function resolveBlockClientIdByPath(path) {
|
|
if (path.length === 0) {
|
|
return null;
|
|
}
|
|
const { getBlocks } = (0, import_data3.select)(import_block_editor.store);
|
|
const postContentBlocks = getPostContentBlocks(getBlocks(), getBlocks);
|
|
let blocks = postContentBlocks;
|
|
for (let i = 0; i < path.length; i++) {
|
|
const block = blocks[path[i]];
|
|
if (!block) {
|
|
return null;
|
|
}
|
|
if (i === path.length - 1) {
|
|
return block.clientId;
|
|
}
|
|
blocks = block.innerBlocks;
|
|
}
|
|
return null;
|
|
}
|
|
function getPostContentBlocks(rootBlocks, getBlocks) {
|
|
const postContentBlock = findBlockByName(rootBlocks, "core/post-content");
|
|
if (postContentBlock) {
|
|
return getBlocks(postContentBlock.clientId);
|
|
}
|
|
return rootBlocks;
|
|
}
|
|
function findBlockByName(blocks, name) {
|
|
for (const block of blocks) {
|
|
if (block.name === name) {
|
|
return block;
|
|
}
|
|
if (block.innerBlocks?.length) {
|
|
const found = findBlockByName(block.innerBlocks, name);
|
|
if (found) {
|
|
return found;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/crdt-utils.mjs
|
|
var import_rich_text = __toESM(require_rich_text(), 1);
|
|
|
|
// packages/core-data/build-module/lock-unlock.mjs
|
|
var import_private_apis3 = __toESM(require_private_apis(), 1);
|
|
var { lock: lock2, unlock: unlock2 } = (0, import_private_apis3.__dangerousOptInToUnstableAPIsOnlyForCoreModules)(
|
|
"I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.",
|
|
"@wordpress/core-data"
|
|
);
|
|
|
|
// packages/core-data/build-module/sync.mjs
|
|
var {
|
|
ConnectionErrorCode: ConnectionErrorCode2,
|
|
createSyncManager: createSyncManager2,
|
|
Delta: Delta2,
|
|
CRDT_DOC_META_PERSISTENCE_KEY: CRDT_DOC_META_PERSISTENCE_KEY2,
|
|
CRDT_RECORD_MAP_KEY: CRDT_RECORD_MAP_KEY2,
|
|
LOCAL_EDITOR_ORIGIN: LOCAL_EDITOR_ORIGIN2,
|
|
LOCAL_UNDO_IGNORED_ORIGIN: LOCAL_UNDO_IGNORED_ORIGIN2,
|
|
retrySyncConnection
|
|
} = unlock2(privateApis);
|
|
var syncManager;
|
|
function getSyncManager() {
|
|
if (syncManager) {
|
|
return syncManager;
|
|
}
|
|
syncManager = createSyncManager2();
|
|
return syncManager;
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/crdt-utils.mjs
|
|
function getRootMap(doc2, key) {
|
|
return doc2.getMap(key);
|
|
}
|
|
function createYMap(partial = {}) {
|
|
return new yjs_exports.Map(Object.entries(partial));
|
|
}
|
|
function isYMap(value) {
|
|
return value instanceof yjs_exports.Map;
|
|
}
|
|
function findBlockByClientIdInDoc(blockId, ydoc) {
|
|
const ymap = getRootMap(ydoc, CRDT_RECORD_MAP_KEY2);
|
|
const blocks = ymap.get("blocks");
|
|
if (!(blocks instanceof yjs_exports.Array)) {
|
|
return null;
|
|
}
|
|
return findBlockByClientIdInBlocks(blockId, blocks);
|
|
}
|
|
var MARKER_START = 57344;
|
|
function pickMarker(text2) {
|
|
const tryCount = 16;
|
|
for (let code = MARKER_START; code < MARKER_START + tryCount; code++) {
|
|
const candidate = String.fromCharCode(code);
|
|
if (!text2.includes(candidate)) {
|
|
return candidate;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function htmlIndexToRichTextOffset(html, htmlIndex) {
|
|
if (!html.includes("<") && !html.includes("&")) {
|
|
return htmlIndex;
|
|
}
|
|
const marker = pickMarker(html);
|
|
if (!marker) {
|
|
return htmlIndex;
|
|
}
|
|
const withMarker = html.slice(0, htmlIndex) + marker + html.slice(htmlIndex);
|
|
const value = (0, import_rich_text.create)({ html: withMarker });
|
|
const markerPos = value.text.indexOf(marker);
|
|
return markerPos === -1 ? htmlIndex : markerPos;
|
|
}
|
|
function richTextOffsetToHtmlIndex(html, richTextOffset) {
|
|
if (!html.includes("<") && !html.includes("&")) {
|
|
return richTextOffset;
|
|
}
|
|
const marker = pickMarker(html);
|
|
if (!marker) {
|
|
return richTextOffset;
|
|
}
|
|
const value = (0, import_rich_text.create)({ html });
|
|
const markerValue = (0, import_rich_text.create)({ text: marker });
|
|
if (value.formats[richTextOffset]) {
|
|
markerValue.formats[0] = value.formats[richTextOffset];
|
|
}
|
|
const withMarker = (0, import_rich_text.insert)(
|
|
value,
|
|
markerValue,
|
|
richTextOffset,
|
|
richTextOffset
|
|
);
|
|
const htmlWithMarker = (0, import_rich_text.toHTMLString)({ value: withMarker });
|
|
const markerIndex = htmlWithMarker.indexOf(marker);
|
|
return markerIndex === -1 ? richTextOffset : markerIndex;
|
|
}
|
|
function findBlockByClientIdInBlocks(blockId, blocks) {
|
|
for (const block of blocks) {
|
|
if (block.get("clientId") === blockId) {
|
|
return block;
|
|
}
|
|
const innerBlocks = block.get("innerBlocks");
|
|
if (innerBlocks && innerBlocks.length > 0) {
|
|
const innerBlock = findBlockByClientIdInBlocks(
|
|
blockId,
|
|
innerBlocks
|
|
);
|
|
if (innerBlock) {
|
|
return innerBlock;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/crdt-user-selections.mjs
|
|
var import_data4 = __toESM(require_data(), 1);
|
|
var import_block_editor2 = __toESM(require_block_editor(), 1);
|
|
var SelectionType = /* @__PURE__ */ ((SelectionType2) => {
|
|
SelectionType2["None"] = "none";
|
|
SelectionType2["Cursor"] = "cursor";
|
|
SelectionType2["SelectionInOneBlock"] = "selection-in-one-block";
|
|
SelectionType2["SelectionInMultipleBlocks"] = "selection-in-multiple-blocks";
|
|
SelectionType2["WholeBlock"] = "whole-block";
|
|
return SelectionType2;
|
|
})(SelectionType || {});
|
|
var SelectionDirection = /* @__PURE__ */ ((SelectionDirection2) => {
|
|
SelectionDirection2["Forward"] = "f";
|
|
SelectionDirection2["Backward"] = "b";
|
|
return SelectionDirection2;
|
|
})(SelectionDirection || {});
|
|
function getSelectionState(selectionStart, selectionEnd, yDoc, options) {
|
|
const { selectionDirection } = options ?? {};
|
|
const ymap = getRootMap(yDoc, CRDT_RECORD_MAP_KEY2);
|
|
const yBlocks = ymap.get("blocks");
|
|
const isSelectionEmpty = Object.keys(selectionStart).length === 0;
|
|
const noSelection = {
|
|
type: "none"
|
|
/* None */
|
|
};
|
|
if (isSelectionEmpty || !yBlocks) {
|
|
return noSelection;
|
|
}
|
|
const isSelectionInOneBlock = selectionStart.clientId === selectionEnd.clientId;
|
|
const isCursorOnly = isSelectionInOneBlock && selectionStart.offset === selectionEnd.offset;
|
|
const isSelectionAWholeBlock = isSelectionInOneBlock && selectionStart.offset === void 0 && selectionEnd.offset === void 0;
|
|
if (isSelectionAWholeBlock) {
|
|
const path = getBlockPathForLocalClientId(selectionStart.clientId);
|
|
const blockPosition = path ? createRelativePositionForBlockPath(path, yBlocks) : null;
|
|
if (!blockPosition) {
|
|
return noSelection;
|
|
}
|
|
return {
|
|
type: "whole-block",
|
|
blockPosition
|
|
};
|
|
} else if (isCursorOnly) {
|
|
const cursorPosition = getCursorPosition(selectionStart, yBlocks);
|
|
if (!cursorPosition) {
|
|
return noSelection;
|
|
}
|
|
return {
|
|
type: "cursor",
|
|
cursorPosition
|
|
};
|
|
} else if (isSelectionInOneBlock) {
|
|
const cursorStartPosition2 = getCursorPosition(
|
|
selectionStart,
|
|
yBlocks
|
|
);
|
|
const cursorEndPosition2 = getCursorPosition(selectionEnd, yBlocks);
|
|
if (!cursorStartPosition2 || !cursorEndPosition2) {
|
|
return noSelection;
|
|
}
|
|
return {
|
|
type: "selection-in-one-block",
|
|
cursorStartPosition: cursorStartPosition2,
|
|
cursorEndPosition: cursorEndPosition2,
|
|
selectionDirection
|
|
};
|
|
}
|
|
const cursorStartPosition = getCursorPosition(selectionStart, yBlocks);
|
|
const cursorEndPosition = getCursorPosition(selectionEnd, yBlocks);
|
|
if (!cursorStartPosition || !cursorEndPosition) {
|
|
return noSelection;
|
|
}
|
|
return {
|
|
type: "selection-in-multiple-blocks",
|
|
cursorStartPosition,
|
|
cursorEndPosition,
|
|
selectionDirection
|
|
};
|
|
}
|
|
function getCursorPosition(selection, blocks) {
|
|
const path = getBlockPathForLocalClientId(selection.clientId);
|
|
const block = path ? findBlockByPath(path, blocks) : null;
|
|
if (!block || !selection.attributeKey || void 0 === selection.offset) {
|
|
return null;
|
|
}
|
|
const attributes = block.get("attributes");
|
|
const currentYText = attributes?.get(selection.attributeKey);
|
|
if (!(currentYText instanceof yjs_exports.Text)) {
|
|
return null;
|
|
}
|
|
const relativePosition = yjs_exports.createRelativePositionFromTypeIndex(
|
|
currentYText,
|
|
richTextOffsetToHtmlIndex(currentYText.toString(), selection.offset)
|
|
);
|
|
return {
|
|
relativePosition,
|
|
absoluteOffset: selection.offset
|
|
};
|
|
}
|
|
function getBlockPathForLocalClientId(clientId) {
|
|
const { getBlockIndex, getBlockRootClientId, getBlockName } = (0, import_data4.select)(import_block_editor2.store);
|
|
const path = [];
|
|
let current = clientId;
|
|
while (current) {
|
|
const index = getBlockIndex(current);
|
|
if (index === -1) {
|
|
return null;
|
|
}
|
|
path.unshift(index);
|
|
const parent = getBlockRootClientId(current);
|
|
if (!parent) {
|
|
break;
|
|
}
|
|
const parentName = getBlockName(parent);
|
|
if (parentName === "core/post-content") {
|
|
break;
|
|
}
|
|
current = parent;
|
|
}
|
|
return path.length > 0 ? path : null;
|
|
}
|
|
function findBlockByPath(path, blocks) {
|
|
let currentBlocks = blocks;
|
|
for (let i = 0; i < path.length; i++) {
|
|
if (path[i] >= currentBlocks.length) {
|
|
return null;
|
|
}
|
|
const block = currentBlocks.get(path[i]);
|
|
if (!block) {
|
|
return null;
|
|
}
|
|
if (i === path.length - 1) {
|
|
return block;
|
|
}
|
|
currentBlocks = block.get("innerBlocks") ?? new yjs_exports.Array();
|
|
}
|
|
return null;
|
|
}
|
|
function createRelativePositionForBlockPath(path, blocks) {
|
|
let currentBlocks = blocks;
|
|
for (let i = 0; i < path.length; i++) {
|
|
if (path[i] >= currentBlocks.length) {
|
|
return null;
|
|
}
|
|
if (i === path.length - 1) {
|
|
return yjs_exports.createRelativePositionFromTypeIndex(
|
|
currentBlocks,
|
|
path[i]
|
|
);
|
|
}
|
|
const block = currentBlocks.get(path[i]);
|
|
currentBlocks = block?.get("innerBlocks") ?? new yjs_exports.Array();
|
|
}
|
|
return null;
|
|
}
|
|
function areSelectionsStatesEqual(selection1, selection2) {
|
|
if (selection1.type !== selection2.type) {
|
|
return false;
|
|
}
|
|
switch (selection1.type) {
|
|
case "none":
|
|
return true;
|
|
case "cursor":
|
|
return areCursorPositionsEqual(
|
|
selection1.cursorPosition,
|
|
selection2.cursorPosition
|
|
);
|
|
case "selection-in-one-block":
|
|
return areCursorPositionsEqual(
|
|
selection1.cursorStartPosition,
|
|
selection2.cursorStartPosition
|
|
) && areCursorPositionsEqual(
|
|
selection1.cursorEndPosition,
|
|
selection2.cursorEndPosition
|
|
) && selection1.selectionDirection === selection2.selectionDirection;
|
|
case "selection-in-multiple-blocks":
|
|
return areCursorPositionsEqual(
|
|
selection1.cursorStartPosition,
|
|
selection2.cursorStartPosition
|
|
) && areCursorPositionsEqual(
|
|
selection1.cursorEndPosition,
|
|
selection2.cursorEndPosition
|
|
) && selection1.selectionDirection === selection2.selectionDirection;
|
|
case "whole-block":
|
|
return yjs_exports.compareRelativePositions(
|
|
selection1.blockPosition,
|
|
selection2.blockPosition
|
|
);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
function areCursorPositionsEqual(cursorPosition1, cursorPosition2) {
|
|
const isRelativePositionEqual = yjs_exports.compareRelativePositions(
|
|
cursorPosition1.relativePosition,
|
|
cursorPosition2.relativePosition
|
|
);
|
|
const isAbsoluteOffsetEqual = cursorPosition1.absoluteOffset === cursorPosition2.absoluteOffset;
|
|
return isRelativePositionEqual && isAbsoluteOffsetEqual;
|
|
}
|
|
|
|
// packages/core-data/build-module/awareness/post-editor-awareness.mjs
|
|
var PostEditorAwareness = class extends BaseAwarenessState {
|
|
constructor(doc2, kind, name, postId) {
|
|
super(doc2);
|
|
this.kind = kind;
|
|
this.name = name;
|
|
this.postId = postId;
|
|
}
|
|
equalityFieldChecks = {
|
|
...baseEqualityFieldChecks,
|
|
editorState: this.areEditorStatesEqual
|
|
};
|
|
onSetUp() {
|
|
super.onSetUp();
|
|
this.subscribeToCollaboratorSelectionChanges();
|
|
}
|
|
/**
|
|
* Subscribe to collaborator selection changes and update the selection state.
|
|
*/
|
|
subscribeToCollaboratorSelectionChanges() {
|
|
const {
|
|
getSelectionStart,
|
|
getSelectionEnd,
|
|
getSelectedBlocksInitialCaretPosition
|
|
} = (0, import_data5.select)(import_block_editor3.store);
|
|
let selectionStart = getSelectionStart();
|
|
let selectionEnd = getSelectionEnd();
|
|
let localCursorTimeout = null;
|
|
let selectionBeforeDebounce = null;
|
|
(0, import_data5.subscribe)(() => {
|
|
const newSelectionStart = getSelectionStart();
|
|
const newSelectionEnd = getSelectionEnd();
|
|
if (newSelectionStart === selectionStart && newSelectionEnd === selectionEnd) {
|
|
return;
|
|
}
|
|
if (!selectionBeforeDebounce) {
|
|
selectionBeforeDebounce = {
|
|
start: selectionStart,
|
|
end: selectionEnd
|
|
};
|
|
}
|
|
selectionStart = newSelectionStart;
|
|
selectionEnd = newSelectionEnd;
|
|
const initialPosition = getSelectedBlocksInitialCaretPosition();
|
|
void this.updateSelectionInEntityRecord(
|
|
selectionStart,
|
|
selectionEnd,
|
|
initialPosition
|
|
);
|
|
if (localCursorTimeout) {
|
|
clearTimeout(localCursorTimeout);
|
|
}
|
|
localCursorTimeout = setTimeout(() => {
|
|
const selectionStateOptions = {};
|
|
if (selectionBeforeDebounce) {
|
|
selectionStateOptions.selectionDirection = detectSelectionDirection(
|
|
selectionBeforeDebounce.start,
|
|
selectionBeforeDebounce.end,
|
|
selectionStart,
|
|
selectionEnd
|
|
);
|
|
selectionBeforeDebounce = null;
|
|
}
|
|
const selectionState = getSelectionState(
|
|
selectionStart,
|
|
selectionEnd,
|
|
this.doc,
|
|
selectionStateOptions
|
|
);
|
|
this.setThrottledLocalStateField(
|
|
"editorState",
|
|
{ selection: selectionState },
|
|
AWARENESS_CURSOR_UPDATE_THROTTLE_IN_MS
|
|
);
|
|
}, LOCAL_CURSOR_UPDATE_DEBOUNCE_IN_MS);
|
|
});
|
|
}
|
|
/**
|
|
* Update the entity record with the current collaborator's selection.
|
|
*
|
|
* @param selectionStart - The start position of the selection.
|
|
* @param selectionEnd - The end position of the selection.
|
|
* @param initialPosition - The initial position of the selection.
|
|
*/
|
|
async updateSelectionInEntityRecord(selectionStart, selectionEnd, initialPosition) {
|
|
const edits = {
|
|
selection: { selectionStart, selectionEnd, initialPosition }
|
|
};
|
|
const options = {
|
|
undoIgnore: true
|
|
};
|
|
(0, import_data5.dispatch)(STORE_NAME).editEntityRecord(
|
|
this.kind,
|
|
this.name,
|
|
this.postId,
|
|
edits,
|
|
options
|
|
);
|
|
}
|
|
/**
|
|
* Check if two editor states are equal.
|
|
*
|
|
* @param state1 - The first editor state.
|
|
* @param state2 - The second editor state.
|
|
* @return True if the editor states are equal, false otherwise.
|
|
*/
|
|
areEditorStatesEqual(state1, state2) {
|
|
if (!state1 || !state2) {
|
|
return state1 === state2;
|
|
}
|
|
if (!state1.selection || !state2.selection) {
|
|
return state1.selection === state2.selection;
|
|
}
|
|
return areSelectionsStatesEqual(state1.selection, state2.selection);
|
|
}
|
|
/**
|
|
* Resolve a selection state to a text index and block client ID.
|
|
*
|
|
* For text-based selections, navigates up from the resolved Y.Text via
|
|
* AbstractType.parent to find the containing block, then resolves the
|
|
* local clientId via the block's tree path.
|
|
* For WholeBlock selections, resolves the block's relative position and
|
|
* then finds the local clientId via tree path.
|
|
*
|
|
* Tree-path resolution is used instead of reading the clientId directly
|
|
* from the Yjs block because the local block-editor store may use different
|
|
* clientIds (e.g. in "Show Template" mode where blocks are cloned).
|
|
*
|
|
* @param selection - The selection state.
|
|
* @return The rich-text offset and block client ID, or nulls if not resolvable.
|
|
*/
|
|
convertSelectionStateToAbsolute(selection) {
|
|
if (selection.type === SelectionType.None) {
|
|
return { richTextOffset: null, localClientId: null };
|
|
}
|
|
if (selection.type === SelectionType.WholeBlock) {
|
|
const absolutePos = yjs_exports.createAbsolutePositionFromRelativePosition(
|
|
selection.blockPosition,
|
|
this.doc
|
|
);
|
|
let localClientId2 = null;
|
|
if (absolutePos && absolutePos.type instanceof yjs_exports.Array) {
|
|
const parentArray = absolutePos.type;
|
|
const block = parentArray.get(absolutePos.index);
|
|
if (block instanceof yjs_exports.Map) {
|
|
const path2 = getBlockPathInYdoc(block);
|
|
localClientId2 = path2 ? resolveBlockClientIdByPath(path2) : null;
|
|
}
|
|
}
|
|
return { richTextOffset: null, localClientId: localClientId2 };
|
|
}
|
|
const cursorPos = "cursorPosition" in selection ? selection.cursorPosition : selection.cursorStartPosition;
|
|
const absolutePosition = yjs_exports.createAbsolutePositionFromRelativePosition(
|
|
cursorPos.relativePosition,
|
|
this.doc
|
|
);
|
|
if (!absolutePosition) {
|
|
return { richTextOffset: null, localClientId: null };
|
|
}
|
|
const yType = absolutePosition.type.parent?.parent;
|
|
const path = yType instanceof yjs_exports.Map ? getBlockPathInYdoc(yType) : null;
|
|
const localClientId = path ? resolveBlockClientIdByPath(path) : null;
|
|
return {
|
|
richTextOffset: htmlIndexToRichTextOffset(
|
|
absolutePosition.type.toString(),
|
|
absolutePosition.index
|
|
),
|
|
localClientId
|
|
};
|
|
}
|
|
/**
|
|
* Type guard to check if a struct is a Y.Item (not Y.GC)
|
|
* @param struct - The struct to check.
|
|
* @return True if the struct is a Y.Item, false otherwise.
|
|
*/
|
|
isYItem(struct) {
|
|
return "content" in struct;
|
|
}
|
|
/**
|
|
* Get data for debugging, using the awareness state.
|
|
*
|
|
* @return {YDocDebugData} The debug data.
|
|
*/
|
|
getDebugData() {
|
|
const ydoc = this.doc;
|
|
const docData = Object.fromEntries(
|
|
Array.from(ydoc.share, ([key, value]) => [
|
|
key,
|
|
value.toJSON()
|
|
])
|
|
);
|
|
const collaboratorMapData = new Map(
|
|
Array.from(this.getSeenStates().entries()).map(
|
|
([clientId, collaboratorState]) => [
|
|
String(clientId),
|
|
{
|
|
name: collaboratorState.collaboratorInfo.name,
|
|
wpUserId: collaboratorState.collaboratorInfo.id
|
|
}
|
|
]
|
|
)
|
|
);
|
|
const serializableClientItems = {};
|
|
ydoc.store.clients.forEach((structs, clientId) => {
|
|
const items2 = structs.filter(this.isYItem);
|
|
serializableClientItems[clientId] = items2.map((item) => {
|
|
const { left, right, ...rest } = item;
|
|
return {
|
|
...rest,
|
|
left: left ? {
|
|
id: left.id,
|
|
length: left.length,
|
|
origin: left.origin,
|
|
content: left.content
|
|
} : null,
|
|
right: right ? {
|
|
id: right.id,
|
|
length: right.length,
|
|
origin: right.origin,
|
|
content: right.content
|
|
} : null
|
|
};
|
|
});
|
|
});
|
|
return {
|
|
doc: docData,
|
|
clients: serializableClientItems,
|
|
collaboratorMap: Object.fromEntries(collaboratorMapData)
|
|
};
|
|
}
|
|
};
|
|
function detectSelectionDirection(prevStart, prevEnd, newStart, newEnd) {
|
|
const startMoved = !areBlockSelectionsEqual(prevStart, newStart);
|
|
const endMoved = !areBlockSelectionsEqual(prevEnd, newEnd);
|
|
if (startMoved && !endMoved) {
|
|
return SelectionDirection.Backward;
|
|
}
|
|
return SelectionDirection.Forward;
|
|
}
|
|
function areBlockSelectionsEqual(a, b) {
|
|
return a.clientId === b.clientId && a.attributeKey === b.attributeKey && a.offset === b.offset;
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/crdt.mjs
|
|
var import_es65 = __toESM(require_es6(), 1);
|
|
var import_blocks3 = __toESM(require_blocks(), 1);
|
|
|
|
// node_modules/uuid/dist/esm-browser/rng.js
|
|
var getRandomValues2;
|
|
var rnds8 = new Uint8Array(16);
|
|
function rng() {
|
|
if (!getRandomValues2) {
|
|
getRandomValues2 = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
|
|
if (!getRandomValues2) {
|
|
throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
|
|
}
|
|
}
|
|
return getRandomValues2(rnds8);
|
|
}
|
|
|
|
// node_modules/uuid/dist/esm-browser/stringify.js
|
|
var byteToHex = [];
|
|
for (let i = 0; i < 256; ++i) {
|
|
byteToHex.push((i + 256).toString(16).slice(1));
|
|
}
|
|
function unsafeStringify(arr, offset = 0) {
|
|
return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
|
|
}
|
|
|
|
// node_modules/uuid/dist/esm-browser/native.js
|
|
var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
|
|
var native_default = {
|
|
randomUUID
|
|
};
|
|
|
|
// node_modules/uuid/dist/esm-browser/v4.js
|
|
function v4(options, buf, offset) {
|
|
if (native_default.randomUUID && !buf && !options) {
|
|
return native_default.randomUUID();
|
|
}
|
|
options = options || {};
|
|
const rnds = options.random || (options.rng || rng)();
|
|
rnds[6] = rnds[6] & 15 | 64;
|
|
rnds[8] = rnds[8] & 63 | 128;
|
|
if (buf) {
|
|
offset = offset || 0;
|
|
for (let i = 0; i < 16; ++i) {
|
|
buf[offset + i] = rnds[i];
|
|
}
|
|
return buf;
|
|
}
|
|
return unsafeStringify(rnds);
|
|
}
|
|
var v4_default = v4;
|
|
|
|
// packages/core-data/build-module/utils/crdt-blocks.mjs
|
|
var import_es64 = __toESM(require_es6(), 1);
|
|
var import_blocks = __toESM(require_blocks(), 1);
|
|
var import_rich_text3 = __toESM(require_rich_text(), 1);
|
|
|
|
// packages/core-data/build-module/utils/crdt-text.mjs
|
|
var import_rich_text2 = __toESM(require_rich_text(), 1);
|
|
var RICH_TEXT_CACHE_MAX_SIZE = 500;
|
|
function createRichTextDataCache(maxSize) {
|
|
const cache3 = /* @__PURE__ */ new Map();
|
|
return function(value) {
|
|
const cached = cache3.get(value);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
const result = import_rich_text2.RichTextData.fromHTMLString(value);
|
|
if (cache3.size >= maxSize) {
|
|
cache3.delete(cache3.keys().next().value);
|
|
}
|
|
cache3.set(value, result);
|
|
return result;
|
|
};
|
|
}
|
|
var getCachedRichTextData = createRichTextDataCache(
|
|
RICH_TEXT_CACHE_MAX_SIZE
|
|
);
|
|
|
|
// packages/core-data/build-module/utils/crdt-blocks.mjs
|
|
var serializableBlocksCache = /* @__PURE__ */ new WeakMap();
|
|
function serializeAttributeValue(value) {
|
|
if (value instanceof import_rich_text3.RichTextData) {
|
|
return value.valueOf();
|
|
}
|
|
if (Array.isArray(value)) {
|
|
return value.map(serializeAttributeValue);
|
|
}
|
|
if (value && typeof value === "object") {
|
|
const result = {};
|
|
for (const [k, v] of Object.entries(value)) {
|
|
result[k] = serializeAttributeValue(v);
|
|
}
|
|
return result;
|
|
}
|
|
return value;
|
|
}
|
|
function makeBlockAttributesSerializable(blockName, attributes) {
|
|
const newAttributes = { ...attributes };
|
|
for (const [key, value] of Object.entries(attributes)) {
|
|
if (isLocalAttribute(blockName, key)) {
|
|
delete newAttributes[key];
|
|
continue;
|
|
}
|
|
newAttributes[key] = serializeAttributeValue(value);
|
|
}
|
|
return newAttributes;
|
|
}
|
|
function makeBlocksSerializable(blocks) {
|
|
return blocks.map((block) => {
|
|
const { name, innerBlocks, attributes, ...rest } = block;
|
|
delete rest.validationIssues;
|
|
return {
|
|
...rest,
|
|
name,
|
|
attributes: makeBlockAttributesSerializable(name, attributes),
|
|
innerBlocks: makeBlocksSerializable(innerBlocks)
|
|
};
|
|
});
|
|
}
|
|
function deserializeAttributeValue(schema, value) {
|
|
if (schema?.type === "rich-text" && typeof value === "string") {
|
|
return getCachedRichTextData(value);
|
|
}
|
|
if (Array.isArray(value)) {
|
|
return value.map(
|
|
(item) => deserializeAttributeValue(schema, item)
|
|
);
|
|
}
|
|
if (value && typeof value === "object") {
|
|
const result = {};
|
|
for (const [key, innerValue] of Object.entries(
|
|
value
|
|
)) {
|
|
result[key] = deserializeAttributeValue(
|
|
schema?.query?.[key],
|
|
innerValue
|
|
);
|
|
}
|
|
return result;
|
|
}
|
|
return value;
|
|
}
|
|
function deserializeBlockAttributes(blocks) {
|
|
return blocks.map((block) => {
|
|
const { name, innerBlocks, attributes, ...rest } = block;
|
|
const newAttributes = { ...attributes };
|
|
for (const [key, value] of Object.entries(attributes)) {
|
|
const schema = getBlockAttributeType(name, key);
|
|
if (schema) {
|
|
newAttributes[key] = deserializeAttributeValue(
|
|
schema,
|
|
value
|
|
);
|
|
}
|
|
}
|
|
return {
|
|
...rest,
|
|
name,
|
|
attributes: newAttributes,
|
|
innerBlocks: deserializeBlockAttributes(innerBlocks ?? [])
|
|
};
|
|
});
|
|
}
|
|
function areBlocksEqual(gblock, yblock) {
|
|
const yblockAsJson = yblock.toJSON();
|
|
const overwrites = {
|
|
innerBlocks: null,
|
|
clientId: null
|
|
};
|
|
const res = (0, import_es64.default)(
|
|
Object.assign({}, gblock, overwrites),
|
|
Object.assign({}, yblockAsJson, overwrites)
|
|
);
|
|
const inners = gblock.innerBlocks || [];
|
|
const yinners = yblock.get("innerBlocks");
|
|
return res && inners.length === yinners?.length && inners.every(
|
|
(block, i) => areBlocksEqual(block, yinners.get(i))
|
|
);
|
|
}
|
|
function createNewYAttributeMap(blockName, attributes) {
|
|
return new yjs_exports.Map(
|
|
Object.entries(attributes).map(
|
|
([attributeName, attributeValue]) => {
|
|
return [
|
|
attributeName,
|
|
createNewYAttributeValue(
|
|
blockName,
|
|
attributeName,
|
|
attributeValue
|
|
)
|
|
];
|
|
}
|
|
)
|
|
);
|
|
}
|
|
function createNewYAttributeValue(blockName, attributeName, attributeValue) {
|
|
const isRichText = isRichTextAttribute(blockName, attributeName);
|
|
if (isRichText) {
|
|
return new yjs_exports.Text(attributeValue?.toString() ?? "");
|
|
}
|
|
return attributeValue;
|
|
}
|
|
function createNewYBlock(block) {
|
|
return createYMap(
|
|
Object.fromEntries(
|
|
Object.entries(block).map(([key, value]) => {
|
|
switch (key) {
|
|
case "attributes": {
|
|
return [
|
|
key,
|
|
createNewYAttributeMap(block.name, value)
|
|
];
|
|
}
|
|
case "innerBlocks": {
|
|
const innerBlocks = new yjs_exports.Array();
|
|
if (!Array.isArray(value)) {
|
|
return [key, innerBlocks];
|
|
}
|
|
innerBlocks.insert(
|
|
0,
|
|
value.map(
|
|
(innerBlock) => createNewYBlock(innerBlock)
|
|
)
|
|
);
|
|
return [key, innerBlocks];
|
|
}
|
|
default:
|
|
return [key, value];
|
|
}
|
|
})
|
|
)
|
|
);
|
|
}
|
|
function mergeCrdtBlocks(yblocks, incomingBlocks, cursorPosition) {
|
|
if (!serializableBlocksCache.has(incomingBlocks)) {
|
|
serializableBlocksCache.set(
|
|
incomingBlocks,
|
|
makeBlocksSerializable(incomingBlocks)
|
|
);
|
|
}
|
|
const blocksToSync = serializableBlocksCache.get(incomingBlocks) ?? [];
|
|
const numOfCommonEntries = Math.min(
|
|
blocksToSync.length ?? 0,
|
|
yblocks.length
|
|
);
|
|
let left = 0;
|
|
let right = 0;
|
|
for (; left < numOfCommonEntries && areBlocksEqual(blocksToSync[left], yblocks.get(left)); left++) {
|
|
}
|
|
for (; right < numOfCommonEntries - left && areBlocksEqual(
|
|
blocksToSync[blocksToSync.length - right - 1],
|
|
yblocks.get(yblocks.length - right - 1)
|
|
); right++) {
|
|
}
|
|
const numOfUpdatesNeeded = numOfCommonEntries - left - right;
|
|
const numOfInsertionsNeeded = Math.max(
|
|
0,
|
|
blocksToSync.length - yblocks.length
|
|
);
|
|
const numOfDeletionsNeeded = Math.max(
|
|
0,
|
|
yblocks.length - blocksToSync.length
|
|
);
|
|
for (let i = 0; i < numOfUpdatesNeeded; i++, left++) {
|
|
const block = blocksToSync[left];
|
|
const yblock = yblocks.get(left);
|
|
Object.entries(block).forEach(([key, value]) => {
|
|
switch (key) {
|
|
case "attributes": {
|
|
const currentAttributes = yblock.get(key);
|
|
if (!currentAttributes) {
|
|
yblock.set(
|
|
key,
|
|
createNewYAttributeMap(block.name, value)
|
|
);
|
|
break;
|
|
}
|
|
Object.entries(value).forEach(
|
|
([attributeName, attributeValue]) => {
|
|
const currentAttribute = currentAttributes?.get(attributeName);
|
|
const isExpectedType = isExpectedAttributeType(
|
|
block.name,
|
|
attributeName,
|
|
currentAttribute
|
|
);
|
|
const isAttributeChanged = !isExpectedType || !(0, import_es64.default)(
|
|
currentAttribute,
|
|
attributeValue
|
|
);
|
|
if (isAttributeChanged) {
|
|
updateYBlockAttribute(
|
|
block.name,
|
|
attributeName,
|
|
attributeValue,
|
|
currentAttributes,
|
|
cursorPosition
|
|
);
|
|
}
|
|
}
|
|
);
|
|
currentAttributes.forEach(
|
|
(_attrValue, attrName) => {
|
|
if (!value.hasOwnProperty(attrName)) {
|
|
currentAttributes.delete(attrName);
|
|
}
|
|
}
|
|
);
|
|
break;
|
|
}
|
|
case "innerBlocks": {
|
|
let yInnerBlocks = yblock.get(key);
|
|
if (!(yInnerBlocks instanceof yjs_exports.Array)) {
|
|
yInnerBlocks = new yjs_exports.Array();
|
|
yblock.set(key, yInnerBlocks);
|
|
}
|
|
mergeCrdtBlocks(
|
|
yInnerBlocks,
|
|
value ?? [],
|
|
cursorPosition
|
|
);
|
|
break;
|
|
}
|
|
default:
|
|
if (!(0, import_es64.default)(block[key], yblock.get(key))) {
|
|
yblock.set(key, value);
|
|
}
|
|
}
|
|
});
|
|
yblock.forEach((_v, k) => {
|
|
if (!block.hasOwnProperty(k)) {
|
|
yblock.delete(k);
|
|
}
|
|
});
|
|
}
|
|
yblocks.delete(left, numOfDeletionsNeeded);
|
|
for (let i = 0; i < numOfInsertionsNeeded; i++, left++) {
|
|
const newBlock = [createNewYBlock(blocksToSync[left])];
|
|
yblocks.insert(left, newBlock);
|
|
}
|
|
const knownClientIds = /* @__PURE__ */ new Set();
|
|
for (let j = 0; j < yblocks.length; j++) {
|
|
const yblock = yblocks.get(j);
|
|
let clientId = yblock.get("clientId");
|
|
if (!clientId) {
|
|
continue;
|
|
}
|
|
if (knownClientIds.has(clientId)) {
|
|
clientId = v4_default();
|
|
yblock.set("clientId", clientId);
|
|
}
|
|
knownClientIds.add(clientId);
|
|
}
|
|
}
|
|
function updateYBlockAttribute(blockName, attributeName, attributeValue, currentAttributes, cursorPosition) {
|
|
const isRichText = isRichTextAttribute(blockName, attributeName);
|
|
const currentAttribute = currentAttributes.get(attributeName);
|
|
if (isRichText && "string" === typeof attributeValue && currentAttributes.has(attributeName) && currentAttribute instanceof yjs_exports.Text) {
|
|
mergeRichTextUpdate(currentAttribute, attributeValue, cursorPosition);
|
|
} else {
|
|
currentAttributes.set(
|
|
attributeName,
|
|
createNewYAttributeValue(blockName, attributeName, attributeValue)
|
|
);
|
|
}
|
|
}
|
|
var cachedBlockAttributeTypes;
|
|
function getBlockAttributeType(blockName, attributeName) {
|
|
if (!cachedBlockAttributeTypes) {
|
|
cachedBlockAttributeTypes = /* @__PURE__ */ new Map();
|
|
for (const blockType of (0, import_blocks.getBlockTypes)()) {
|
|
cachedBlockAttributeTypes.set(
|
|
blockType.name,
|
|
new Map(
|
|
Object.entries(blockType.attributes ?? {}).map(
|
|
([name, definition]) => {
|
|
const { role, type, query } = definition;
|
|
return [name, { role, type, query }];
|
|
}
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
return cachedBlockAttributeTypes.get(blockName)?.get(attributeName);
|
|
}
|
|
function isExpectedAttributeType(blockName, attributeName, attributeValue) {
|
|
const expectedAttributeType = getBlockAttributeType(
|
|
blockName,
|
|
attributeName
|
|
)?.type;
|
|
if (expectedAttributeType === "rich-text") {
|
|
return attributeValue instanceof yjs_exports.Text;
|
|
}
|
|
if (expectedAttributeType === "string") {
|
|
return typeof attributeValue === "string";
|
|
}
|
|
return true;
|
|
}
|
|
function isLocalAttribute(blockName, attributeName) {
|
|
return "local" === getBlockAttributeType(blockName, attributeName)?.role;
|
|
}
|
|
function isRichTextAttribute(blockName, attributeName) {
|
|
return "rich-text" === getBlockAttributeType(blockName, attributeName)?.type;
|
|
}
|
|
var localDoc;
|
|
function mergeRichTextUpdate(blockYText, updatedValue, cursorPosition = null) {
|
|
if (!localDoc) {
|
|
localDoc = new yjs_exports.Doc();
|
|
}
|
|
const localYText = localDoc.getText("temporary-text");
|
|
localYText.delete(0, localYText.length);
|
|
localYText.insert(0, updatedValue);
|
|
const currentValueAsDelta = new Delta2(blockYText.toDelta());
|
|
const updatedValueAsDelta = new Delta2(localYText.toDelta());
|
|
const deltaDiff = currentValueAsDelta.diffWithCursor(
|
|
updatedValueAsDelta,
|
|
cursorPosition
|
|
);
|
|
blockYText.applyDelta(deltaDiff.ops);
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/crdt-selection.mjs
|
|
var import_data6 = __toESM(require_data(), 1);
|
|
var import_block_editor4 = __toESM(require_block_editor(), 1);
|
|
var import_blocks2 = __toESM(require_blocks(), 1);
|
|
|
|
// packages/core-data/build-module/utils/block-selection-history.mjs
|
|
var SELECTION_HISTORY_DEFAULT_SIZE = 5;
|
|
var YSelectionType = /* @__PURE__ */ ((YSelectionType2) => {
|
|
YSelectionType2["RelativeSelection"] = "RelativeSelection";
|
|
YSelectionType2["BlockSelection"] = "BlockSelection";
|
|
return YSelectionType2;
|
|
})(YSelectionType || {});
|
|
function createBlockSelectionHistory(ydoc, historySize = SELECTION_HISTORY_DEFAULT_SIZE) {
|
|
let history = [];
|
|
const getSelectionHistory2 = () => {
|
|
return history.slice(0);
|
|
};
|
|
const updateSelection = (newSelection) => {
|
|
if (!newSelection?.selectionStart?.clientId || !newSelection?.selectionEnd?.clientId) {
|
|
return;
|
|
}
|
|
const { selectionStart, selectionEnd } = newSelection;
|
|
const start = convertWPBlockSelectionToSelection(
|
|
selectionStart,
|
|
ydoc
|
|
);
|
|
const end = convertWPBlockSelectionToSelection(selectionEnd, ydoc);
|
|
addToHistory({ start, end });
|
|
};
|
|
const addToHistory = (yFullSelection) => {
|
|
const startClientId = yFullSelection.start.clientId;
|
|
const endClientId = yFullSelection.end.clientId;
|
|
history = history.filter((entry) => {
|
|
const isSameBlockCombination = entry.start.clientId === startClientId && entry.end.clientId === endClientId;
|
|
return !isSameBlockCombination;
|
|
});
|
|
history.unshift(yFullSelection);
|
|
if (history.length > historySize + 1) {
|
|
history = history.slice(0, historySize + 1);
|
|
}
|
|
};
|
|
return {
|
|
getSelectionHistory: getSelectionHistory2,
|
|
updateSelection
|
|
};
|
|
}
|
|
function convertWPBlockSelectionToSelection(selection, ydoc) {
|
|
const clientId = selection.clientId;
|
|
const block = findBlockByClientIdInDoc(clientId, ydoc);
|
|
const attributes = block?.get("attributes");
|
|
const attributeKey = selection.attributeKey;
|
|
const changedYText = attributeKey ? attributes?.get(attributeKey) : void 0;
|
|
const isYText = changedYText instanceof yjs_exports.Text;
|
|
const isFullyDefinedSelection = attributeKey && clientId;
|
|
if (!isYText || !isFullyDefinedSelection) {
|
|
return {
|
|
type: "BlockSelection",
|
|
clientId
|
|
};
|
|
}
|
|
const offset = selection.offset ?? 0;
|
|
const relativePosition = yjs_exports.createRelativePositionFromTypeIndex(
|
|
changedYText,
|
|
richTextOffsetToHtmlIndex(changedYText.toString(), offset)
|
|
);
|
|
return {
|
|
type: "RelativeSelection",
|
|
attributeKey,
|
|
relativePosition,
|
|
clientId,
|
|
offset
|
|
};
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/crdt-selection.mjs
|
|
var selectionHistoryMap = /* @__PURE__ */ new WeakMap();
|
|
function getBlockSelectionHistory(ydoc) {
|
|
let history = selectionHistoryMap.get(ydoc);
|
|
if (!history) {
|
|
history = createBlockSelectionHistory(ydoc);
|
|
selectionHistoryMap.set(ydoc, history);
|
|
}
|
|
return history;
|
|
}
|
|
function getSelectionHistory(ydoc) {
|
|
return getBlockSelectionHistory(ydoc).getSelectionHistory();
|
|
}
|
|
function updateSelectionHistory(ydoc, wpSelection) {
|
|
return getBlockSelectionHistory(ydoc).updateSelection(wpSelection);
|
|
}
|
|
function convertYSelectionToBlockSelection(ySelection, ydoc) {
|
|
if (ySelection.type === YSelectionType.RelativeSelection) {
|
|
const { relativePosition, attributeKey, clientId } = ySelection;
|
|
const absolutePosition = yjs_exports.createAbsolutePositionFromRelativePosition(
|
|
relativePosition,
|
|
ydoc
|
|
);
|
|
if (absolutePosition) {
|
|
return {
|
|
clientId,
|
|
attributeKey,
|
|
offset: htmlIndexToRichTextOffset(
|
|
absolutePosition.type.toString(),
|
|
absolutePosition.index
|
|
)
|
|
};
|
|
}
|
|
} else if (ySelection.type === YSelectionType.BlockSelection) {
|
|
return {
|
|
clientId: ySelection.clientId,
|
|
attributeKey: void 0,
|
|
offset: void 0
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
function convertYFullSelectionToWPSelection(yFullSelection, ydoc) {
|
|
const { start, end } = yFullSelection;
|
|
const startBlock = findBlockByClientIdInDoc(start.clientId, ydoc);
|
|
const endBlock = findBlockByClientIdInDoc(end.clientId, ydoc);
|
|
if (!startBlock || !endBlock) {
|
|
return null;
|
|
}
|
|
const startBlockSelection = convertYSelectionToBlockSelection(
|
|
start,
|
|
ydoc
|
|
);
|
|
const endBlockSelection = convertYSelectionToBlockSelection(end, ydoc);
|
|
if (startBlockSelection === null || endBlockSelection === null) {
|
|
return null;
|
|
}
|
|
return {
|
|
selectionStart: startBlockSelection,
|
|
selectionEnd: endBlockSelection
|
|
};
|
|
}
|
|
function findSelectionFromHistory(ydoc, selectionHistory) {
|
|
for (const positionToTry of selectionHistory) {
|
|
const result = convertYFullSelectionToWPSelection(
|
|
positionToTry,
|
|
ydoc
|
|
);
|
|
if (result !== null) {
|
|
return result;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function restoreSelection(selectionHistory, ydoc) {
|
|
const selectionToRestore = findSelectionFromHistory(
|
|
ydoc,
|
|
selectionHistory
|
|
);
|
|
if (selectionToRestore === null) {
|
|
return;
|
|
}
|
|
const { getBlock } = (0, import_data6.select)(import_block_editor4.store);
|
|
const { resetSelection } = (0, import_data6.dispatch)(import_block_editor4.store);
|
|
const { selectionStart, selectionEnd } = selectionToRestore;
|
|
const isSelectionInSameBlock = selectionStart.clientId === selectionEnd.clientId;
|
|
if (isSelectionInSameBlock) {
|
|
const block = getBlock(selectionStart.clientId);
|
|
const isBlockEmpty = block && (0, import_blocks2.isUnmodifiedBlock)(block);
|
|
const isBeginningOfEmptyBlock = 0 === selectionStart.offset && 0 === selectionEnd.offset && isBlockEmpty && !selectionStart.attributeKey && !selectionEnd.attributeKey;
|
|
if (isBeginningOfEmptyBlock) {
|
|
const selectionStartWithoutOffset = {
|
|
clientId: selectionStart.clientId
|
|
};
|
|
const selectionEndWithoutOffset = {
|
|
clientId: selectionEnd.clientId
|
|
};
|
|
resetSelection(
|
|
selectionStartWithoutOffset,
|
|
selectionEndWithoutOffset,
|
|
0
|
|
);
|
|
} else {
|
|
resetSelection(selectionStart, selectionEnd, 0);
|
|
}
|
|
} else {
|
|
resetSelection(selectionEnd, selectionEnd, 0);
|
|
}
|
|
}
|
|
function getShiftedSelection(ydoc, selectionHistory) {
|
|
if (selectionHistory.length === 0) {
|
|
return null;
|
|
}
|
|
const { start, end } = selectionHistory[0];
|
|
if (start.type === YSelectionType.BlockSelection || end.type === YSelectionType.BlockSelection) {
|
|
return null;
|
|
}
|
|
const selectionStart = convertYSelectionToBlockSelection(start, ydoc);
|
|
const selectionEnd = convertYSelectionToBlockSelection(end, ydoc);
|
|
if (!selectionStart || !selectionEnd) {
|
|
return null;
|
|
}
|
|
const startShifted = selectionStart.offset !== start.offset;
|
|
const endShifted = selectionEnd.offset !== end.offset;
|
|
if (!startShifted && !endShifted) {
|
|
return null;
|
|
}
|
|
return { selectionStart, selectionEnd };
|
|
}
|
|
|
|
// packages/core-data/build-module/utils/crdt.mjs
|
|
var POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE = "_crdt_document";
|
|
var disallowedPostMetaKeys = /* @__PURE__ */ new Set([
|
|
POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE
|
|
]);
|
|
function defaultApplyChangesToCRDTDoc(ydoc, changes) {
|
|
const ymap = getRootMap(ydoc, CRDT_RECORD_MAP_KEY2);
|
|
Object.entries(changes).forEach(([key, newValue]) => {
|
|
if ("function" === typeof newValue) {
|
|
return;
|
|
}
|
|
switch (key) {
|
|
// Add support for additional data types here.
|
|
default: {
|
|
const currentValue = ymap.get(key);
|
|
updateMapValue(ymap, key, currentValue, newValue);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function applyPostChangesToCRDTDoc(ydoc, changes, syncedProperties) {
|
|
const ymap = getRootMap(ydoc, CRDT_RECORD_MAP_KEY2);
|
|
Object.keys(changes).forEach((key) => {
|
|
if (!syncedProperties.has(key)) {
|
|
return;
|
|
}
|
|
const newValue = changes[key];
|
|
if ("function" === typeof newValue) {
|
|
return;
|
|
}
|
|
switch (key) {
|
|
case "blocks": {
|
|
if (!newValue) {
|
|
ymap.set(key, void 0);
|
|
break;
|
|
}
|
|
let currentBlocks = ymap.get(key);
|
|
if (!(currentBlocks instanceof yjs_exports.Array)) {
|
|
currentBlocks = new yjs_exports.Array();
|
|
ymap.set(key, currentBlocks);
|
|
}
|
|
const cursorPosition = changes.selection?.selectionStart?.offset ?? null;
|
|
mergeCrdtBlocks(currentBlocks, newValue, cursorPosition);
|
|
break;
|
|
}
|
|
case "content":
|
|
case "excerpt":
|
|
case "title": {
|
|
const currentValue = ymap.get(key);
|
|
let rawValue = getRawValue(newValue);
|
|
if (key === "title" && !currentValue?.toString() && "Auto Draft" === rawValue) {
|
|
rawValue = "";
|
|
}
|
|
if (currentValue instanceof yjs_exports.Text) {
|
|
mergeRichTextUpdate(currentValue, rawValue ?? "");
|
|
} else {
|
|
const newYText = new yjs_exports.Text(rawValue ?? "");
|
|
ymap.set(key, newYText);
|
|
}
|
|
break;
|
|
}
|
|
// "Meta" is overloaded term; here, it refers to post meta.
|
|
case "meta": {
|
|
let metaMap = ymap.get("meta");
|
|
if (!isYMap(metaMap)) {
|
|
metaMap = createYMap();
|
|
ymap.set("meta", metaMap);
|
|
}
|
|
Object.entries(newValue ?? {}).forEach(
|
|
([metaKey, metaValue]) => {
|
|
if (disallowedPostMetaKeys.has(metaKey)) {
|
|
return;
|
|
}
|
|
updateMapValue(
|
|
metaMap,
|
|
metaKey,
|
|
metaMap.get(metaKey),
|
|
// current value in CRDT
|
|
metaValue
|
|
// new value from changes
|
|
);
|
|
}
|
|
);
|
|
break;
|
|
}
|
|
case "slug": {
|
|
if (!newValue) {
|
|
break;
|
|
}
|
|
const currentValue = ymap.get(key);
|
|
updateMapValue(ymap, key, currentValue, newValue);
|
|
break;
|
|
}
|
|
// Add support for additional properties here.
|
|
default: {
|
|
const currentValue = ymap.get(key);
|
|
updateMapValue(ymap, key, currentValue, newValue);
|
|
}
|
|
}
|
|
});
|
|
if (changes.selection) {
|
|
const selection = changes.selection;
|
|
setTimeout(() => {
|
|
updateSelectionHistory(ydoc, selection);
|
|
}, 0);
|
|
}
|
|
}
|
|
function defaultGetChangesFromCRDTDoc(crdtDoc) {
|
|
return getRootMap(crdtDoc, CRDT_RECORD_MAP_KEY2).toJSON();
|
|
}
|
|
function getPostChangesFromCRDTDoc(ydoc, editedRecord, syncedProperties) {
|
|
const ymap = getRootMap(ydoc, CRDT_RECORD_MAP_KEY2);
|
|
let allowedMetaChanges = {};
|
|
const changes = Object.fromEntries(
|
|
Object.entries(ymap.toJSON()).filter(([key, newValue]) => {
|
|
if (!syncedProperties.has(key)) {
|
|
return false;
|
|
}
|
|
const currentValue = editedRecord[key];
|
|
switch (key) {
|
|
case "blocks": {
|
|
if (ydoc.meta?.get(CRDT_DOC_META_PERSISTENCE_KEY2) && editedRecord.content) {
|
|
const blocksJson = ymap.get("blocks")?.toJSON() ?? [];
|
|
return (0, import_blocks3.__unstableSerializeAndClean)(blocksJson).trim() !== getRawValue(editedRecord.content);
|
|
}
|
|
return true;
|
|
}
|
|
case "date": {
|
|
const currentDateIsFloating = null === currentValue || editedRecord.modified === currentValue;
|
|
if (currentDateIsFloating) {
|
|
return false;
|
|
}
|
|
return haveValuesChanged(currentValue, newValue);
|
|
}
|
|
case "meta": {
|
|
const currentMeta = currentValue ?? {};
|
|
allowedMetaChanges = Object.fromEntries(
|
|
Object.entries(newValue ?? {}).filter(
|
|
([metaKey]) => {
|
|
if (disallowedPostMetaKeys.has(metaKey)) {
|
|
return false;
|
|
}
|
|
return metaKey in currentMeta;
|
|
}
|
|
)
|
|
);
|
|
const mergedValue = {
|
|
...currentMeta,
|
|
...allowedMetaChanges
|
|
};
|
|
return haveValuesChanged(currentValue, mergedValue);
|
|
}
|
|
case "status": {
|
|
if ("auto-draft" === newValue) {
|
|
return false;
|
|
}
|
|
return haveValuesChanged(currentValue, newValue);
|
|
}
|
|
case "content":
|
|
case "excerpt":
|
|
case "title": {
|
|
return haveValuesChanged(
|
|
getRawValue(currentValue),
|
|
newValue
|
|
);
|
|
}
|
|
// Add support for additional data types here.
|
|
default: {
|
|
return haveValuesChanged(currentValue, newValue);
|
|
}
|
|
}
|
|
})
|
|
);
|
|
if (changes.blocks) {
|
|
changes.blocks = deserializeBlockAttributes(
|
|
changes.blocks
|
|
);
|
|
}
|
|
if ("object" === typeof changes.meta) {
|
|
changes.meta = {
|
|
...editedRecord.meta,
|
|
...allowedMetaChanges
|
|
};
|
|
}
|
|
const selectionHistory = getSelectionHistory(ydoc);
|
|
const shiftedSelection = getShiftedSelection(ydoc, selectionHistory);
|
|
if (shiftedSelection) {
|
|
changes.selection = {
|
|
...shiftedSelection,
|
|
initialPosition: 0
|
|
};
|
|
}
|
|
return changes;
|
|
}
|
|
var defaultSyncConfig = {
|
|
applyChangesToCRDTDoc: defaultApplyChangesToCRDTDoc,
|
|
createAwareness: (ydoc) => new BaseAwareness(ydoc),
|
|
getChangesFromCRDTDoc: defaultGetChangesFromCRDTDoc
|
|
};
|
|
var defaultCollectionSyncConfig = {
|
|
applyChangesToCRDTDoc: () => {
|
|
},
|
|
getChangesFromCRDTDoc: () => ({}),
|
|
shouldSync: (_, objectId) => null === objectId
|
|
};
|
|
function getRawValue(value) {
|
|
if ("string" === typeof value) {
|
|
return value;
|
|
}
|
|
if (value && "object" === typeof value && "raw" in value && "string" === typeof value.raw) {
|
|
return value.raw;
|
|
}
|
|
return void 0;
|
|
}
|
|
function haveValuesChanged(currentValue, newValue) {
|
|
return !(0, import_es65.default)(currentValue, newValue);
|
|
}
|
|
function updateMapValue(map2, key, currentValue, newValue) {
|
|
if (void 0 === newValue) {
|
|
map2.delete(key);
|
|
return;
|
|
}
|
|
if (haveValuesChanged(currentValue, newValue)) {
|
|
map2.set(key, newValue);
|
|
}
|
|
}
|
|
|
|
// packages/core-data/build-module/entities.mjs
|
|
var DEFAULT_ENTITY_KEY = "id";
|
|
var POST_RAW_ATTRIBUTES = ["title", "excerpt", "content"];
|
|
var blocksTransientEdits = {
|
|
blocks: {
|
|
read: (record) => (0, import_blocks4.parse)(record.content?.raw ?? ""),
|
|
write: (record) => ({
|
|
content: (0, import_blocks4.__unstableSerializeAndClean)(record.blocks)
|
|
})
|
|
}
|
|
};
|
|
var rootEntitiesConfig = [
|
|
{
|
|
label: (0, import_i18n.__)("Base"),
|
|
kind: "root",
|
|
key: false,
|
|
name: "__unstableBase",
|
|
baseURL: "/",
|
|
baseURLParams: {
|
|
// Please also change the preload path when changing this.
|
|
// @see lib/compat/wordpress-7.0/preload.php
|
|
_fields: [
|
|
"description",
|
|
"gmt_offset",
|
|
"home",
|
|
"image_sizes",
|
|
"image_size_threshold",
|
|
"image_output_formats",
|
|
"jpeg_interlaced",
|
|
"png_interlaced",
|
|
"gif_interlaced",
|
|
"name",
|
|
"site_icon",
|
|
"site_icon_url",
|
|
"site_logo",
|
|
"timezone_string",
|
|
"url",
|
|
"page_for_posts",
|
|
"page_on_front",
|
|
"show_on_front"
|
|
].join(",")
|
|
},
|
|
// The entity doesn't support selecting multiple records.
|
|
// The property is maintained for backward compatibility.
|
|
plural: "__unstableBases"
|
|
},
|
|
{
|
|
label: (0, import_i18n.__)("Post Type"),
|
|
name: "postType",
|
|
kind: "root",
|
|
key: "slug",
|
|
baseURL: "/wp/v2/types",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "postTypes"
|
|
},
|
|
{
|
|
name: "media",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/media",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "mediaItems",
|
|
label: (0, import_i18n.__)("Media"),
|
|
rawAttributes: ["caption", "title", "description"],
|
|
supportsPagination: true
|
|
},
|
|
{
|
|
name: "taxonomy",
|
|
kind: "root",
|
|
key: "slug",
|
|
baseURL: "/wp/v2/taxonomies",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "taxonomies",
|
|
label: (0, import_i18n.__)("Taxonomy")
|
|
},
|
|
{
|
|
name: "sidebar",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/sidebars",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "sidebars",
|
|
transientEdits: { blocks: true },
|
|
label: (0, import_i18n.__)("Widget areas")
|
|
},
|
|
{
|
|
name: "widget",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/widgets",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "widgets",
|
|
transientEdits: { blocks: true },
|
|
label: (0, import_i18n.__)("Widgets")
|
|
},
|
|
{
|
|
name: "widgetType",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/widget-types",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "widgetTypes",
|
|
label: (0, import_i18n.__)("Widget types")
|
|
},
|
|
{
|
|
label: (0, import_i18n.__)("User"),
|
|
name: "user",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/users",
|
|
getTitle: (record) => record?.name || record?.slug,
|
|
baseURLParams: { context: "edit" },
|
|
plural: "users",
|
|
supportsPagination: true
|
|
},
|
|
{
|
|
name: "comment",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/comments",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "comments",
|
|
label: (0, import_i18n.__)("Comment"),
|
|
supportsPagination: true,
|
|
syncConfig: defaultCollectionSyncConfig
|
|
},
|
|
{
|
|
name: "menu",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/menus",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "menus",
|
|
label: (0, import_i18n.__)("Menu"),
|
|
supportsPagination: true
|
|
},
|
|
{
|
|
name: "menuItem",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/menu-items",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "menuItems",
|
|
label: (0, import_i18n.__)("Menu Item"),
|
|
rawAttributes: ["title"],
|
|
supportsPagination: true
|
|
},
|
|
{
|
|
name: "menuLocation",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/menu-locations",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "menuLocations",
|
|
label: (0, import_i18n.__)("Menu Location"),
|
|
key: "name"
|
|
},
|
|
{
|
|
label: (0, import_i18n.__)("Global Styles"),
|
|
name: "globalStyles",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/global-styles",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "globalStylesVariations",
|
|
// Should be different from name.
|
|
getTitle: () => (0, import_i18n.__)("Custom Styles"),
|
|
getRevisionsUrl: (parentId, revisionId) => `/wp/v2/global-styles/${parentId}/revisions${revisionId ? "/" + revisionId : ""}`,
|
|
supportsPagination: true
|
|
},
|
|
{
|
|
label: (0, import_i18n.__)("Themes"),
|
|
name: "theme",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/themes",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "themes",
|
|
key: "stylesheet"
|
|
},
|
|
{
|
|
label: (0, import_i18n.__)("Plugins"),
|
|
name: "plugin",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/plugins",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "plugins",
|
|
key: "plugin"
|
|
},
|
|
{
|
|
label: (0, import_i18n.__)("Status"),
|
|
name: "status",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/statuses",
|
|
baseURLParams: { context: "edit" },
|
|
plural: "statuses",
|
|
key: "slug"
|
|
},
|
|
{
|
|
label: (0, import_i18n.__)("Registered Templates"),
|
|
name: "registeredTemplate",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/registered-templates",
|
|
key: "id"
|
|
},
|
|
{
|
|
label: (0, import_i18n.__)("Font Collections"),
|
|
name: "fontCollection",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/font-collections",
|
|
baseURLParams: { context: "view" },
|
|
plural: "fontCollections",
|
|
key: "slug"
|
|
},
|
|
{
|
|
label: (0, import_i18n.__)("Icons"),
|
|
name: "icon",
|
|
kind: "root",
|
|
baseURL: "/wp/v2/icons",
|
|
baseURLParams: { context: "view" },
|
|
plural: "icons",
|
|
key: "name"
|
|
}
|
|
];
|
|
var deprecatedEntities = {
|
|
root: {
|
|
media: {
|
|
since: "6.9",
|
|
alternative: {
|
|
kind: "postType",
|
|
name: "attachment"
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var additionalEntityConfigLoaders = [
|
|
{ kind: "postType", loadEntities: loadPostTypeEntities },
|
|
{ kind: "taxonomy", loadEntities: loadTaxonomyEntities },
|
|
{
|
|
kind: "root",
|
|
name: "site",
|
|
plural: "sites",
|
|
loadEntities: loadSiteEntity
|
|
}
|
|
];
|
|
var prePersistPostType = async (persistedRecord, edits, name, isTemplate) => {
|
|
const newEdits = {};
|
|
if (!isTemplate && persistedRecord?.status === "auto-draft") {
|
|
if (!edits.status && !newEdits.status) {
|
|
newEdits.status = "draft";
|
|
}
|
|
if ((!edits.title || edits.title === "Auto Draft") && !newEdits.title && (!persistedRecord?.title || persistedRecord?.title === "Auto Draft")) {
|
|
newEdits.title = "";
|
|
}
|
|
}
|
|
if (persistedRecord) {
|
|
const objectType = `postType/${name}`;
|
|
const objectId = persistedRecord.id;
|
|
const serializedDoc = await getSyncManager()?.createPersistedCRDTDoc(
|
|
objectType,
|
|
objectId
|
|
);
|
|
if (serializedDoc) {
|
|
newEdits.meta = {
|
|
...edits.meta,
|
|
[POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE]: serializedDoc
|
|
};
|
|
}
|
|
}
|
|
return newEdits;
|
|
};
|
|
async function loadPostTypeEntities() {
|
|
const postTypesPromise = (0, import_api_fetch2.default)({ path: "/wp/v2/types?context=view" });
|
|
const taxonomiesPromise = window._wpCollaborationEnabled ? (0, import_api_fetch2.default)({ path: "/wp/v2/taxonomies?context=view" }) : Promise.resolve({});
|
|
const [postTypes, taxonomies] = await Promise.all([
|
|
postTypesPromise,
|
|
taxonomiesPromise
|
|
]);
|
|
return Object.entries(postTypes ?? {}).map(([name, postType]) => {
|
|
const isTemplate = ["wp_template", "wp_template_part"].includes(
|
|
name
|
|
);
|
|
const namespace = postType?.rest_namespace ?? "wp/v2";
|
|
const syncedProperties = /* @__PURE__ */ new Set([
|
|
"author",
|
|
"blocks",
|
|
"content",
|
|
"comment_status",
|
|
"date",
|
|
"excerpt",
|
|
"featured_media",
|
|
"format",
|
|
"meta",
|
|
"ping_status",
|
|
"slug",
|
|
"status",
|
|
"sticky",
|
|
"template",
|
|
"title",
|
|
...postType.taxonomies?.map((taxonomy) => taxonomies?.[taxonomy]?.rest_base)?.filter(Boolean) ?? []
|
|
]);
|
|
const entity2 = {
|
|
kind: "postType",
|
|
baseURL: `/${namespace}/${postType.rest_base}`,
|
|
baseURLParams: { context: "edit" },
|
|
name,
|
|
label: postType.name,
|
|
transientEdits: {
|
|
...blocksTransientEdits,
|
|
selection: true
|
|
},
|
|
mergedEdits: { meta: true },
|
|
rawAttributes: POST_RAW_ATTRIBUTES,
|
|
getTitle: (record) => record?.title?.rendered || record?.title || (isTemplate ? capitalCase(record.slug ?? "") : String(record.id)),
|
|
__unstablePrePersist: (persistedRecord, edits) => prePersistPostType(persistedRecord, edits, name, isTemplate),
|
|
__unstable_rest_base: postType.rest_base,
|
|
supportsPagination: true,
|
|
getRevisionsUrl: (parentId, revisionId) => `/${namespace}/${postType.rest_base}/${parentId}/revisions${revisionId ? "/" + revisionId : ""}`,
|
|
revisionKey: isTemplate && !window?.__experimentalTemplateActivate ? "wp_id" : DEFAULT_ENTITY_KEY
|
|
};
|
|
entity2.syncConfig = {
|
|
/**
|
|
* Apply changes from the local editor to the local CRDT document so
|
|
* that those changes can be synced to other peers (via the provider).
|
|
*
|
|
* @param {import('@wordpress/sync').CRDTDoc} crdtDoc
|
|
* @param {Partial< import('@wordpress/sync').ObjectData >} changes
|
|
* @return {void}
|
|
*/
|
|
applyChangesToCRDTDoc: (crdtDoc, changes) => applyPostChangesToCRDTDoc(crdtDoc, changes, syncedProperties),
|
|
/**
|
|
* Create the awareness instance for the entity's CRDT document.
|
|
*
|
|
* @param {import('@wordpress/sync').CRDTDoc} ydoc
|
|
* @param {import('@wordpress/sync').ObjectID} objectId
|
|
* @return {import('@wordpress/sync').Awareness} Awareness instance
|
|
*/
|
|
createAwareness: (ydoc, objectId) => {
|
|
const kind = "postType";
|
|
const id2 = parseInt(objectId, 10);
|
|
return new PostEditorAwareness(ydoc, kind, name, id2);
|
|
},
|
|
/**
|
|
* Extract changes from a CRDT document that can be used to update the
|
|
* local editor state.
|
|
*
|
|
* @param {import('@wordpress/sync').CRDTDoc} crdtDoc
|
|
* @param {import('@wordpress/sync').ObjectData} editedRecord
|
|
* @return {Partial< import('@wordpress/sync').ObjectData >} Changes to record
|
|
*/
|
|
getChangesFromCRDTDoc: (crdtDoc, editedRecord) => getPostChangesFromCRDTDoc(
|
|
crdtDoc,
|
|
editedRecord,
|
|
syncedProperties
|
|
),
|
|
/**
|
|
* Extract changes from a CRDT document that can be used to update the
|
|
* local editor state.
|
|
*
|
|
* @param {import('@wordpress/sync').ObjectData} record
|
|
* @return {Partial< import('@wordpress/sync').ObjectData >} Changes to record
|
|
*/
|
|
getPersistedCRDTDoc: (record) => {
|
|
return record?.meta?.[POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE] || null;
|
|
}
|
|
};
|
|
return entity2;
|
|
});
|
|
}
|
|
async function loadTaxonomyEntities() {
|
|
const taxonomies = await (0, import_api_fetch2.default)({
|
|
path: "/wp/v2/taxonomies?context=view"
|
|
});
|
|
return Object.entries(taxonomies ?? {}).map(([name, taxonomy]) => {
|
|
const namespace = taxonomy?.rest_namespace ?? "wp/v2";
|
|
const entity2 = {
|
|
kind: "taxonomy",
|
|
baseURL: `/${namespace}/${taxonomy.rest_base}`,
|
|
baseURLParams: { context: "edit" },
|
|
name,
|
|
label: taxonomy.name,
|
|
getTitle: (record) => record?.name,
|
|
supportsPagination: true
|
|
};
|
|
entity2.syncConfig = defaultSyncConfig;
|
|
return entity2;
|
|
});
|
|
}
|
|
async function loadSiteEntity() {
|
|
const entity2 = {
|
|
label: (0, import_i18n.__)("Site"),
|
|
name: "site",
|
|
kind: "root",
|
|
key: false,
|
|
baseURL: "/wp/v2/settings",
|
|
meta: {}
|
|
};
|
|
const site = await (0, import_api_fetch2.default)({
|
|
path: entity2.baseURL,
|
|
method: "OPTIONS"
|
|
});
|
|
const labels = {};
|
|
Object.entries(site?.schema?.properties ?? {}).forEach(
|
|
([key, value]) => {
|
|
if (typeof value === "object" && value.title) {
|
|
labels[key] = value.title;
|
|
}
|
|
}
|
|
);
|
|
return [{ ...entity2, meta: { labels } }];
|
|
}
|
|
var getMethodName = (kind, name, prefix = "get") => {
|
|
const kindPrefix = kind === "root" ? "" : pascalCase(kind);
|
|
const suffix = pascalCase(name);
|
|
return `${prefix}${kindPrefix}${suffix}`;
|
|
};
|
|
|
|
// packages/core-data/build-module/queried-data/reducer.mjs
|
|
function getContextFromAction(action) {
|
|
const { query } = action;
|
|
if (!query) {
|
|
return "default";
|
|
}
|
|
const queryParts = get_query_parts_default(query);
|
|
return queryParts.context;
|
|
}
|
|
function getMergedItemIds(itemIds, nextItemIds, page, perPage) {
|
|
const receivedAllIds = page === 1 && perPage === -1;
|
|
if (receivedAllIds) {
|
|
return nextItemIds;
|
|
}
|
|
const nextItemIdsStartIndex = (page - 1) * perPage;
|
|
const size2 = Math.max(
|
|
itemIds?.length ?? 0,
|
|
nextItemIdsStartIndex + nextItemIds.length
|
|
);
|
|
const mergedItemIds = new Array(size2);
|
|
for (let i = 0; i < size2; i++) {
|
|
const isInNextItemsRange = i >= nextItemIdsStartIndex && i < nextItemIdsStartIndex + perPage;
|
|
mergedItemIds[i] = isInNextItemsRange ? nextItemIds[i - nextItemIdsStartIndex] : itemIds?.[i];
|
|
}
|
|
return mergedItemIds;
|
|
}
|
|
function removeEntitiesById(entities2, ids) {
|
|
return Object.fromEntries(
|
|
Object.entries(entities2).filter(
|
|
([id2]) => !ids.some((itemId) => {
|
|
if (Number.isInteger(itemId)) {
|
|
return itemId === +id2;
|
|
}
|
|
return itemId === id2;
|
|
})
|
|
)
|
|
);
|
|
}
|
|
function items(state = {}, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_ITEMS": {
|
|
const context = getContextFromAction(action);
|
|
const key = action.key || DEFAULT_ENTITY_KEY;
|
|
const itemsList = Array.isArray(action.items) ? action.items : [action.items];
|
|
return {
|
|
...state,
|
|
[context]: {
|
|
...state[context],
|
|
...Object.fromEntries(
|
|
itemsList.map((item) => [
|
|
item?.[key],
|
|
conservativeMapItem(
|
|
state?.[context]?.[item?.[key]],
|
|
item
|
|
)
|
|
])
|
|
)
|
|
}
|
|
};
|
|
}
|
|
case "REMOVE_ITEMS":
|
|
return Object.fromEntries(
|
|
Object.entries(state).map(([itemId, contextState]) => [
|
|
itemId,
|
|
removeEntitiesById(contextState, action.itemIds)
|
|
])
|
|
);
|
|
}
|
|
return state;
|
|
}
|
|
function itemIsComplete(state = {}, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_ITEMS": {
|
|
const context = getContextFromAction(action);
|
|
const { query, key = DEFAULT_ENTITY_KEY } = action;
|
|
const itemsList = Array.isArray(action.items) ? action.items : [action.items];
|
|
const queryParts = query ? get_query_parts_default(query) : {};
|
|
const isCompleteQuery = !query || !Array.isArray(queryParts.fields);
|
|
return {
|
|
...state,
|
|
[context]: {
|
|
...state[context],
|
|
...itemsList.reduce((result, item) => {
|
|
const itemId = item?.[key];
|
|
result[itemId] = state?.[context]?.[itemId] || isCompleteQuery;
|
|
return result;
|
|
}, {})
|
|
}
|
|
};
|
|
}
|
|
case "REMOVE_ITEMS":
|
|
return Object.fromEntries(
|
|
Object.entries(state).map(([itemId, contextState]) => [
|
|
itemId,
|
|
removeEntitiesById(contextState, action.itemIds)
|
|
])
|
|
);
|
|
}
|
|
return state;
|
|
}
|
|
var receiveQueries = (0, import_compose.compose)([
|
|
// Limit to matching action type so we don't attempt to replace action on
|
|
// an unhandled action.
|
|
if_matching_action_default((action) => "query" in action),
|
|
// Inject query parts into action for use both in `onSubKey` and reducer.
|
|
replace_action_default((action) => {
|
|
if (action.query) {
|
|
return {
|
|
...action,
|
|
...get_query_parts_default(action.query)
|
|
};
|
|
}
|
|
return action;
|
|
}),
|
|
on_sub_key_default("context"),
|
|
// Queries shape is shared, but keyed by query `stableKey` part. Original
|
|
// reducer tracks only a single query object.
|
|
on_sub_key_default("stableKey")
|
|
])((state = {}, action) => {
|
|
if (action.type !== "RECEIVE_ITEMS") {
|
|
return state;
|
|
}
|
|
if (!Array.isArray(action.items)) {
|
|
return state;
|
|
}
|
|
const key = action.key ?? DEFAULT_ENTITY_KEY;
|
|
return {
|
|
itemIds: getMergedItemIds(
|
|
state?.itemIds || [],
|
|
action.items.map((item) => item?.[key]).filter(Boolean),
|
|
action.page,
|
|
action.perPage
|
|
),
|
|
meta: action.meta
|
|
};
|
|
});
|
|
var queries = (state = {}, action) => {
|
|
switch (action.type) {
|
|
case "RECEIVE_ITEMS":
|
|
return receiveQueries(state, action);
|
|
case "REMOVE_ITEMS":
|
|
const removedItems = action.itemIds.reduce((result, itemId) => {
|
|
result[itemId] = true;
|
|
return result;
|
|
}, {});
|
|
return Object.fromEntries(
|
|
Object.entries(state).map(
|
|
([queryGroup, contextQueries]) => [
|
|
queryGroup,
|
|
Object.fromEntries(
|
|
Object.entries(contextQueries).map(
|
|
([query, queryItems]) => [
|
|
query,
|
|
{
|
|
...queryItems,
|
|
itemIds: queryItems.itemIds.filter(
|
|
(queryId) => !removedItems[queryId]
|
|
)
|
|
}
|
|
]
|
|
)
|
|
)
|
|
]
|
|
)
|
|
);
|
|
default:
|
|
return state;
|
|
}
|
|
};
|
|
var reducer_default = (0, import_data7.combineReducers)({
|
|
items,
|
|
itemIsComplete,
|
|
queries
|
|
});
|
|
|
|
// packages/core-data/build-module/reducer.mjs
|
|
function users(state = { byId: {}, queries: {} }, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_USER_QUERY":
|
|
return {
|
|
byId: {
|
|
...state.byId,
|
|
// Key users by their ID.
|
|
...action.users.reduce(
|
|
(newUsers, user) => ({
|
|
...newUsers,
|
|
[user.id]: user
|
|
}),
|
|
{}
|
|
)
|
|
},
|
|
queries: {
|
|
...state.queries,
|
|
[action.queryID]: action.users.map((user) => user.id)
|
|
}
|
|
};
|
|
}
|
|
return state;
|
|
}
|
|
function currentUser(state = {}, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_CURRENT_USER":
|
|
return action.currentUser;
|
|
}
|
|
return state;
|
|
}
|
|
function currentTheme(state = void 0, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_CURRENT_THEME":
|
|
return action.currentTheme.stylesheet;
|
|
}
|
|
return state;
|
|
}
|
|
function currentGlobalStylesId(state = void 0, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_CURRENT_GLOBAL_STYLES_ID":
|
|
return action.id;
|
|
}
|
|
return state;
|
|
}
|
|
function themeBaseGlobalStyles(state = {}, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_THEME_GLOBAL_STYLES":
|
|
return {
|
|
...state,
|
|
[action.stylesheet]: action.globalStyles
|
|
};
|
|
}
|
|
return state;
|
|
}
|
|
function themeGlobalStyleVariations(state = {}, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_THEME_GLOBAL_STYLE_VARIATIONS":
|
|
return {
|
|
...state,
|
|
[action.stylesheet]: action.variations
|
|
};
|
|
}
|
|
return state;
|
|
}
|
|
var withMultiEntityRecordEdits = (reducer) => (state, action) => {
|
|
if (action.type === "UNDO" || action.type === "REDO") {
|
|
const { record } = action;
|
|
let newState = state;
|
|
record.forEach(({ id: { kind, name, recordId }, changes }) => {
|
|
newState = reducer(newState, {
|
|
type: "EDIT_ENTITY_RECORD",
|
|
kind,
|
|
name,
|
|
recordId,
|
|
edits: Object.entries(changes).reduce(
|
|
(acc, [key, value]) => {
|
|
acc[key] = action.type === "UNDO" ? value.from : value.to;
|
|
return acc;
|
|
},
|
|
{}
|
|
)
|
|
});
|
|
});
|
|
return newState;
|
|
}
|
|
return reducer(state, action);
|
|
};
|
|
function entity(entityConfig) {
|
|
return (0, import_compose2.compose)([
|
|
withMultiEntityRecordEdits,
|
|
// Limit to matching action type so we don't attempt to replace action on
|
|
// an unhandled action.
|
|
if_matching_action_default(
|
|
(action) => action.name && action.kind && action.name === entityConfig.name && action.kind === entityConfig.kind
|
|
),
|
|
// Inject the entity config into the action.
|
|
replace_action_default((action) => {
|
|
return {
|
|
key: entityConfig.key || DEFAULT_ENTITY_KEY,
|
|
...action
|
|
};
|
|
})
|
|
])(
|
|
(0, import_data8.combineReducers)({
|
|
queriedData: reducer_default,
|
|
edits: (state = {}, action) => {
|
|
switch (action.type) {
|
|
case "RECEIVE_ITEMS":
|
|
const context = action?.query?.context ?? "default";
|
|
if (context !== "default") {
|
|
return state;
|
|
}
|
|
const nextState = { ...state };
|
|
const itemsList = Array.isArray(action.items) ? action.items : [action.items];
|
|
for (const record of itemsList) {
|
|
const recordId = record?.[action.key];
|
|
const edits = nextState[recordId];
|
|
if (!edits) {
|
|
continue;
|
|
}
|
|
const nextEdits2 = Object.keys(edits).reduce(
|
|
(acc, key) => {
|
|
if (
|
|
// Edits are the "raw" attribute values, but records may have
|
|
// objects with more properties, so we use `get` here for the
|
|
// comparison.
|
|
!(0, import_es66.default)(
|
|
edits[key],
|
|
record[key]?.raw ?? record[key]
|
|
) && // Sometimes the server alters the sent value which means
|
|
// we need to also remove the edits before the api request.
|
|
(!action.persistedEdits || !(0, import_es66.default)(
|
|
edits[key],
|
|
action.persistedEdits[key]
|
|
))
|
|
) {
|
|
acc[key] = edits[key];
|
|
}
|
|
return acc;
|
|
},
|
|
{}
|
|
);
|
|
if (Object.keys(nextEdits2).length) {
|
|
nextState[recordId] = nextEdits2;
|
|
} else {
|
|
delete nextState[recordId];
|
|
}
|
|
}
|
|
return nextState;
|
|
case "EDIT_ENTITY_RECORD":
|
|
const nextEdits = {
|
|
...state[action.recordId],
|
|
...action.edits
|
|
};
|
|
Object.keys(nextEdits).forEach((key) => {
|
|
if (nextEdits[key] === void 0) {
|
|
delete nextEdits[key];
|
|
}
|
|
});
|
|
return {
|
|
...state,
|
|
[action.recordId]: nextEdits
|
|
};
|
|
}
|
|
return state;
|
|
},
|
|
saving: (state = {}, action) => {
|
|
switch (action.type) {
|
|
case "SAVE_ENTITY_RECORD_START":
|
|
case "SAVE_ENTITY_RECORD_FINISH":
|
|
return {
|
|
...state,
|
|
[action.recordId]: {
|
|
pending: action.type === "SAVE_ENTITY_RECORD_START",
|
|
error: action.error,
|
|
isAutosave: action.isAutosave
|
|
}
|
|
};
|
|
}
|
|
return state;
|
|
},
|
|
deleting: (state = {}, action) => {
|
|
switch (action.type) {
|
|
case "DELETE_ENTITY_RECORD_START":
|
|
case "DELETE_ENTITY_RECORD_FINISH":
|
|
return {
|
|
...state,
|
|
[action.recordId]: {
|
|
pending: action.type === "DELETE_ENTITY_RECORD_START",
|
|
error: action.error
|
|
}
|
|
};
|
|
}
|
|
return state;
|
|
},
|
|
revisions: (state = {}, action) => {
|
|
if (action.type === "RECEIVE_ITEM_REVISIONS") {
|
|
const recordKey = action.recordKey;
|
|
delete action.recordKey;
|
|
const newState = reducer_default(state[recordKey], {
|
|
...action,
|
|
type: "RECEIVE_ITEMS"
|
|
});
|
|
return {
|
|
...state,
|
|
[recordKey]: newState
|
|
};
|
|
}
|
|
if (action.type === "REMOVE_ITEMS") {
|
|
return Object.fromEntries(
|
|
Object.entries(state).filter(
|
|
([id2]) => !action.itemIds.some((itemId) => {
|
|
if (Number.isInteger(itemId)) {
|
|
return itemId === +id2;
|
|
}
|
|
return itemId === id2;
|
|
})
|
|
)
|
|
);
|
|
}
|
|
return state;
|
|
}
|
|
})
|
|
);
|
|
}
|
|
function entitiesConfig(state = rootEntitiesConfig, action) {
|
|
switch (action.type) {
|
|
case "ADD_ENTITIES":
|
|
return [...state, ...action.entities];
|
|
}
|
|
return state;
|
|
}
|
|
var entities = (state = {}, action) => {
|
|
const newConfig = entitiesConfig(state.config, action);
|
|
let entitiesDataReducer = state.reducer;
|
|
if (!entitiesDataReducer || newConfig !== state.config) {
|
|
const entitiesByKind = newConfig.reduce((acc, record) => {
|
|
const { kind } = record;
|
|
if (!acc[kind]) {
|
|
acc[kind] = [];
|
|
}
|
|
acc[kind].push(record);
|
|
return acc;
|
|
}, {});
|
|
entitiesDataReducer = (0, import_data8.combineReducers)(
|
|
Object.fromEntries(
|
|
Object.entries(entitiesByKind).map(
|
|
([kind, subEntities]) => {
|
|
const kindReducer = (0, import_data8.combineReducers)(
|
|
Object.fromEntries(
|
|
subEntities.map((entityConfig) => [
|
|
entityConfig.name,
|
|
entity(entityConfig)
|
|
])
|
|
)
|
|
);
|
|
return [kind, kindReducer];
|
|
}
|
|
)
|
|
)
|
|
);
|
|
}
|
|
const newData = entitiesDataReducer(state.records, action);
|
|
if (newData === state.records && newConfig === state.config && entitiesDataReducer === state.reducer) {
|
|
return state;
|
|
}
|
|
return {
|
|
reducer: entitiesDataReducer,
|
|
records: newData,
|
|
config: newConfig
|
|
};
|
|
};
|
|
function undoManager(state = (0, import_undo_manager2.createUndoManager)()) {
|
|
return state;
|
|
}
|
|
function editsReference(state = {}, action) {
|
|
switch (action.type) {
|
|
case "EDIT_ENTITY_RECORD":
|
|
case "UNDO":
|
|
case "REDO":
|
|
return {};
|
|
}
|
|
return state;
|
|
}
|
|
function embedPreviews(state = {}, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_EMBED_PREVIEW":
|
|
const { url, preview } = action;
|
|
return {
|
|
...state,
|
|
[url]: preview
|
|
};
|
|
}
|
|
return state;
|
|
}
|
|
function userPermissions(state = {}, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_USER_PERMISSION":
|
|
return {
|
|
...state,
|
|
[action.key]: action.isAllowed
|
|
};
|
|
case "RECEIVE_USER_PERMISSIONS":
|
|
return {
|
|
...state,
|
|
...action.permissions
|
|
};
|
|
}
|
|
return state;
|
|
}
|
|
function autosaves(state = {}, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_AUTOSAVES":
|
|
const { postId, autosaves: autosavesData } = action;
|
|
return {
|
|
...state,
|
|
[postId]: autosavesData
|
|
};
|
|
}
|
|
return state;
|
|
}
|
|
function blockPatterns(state = [], action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_BLOCK_PATTERNS":
|
|
return action.patterns;
|
|
}
|
|
return state;
|
|
}
|
|
function blockPatternCategories(state = [], action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_BLOCK_PATTERN_CATEGORIES":
|
|
return action.categories;
|
|
}
|
|
return state;
|
|
}
|
|
function userPatternCategories(state = [], action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_USER_PATTERN_CATEGORIES":
|
|
return action.patternCategories;
|
|
}
|
|
return state;
|
|
}
|
|
function navigationFallbackId(state = null, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_NAVIGATION_FALLBACK_ID":
|
|
return action.fallbackId;
|
|
}
|
|
return state;
|
|
}
|
|
function themeGlobalStyleRevisions(state = {}, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_THEME_GLOBAL_STYLE_REVISIONS":
|
|
return {
|
|
...state,
|
|
[action.currentId]: action.revisions
|
|
};
|
|
}
|
|
return state;
|
|
}
|
|
function defaultTemplates(state = {}, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_DEFAULT_TEMPLATE":
|
|
return {
|
|
...state,
|
|
[JSON.stringify(action.query)]: action.templateId
|
|
};
|
|
}
|
|
return state;
|
|
}
|
|
function registeredPostMeta(state = {}, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_REGISTERED_POST_META":
|
|
return {
|
|
...state,
|
|
[action.postType]: action.registeredPostMeta
|
|
};
|
|
}
|
|
return state;
|
|
}
|
|
function editorSettings(state = null, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_EDITOR_SETTINGS":
|
|
return action.settings;
|
|
}
|
|
return state;
|
|
}
|
|
function editorAssets(state = null, action) {
|
|
switch (action.type) {
|
|
case "RECEIVE_EDITOR_ASSETS":
|
|
return action.assets;
|
|
}
|
|
return state;
|
|
}
|
|
function syncConnectionStatuses(state = {}, action) {
|
|
switch (action.type) {
|
|
case "SET_SYNC_CONNECTION_STATUS": {
|
|
const key = `${action.kind}/${action.name}:${action.key}`;
|
|
return {
|
|
...state,
|
|
[key]: action.status
|
|
};
|
|
}
|
|
case "CLEAR_SYNC_CONNECTION_STATUS": {
|
|
const key = `${action.kind}/${action.name}:${action.key}`;
|
|
const { [key]: _, ...rest } = state;
|
|
return rest;
|
|
}
|
|
}
|
|
return state;
|
|
}
|
|
function collaborationSupported(state = true, action) {
|
|
switch (action.type) {
|
|
case "SET_COLLABORATION_SUPPORTED":
|
|
return action.supported;
|
|
case "SET_SYNC_CONNECTION_STATUS":
|
|
if (ConnectionErrorCode2.DOCUMENT_SIZE_LIMIT_EXCEEDED === action.status?.error?.code) {
|
|
return false;
|
|
}
|
|
return state;
|
|
}
|
|
return state;
|
|
}
|
|
var reducer_default2 = (0, import_data8.combineReducers)({
|
|
users,
|
|
currentTheme,
|
|
currentGlobalStylesId,
|
|
currentUser,
|
|
themeGlobalStyleVariations,
|
|
themeBaseGlobalStyles,
|
|
themeGlobalStyleRevisions,
|
|
entities,
|
|
editsReference,
|
|
undoManager,
|
|
embedPreviews,
|
|
userPermissions,
|
|
autosaves,
|
|
blockPatterns,
|
|
blockPatternCategories,
|
|
userPatternCategories,
|
|
navigationFallbackId,
|
|
defaultTemplates,
|
|
registeredPostMeta,
|
|
editorSettings,
|
|
editorAssets,
|
|
syncConnectionStatuses,
|
|
collaborationSupported
|
|
});
|
|
|
|
// packages/core-data/build-module/selectors.mjs
|
|
var selectors_exports = {};
|
|
__export(selectors_exports, {
|
|
__experimentalGetCurrentGlobalStylesId: () => __experimentalGetCurrentGlobalStylesId,
|
|
__experimentalGetCurrentThemeBaseGlobalStyles: () => __experimentalGetCurrentThemeBaseGlobalStyles,
|
|
__experimentalGetCurrentThemeGlobalStylesVariations: () => __experimentalGetCurrentThemeGlobalStylesVariations,
|
|
__experimentalGetDirtyEntityRecords: () => __experimentalGetDirtyEntityRecords,
|
|
__experimentalGetEntitiesBeingSaved: () => __experimentalGetEntitiesBeingSaved,
|
|
__experimentalGetEntityRecordNoResolver: () => __experimentalGetEntityRecordNoResolver,
|
|
canUser: () => canUser,
|
|
canUserEditEntityRecord: () => canUserEditEntityRecord,
|
|
getAuthors: () => getAuthors,
|
|
getAutosave: () => getAutosave,
|
|
getAutosaves: () => getAutosaves,
|
|
getBlockPatternCategories: () => getBlockPatternCategories,
|
|
getBlockPatterns: () => getBlockPatterns,
|
|
getCurrentTheme: () => getCurrentTheme,
|
|
getCurrentThemeGlobalStylesRevisions: () => getCurrentThemeGlobalStylesRevisions,
|
|
getCurrentUser: () => getCurrentUser,
|
|
getDefaultTemplateId: () => getDefaultTemplateId,
|
|
getEditedEntityRecord: () => getEditedEntityRecord,
|
|
getEmbedPreview: () => getEmbedPreview,
|
|
getEntitiesByKind: () => getEntitiesByKind,
|
|
getEntitiesConfig: () => getEntitiesConfig,
|
|
getEntity: () => getEntity,
|
|
getEntityConfig: () => getEntityConfig,
|
|
getEntityRecord: () => getEntityRecord,
|
|
getEntityRecordEdits: () => getEntityRecordEdits,
|
|
getEntityRecordNonTransientEdits: () => getEntityRecordNonTransientEdits,
|
|
getEntityRecords: () => getEntityRecords,
|
|
getEntityRecordsTotalItems: () => getEntityRecordsTotalItems,
|
|
getEntityRecordsTotalPages: () => getEntityRecordsTotalPages,
|
|
getLastEntityDeleteError: () => getLastEntityDeleteError,
|
|
getLastEntitySaveError: () => getLastEntitySaveError,
|
|
getRawEntityRecord: () => getRawEntityRecord,
|
|
getRedoEdit: () => getRedoEdit,
|
|
getReferenceByDistinctEdits: () => getReferenceByDistinctEdits,
|
|
getRevision: () => getRevision,
|
|
getRevisions: () => getRevisions,
|
|
getThemeSupports: () => getThemeSupports,
|
|
getUndoEdit: () => getUndoEdit,
|
|
getUserPatternCategories: () => getUserPatternCategories,
|
|
getUserQueryResults: () => getUserQueryResults,
|
|
hasEditsForEntityRecord: () => hasEditsForEntityRecord,
|
|
hasEntityRecord: () => hasEntityRecord,
|
|
hasEntityRecords: () => hasEntityRecords,
|
|
hasFetchedAutosaves: () => hasFetchedAutosaves,
|
|
hasRedo: () => hasRedo,
|
|
hasUndo: () => hasUndo,
|
|
isAutosavingEntityRecord: () => isAutosavingEntityRecord,
|
|
isDeletingEntityRecord: () => isDeletingEntityRecord,
|
|
isPreviewEmbedFallback: () => isPreviewEmbedFallback,
|
|
isRequestingEmbedPreview: () => isRequestingEmbedPreview,
|
|
isSavingEntityRecord: () => isSavingEntityRecord
|
|
});
|
|
var import_data10 = __toESM(require_data(), 1);
|
|
var import_url2 = __toESM(require_url(), 1);
|
|
var import_deprecated2 = __toESM(require_deprecated(), 1);
|
|
|
|
// packages/core-data/build-module/private-selectors.mjs
|
|
var private_selectors_exports = {};
|
|
__export(private_selectors_exports, {
|
|
getBlockPatternsForPostType: () => getBlockPatternsForPostType,
|
|
getEditorAssets: () => getEditorAssets,
|
|
getEditorSettings: () => getEditorSettings,
|
|
getEntityRecordPermissions: () => getEntityRecordPermissions,
|
|
getEntityRecordsPermissions: () => getEntityRecordsPermissions,
|
|
getHomePage: () => getHomePage,
|
|
getNavigationFallbackId: () => getNavigationFallbackId,
|
|
getPostsPageId: () => getPostsPageId,
|
|
getRegisteredPostMeta: () => getRegisteredPostMeta,
|
|
getSyncConnectionStatus: () => getSyncConnectionStatus,
|
|
getTemplateId: () => getTemplateId,
|
|
getUndoManager: () => getUndoManager,
|
|
isCollaborationSupported: () => isCollaborationSupported
|
|
});
|
|
var import_data9 = __toESM(require_data(), 1);
|
|
|
|
// packages/core-data/build-module/utils/log-entity-deprecation.mjs
|
|
var import_deprecated = __toESM(require_deprecated(), 1);
|
|
var loggedAlready = false;
|
|
function logEntityDeprecation(kind, name, functionName, {
|
|
alternativeFunctionName,
|
|
isShorthandSelector = false
|
|
} = {}) {
|
|
const deprecation = deprecatedEntities[kind]?.[name];
|
|
if (!deprecation) {
|
|
return;
|
|
}
|
|
if (!loggedAlready) {
|
|
const { alternative } = deprecation;
|
|
const message = isShorthandSelector ? `'${functionName}'` : `The '${kind}', '${name}' entity (used via '${functionName}')`;
|
|
let alternativeMessage = `the '${alternative.kind}', '${alternative.name}' entity`;
|
|
if (alternativeFunctionName) {
|
|
alternativeMessage += ` via the '${alternativeFunctionName}' function`;
|
|
}
|
|
(0, import_deprecated.default)(message, {
|
|
...deprecation,
|
|
alternative: alternativeMessage
|
|
});
|
|
}
|
|
loggedAlready = true;
|
|
setTimeout(() => {
|
|
loggedAlready = false;
|
|
}, 0);
|
|
}
|
|
|
|
// packages/core-data/build-module/private-selectors.mjs
|
|
function getUndoManager(state) {
|
|
return getSyncManager()?.undoManager ?? state.undoManager;
|
|
}
|
|
function getNavigationFallbackId(state) {
|
|
return state.navigationFallbackId;
|
|
}
|
|
var getBlockPatternsForPostType = (0, import_data9.createRegistrySelector)(
|
|
(select5) => (0, import_data9.createSelector)(
|
|
(state, postType) => select5(STORE_NAME).getBlockPatterns().filter(
|
|
({ postTypes }) => !postTypes || Array.isArray(postTypes) && postTypes.includes(postType)
|
|
),
|
|
() => [select5(STORE_NAME).getBlockPatterns()]
|
|
)
|
|
);
|
|
var getEntityRecordsPermissions = (0, import_data9.createRegistrySelector)(
|
|
(select5) => (0, import_data9.createSelector)(
|
|
(state, kind, name, ids) => {
|
|
const normalizedIds = Array.isArray(ids) ? ids : [ids];
|
|
return normalizedIds.map((id2) => ({
|
|
delete: select5(STORE_NAME).canUser("delete", {
|
|
kind,
|
|
name,
|
|
id: id2
|
|
}),
|
|
update: select5(STORE_NAME).canUser("update", {
|
|
kind,
|
|
name,
|
|
id: id2
|
|
})
|
|
}));
|
|
},
|
|
(state) => [state.userPermissions]
|
|
)
|
|
);
|
|
function getEntityRecordPermissions(state, kind, name, id2) {
|
|
logEntityDeprecation(kind, name, "getEntityRecordPermissions");
|
|
return getEntityRecordsPermissions(state, kind, name, id2)[0];
|
|
}
|
|
function getRegisteredPostMeta(state, postType) {
|
|
return state.registeredPostMeta?.[postType] ?? {};
|
|
}
|
|
function normalizePageId(value) {
|
|
if (!value || !["number", "string"].includes(typeof value)) {
|
|
return null;
|
|
}
|
|
if (Number(value) === 0) {
|
|
return null;
|
|
}
|
|
return value.toString();
|
|
}
|
|
var getHomePage = (0, import_data9.createRegistrySelector)(
|
|
(select5) => (0, import_data9.createSelector)(
|
|
() => {
|
|
const siteData = select5(STORE_NAME).getEntityRecord(
|
|
"root",
|
|
"__unstableBase"
|
|
);
|
|
if (!siteData) {
|
|
return null;
|
|
}
|
|
const homepageId = siteData?.show_on_front === "page" ? normalizePageId(siteData.page_on_front) : null;
|
|
if (homepageId) {
|
|
return { postType: "page", postId: homepageId };
|
|
}
|
|
const frontPageTemplateId = select5(
|
|
STORE_NAME
|
|
).getDefaultTemplateId({
|
|
slug: "front-page"
|
|
});
|
|
if (!frontPageTemplateId) {
|
|
return null;
|
|
}
|
|
return { postType: "wp_template", postId: frontPageTemplateId };
|
|
},
|
|
(state) => [
|
|
// Even though getDefaultTemplateId.shouldInvalidate returns true when root/site changes,
|
|
// it doesn't seem to invalidate this cache, I'm not sure why.
|
|
getEntityRecord(state, "root", "site"),
|
|
getEntityRecord(state, "root", "__unstableBase"),
|
|
getDefaultTemplateId(state, {
|
|
slug: "front-page"
|
|
})
|
|
]
|
|
)
|
|
);
|
|
var getPostsPageId = (0, import_data9.createRegistrySelector)((select5) => () => {
|
|
const siteData = select5(STORE_NAME).getEntityRecord(
|
|
"root",
|
|
"__unstableBase"
|
|
);
|
|
return siteData?.show_on_front === "page" ? normalizePageId(siteData.page_for_posts) : null;
|
|
});
|
|
var getTemplateId = (0, import_data9.createRegistrySelector)(
|
|
(select5) => (state, postType, postId) => {
|
|
const homepage = unlock2(select5(STORE_NAME)).getHomePage();
|
|
if (!homepage) {
|
|
return;
|
|
}
|
|
if (postType === "page" && postType === homepage?.postType && postId.toString() === homepage?.postId) {
|
|
const templates = select5(STORE_NAME).getEntityRecords(
|
|
"postType",
|
|
"wp_template",
|
|
{
|
|
per_page: -1
|
|
}
|
|
);
|
|
if (!templates) {
|
|
return;
|
|
}
|
|
const id2 = templates.find(({ slug }) => slug === "front-page")?.id;
|
|
if (id2) {
|
|
return id2;
|
|
}
|
|
}
|
|
const editedEntity = select5(STORE_NAME).getEditedEntityRecord(
|
|
"postType",
|
|
postType,
|
|
postId
|
|
);
|
|
if (!editedEntity) {
|
|
return;
|
|
}
|
|
const postsPageId = unlock2(select5(STORE_NAME)).getPostsPageId();
|
|
if (postType === "page" && postsPageId === postId.toString()) {
|
|
return select5(STORE_NAME).getDefaultTemplateId({
|
|
slug: "home"
|
|
});
|
|
}
|
|
const currentTemplateSlug = editedEntity.template;
|
|
if (currentTemplateSlug) {
|
|
const currentTemplate = select5(STORE_NAME).getEntityRecords("postType", "wp_template", {
|
|
per_page: -1
|
|
})?.find(({ slug }) => slug === currentTemplateSlug);
|
|
if (currentTemplate) {
|
|
return currentTemplate.id;
|
|
}
|
|
}
|
|
let slugToCheck;
|
|
if (editedEntity.slug) {
|
|
slugToCheck = postType === "page" ? `${postType}-${editedEntity.slug}` : `single-${postType}-${editedEntity.slug}`;
|
|
} else {
|
|
slugToCheck = postType === "page" ? "page" : `single-${postType}`;
|
|
}
|
|
return select5(STORE_NAME).getDefaultTemplateId({
|
|
slug: slugToCheck
|
|
});
|
|
}
|
|
);
|
|
function getEditorSettings(state) {
|
|
return state.editorSettings;
|
|
}
|
|
function getEditorAssets(state) {
|
|
return state.editorAssets;
|
|
}
|
|
function isCollaborationSupported(state) {
|
|
return state.collaborationSupported;
|
|
}
|
|
function getSyncConnectionStatus(state) {
|
|
if (!state.syncConnectionStatuses) {
|
|
return void 0;
|
|
}
|
|
const PRIORITIZED_STATUSES = ["disconnected", "connecting", "connected"];
|
|
let coalesced;
|
|
for (const status of Object.values(state.syncConnectionStatuses)) {
|
|
if (!coalesced || PRIORITIZED_STATUSES.indexOf(status.status) < PRIORITIZED_STATUSES.indexOf(coalesced.status)) {
|
|
coalesced = status;
|
|
}
|
|
}
|
|
return coalesced;
|
|
}
|
|
|
|
// packages/core-data/build-module/selectors.mjs
|
|
var EMPTY_OBJECT = {};
|
|
var isRequestingEmbedPreview = (0, import_data10.createRegistrySelector)(
|
|
(select5) => (state, url) => {
|
|
return select5(STORE_NAME).isResolving("getEmbedPreview", [
|
|
url
|
|
]);
|
|
}
|
|
);
|
|
function getAuthors(state, query) {
|
|
(0, import_deprecated2.default)("select( 'core' ).getAuthors()", {
|
|
since: "5.9",
|
|
alternative: "select( 'core' ).getUsers({ who: 'authors' })"
|
|
});
|
|
const path = (0, import_url2.addQueryArgs)(
|
|
"/wp/v2/users/?who=authors&per_page=100",
|
|
query
|
|
);
|
|
return getUserQueryResults(state, path);
|
|
}
|
|
function getCurrentUser(state) {
|
|
return state.currentUser;
|
|
}
|
|
var getUserQueryResults = (0, import_data10.createSelector)(
|
|
(state, queryID) => {
|
|
const queryResults = state.users.queries[queryID] ?? [];
|
|
return queryResults.map((id2) => state.users.byId[id2]);
|
|
},
|
|
(state, queryID) => [
|
|
state.users.queries[queryID],
|
|
state.users.byId
|
|
]
|
|
);
|
|
function getEntitiesByKind(state, kind) {
|
|
(0, import_deprecated2.default)("wp.data.select( 'core' ).getEntitiesByKind()", {
|
|
since: "6.0",
|
|
alternative: "wp.data.select( 'core' ).getEntitiesConfig()"
|
|
});
|
|
return getEntitiesConfig(state, kind);
|
|
}
|
|
var getEntitiesConfig = (0, import_data10.createSelector)(
|
|
(state, kind) => state.entities.config.filter((entity2) => entity2.kind === kind),
|
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
(state, kind) => state.entities.config
|
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
);
|
|
function getEntity(state, kind, name) {
|
|
(0, import_deprecated2.default)("wp.data.select( 'core' ).getEntity()", {
|
|
since: "6.0",
|
|
alternative: "wp.data.select( 'core' ).getEntityConfig()"
|
|
});
|
|
return getEntityConfig(state, kind, name);
|
|
}
|
|
function getEntityConfig(state, kind, name) {
|
|
logEntityDeprecation(kind, name, "getEntityConfig");
|
|
return state.entities.config?.find(
|
|
(config) => config.kind === kind && config.name === name
|
|
);
|
|
}
|
|
var getEntityRecord = (0, import_data10.createSelector)(
|
|
((state, kind, name, key, query) => {
|
|
logEntityDeprecation(kind, name, "getEntityRecord");
|
|
const queriedState = state.entities.records?.[kind]?.[name]?.queriedData;
|
|
if (!queriedState) {
|
|
return void 0;
|
|
}
|
|
const context = query?.context ?? "default";
|
|
if (!query || !query._fields) {
|
|
if (!queriedState.itemIsComplete[context]?.[key]) {
|
|
return void 0;
|
|
}
|
|
return queriedState.items[context][key];
|
|
}
|
|
const item = queriedState.items[context]?.[key];
|
|
if (!item) {
|
|
return item;
|
|
}
|
|
const filteredItem = {};
|
|
const fields = get_normalized_comma_separable_default(query._fields) ?? [];
|
|
for (let f = 0; f < fields.length; f++) {
|
|
const field = fields[f].split(".");
|
|
let value = item;
|
|
field.forEach((fieldName) => {
|
|
value = value?.[fieldName];
|
|
});
|
|
setNestedValue(filteredItem, field, value);
|
|
}
|
|
return filteredItem;
|
|
}),
|
|
(state, kind, name, recordId, query) => {
|
|
const context = query?.context ?? "default";
|
|
const queriedState = state.entities.records?.[kind]?.[name]?.queriedData;
|
|
return [
|
|
queriedState?.items[context]?.[recordId],
|
|
queriedState?.itemIsComplete[context]?.[recordId]
|
|
];
|
|
}
|
|
);
|
|
getEntityRecord.__unstableNormalizeArgs = (args2) => {
|
|
const newArgs = [...args2];
|
|
const recordKey = newArgs?.[2];
|
|
newArgs[2] = isNumericID(recordKey) ? Number(recordKey) : recordKey;
|
|
return newArgs;
|
|
};
|
|
function hasEntityRecord(state, kind, name, key, query) {
|
|
const queriedState = state.entities.records?.[kind]?.[name]?.queriedData;
|
|
if (!queriedState) {
|
|
return false;
|
|
}
|
|
const context = query?.context ?? "default";
|
|
if (!query || !query._fields) {
|
|
return !!queriedState.itemIsComplete[context]?.[key];
|
|
}
|
|
const item = queriedState.items[context]?.[key];
|
|
if (!item) {
|
|
return false;
|
|
}
|
|
const fields = get_normalized_comma_separable_default(query._fields) ?? [];
|
|
for (let i = 0; i < fields.length; i++) {
|
|
const path = fields[i].split(".");
|
|
let value = item;
|
|
for (let p = 0; p < path.length; p++) {
|
|
const part = path[p];
|
|
if (!value || !Object.hasOwn(value, part)) {
|
|
return false;
|
|
}
|
|
value = value[part];
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
function __experimentalGetEntityRecordNoResolver(state, kind, name, key) {
|
|
return getEntityRecord(state, kind, name, key);
|
|
}
|
|
var getRawEntityRecord = (0, import_data10.createSelector)(
|
|
(state, kind, name, key) => {
|
|
logEntityDeprecation(kind, name, "getRawEntityRecord");
|
|
const record = getEntityRecord(
|
|
state,
|
|
kind,
|
|
name,
|
|
key
|
|
);
|
|
return record && Object.keys(record).reduce((accumulator, _key) => {
|
|
if (isRawAttribute(getEntityConfig(state, kind, name), _key)) {
|
|
accumulator[_key] = record[_key]?.raw !== void 0 ? record[_key]?.raw : record[_key];
|
|
} else {
|
|
accumulator[_key] = record[_key];
|
|
}
|
|
return accumulator;
|
|
}, {});
|
|
},
|
|
(state, kind, name, recordId, query) => {
|
|
const context = query?.context ?? "default";
|
|
return [
|
|
state.entities.config,
|
|
state.entities.records?.[kind]?.[name]?.queriedData?.items[context]?.[recordId],
|
|
state.entities.records?.[kind]?.[name]?.queriedData?.itemIsComplete[context]?.[recordId]
|
|
];
|
|
}
|
|
);
|
|
function hasEntityRecords(state, kind, name, query) {
|
|
logEntityDeprecation(kind, name, "hasEntityRecords");
|
|
return Array.isArray(getEntityRecords(state, kind, name, query));
|
|
}
|
|
var getEntityRecords = ((state, kind, name, query) => {
|
|
logEntityDeprecation(kind, name, "getEntityRecords");
|
|
const queriedState = state.entities.records?.[kind]?.[name]?.queriedData;
|
|
if (!queriedState) {
|
|
return null;
|
|
}
|
|
return getQueriedItems(queriedState, query);
|
|
});
|
|
var getEntityRecordsTotalItems = (state, kind, name, query) => {
|
|
logEntityDeprecation(kind, name, "getEntityRecordsTotalItems");
|
|
const queriedState = state.entities.records?.[kind]?.[name]?.queriedData;
|
|
if (!queriedState) {
|
|
return null;
|
|
}
|
|
return getQueriedTotalItems(queriedState, query);
|
|
};
|
|
var getEntityRecordsTotalPages = (state, kind, name, query) => {
|
|
logEntityDeprecation(kind, name, "getEntityRecordsTotalPages");
|
|
const queriedState = state.entities.records?.[kind]?.[name]?.queriedData;
|
|
if (!queriedState) {
|
|
return null;
|
|
}
|
|
if (query?.per_page === -1) {
|
|
return 1;
|
|
}
|
|
const totalItems = getQueriedTotalItems(queriedState, query);
|
|
if (!totalItems) {
|
|
return totalItems;
|
|
}
|
|
if (!query?.per_page) {
|
|
return getQueriedTotalPages(queriedState, query);
|
|
}
|
|
return Math.ceil(totalItems / query.per_page);
|
|
};
|
|
var __experimentalGetDirtyEntityRecords = (0, import_data10.createSelector)(
|
|
(state) => {
|
|
const {
|
|
entities: { records }
|
|
} = state;
|
|
const dirtyRecords = [];
|
|
Object.keys(records).forEach((kind) => {
|
|
Object.keys(records[kind]).forEach((name) => {
|
|
const primaryKeys = Object.keys(records[kind][name].edits).filter(
|
|
(primaryKey) => (
|
|
// The entity record must exist (not be deleted),
|
|
// and it must have edits.
|
|
getEntityRecord(state, kind, name, primaryKey) && hasEditsForEntityRecord(state, kind, name, primaryKey)
|
|
)
|
|
);
|
|
if (primaryKeys.length) {
|
|
const entityConfig = getEntityConfig(state, kind, name);
|
|
primaryKeys.forEach((primaryKey) => {
|
|
const entityRecord = getEditedEntityRecord(
|
|
state,
|
|
kind,
|
|
name,
|
|
primaryKey
|
|
);
|
|
dirtyRecords.push({
|
|
// We avoid using primaryKey because it's transformed into a string
|
|
// when it's used as an object key.
|
|
key: entityRecord ? entityRecord[entityConfig.key || DEFAULT_ENTITY_KEY] : void 0,
|
|
title: entityConfig?.getTitle?.(entityRecord) || "",
|
|
name,
|
|
kind
|
|
});
|
|
});
|
|
}
|
|
});
|
|
});
|
|
return dirtyRecords;
|
|
},
|
|
(state) => [state.entities.records]
|
|
);
|
|
var __experimentalGetEntitiesBeingSaved = (0, import_data10.createSelector)(
|
|
(state) => {
|
|
const {
|
|
entities: { records }
|
|
} = state;
|
|
const recordsBeingSaved = [];
|
|
Object.keys(records).forEach((kind) => {
|
|
Object.keys(records[kind]).forEach((name) => {
|
|
const primaryKeys = Object.keys(records[kind][name].saving).filter(
|
|
(primaryKey) => isSavingEntityRecord(state, kind, name, primaryKey)
|
|
);
|
|
if (primaryKeys.length) {
|
|
const entityConfig = getEntityConfig(state, kind, name);
|
|
primaryKeys.forEach((primaryKey) => {
|
|
const entityRecord = getEditedEntityRecord(
|
|
state,
|
|
kind,
|
|
name,
|
|
primaryKey
|
|
);
|
|
recordsBeingSaved.push({
|
|
// We avoid using primaryKey because it's transformed into a string
|
|
// when it's used as an object key.
|
|
key: entityRecord ? entityRecord[entityConfig.key || DEFAULT_ENTITY_KEY] : void 0,
|
|
title: entityConfig?.getTitle?.(entityRecord) || "",
|
|
name,
|
|
kind
|
|
});
|
|
});
|
|
}
|
|
});
|
|
});
|
|
return recordsBeingSaved;
|
|
},
|
|
(state) => [state.entities.records]
|
|
);
|
|
function getEntityRecordEdits(state, kind, name, recordId) {
|
|
logEntityDeprecation(kind, name, "getEntityRecordEdits");
|
|
return state.entities.records?.[kind]?.[name]?.edits?.[recordId];
|
|
}
|
|
var getEntityRecordNonTransientEdits = (0, import_data10.createSelector)(
|
|
(state, kind, name, recordId) => {
|
|
logEntityDeprecation(kind, name, "getEntityRecordNonTransientEdits");
|
|
const { transientEdits } = getEntityConfig(state, kind, name) || {};
|
|
const edits = getEntityRecordEdits(state, kind, name, recordId) || {};
|
|
if (!transientEdits) {
|
|
return edits;
|
|
}
|
|
return Object.keys(edits).reduce((acc, key) => {
|
|
if (!transientEdits[key]) {
|
|
acc[key] = edits[key];
|
|
}
|
|
return acc;
|
|
}, {});
|
|
},
|
|
(state, kind, name, recordId) => [
|
|
state.entities.config,
|
|
state.entities.records?.[kind]?.[name]?.edits?.[recordId]
|
|
]
|
|
);
|
|
function hasEditsForEntityRecord(state, kind, name, recordId) {
|
|
logEntityDeprecation(kind, name, "hasEditsForEntityRecord");
|
|
return isSavingEntityRecord(state, kind, name, recordId) || Object.keys(
|
|
getEntityRecordNonTransientEdits(state, kind, name, recordId)
|
|
).length > 0;
|
|
}
|
|
var getEditedEntityRecord = (0, import_data10.createSelector)(
|
|
(state, kind, name, recordId) => {
|
|
logEntityDeprecation(kind, name, "getEditedEntityRecord");
|
|
const raw = getRawEntityRecord(state, kind, name, recordId);
|
|
const edited = getEntityRecordEdits(state, kind, name, recordId);
|
|
if (!raw && !edited) {
|
|
return false;
|
|
}
|
|
return {
|
|
...raw,
|
|
...edited
|
|
};
|
|
},
|
|
(state, kind, name, recordId, query) => {
|
|
const context = query?.context ?? "default";
|
|
return [
|
|
state.entities.config,
|
|
state.entities.records?.[kind]?.[name]?.queriedData.items[context]?.[recordId],
|
|
state.entities.records?.[kind]?.[name]?.queriedData.itemIsComplete[context]?.[recordId],
|
|
state.entities.records?.[kind]?.[name]?.edits?.[recordId]
|
|
];
|
|
}
|
|
);
|
|
function isAutosavingEntityRecord(state, kind, name, recordId) {
|
|
logEntityDeprecation(kind, name, "isAutosavingEntityRecord");
|
|
const { pending, isAutosave } = state.entities.records?.[kind]?.[name]?.saving?.[recordId] ?? {};
|
|
return Boolean(pending && isAutosave);
|
|
}
|
|
function isSavingEntityRecord(state, kind, name, recordId) {
|
|
logEntityDeprecation(kind, name, "isSavingEntityRecord");
|
|
return state.entities.records?.[kind]?.[name]?.saving?.[recordId]?.pending ?? false;
|
|
}
|
|
function isDeletingEntityRecord(state, kind, name, recordId) {
|
|
logEntityDeprecation(kind, name, "isDeletingEntityRecord");
|
|
return state.entities.records?.[kind]?.[name]?.deleting?.[recordId]?.pending ?? false;
|
|
}
|
|
function getLastEntitySaveError(state, kind, name, recordId) {
|
|
logEntityDeprecation(kind, name, "getLastEntitySaveError");
|
|
return state.entities.records?.[kind]?.[name]?.saving?.[recordId]?.error;
|
|
}
|
|
function getLastEntityDeleteError(state, kind, name, recordId) {
|
|
logEntityDeprecation(kind, name, "getLastEntityDeleteError");
|
|
return state.entities.records?.[kind]?.[name]?.deleting?.[recordId]?.error;
|
|
}
|
|
function getUndoEdit(state) {
|
|
(0, import_deprecated2.default)("select( 'core' ).getUndoEdit()", {
|
|
since: "6.3"
|
|
});
|
|
return void 0;
|
|
}
|
|
function getRedoEdit(state) {
|
|
(0, import_deprecated2.default)("select( 'core' ).getRedoEdit()", {
|
|
since: "6.3"
|
|
});
|
|
return void 0;
|
|
}
|
|
function hasUndo(state) {
|
|
return getUndoManager(state).hasUndo();
|
|
}
|
|
function hasRedo(state) {
|
|
return getUndoManager(state).hasRedo();
|
|
}
|
|
function getCurrentTheme(state) {
|
|
if (!state.currentTheme) {
|
|
return null;
|
|
}
|
|
return getEntityRecord(state, "root", "theme", state.currentTheme);
|
|
}
|
|
function __experimentalGetCurrentGlobalStylesId(state) {
|
|
return state.currentGlobalStylesId;
|
|
}
|
|
function getThemeSupports(state) {
|
|
return getCurrentTheme(state)?.theme_supports ?? EMPTY_OBJECT;
|
|
}
|
|
function getEmbedPreview(state, url) {
|
|
return state.embedPreviews[url];
|
|
}
|
|
function isPreviewEmbedFallback(state, url) {
|
|
const preview = state.embedPreviews[url];
|
|
const oEmbedLinkCheck = '<a href="' + url + '">' + url + "</a>";
|
|
if (!preview) {
|
|
return false;
|
|
}
|
|
return preview.html === oEmbedLinkCheck;
|
|
}
|
|
function canUser(state, action, resource, id2) {
|
|
const isEntity = typeof resource === "object";
|
|
if (isEntity && (!resource.kind || !resource.name)) {
|
|
return false;
|
|
}
|
|
if (isEntity) {
|
|
logEntityDeprecation(resource.kind, resource.name, "canUser");
|
|
}
|
|
const key = getUserPermissionCacheKey(action, resource, id2);
|
|
return state.userPermissions[key];
|
|
}
|
|
function canUserEditEntityRecord(state, kind, name, recordId) {
|
|
(0, import_deprecated2.default)(`wp.data.select( 'core' ).canUserEditEntityRecord()`, {
|
|
since: "6.7",
|
|
alternative: `wp.data.select( 'core' ).canUser( 'update', { kind, name, id } )`
|
|
});
|
|
return canUser(state, "update", { kind, name, id: recordId });
|
|
}
|
|
function getAutosaves(state, postType, postId) {
|
|
return state.autosaves[postId];
|
|
}
|
|
function getAutosave(state, postType, postId, authorId) {
|
|
if (authorId === void 0) {
|
|
return;
|
|
}
|
|
const autosaves2 = state.autosaves[postId];
|
|
return autosaves2?.find(
|
|
(autosave) => autosave.author === authorId
|
|
);
|
|
}
|
|
var hasFetchedAutosaves = (0, import_data10.createRegistrySelector)(
|
|
(select5) => (state, postType, postId) => {
|
|
return select5(STORE_NAME).hasFinishedResolution("getAutosaves", [
|
|
postType,
|
|
postId
|
|
]);
|
|
}
|
|
);
|
|
function getReferenceByDistinctEdits(state) {
|
|
return state.editsReference;
|
|
}
|
|
function __experimentalGetCurrentThemeBaseGlobalStyles(state) {
|
|
const currentTheme2 = getCurrentTheme(state);
|
|
if (!currentTheme2) {
|
|
return null;
|
|
}
|
|
return state.themeBaseGlobalStyles[currentTheme2.stylesheet];
|
|
}
|
|
function __experimentalGetCurrentThemeGlobalStylesVariations(state) {
|
|
const currentTheme2 = getCurrentTheme(state);
|
|
if (!currentTheme2) {
|
|
return null;
|
|
}
|
|
return state.themeGlobalStyleVariations[currentTheme2.stylesheet];
|
|
}
|
|
function getBlockPatterns(state) {
|
|
return state.blockPatterns;
|
|
}
|
|
function getBlockPatternCategories(state) {
|
|
return state.blockPatternCategories;
|
|
}
|
|
function getUserPatternCategories(state) {
|
|
return state.userPatternCategories;
|
|
}
|
|
function getCurrentThemeGlobalStylesRevisions(state) {
|
|
(0, import_deprecated2.default)("select( 'core' ).getCurrentThemeGlobalStylesRevisions()", {
|
|
since: "6.5.0",
|
|
alternative: "select( 'core' ).getRevisions( 'root', 'globalStyles', ${ recordKey } )"
|
|
});
|
|
const currentGlobalStylesId2 = __experimentalGetCurrentGlobalStylesId(state);
|
|
if (!currentGlobalStylesId2) {
|
|
return null;
|
|
}
|
|
return state.themeGlobalStyleRevisions[currentGlobalStylesId2];
|
|
}
|
|
function getDefaultTemplateId(state, query) {
|
|
return state.defaultTemplates[JSON.stringify(query)];
|
|
}
|
|
var getRevisions = (state, kind, name, recordKey, query) => {
|
|
logEntityDeprecation(kind, name, "getRevisions");
|
|
const queriedStateRevisions = state.entities.records?.[kind]?.[name]?.revisions?.[recordKey];
|
|
if (!queriedStateRevisions) {
|
|
return null;
|
|
}
|
|
return getQueriedItems(queriedStateRevisions, query);
|
|
};
|
|
var getRevision = (0, import_data10.createSelector)(
|
|
(state, kind, name, recordKey, revisionKey, query) => {
|
|
logEntityDeprecation(kind, name, "getRevision");
|
|
const queriedState = state.entities.records?.[kind]?.[name]?.revisions?.[recordKey];
|
|
if (!queriedState) {
|
|
return void 0;
|
|
}
|
|
const context = query?.context ?? "default";
|
|
if (!query || !query._fields) {
|
|
if (!queriedState.itemIsComplete[context]?.[revisionKey]) {
|
|
return void 0;
|
|
}
|
|
return queriedState.items[context][revisionKey];
|
|
}
|
|
const item = queriedState.items[context]?.[revisionKey];
|
|
if (!item) {
|
|
return item;
|
|
}
|
|
const filteredItem = {};
|
|
const fields = get_normalized_comma_separable_default(query._fields) ?? [];
|
|
for (let f = 0; f < fields.length; f++) {
|
|
const field = fields[f].split(".");
|
|
let value = item;
|
|
field.forEach((fieldName) => {
|
|
value = value?.[fieldName];
|
|
});
|
|
setNestedValue(filteredItem, field, value);
|
|
}
|
|
return filteredItem;
|
|
},
|
|
(state, kind, name, recordKey, revisionKey, query) => {
|
|
const context = query?.context ?? "default";
|
|
const queriedState = state.entities.records?.[kind]?.[name]?.revisions?.[recordKey];
|
|
return [
|
|
queriedState?.items?.[context]?.[revisionKey],
|
|
queriedState?.itemIsComplete?.[context]?.[revisionKey]
|
|
];
|
|
}
|
|
);
|
|
|
|
// packages/core-data/build-module/actions.mjs
|
|
var actions_exports = {};
|
|
__export(actions_exports, {
|
|
__experimentalBatch: () => __experimentalBatch,
|
|
__experimentalReceiveCurrentGlobalStylesId: () => __experimentalReceiveCurrentGlobalStylesId,
|
|
__experimentalReceiveThemeBaseGlobalStyles: () => __experimentalReceiveThemeBaseGlobalStyles,
|
|
__experimentalReceiveThemeGlobalStyleVariations: () => __experimentalReceiveThemeGlobalStyleVariations,
|
|
__experimentalSaveSpecifiedEntityEdits: () => __experimentalSaveSpecifiedEntityEdits,
|
|
__unstableCreateUndoLevel: () => __unstableCreateUndoLevel,
|
|
addEntities: () => addEntities,
|
|
clearEntityRecordEdits: () => clearEntityRecordEdits,
|
|
deleteEntityRecord: () => deleteEntityRecord,
|
|
editEntityRecord: () => editEntityRecord,
|
|
receiveAutosaves: () => receiveAutosaves,
|
|
receiveCurrentTheme: () => receiveCurrentTheme,
|
|
receiveCurrentUser: () => receiveCurrentUser,
|
|
receiveDefaultTemplateId: () => receiveDefaultTemplateId,
|
|
receiveEmbedPreview: () => receiveEmbedPreview,
|
|
receiveEntityRecords: () => receiveEntityRecords,
|
|
receiveNavigationFallbackId: () => receiveNavigationFallbackId,
|
|
receiveRevisions: () => receiveRevisions,
|
|
receiveThemeGlobalStyleRevisions: () => receiveThemeGlobalStyleRevisions,
|
|
receiveThemeSupports: () => receiveThemeSupports,
|
|
receiveUploadPermissions: () => receiveUploadPermissions,
|
|
receiveUserPermission: () => receiveUserPermission,
|
|
receiveUserPermissions: () => receiveUserPermissions,
|
|
receiveUserQuery: () => receiveUserQuery,
|
|
redo: () => redo,
|
|
saveEditedEntityRecord: () => saveEditedEntityRecord,
|
|
saveEntityRecord: () => saveEntityRecord,
|
|
undo: () => undo
|
|
});
|
|
var import_es67 = __toESM(require_es6(), 1);
|
|
var import_api_fetch4 = __toESM(require_api_fetch(), 1);
|
|
var import_url3 = __toESM(require_url(), 1);
|
|
var import_deprecated3 = __toESM(require_deprecated(), 1);
|
|
|
|
// packages/core-data/build-module/batch/default-processor.mjs
|
|
var import_api_fetch3 = __toESM(require_api_fetch(), 1);
|
|
var maxItems = null;
|
|
function chunk(arr, chunkSize) {
|
|
const tmp = [...arr];
|
|
const cache3 = [];
|
|
while (tmp.length) {
|
|
cache3.push(tmp.splice(0, chunkSize));
|
|
}
|
|
return cache3;
|
|
}
|
|
async function defaultProcessor(requests) {
|
|
if (maxItems === null) {
|
|
const preflightResponse = await (0, import_api_fetch3.default)({
|
|
path: "/batch/v1",
|
|
method: "OPTIONS"
|
|
});
|
|
maxItems = preflightResponse.endpoints[0].args.requests.maxItems;
|
|
}
|
|
const results = [];
|
|
for (const batchRequests of chunk(requests, maxItems)) {
|
|
const batchResponse = await (0, import_api_fetch3.default)({
|
|
path: "/batch/v1",
|
|
method: "POST",
|
|
data: {
|
|
validation: "require-all-validate",
|
|
requests: batchRequests.map((request) => ({
|
|
path: request.path,
|
|
body: request.data,
|
|
// Rename 'data' to 'body'.
|
|
method: request.method,
|
|
headers: request.headers
|
|
}))
|
|
}
|
|
});
|
|
let batchResults;
|
|
if (batchResponse.failed) {
|
|
batchResults = batchResponse.responses.map((response) => ({
|
|
error: response?.body
|
|
}));
|
|
} else {
|
|
batchResults = batchResponse.responses.map((response) => {
|
|
const result = {};
|
|
if (response.status >= 200 && response.status < 300) {
|
|
result.output = response.body;
|
|
} else {
|
|
result.error = response.body;
|
|
}
|
|
return result;
|
|
});
|
|
}
|
|
results.push(...batchResults);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
// packages/core-data/build-module/batch/create-batch.mjs
|
|
function createBatch(processor = defaultProcessor) {
|
|
let lastId = 0;
|
|
let queue = [];
|
|
const pending = new ObservableSet();
|
|
return {
|
|
/**
|
|
* Adds an input to the batch and returns a promise that is resolved or
|
|
* rejected when the input is processed by `batch.run()`.
|
|
*
|
|
* You may also pass a thunk which allows inputs to be added
|
|
* asynchronously.
|
|
*
|
|
* ```
|
|
* // Both are allowed:
|
|
* batch.add( { path: '/v1/books', ... } );
|
|
* batch.add( ( add ) => add( { path: '/v1/books', ... } ) );
|
|
* ```
|
|
*
|
|
* If a thunk is passed, `batch.run()` will pause until either:
|
|
*
|
|
* - The thunk calls its `add` argument, or;
|
|
* - The thunk returns a promise and that promise resolves, or;
|
|
* - The thunk returns a non-promise.
|
|
*
|
|
* @param {any|Function} inputOrThunk Input to add or thunk to execute.
|
|
*
|
|
* @return {Promise|any} If given an input, returns a promise that
|
|
* is resolved or rejected when the batch is
|
|
* processed. If given a thunk, returns the return
|
|
* value of that thunk.
|
|
*/
|
|
add(inputOrThunk) {
|
|
const id2 = ++lastId;
|
|
pending.add(id2);
|
|
const add = (input) => new Promise((resolve, reject) => {
|
|
queue.push({
|
|
input,
|
|
resolve,
|
|
reject
|
|
});
|
|
pending.delete(id2);
|
|
});
|
|
if (typeof inputOrThunk === "function") {
|
|
return Promise.resolve(inputOrThunk(add)).finally(() => {
|
|
pending.delete(id2);
|
|
});
|
|
}
|
|
return add(inputOrThunk);
|
|
},
|
|
/**
|
|
* Runs the batch. This calls `batchProcessor` and resolves or rejects
|
|
* all promises returned by `add()`.
|
|
*
|
|
* @return {Promise<boolean>} A promise that resolves to a boolean that is true
|
|
* if the processor returned no errors.
|
|
*/
|
|
async run() {
|
|
if (pending.size) {
|
|
await new Promise((resolve) => {
|
|
const unsubscribe = pending.subscribe(() => {
|
|
if (!pending.size) {
|
|
unsubscribe();
|
|
resolve(void 0);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
let results;
|
|
try {
|
|
results = await processor(
|
|
queue.map(({ input }) => input)
|
|
);
|
|
if (results.length !== queue.length) {
|
|
throw new Error(
|
|
"run: Array returned by processor must be same size as input array."
|
|
);
|
|
}
|
|
} catch (error) {
|
|
for (const { reject } of queue) {
|
|
reject(error);
|
|
}
|
|
throw error;
|
|
}
|
|
let isSuccess = true;
|
|
results.forEach((result, key) => {
|
|
const queueItem = queue[key];
|
|
if (result?.error) {
|
|
queueItem?.reject(result.error);
|
|
isSuccess = false;
|
|
} else {
|
|
queueItem?.resolve(result?.output ?? result);
|
|
}
|
|
});
|
|
queue = [];
|
|
return isSuccess;
|
|
}
|
|
};
|
|
}
|
|
var ObservableSet = class {
|
|
constructor(...args2) {
|
|
this.set = new Set(...args2);
|
|
this.subscribers = /* @__PURE__ */ new Set();
|
|
}
|
|
get size() {
|
|
return this.set.size;
|
|
}
|
|
add(value) {
|
|
this.set.add(value);
|
|
this.subscribers.forEach((subscriber) => subscriber());
|
|
return this;
|
|
}
|
|
delete(value) {
|
|
const isSuccess = this.set.delete(value);
|
|
this.subscribers.forEach((subscriber) => subscriber());
|
|
return isSuccess;
|
|
}
|
|
subscribe(subscriber) {
|
|
this.subscribers.add(subscriber);
|
|
return () => {
|
|
this.subscribers.delete(subscriber);
|
|
};
|
|
}
|
|
};
|
|
|
|
// packages/core-data/build-module/actions.mjs
|
|
function addTitleToAutoDraft(record) {
|
|
return record.status === "auto-draft" ? { ...record, title: "" } : record;
|
|
}
|
|
function receiveUserQuery(queryID, users2) {
|
|
return {
|
|
type: "RECEIVE_USER_QUERY",
|
|
users: Array.isArray(users2) ? users2 : [users2],
|
|
queryID
|
|
};
|
|
}
|
|
function receiveCurrentUser(currentUser2) {
|
|
return {
|
|
type: "RECEIVE_CURRENT_USER",
|
|
currentUser: currentUser2
|
|
};
|
|
}
|
|
function addEntities(entities2) {
|
|
return {
|
|
type: "ADD_ENTITIES",
|
|
entities: entities2
|
|
};
|
|
}
|
|
function receiveEntityRecords(kind, name, records, query = void 0, invalidateCache = false, edits = void 0, meta = void 0) {
|
|
if (kind === "postType") {
|
|
records = Array.isArray(records) ? records.map(addTitleToAutoDraft) : addTitleToAutoDraft(records);
|
|
}
|
|
let action;
|
|
if (query) {
|
|
action = receiveQueriedItems(records, query, edits, meta);
|
|
} else {
|
|
action = receiveItems(records, edits, meta);
|
|
}
|
|
return {
|
|
...action,
|
|
kind,
|
|
name,
|
|
invalidateCache
|
|
};
|
|
}
|
|
function receiveCurrentTheme(currentTheme2) {
|
|
return {
|
|
type: "RECEIVE_CURRENT_THEME",
|
|
currentTheme: currentTheme2
|
|
};
|
|
}
|
|
function __experimentalReceiveCurrentGlobalStylesId(currentGlobalStylesId2) {
|
|
return {
|
|
type: "RECEIVE_CURRENT_GLOBAL_STYLES_ID",
|
|
id: currentGlobalStylesId2
|
|
};
|
|
}
|
|
function __experimentalReceiveThemeBaseGlobalStyles(stylesheet, globalStyles) {
|
|
return {
|
|
type: "RECEIVE_THEME_GLOBAL_STYLES",
|
|
stylesheet,
|
|
globalStyles
|
|
};
|
|
}
|
|
function __experimentalReceiveThemeGlobalStyleVariations(stylesheet, variations) {
|
|
return {
|
|
type: "RECEIVE_THEME_GLOBAL_STYLE_VARIATIONS",
|
|
stylesheet,
|
|
variations
|
|
};
|
|
}
|
|
function receiveThemeSupports() {
|
|
(0, import_deprecated3.default)("wp.data.dispatch( 'core' ).receiveThemeSupports", {
|
|
since: "5.9"
|
|
});
|
|
return {
|
|
type: "DO_NOTHING"
|
|
};
|
|
}
|
|
function receiveThemeGlobalStyleRevisions(currentId, revisions) {
|
|
(0, import_deprecated3.default)(
|
|
"wp.data.dispatch( 'core' ).receiveThemeGlobalStyleRevisions()",
|
|
{
|
|
since: "6.5.0",
|
|
alternative: "wp.data.dispatch( 'core' ).receiveRevisions"
|
|
}
|
|
);
|
|
return {
|
|
type: "RECEIVE_THEME_GLOBAL_STYLE_REVISIONS",
|
|
currentId,
|
|
revisions
|
|
};
|
|
}
|
|
function receiveEmbedPreview(url, preview) {
|
|
return {
|
|
type: "RECEIVE_EMBED_PREVIEW",
|
|
url,
|
|
preview
|
|
};
|
|
}
|
|
var deleteEntityRecord = (kind, name, recordId, query, { __unstableFetch = import_api_fetch4.default, throwOnError = false } = {}) => async ({ dispatch: dispatch3, resolveSelect: resolveSelect2 }) => {
|
|
logEntityDeprecation(kind, name, "deleteEntityRecord");
|
|
const configs = await resolveSelect2.getEntitiesConfig(kind);
|
|
const entityConfig = configs.find(
|
|
(config) => config.kind === kind && config.name === name
|
|
);
|
|
let error;
|
|
let deletedRecord = false;
|
|
if (!entityConfig) {
|
|
return;
|
|
}
|
|
const lock3 = await dispatch3.__unstableAcquireStoreLock(
|
|
STORE_NAME,
|
|
["entities", "records", kind, name, recordId],
|
|
{ exclusive: true }
|
|
);
|
|
try {
|
|
dispatch3({
|
|
type: "DELETE_ENTITY_RECORD_START",
|
|
kind,
|
|
name,
|
|
recordId
|
|
});
|
|
let hasError = false;
|
|
let { baseURL } = entityConfig;
|
|
if (kind === "postType" && name === "wp_template" && (recordId && typeof recordId === "string" && !/^\d+$/.test(recordId) || !window?.__experimentalTemplateActivate)) {
|
|
baseURL = baseURL.slice(0, baseURL.lastIndexOf("/")) + "/templates";
|
|
}
|
|
try {
|
|
let path = `${baseURL}/${recordId}`;
|
|
if (query) {
|
|
path = (0, import_url3.addQueryArgs)(path, query);
|
|
}
|
|
deletedRecord = await __unstableFetch({
|
|
path,
|
|
method: "DELETE"
|
|
});
|
|
await dispatch3(removeItems(kind, name, recordId, true));
|
|
if (entityConfig.syncConfig) {
|
|
const objectType = `${kind}/${name}`;
|
|
const objectId = recordId;
|
|
getSyncManager()?.unload(objectType, objectId);
|
|
}
|
|
} catch (_error) {
|
|
hasError = true;
|
|
error = _error;
|
|
}
|
|
dispatch3({
|
|
type: "DELETE_ENTITY_RECORD_FINISH",
|
|
kind,
|
|
name,
|
|
recordId,
|
|
error
|
|
});
|
|
if (hasError && throwOnError) {
|
|
throw error;
|
|
}
|
|
return deletedRecord;
|
|
} finally {
|
|
dispatch3.__unstableReleaseStoreLock(lock3);
|
|
}
|
|
};
|
|
var editEntityRecord = (kind, name, recordId, edits, options = {}) => ({ select: select5, dispatch: dispatch3 }) => {
|
|
logEntityDeprecation(kind, name, "editEntityRecord");
|
|
const entityConfig = select5.getEntityConfig(kind, name);
|
|
if (!entityConfig) {
|
|
throw new Error(
|
|
`The entity being edited (${kind}, ${name}) does not have a loaded config.`
|
|
);
|
|
}
|
|
const { mergedEdits = {} } = entityConfig;
|
|
const record = select5.getRawEntityRecord(kind, name, recordId);
|
|
const editedRecord = select5.getEditedEntityRecord(
|
|
kind,
|
|
name,
|
|
recordId
|
|
);
|
|
const editsWithMerges = Object.keys(edits).reduce((acc, key) => {
|
|
acc[key] = mergedEdits[key] ? { ...editedRecord[key], ...edits[key] } : edits[key];
|
|
return acc;
|
|
}, {});
|
|
const edit = {
|
|
kind,
|
|
name,
|
|
recordId,
|
|
// Clear edits when they are equal to their persisted counterparts
|
|
// so that the property is not considered dirty.
|
|
edits: Object.keys(edits).reduce((acc, key) => {
|
|
const recordValue = record[key];
|
|
const value = editsWithMerges[key];
|
|
acc[key] = (0, import_es67.default)(recordValue, value) ? void 0 : value;
|
|
return acc;
|
|
}, {})
|
|
};
|
|
if (entityConfig.syncConfig) {
|
|
const objectType = `${kind}/${name}`;
|
|
const objectId = recordId;
|
|
const isNewUndoLevel = options.undoIgnore ? false : !options.isCached;
|
|
const origin2 = options.undoIgnore ? LOCAL_UNDO_IGNORED_ORIGIN2 : LOCAL_EDITOR_ORIGIN2;
|
|
getSyncManager()?.update(
|
|
objectType,
|
|
objectId,
|
|
editsWithMerges,
|
|
origin2,
|
|
{ isNewUndoLevel }
|
|
);
|
|
}
|
|
if (!options.undoIgnore) {
|
|
select5.getUndoManager().addRecord(
|
|
[
|
|
{
|
|
id: { kind, name, recordId },
|
|
changes: Object.keys(edits).reduce((acc, key) => {
|
|
acc[key] = {
|
|
from: editedRecord[key],
|
|
to: edits[key]
|
|
};
|
|
return acc;
|
|
}, {})
|
|
}
|
|
],
|
|
options.isCached
|
|
);
|
|
}
|
|
dispatch3({
|
|
type: "EDIT_ENTITY_RECORD",
|
|
...edit
|
|
});
|
|
};
|
|
var clearEntityRecordEdits = (kind, name, recordId) => ({ select: select5, dispatch: dispatch3 }) => {
|
|
const entityConfig = select5.getEntityConfig(kind, name);
|
|
logEntityDeprecation(kind, name, "clearEntityRecordEdits");
|
|
if (!entityConfig) {
|
|
throw new Error(
|
|
`The entity being edited (${kind}, ${name}) does not have a loaded config.`
|
|
);
|
|
}
|
|
const currentEdits = select5.getEntityRecordEdits(
|
|
kind,
|
|
name,
|
|
recordId
|
|
);
|
|
if (!currentEdits) {
|
|
return;
|
|
}
|
|
const clearedEdits = Object.keys(currentEdits).reduce(
|
|
(acc, key) => {
|
|
acc[key] = void 0;
|
|
return acc;
|
|
},
|
|
{}
|
|
);
|
|
dispatch3({
|
|
type: "EDIT_ENTITY_RECORD",
|
|
kind,
|
|
name,
|
|
recordId,
|
|
edits: clearedEdits
|
|
});
|
|
};
|
|
var undo = () => ({ select: select5, dispatch: dispatch3 }) => {
|
|
const undoRecord = select5.getUndoManager().undo();
|
|
if (!undoRecord) {
|
|
return;
|
|
}
|
|
dispatch3({
|
|
type: "UNDO",
|
|
record: undoRecord
|
|
});
|
|
};
|
|
var redo = () => ({ select: select5, dispatch: dispatch3 }) => {
|
|
const redoRecord = select5.getUndoManager().redo();
|
|
if (!redoRecord) {
|
|
return;
|
|
}
|
|
dispatch3({
|
|
type: "REDO",
|
|
record: redoRecord
|
|
});
|
|
};
|
|
var __unstableCreateUndoLevel = () => ({ select: select5 }) => {
|
|
select5.getUndoManager().addRecord();
|
|
};
|
|
var saveEntityRecord = (kind, name, record, {
|
|
isAutosave = false,
|
|
__unstableFetch = import_api_fetch4.default,
|
|
throwOnError = false
|
|
} = {}) => async ({ select: select5, resolveSelect: resolveSelect2, dispatch: dispatch3 }) => {
|
|
logEntityDeprecation(kind, name, "saveEntityRecord");
|
|
const configs = await resolveSelect2.getEntitiesConfig(kind);
|
|
const entityConfig = configs.find(
|
|
(config) => config.kind === kind && config.name === name
|
|
);
|
|
if (!entityConfig) {
|
|
return;
|
|
}
|
|
const entityIdKey = entityConfig.key ?? DEFAULT_ENTITY_KEY;
|
|
const recordId = record[entityIdKey];
|
|
const isNewRecord = !!entityIdKey && !recordId;
|
|
const lock3 = await dispatch3.__unstableAcquireStoreLock(
|
|
STORE_NAME,
|
|
["entities", "records", kind, name, recordId || v4_default()],
|
|
{ exclusive: true }
|
|
);
|
|
try {
|
|
for (const [key, value] of Object.entries(record)) {
|
|
if (typeof value === "function") {
|
|
const evaluatedValue = value(
|
|
select5.getEditedEntityRecord(kind, name, recordId)
|
|
);
|
|
dispatch3.editEntityRecord(
|
|
kind,
|
|
name,
|
|
recordId,
|
|
{
|
|
[key]: evaluatedValue
|
|
},
|
|
{ undoIgnore: true }
|
|
);
|
|
record[key] = evaluatedValue;
|
|
}
|
|
}
|
|
dispatch3({
|
|
type: "SAVE_ENTITY_RECORD_START",
|
|
kind,
|
|
name,
|
|
recordId,
|
|
isAutosave
|
|
});
|
|
let updatedRecord;
|
|
let error;
|
|
let hasError = false;
|
|
let { baseURL } = entityConfig;
|
|
if (kind === "postType" && name === "wp_template" && (recordId && typeof recordId === "string" && !/^\d+$/.test(recordId) || !window?.__experimentalTemplateActivate)) {
|
|
baseURL = baseURL.slice(0, baseURL.lastIndexOf("/")) + "/templates";
|
|
}
|
|
try {
|
|
const path = `${baseURL}${recordId ? "/" + recordId : ""}`;
|
|
const persistedRecord = !isNewRecord ? select5.getRawEntityRecord(kind, name, recordId) : {};
|
|
if (isAutosave) {
|
|
const currentUser2 = select5.getCurrentUser();
|
|
const currentUserId = currentUser2 ? currentUser2.id : void 0;
|
|
const autosavePost = await resolveSelect2.getAutosave(
|
|
persistedRecord.type,
|
|
persistedRecord.id,
|
|
currentUserId
|
|
);
|
|
let data = {
|
|
...persistedRecord,
|
|
...autosavePost,
|
|
...record
|
|
};
|
|
data = Object.keys(data).reduce(
|
|
(acc, key) => {
|
|
if ([
|
|
"title",
|
|
"excerpt",
|
|
"content",
|
|
"meta"
|
|
].includes(key)) {
|
|
acc[key] = data[key];
|
|
}
|
|
return acc;
|
|
},
|
|
{
|
|
// Do not update the `status` if we have edited it when auto saving.
|
|
// It's very important to let the user explicitly save this change,
|
|
// because it can lead to unexpected results. An example would be to
|
|
// have a draft post and change the status to publish.
|
|
status: data.status === "auto-draft" ? "draft" : void 0
|
|
}
|
|
);
|
|
updatedRecord = await __unstableFetch({
|
|
path: `${path}/autosaves`,
|
|
method: "POST",
|
|
data
|
|
});
|
|
if (persistedRecord.id === updatedRecord.id) {
|
|
let newRecord = {
|
|
...persistedRecord,
|
|
...data,
|
|
...updatedRecord
|
|
};
|
|
newRecord = Object.keys(newRecord).reduce(
|
|
(acc, key) => {
|
|
if (["title", "excerpt", "content"].includes(
|
|
key
|
|
)) {
|
|
acc[key] = newRecord[key];
|
|
} else if (key === "status") {
|
|
acc[key] = persistedRecord.status === "auto-draft" && newRecord.status === "draft" ? newRecord.status : persistedRecord.status;
|
|
} else {
|
|
acc[key] = persistedRecord[key];
|
|
}
|
|
return acc;
|
|
},
|
|
{}
|
|
);
|
|
dispatch3.receiveEntityRecords(
|
|
kind,
|
|
name,
|
|
newRecord,
|
|
void 0,
|
|
true
|
|
);
|
|
} else {
|
|
dispatch3.receiveAutosaves(
|
|
persistedRecord.id,
|
|
updatedRecord
|
|
);
|
|
}
|
|
} else {
|
|
let edits = record;
|
|
if (entityConfig.__unstablePrePersist) {
|
|
edits = {
|
|
...edits,
|
|
...await entityConfig.__unstablePrePersist(
|
|
persistedRecord,
|
|
edits
|
|
)
|
|
};
|
|
}
|
|
updatedRecord = await __unstableFetch({
|
|
path,
|
|
method: recordId ? "PUT" : "POST",
|
|
data: edits
|
|
});
|
|
dispatch3.receiveEntityRecords(
|
|
kind,
|
|
name,
|
|
updatedRecord,
|
|
void 0,
|
|
true,
|
|
edits
|
|
);
|
|
if (entityConfig.syncConfig) {
|
|
getSyncManager()?.update(
|
|
`${kind}/${name}`,
|
|
recordId,
|
|
updatedRecord,
|
|
LOCAL_UNDO_IGNORED_ORIGIN2,
|
|
{ isSave: true }
|
|
);
|
|
}
|
|
}
|
|
} catch (_error) {
|
|
hasError = true;
|
|
error = _error;
|
|
}
|
|
dispatch3({
|
|
type: "SAVE_ENTITY_RECORD_FINISH",
|
|
kind,
|
|
name,
|
|
recordId,
|
|
error,
|
|
isAutosave
|
|
});
|
|
if (hasError && throwOnError) {
|
|
throw error;
|
|
}
|
|
return updatedRecord;
|
|
} finally {
|
|
dispatch3.__unstableReleaseStoreLock(lock3);
|
|
}
|
|
};
|
|
var __experimentalBatch = (requests) => async ({ dispatch: dispatch3 }) => {
|
|
const batch = createBatch();
|
|
const api = {
|
|
saveEntityRecord(kind, name, record, options) {
|
|
return batch.add(
|
|
(add) => dispatch3.saveEntityRecord(kind, name, record, {
|
|
...options,
|
|
__unstableFetch: add
|
|
})
|
|
);
|
|
},
|
|
saveEditedEntityRecord(kind, name, recordId, options) {
|
|
return batch.add(
|
|
(add) => dispatch3.saveEditedEntityRecord(kind, name, recordId, {
|
|
...options,
|
|
__unstableFetch: add
|
|
})
|
|
);
|
|
},
|
|
deleteEntityRecord(kind, name, recordId, query, options) {
|
|
return batch.add(
|
|
(add) => dispatch3.deleteEntityRecord(kind, name, recordId, query, {
|
|
...options,
|
|
__unstableFetch: add
|
|
})
|
|
);
|
|
}
|
|
};
|
|
const resultPromises = requests.map((request) => request(api));
|
|
const [, ...results] = await Promise.all([
|
|
batch.run(),
|
|
...resultPromises
|
|
]);
|
|
return results;
|
|
};
|
|
var saveEditedEntityRecord = (kind, name, recordId, options) => async ({ select: select5, dispatch: dispatch3, resolveSelect: resolveSelect2 }) => {
|
|
logEntityDeprecation(kind, name, "saveEditedEntityRecord");
|
|
if (!select5.hasEditsForEntityRecord(kind, name, recordId)) {
|
|
return;
|
|
}
|
|
const configs = await resolveSelect2.getEntitiesConfig(kind);
|
|
const entityConfig = configs.find(
|
|
(config) => config.kind === kind && config.name === name
|
|
);
|
|
if (!entityConfig) {
|
|
return;
|
|
}
|
|
const entityIdKey = entityConfig.key || DEFAULT_ENTITY_KEY;
|
|
const edits = select5.getEntityRecordNonTransientEdits(
|
|
kind,
|
|
name,
|
|
recordId
|
|
);
|
|
const record = { [entityIdKey]: recordId, ...edits };
|
|
return await dispatch3.saveEntityRecord(kind, name, record, options);
|
|
};
|
|
var __experimentalSaveSpecifiedEntityEdits = (kind, name, recordId, itemsToSave, options) => async ({ select: select5, dispatch: dispatch3, resolveSelect: resolveSelect2 }) => {
|
|
logEntityDeprecation(
|
|
kind,
|
|
name,
|
|
"__experimentalSaveSpecifiedEntityEdits"
|
|
);
|
|
if (!select5.hasEditsForEntityRecord(kind, name, recordId)) {
|
|
return;
|
|
}
|
|
const edits = select5.getEntityRecordNonTransientEdits(
|
|
kind,
|
|
name,
|
|
recordId
|
|
);
|
|
const editsToSave = {};
|
|
for (const item of itemsToSave) {
|
|
setNestedValue(editsToSave, item, getNestedValue(edits, item));
|
|
}
|
|
const configs = await resolveSelect2.getEntitiesConfig(kind);
|
|
const entityConfig = configs.find(
|
|
(config) => config.kind === kind && config.name === name
|
|
);
|
|
const entityIdKey = entityConfig?.key || DEFAULT_ENTITY_KEY;
|
|
if (recordId) {
|
|
editsToSave[entityIdKey] = recordId;
|
|
}
|
|
return await dispatch3.saveEntityRecord(
|
|
kind,
|
|
name,
|
|
editsToSave,
|
|
options
|
|
);
|
|
};
|
|
function receiveUploadPermissions(hasUploadPermissions) {
|
|
(0, import_deprecated3.default)("wp.data.dispatch( 'core' ).receiveUploadPermissions", {
|
|
since: "5.9",
|
|
alternative: "receiveUserPermission"
|
|
});
|
|
return receiveUserPermission("create/media", hasUploadPermissions);
|
|
}
|
|
function receiveUserPermission(key, isAllowed) {
|
|
return {
|
|
type: "RECEIVE_USER_PERMISSION",
|
|
key,
|
|
isAllowed
|
|
};
|
|
}
|
|
function receiveUserPermissions(permissions) {
|
|
return {
|
|
type: "RECEIVE_USER_PERMISSIONS",
|
|
permissions
|
|
};
|
|
}
|
|
function receiveAutosaves(postId, autosaves2) {
|
|
return {
|
|
type: "RECEIVE_AUTOSAVES",
|
|
postId,
|
|
autosaves: Array.isArray(autosaves2) ? autosaves2 : [autosaves2]
|
|
};
|
|
}
|
|
function receiveNavigationFallbackId(fallbackId) {
|
|
return {
|
|
type: "RECEIVE_NAVIGATION_FALLBACK_ID",
|
|
fallbackId
|
|
};
|
|
}
|
|
function receiveDefaultTemplateId(query, templateId) {
|
|
return {
|
|
type: "RECEIVE_DEFAULT_TEMPLATE",
|
|
query,
|
|
templateId
|
|
};
|
|
}
|
|
var receiveRevisions = (kind, name, recordKey, records, query, invalidateCache = false, meta) => async ({ dispatch: dispatch3, resolveSelect: resolveSelect2 }) => {
|
|
logEntityDeprecation(kind, name, "receiveRevisions");
|
|
const configs = await resolveSelect2.getEntitiesConfig(kind);
|
|
const entityConfig = configs.find(
|
|
(config) => config.kind === kind && config.name === name
|
|
);
|
|
const key = entityConfig && entityConfig?.revisionKey ? entityConfig.revisionKey : DEFAULT_ENTITY_KEY;
|
|
dispatch3({
|
|
type: "RECEIVE_ITEM_REVISIONS",
|
|
key,
|
|
items: records,
|
|
recordKey,
|
|
meta,
|
|
query,
|
|
kind,
|
|
name,
|
|
invalidateCache
|
|
});
|
|
};
|
|
|
|
// packages/core-data/build-module/private-actions.mjs
|
|
var private_actions_exports = {};
|
|
__export(private_actions_exports, {
|
|
editMediaEntity: () => editMediaEntity,
|
|
receiveEditorAssets: () => receiveEditorAssets,
|
|
receiveEditorSettings: () => receiveEditorSettings,
|
|
receiveRegisteredPostMeta: () => receiveRegisteredPostMeta,
|
|
setCollaborationSupported: () => setCollaborationSupported,
|
|
setSyncConnectionStatus: () => setSyncConnectionStatus
|
|
});
|
|
var import_api_fetch5 = __toESM(require_api_fetch(), 1);
|
|
function receiveRegisteredPostMeta(postType, registeredPostMeta2) {
|
|
return {
|
|
type: "RECEIVE_REGISTERED_POST_META",
|
|
postType,
|
|
registeredPostMeta: registeredPostMeta2
|
|
};
|
|
}
|
|
var editMediaEntity = (recordId, edits = {}, { __unstableFetch = import_api_fetch5.default, throwOnError = false } = {}) => async ({ dispatch: dispatch3, resolveSelect: resolveSelect2 }) => {
|
|
if (!recordId) {
|
|
return;
|
|
}
|
|
const kind = "postType";
|
|
const name = "attachment";
|
|
const configs = await resolveSelect2.getEntitiesConfig(kind);
|
|
const entityConfig = configs.find(
|
|
(config) => config.kind === kind && config.name === name
|
|
);
|
|
if (!entityConfig) {
|
|
return;
|
|
}
|
|
const lock3 = await dispatch3.__unstableAcquireStoreLock(
|
|
STORE_NAME,
|
|
["entities", "records", kind, name, recordId],
|
|
{ exclusive: true }
|
|
);
|
|
let updatedRecord;
|
|
let error;
|
|
let hasError = false;
|
|
try {
|
|
dispatch3({
|
|
type: "SAVE_ENTITY_RECORD_START",
|
|
kind,
|
|
name,
|
|
recordId
|
|
});
|
|
try {
|
|
const path = `${entityConfig.baseURL}/${recordId}/edit`;
|
|
const newRecord = await __unstableFetch({
|
|
path,
|
|
method: "POST",
|
|
data: {
|
|
...edits
|
|
}
|
|
});
|
|
if (newRecord) {
|
|
dispatch3.receiveEntityRecords(
|
|
kind,
|
|
name,
|
|
newRecord,
|
|
void 0,
|
|
true,
|
|
void 0,
|
|
void 0
|
|
);
|
|
updatedRecord = newRecord;
|
|
}
|
|
} catch (e) {
|
|
error = e;
|
|
hasError = true;
|
|
}
|
|
dispatch3({
|
|
type: "SAVE_ENTITY_RECORD_FINISH",
|
|
kind,
|
|
name,
|
|
recordId,
|
|
error
|
|
});
|
|
if (hasError && throwOnError) {
|
|
throw error;
|
|
}
|
|
return updatedRecord;
|
|
} finally {
|
|
dispatch3.__unstableReleaseStoreLock(lock3);
|
|
}
|
|
};
|
|
function receiveEditorSettings(settings) {
|
|
return {
|
|
type: "RECEIVE_EDITOR_SETTINGS",
|
|
settings
|
|
};
|
|
}
|
|
function receiveEditorAssets(assets) {
|
|
return {
|
|
type: "RECEIVE_EDITOR_ASSETS",
|
|
assets
|
|
};
|
|
}
|
|
var setCollaborationSupported = (supported) => ({ dispatch: dispatch3 }) => {
|
|
dispatch3({ type: "SET_COLLABORATION_SUPPORTED", supported });
|
|
};
|
|
function setSyncConnectionStatus(kind, name, key, status) {
|
|
if (!status) {
|
|
return {
|
|
type: "CLEAR_SYNC_CONNECTION_STATUS",
|
|
kind,
|
|
name,
|
|
key
|
|
};
|
|
}
|
|
return {
|
|
type: "SET_SYNC_CONNECTION_STATUS",
|
|
kind,
|
|
name,
|
|
key,
|
|
status
|
|
};
|
|
}
|
|
|
|
// packages/core-data/build-module/resolvers.mjs
|
|
var resolvers_exports = {};
|
|
__export(resolvers_exports, {
|
|
__experimentalGetCurrentGlobalStylesId: () => __experimentalGetCurrentGlobalStylesId2,
|
|
__experimentalGetCurrentThemeBaseGlobalStyles: () => __experimentalGetCurrentThemeBaseGlobalStyles2,
|
|
__experimentalGetCurrentThemeGlobalStylesVariations: () => __experimentalGetCurrentThemeGlobalStylesVariations2,
|
|
canUser: () => canUser2,
|
|
canUserEditEntityRecord: () => canUserEditEntityRecord2,
|
|
getAuthors: () => getAuthors2,
|
|
getAutosave: () => getAutosave2,
|
|
getAutosaves: () => getAutosaves2,
|
|
getBlockPatternCategories: () => getBlockPatternCategories2,
|
|
getBlockPatterns: () => getBlockPatterns2,
|
|
getCurrentTheme: () => getCurrentTheme2,
|
|
getCurrentThemeGlobalStylesRevisions: () => getCurrentThemeGlobalStylesRevisions2,
|
|
getCurrentUser: () => getCurrentUser2,
|
|
getDefaultTemplateId: () => getDefaultTemplateId2,
|
|
getEditedEntityRecord: () => getEditedEntityRecord2,
|
|
getEditorAssets: () => getEditorAssets2,
|
|
getEditorSettings: () => getEditorSettings2,
|
|
getEmbedPreview: () => getEmbedPreview2,
|
|
getEntitiesConfig: () => getEntitiesConfig2,
|
|
getEntityRecord: () => getEntityRecord2,
|
|
getEntityRecords: () => getEntityRecords2,
|
|
getEntityRecordsTotalItems: () => getEntityRecordsTotalItems2,
|
|
getEntityRecordsTotalPages: () => getEntityRecordsTotalPages2,
|
|
getNavigationFallbackId: () => getNavigationFallbackId2,
|
|
getRawEntityRecord: () => getRawEntityRecord2,
|
|
getRegisteredPostMeta: () => getRegisteredPostMeta2,
|
|
getRevision: () => getRevision2,
|
|
getRevisions: () => getRevisions2,
|
|
getThemeSupports: () => getThemeSupports2,
|
|
getUserPatternCategories: () => getUserPatternCategories2
|
|
});
|
|
var import_url6 = __toESM(require_url(), 1);
|
|
var import_html_entities2 = __toESM(require_html_entities(), 1);
|
|
var import_api_fetch9 = __toESM(require_api_fetch(), 1);
|
|
|
|
// packages/core-data/build-module/fetch/index.mjs
|
|
var import_api_fetch8 = __toESM(require_api_fetch(), 1);
|
|
|
|
// packages/core-data/build-module/fetch/__experimental-fetch-link-suggestions.mjs
|
|
var import_api_fetch6 = __toESM(require_api_fetch(), 1);
|
|
var import_url4 = __toESM(require_url(), 1);
|
|
var import_html_entities = __toESM(require_html_entities(), 1);
|
|
var import_i18n2 = __toESM(require_i18n(), 1);
|
|
async function fetchLinkSuggestions(search, searchOptions = {}, editorSettings2 = {}) {
|
|
const searchOptionsToUse = searchOptions.isInitialSuggestions && searchOptions.initialSuggestionsSearchOptions ? {
|
|
...searchOptions,
|
|
...searchOptions.initialSuggestionsSearchOptions
|
|
} : searchOptions;
|
|
const {
|
|
type,
|
|
subtype,
|
|
page,
|
|
perPage = searchOptions.isInitialSuggestions ? 3 : 20
|
|
} = searchOptionsToUse;
|
|
const { disablePostFormats = false } = editorSettings2;
|
|
const queries2 = [];
|
|
if (!type || type === "post") {
|
|
queries2.push(
|
|
(0, import_api_fetch6.default)({
|
|
path: (0, import_url4.addQueryArgs)("/wp/v2/search", {
|
|
search,
|
|
page,
|
|
per_page: perPage,
|
|
type: "post",
|
|
subtype
|
|
})
|
|
}).then((results2) => {
|
|
return results2.map((result) => {
|
|
return {
|
|
id: result.id,
|
|
url: result.url,
|
|
title: (0, import_html_entities.decodeEntities)(result.title || "") || (0, import_i18n2.__)("(no title)"),
|
|
type: result.subtype || result.type,
|
|
kind: "post-type"
|
|
};
|
|
});
|
|
}).catch(() => [])
|
|
// Fail by returning no results.
|
|
);
|
|
}
|
|
if (!type || type === "term") {
|
|
queries2.push(
|
|
(0, import_api_fetch6.default)({
|
|
path: (0, import_url4.addQueryArgs)("/wp/v2/search", {
|
|
search,
|
|
page,
|
|
per_page: perPage,
|
|
type: "term",
|
|
subtype
|
|
})
|
|
}).then((results2) => {
|
|
return results2.map((result) => {
|
|
return {
|
|
id: result.id,
|
|
url: result.url,
|
|
title: (0, import_html_entities.decodeEntities)(result.title || "") || (0, import_i18n2.__)("(no title)"),
|
|
type: result.subtype || result.type,
|
|
kind: "taxonomy"
|
|
};
|
|
});
|
|
}).catch(() => [])
|
|
// Fail by returning no results.
|
|
);
|
|
}
|
|
if (!disablePostFormats && (!type || type === "post-format")) {
|
|
queries2.push(
|
|
(0, import_api_fetch6.default)({
|
|
path: (0, import_url4.addQueryArgs)("/wp/v2/search", {
|
|
search,
|
|
page,
|
|
per_page: perPage,
|
|
type: "post-format",
|
|
subtype
|
|
})
|
|
}).then((results2) => {
|
|
return results2.map((result) => {
|
|
return {
|
|
id: result.id,
|
|
url: result.url,
|
|
title: (0, import_html_entities.decodeEntities)(result.title || "") || (0, import_i18n2.__)("(no title)"),
|
|
type: result.subtype || result.type,
|
|
kind: "taxonomy"
|
|
};
|
|
});
|
|
}).catch(() => [])
|
|
// Fail by returning no results.
|
|
);
|
|
}
|
|
if (!type || type === "attachment") {
|
|
queries2.push(
|
|
(0, import_api_fetch6.default)({
|
|
path: (0, import_url4.addQueryArgs)("/wp/v2/media", {
|
|
search,
|
|
page,
|
|
per_page: perPage
|
|
})
|
|
}).then((results2) => {
|
|
return results2.map((result) => {
|
|
return {
|
|
id: result.id,
|
|
url: result.source_url,
|
|
title: (0, import_html_entities.decodeEntities)(result.title.rendered || "") || (0, import_i18n2.__)("(no title)"),
|
|
type: result.type,
|
|
kind: "media"
|
|
};
|
|
});
|
|
}).catch(() => [])
|
|
// Fail by returning no results.
|
|
);
|
|
}
|
|
const responses = await Promise.all(queries2);
|
|
let results = responses.flat();
|
|
results = results.filter((result) => !!result.id);
|
|
results = sortResults(results, search);
|
|
results = results.slice(0, perPage);
|
|
return results;
|
|
}
|
|
function sortResults(results, search) {
|
|
const searchTokens = tokenize2(search);
|
|
const scores = {};
|
|
for (const result of results) {
|
|
if (result.title) {
|
|
const titleTokens = tokenize2(result.title);
|
|
const exactMatchingTokens = titleTokens.filter(
|
|
(titleToken) => searchTokens.some(
|
|
(searchToken) => titleToken === searchToken
|
|
)
|
|
);
|
|
const subMatchingTokens = titleTokens.filter(
|
|
(titleToken) => searchTokens.some(
|
|
(searchToken) => titleToken !== searchToken && titleToken.includes(searchToken)
|
|
)
|
|
);
|
|
const exactMatchScore = exactMatchingTokens.length / titleTokens.length * 10;
|
|
const subMatchScore = subMatchingTokens.length / titleTokens.length;
|
|
scores[result.id] = exactMatchScore + subMatchScore;
|
|
} else {
|
|
scores[result.id] = 0;
|
|
}
|
|
}
|
|
return results.sort((a, b) => scores[b.id] - scores[a.id]);
|
|
}
|
|
function tokenize2(text2) {
|
|
return text2.toLowerCase().match(/[\p{L}\p{N}]+/gu) || [];
|
|
}
|
|
|
|
// packages/core-data/build-module/fetch/__experimental-fetch-url-data.mjs
|
|
var import_api_fetch7 = __toESM(require_api_fetch(), 1);
|
|
var import_url5 = __toESM(require_url(), 1);
|
|
var CACHE = /* @__PURE__ */ new Map();
|
|
var fetchUrlData = async (url, options = {}) => {
|
|
const endpoint = "/wp-block-editor/v1/url-details";
|
|
const args2 = {
|
|
url: (0, import_url5.prependHTTP)(url)
|
|
};
|
|
if (!(0, import_url5.isURL)(url)) {
|
|
return Promise.reject(`${url} is not a valid URL.`);
|
|
}
|
|
const protocol = (0, import_url5.getProtocol)(url);
|
|
if (!protocol || !(0, import_url5.isValidProtocol)(protocol) || !protocol.startsWith("http") || !/^https?:\/\/[^\/\s]/i.test(url)) {
|
|
return Promise.reject(
|
|
`${url} does not have a valid protocol. URLs must be "http" based`
|
|
);
|
|
}
|
|
if (CACHE.has(url)) {
|
|
return CACHE.get(url);
|
|
}
|
|
return (0, import_api_fetch7.default)({
|
|
path: (0, import_url5.addQueryArgs)(endpoint, args2),
|
|
...options
|
|
}).then((res) => {
|
|
CACHE.set(url, res);
|
|
return res;
|
|
});
|
|
};
|
|
var experimental_fetch_url_data_default = fetchUrlData;
|
|
|
|
// packages/core-data/build-module/fetch/index.mjs
|
|
async function fetchBlockPatterns() {
|
|
const restPatterns = await (0, import_api_fetch8.default)({
|
|
path: "/wp/v2/block-patterns/patterns"
|
|
});
|
|
if (!restPatterns) {
|
|
return [];
|
|
}
|
|
return restPatterns.map(
|
|
(pattern) => Object.fromEntries(
|
|
Object.entries(pattern).map(([key, value]) => [
|
|
camelCase(key),
|
|
value
|
|
])
|
|
)
|
|
);
|
|
}
|
|
|
|
// packages/core-data/build-module/resolvers.mjs
|
|
var getAuthors2 = (query) => async ({ dispatch: dispatch3 }) => {
|
|
const path = (0, import_url6.addQueryArgs)(
|
|
"/wp/v2/users/?who=authors&per_page=100",
|
|
query
|
|
);
|
|
const users2 = await (0, import_api_fetch9.default)({ path });
|
|
dispatch3.receiveUserQuery(path, users2);
|
|
};
|
|
var getCurrentUser2 = () => async ({ dispatch: dispatch3 }) => {
|
|
const currentUser2 = await (0, import_api_fetch9.default)({ path: "/wp/v2/users/me" });
|
|
dispatch3.receiveCurrentUser(currentUser2);
|
|
};
|
|
var getEntityRecord2 = (kind, name, key = "", query) => async ({ select: select5, dispatch: dispatch3, registry, resolveSelect: resolveSelect2 }) => {
|
|
const configs = await resolveSelect2.getEntitiesConfig(kind);
|
|
const entityConfig = configs.find(
|
|
(config) => config.name === name && config.kind === kind
|
|
);
|
|
if (!entityConfig) {
|
|
return;
|
|
}
|
|
const lock3 = await dispatch3.__unstableAcquireStoreLock(
|
|
STORE_NAME,
|
|
["entities", "records", kind, name, key],
|
|
{ exclusive: false }
|
|
);
|
|
try {
|
|
if (query !== void 0 && query._fields) {
|
|
query = {
|
|
...query,
|
|
_fields: [
|
|
.../* @__PURE__ */ new Set([
|
|
...get_normalized_comma_separable_default(query._fields) || [],
|
|
entityConfig.key || DEFAULT_ENTITY_KEY
|
|
])
|
|
].join()
|
|
};
|
|
}
|
|
if (query !== void 0 && query._fields) {
|
|
const hasRecord = select5.hasEntityRecord(
|
|
kind,
|
|
name,
|
|
key,
|
|
query
|
|
);
|
|
if (hasRecord) {
|
|
return;
|
|
}
|
|
}
|
|
let { baseURL } = entityConfig;
|
|
if (kind === "postType" && name === "wp_template" && (key && typeof key === "string" && !/^\d+$/.test(key) || !window?.__experimentalTemplateActivate)) {
|
|
baseURL = baseURL.slice(0, baseURL.lastIndexOf("/")) + "/templates";
|
|
}
|
|
const path = (0, import_url6.addQueryArgs)(baseURL + (key ? "/" + key : ""), {
|
|
...entityConfig.baseURLParams,
|
|
...query
|
|
});
|
|
const response = await (0, import_api_fetch9.default)({ path, parse: false });
|
|
const record = await response.json();
|
|
const permissions = getUserPermissionsFromAllowHeader(
|
|
response.headers?.get("allow")
|
|
);
|
|
const canUserResolutionsArgs = [];
|
|
const receiveUserPermissionArgs = {};
|
|
for (const action of ALLOWED_RESOURCE_ACTIONS) {
|
|
receiveUserPermissionArgs[getUserPermissionCacheKey(action, {
|
|
kind,
|
|
name,
|
|
id: key
|
|
})] = permissions[action];
|
|
canUserResolutionsArgs.push([
|
|
action,
|
|
{ kind, name, id: key }
|
|
]);
|
|
}
|
|
if (entityConfig.syncConfig && isNumericID(key) && !query) {
|
|
const objectType = `${kind}/${name}`;
|
|
const objectId = key;
|
|
const recordWithTransients = { ...record };
|
|
Object.entries(entityConfig.transientEdits ?? {}).filter(
|
|
([propName, transientConfig]) => void 0 === recordWithTransients[propName] && transientConfig && "object" === typeof transientConfig && "read" in transientConfig && "function" === typeof transientConfig.read
|
|
).forEach(([propName, transientConfig]) => {
|
|
recordWithTransients[propName] = transientConfig.read(recordWithTransients);
|
|
});
|
|
void getSyncManager()?.load(
|
|
entityConfig.syncConfig,
|
|
objectType,
|
|
objectId,
|
|
recordWithTransients,
|
|
{
|
|
// Handle edits sourced from the sync manager.
|
|
editRecord: (edits, options = {}) => {
|
|
if (!Object.keys(edits).length) {
|
|
return;
|
|
}
|
|
dispatch3({
|
|
type: "EDIT_ENTITY_RECORD",
|
|
kind,
|
|
name,
|
|
recordId: key,
|
|
edits,
|
|
meta: {
|
|
undo: void 0
|
|
},
|
|
options
|
|
});
|
|
},
|
|
// Get the current entity record (with edits)
|
|
getEditedRecord: async () => await resolveSelect2.getEditedEntityRecord(
|
|
kind,
|
|
name,
|
|
key
|
|
),
|
|
// Handle sync connection status changes.
|
|
onStatusChange: (status) => {
|
|
dispatch3.setSyncConnectionStatus(
|
|
kind,
|
|
name,
|
|
key,
|
|
status
|
|
);
|
|
},
|
|
// Refetch the current entity record from the database.
|
|
refetchRecord: async () => {
|
|
dispatch3.receiveEntityRecords(
|
|
kind,
|
|
name,
|
|
await (0, import_api_fetch9.default)({ path, parse: true }),
|
|
query
|
|
);
|
|
},
|
|
// Persist the CRDT document.
|
|
//
|
|
// TODO: Currently, persisted CRDT documents are stored in post meta.
|
|
// This effectively means that only post entities support CRDT
|
|
// persistence. As we add support for syncing additional entity,
|
|
// we'll need to revisit where persisted CRDT documents are stored.
|
|
persistCRDTDoc: () => {
|
|
resolveSelect2.getEditedEntityRecord(kind, name, key).then((editedRecord) => {
|
|
const { meta, status } = editedRecord;
|
|
if ("auto-draft" === status || !meta) {
|
|
return;
|
|
}
|
|
dispatch3.saveEntityRecord(
|
|
kind,
|
|
name,
|
|
editedRecord
|
|
);
|
|
});
|
|
},
|
|
addUndoMeta: (ydoc, meta) => {
|
|
const selectionHistory = getSelectionHistory(ydoc);
|
|
if (selectionHistory) {
|
|
meta.set(
|
|
"selectionHistory",
|
|
selectionHistory
|
|
);
|
|
}
|
|
},
|
|
restoreUndoMeta: (ydoc, meta) => {
|
|
const selectionHistory = meta.get("selectionHistory");
|
|
if (selectionHistory) {
|
|
setTimeout(() => {
|
|
restoreSelection(selectionHistory, ydoc);
|
|
}, 0);
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
registry.batch(() => {
|
|
dispatch3.receiveEntityRecords(kind, name, record, query);
|
|
dispatch3.receiveUserPermissions(receiveUserPermissionArgs);
|
|
dispatch3.finishResolutions("canUser", canUserResolutionsArgs);
|
|
});
|
|
} finally {
|
|
dispatch3.__unstableReleaseStoreLock(lock3);
|
|
}
|
|
};
|
|
getEntityRecord2.shouldInvalidate = (action, kind, name) => {
|
|
return kind === "root" && name === "site" && (action.type === "RECEIVE_ITEMS" && // Making sure persistedEdits is set seems to be the only way of
|
|
// knowing whether it's an update or fetch. Only an update would
|
|
// have persistedEdits.
|
|
action.persistedEdits && action.persistedEdits.status !== "auto-draft" || action.type === "REMOVE_ITEMS") && action.kind === "postType" && action.name === "wp_template";
|
|
};
|
|
var getRawEntityRecord2 = forward_resolver_default("getEntityRecord");
|
|
var getEditedEntityRecord2 = forward_resolver_default("getEntityRecord");
|
|
var getEntityRecords2 = (kind, name, query = {}) => async ({ dispatch: dispatch3, registry, resolveSelect: resolveSelect2 }) => {
|
|
const configs = await resolveSelect2.getEntitiesConfig(kind);
|
|
const entityConfig = configs.find(
|
|
(config) => config.name === name && config.kind === kind
|
|
);
|
|
if (!entityConfig) {
|
|
return;
|
|
}
|
|
const lock3 = await dispatch3.__unstableAcquireStoreLock(
|
|
STORE_NAME,
|
|
["entities", "records", kind, name],
|
|
{ exclusive: false }
|
|
);
|
|
const rawQuery = { ...query };
|
|
const key = entityConfig.key || DEFAULT_ENTITY_KEY;
|
|
function getResolutionsArgs(records, recordsQuery) {
|
|
const queryArgs = Object.fromEntries(
|
|
Object.entries(recordsQuery).filter(([k, v]) => {
|
|
return ["context", "_fields"].includes(k) && !!v;
|
|
})
|
|
);
|
|
return records.filter((record) => record?.[key]).map((record) => [
|
|
kind,
|
|
name,
|
|
record[key],
|
|
Object.keys(queryArgs).length > 0 ? queryArgs : void 0
|
|
]);
|
|
}
|
|
try {
|
|
if (query._fields) {
|
|
query = {
|
|
...query,
|
|
_fields: [
|
|
.../* @__PURE__ */ new Set([
|
|
...get_normalized_comma_separable_default(query._fields) || [],
|
|
key
|
|
])
|
|
].join()
|
|
};
|
|
}
|
|
let { baseURL } = entityConfig;
|
|
const { combinedTemplates = true } = query;
|
|
if (kind === "postType" && name === "wp_template" && combinedTemplates) {
|
|
baseURL = baseURL.slice(0, baseURL.lastIndexOf("/")) + "/templates";
|
|
}
|
|
const path = (0, import_url6.addQueryArgs)(baseURL, {
|
|
...entityConfig.baseURLParams,
|
|
...query
|
|
});
|
|
let records = [], meta;
|
|
if (entityConfig.supportsPagination && query.per_page !== -1) {
|
|
const response = await (0, import_api_fetch9.default)({ path, parse: false });
|
|
records = Object.values(await response.json());
|
|
meta = {
|
|
totalItems: parseInt(
|
|
response.headers.get("X-WP-Total")
|
|
),
|
|
totalPages: parseInt(
|
|
response.headers.get("X-WP-TotalPages")
|
|
)
|
|
};
|
|
} else if (query.per_page === -1 && query[RECEIVE_INTERMEDIATE_RESULTS] === true) {
|
|
let page = 1;
|
|
let totalPages;
|
|
do {
|
|
const response = await (0, import_api_fetch9.default)({
|
|
path: (0, import_url6.addQueryArgs)(path, { page, per_page: 100 }),
|
|
parse: false
|
|
});
|
|
const pageRecords = Object.values(await response.json());
|
|
totalPages = parseInt(
|
|
response.headers.get("X-WP-TotalPages")
|
|
);
|
|
if (!meta) {
|
|
meta = {
|
|
totalItems: parseInt(
|
|
response.headers.get("X-WP-Total")
|
|
),
|
|
totalPages: 1
|
|
};
|
|
}
|
|
records.push(...pageRecords);
|
|
registry.batch(() => {
|
|
dispatch3.receiveEntityRecords(
|
|
kind,
|
|
name,
|
|
records,
|
|
query,
|
|
false,
|
|
void 0,
|
|
meta
|
|
);
|
|
dispatch3.finishResolutions(
|
|
"getEntityRecord",
|
|
getResolutionsArgs(pageRecords, rawQuery)
|
|
);
|
|
});
|
|
page++;
|
|
} while (page <= totalPages);
|
|
} else {
|
|
records = Object.values(await (0, import_api_fetch9.default)({ path }));
|
|
meta = {
|
|
totalItems: records.length,
|
|
totalPages: 1
|
|
};
|
|
}
|
|
if (entityConfig.syncConfig && -1 === query.per_page) {
|
|
const objectType = `${kind}/${name}`;
|
|
getSyncManager()?.loadCollection(
|
|
entityConfig.syncConfig,
|
|
objectType,
|
|
{
|
|
onStatusChange: (status) => {
|
|
dispatch3.setSyncConnectionStatus(
|
|
kind,
|
|
name,
|
|
null,
|
|
status
|
|
);
|
|
},
|
|
refetchRecords: async () => {
|
|
dispatch3.receiveEntityRecords(
|
|
kind,
|
|
name,
|
|
await (0, import_api_fetch9.default)({ path, parse: true }),
|
|
query
|
|
);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
if (query._fields) {
|
|
records = records.map((record) => {
|
|
query._fields.split(",").forEach((field) => {
|
|
if (!record.hasOwnProperty(field)) {
|
|
record[field] = void 0;
|
|
}
|
|
});
|
|
return record;
|
|
});
|
|
}
|
|
registry.batch(() => {
|
|
dispatch3.receiveEntityRecords(
|
|
kind,
|
|
name,
|
|
records,
|
|
query,
|
|
false,
|
|
void 0,
|
|
meta
|
|
);
|
|
const targetHints = records.filter(
|
|
(record) => !!record?.[key] && !!record?._links?.self?.[0]?.targetHints?.allow
|
|
).map((record) => ({
|
|
id: record[key],
|
|
permissions: getUserPermissionsFromAllowHeader(
|
|
record._links.self[0].targetHints.allow
|
|
)
|
|
}));
|
|
const canUserResolutionsArgs = [];
|
|
const receiveUserPermissionArgs = {};
|
|
for (const targetHint of targetHints) {
|
|
for (const action of ALLOWED_RESOURCE_ACTIONS) {
|
|
canUserResolutionsArgs.push([
|
|
action,
|
|
{ kind, name, id: targetHint.id }
|
|
]);
|
|
receiveUserPermissionArgs[getUserPermissionCacheKey(action, {
|
|
kind,
|
|
name,
|
|
id: targetHint.id
|
|
})] = targetHint.permissions[action];
|
|
}
|
|
}
|
|
if (targetHints.length > 0) {
|
|
dispatch3.receiveUserPermissions(
|
|
receiveUserPermissionArgs
|
|
);
|
|
dispatch3.finishResolutions(
|
|
"canUser",
|
|
canUserResolutionsArgs
|
|
);
|
|
}
|
|
dispatch3.finishResolutions(
|
|
"getEntityRecord",
|
|
getResolutionsArgs(records, rawQuery)
|
|
);
|
|
dispatch3.__unstableReleaseStoreLock(lock3);
|
|
});
|
|
} catch (e) {
|
|
dispatch3.__unstableReleaseStoreLock(lock3);
|
|
}
|
|
};
|
|
getEntityRecords2.shouldInvalidate = (action, kind, name) => {
|
|
return (action.type === "RECEIVE_ITEMS" || action.type === "REMOVE_ITEMS") && action.invalidateCache && kind === action.kind && name === action.name;
|
|
};
|
|
var getEntityRecordsTotalItems2 = forward_resolver_default("getEntityRecords");
|
|
var getEntityRecordsTotalPages2 = forward_resolver_default("getEntityRecords");
|
|
var getCurrentTheme2 = () => async ({ dispatch: dispatch3, resolveSelect: resolveSelect2 }) => {
|
|
const activeThemes = await resolveSelect2.getEntityRecords(
|
|
"root",
|
|
"theme",
|
|
{ status: "active" }
|
|
);
|
|
dispatch3.receiveCurrentTheme(activeThemes[0]);
|
|
};
|
|
var getThemeSupports2 = forward_resolver_default("getCurrentTheme");
|
|
var getEmbedPreview2 = (url) => async ({ dispatch: dispatch3 }) => {
|
|
try {
|
|
const embedProxyResponse = await (0, import_api_fetch9.default)({
|
|
path: (0, import_url6.addQueryArgs)("/oembed/1.0/proxy", { url })
|
|
});
|
|
dispatch3.receiveEmbedPreview(url, embedProxyResponse);
|
|
} catch (error) {
|
|
dispatch3.receiveEmbedPreview(url, false);
|
|
}
|
|
};
|
|
var canUser2 = (requestedAction, resource, id2) => async ({ dispatch: dispatch3, registry, resolveSelect: resolveSelect2 }) => {
|
|
if (!ALLOWED_RESOURCE_ACTIONS.includes(requestedAction)) {
|
|
throw new Error(`'${requestedAction}' is not a valid action.`);
|
|
}
|
|
const { hasStartedResolution } = registry.select(STORE_NAME);
|
|
for (const relatedAction of ALLOWED_RESOURCE_ACTIONS) {
|
|
if (relatedAction === requestedAction) {
|
|
continue;
|
|
}
|
|
const isAlreadyResolving = hasStartedResolution("canUser", [
|
|
relatedAction,
|
|
resource,
|
|
id2
|
|
]);
|
|
if (isAlreadyResolving) {
|
|
return;
|
|
}
|
|
}
|
|
let resourcePath = null;
|
|
if (typeof resource === "object") {
|
|
if (!resource.kind || !resource.name) {
|
|
throw new Error("The entity resource object is not valid.");
|
|
}
|
|
const configs = await resolveSelect2.getEntitiesConfig(
|
|
resource.kind
|
|
);
|
|
const entityConfig = configs.find(
|
|
(config) => config.name === resource.name && config.kind === resource.kind
|
|
);
|
|
if (!entityConfig) {
|
|
return;
|
|
}
|
|
resourcePath = entityConfig.baseURL + (resource.id ? "/" + resource.id : "");
|
|
} else {
|
|
resourcePath = `/wp/v2/${resource}` + (id2 ? "/" + id2 : "");
|
|
}
|
|
let response;
|
|
try {
|
|
response = await (0, import_api_fetch9.default)({
|
|
path: resourcePath,
|
|
method: "OPTIONS",
|
|
parse: false
|
|
});
|
|
} catch (error) {
|
|
return;
|
|
}
|
|
const permissions = getUserPermissionsFromAllowHeader(
|
|
response.headers?.get("allow")
|
|
);
|
|
registry.batch(() => {
|
|
for (const action of ALLOWED_RESOURCE_ACTIONS) {
|
|
const key = getUserPermissionCacheKey(action, resource, id2);
|
|
dispatch3.receiveUserPermission(key, permissions[action]);
|
|
if (action !== requestedAction) {
|
|
dispatch3.finishResolution("canUser", [
|
|
action,
|
|
resource,
|
|
id2
|
|
]);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
var canUserEditEntityRecord2 = (kind, name, recordId) => async ({ dispatch: dispatch3 }) => {
|
|
await dispatch3(canUser2("update", { kind, name, id: recordId }));
|
|
};
|
|
var getAutosaves2 = (postType, postId) => async ({ dispatch: dispatch3, resolveSelect: resolveSelect2 }) => {
|
|
const {
|
|
rest_base: restBase,
|
|
rest_namespace: restNamespace = "wp/v2",
|
|
supports
|
|
} = await resolveSelect2.getPostType(postType);
|
|
if (!supports?.autosave) {
|
|
return;
|
|
}
|
|
const autosaves2 = await (0, import_api_fetch9.default)({
|
|
path: `/${restNamespace}/${restBase}/${postId}/autosaves?context=edit`
|
|
});
|
|
if (autosaves2 && autosaves2.length) {
|
|
dispatch3.receiveAutosaves(postId, autosaves2);
|
|
}
|
|
};
|
|
var getAutosave2 = (postType, postId) => async ({ resolveSelect: resolveSelect2 }) => {
|
|
await resolveSelect2.getAutosaves(postType, postId);
|
|
};
|
|
var __experimentalGetCurrentGlobalStylesId2 = () => async ({ dispatch: dispatch3, resolveSelect: resolveSelect2 }) => {
|
|
const activeThemes = await resolveSelect2.getEntityRecords(
|
|
"root",
|
|
"theme",
|
|
{ status: "active" }
|
|
);
|
|
const globalStylesURL = activeThemes?.[0]?._links?.["wp:user-global-styles"]?.[0]?.href;
|
|
if (!globalStylesURL) {
|
|
return;
|
|
}
|
|
const matches = globalStylesURL.match(/\/(\d+)(?:\?|$)/);
|
|
const id2 = matches ? Number(matches[1]) : null;
|
|
if (id2) {
|
|
dispatch3.__experimentalReceiveCurrentGlobalStylesId(id2);
|
|
}
|
|
};
|
|
var __experimentalGetCurrentThemeBaseGlobalStyles2 = () => async ({ resolveSelect: resolveSelect2, dispatch: dispatch3 }) => {
|
|
const currentTheme2 = await resolveSelect2.getCurrentTheme();
|
|
const themeGlobalStyles = await (0, import_api_fetch9.default)({
|
|
path: `/wp/v2/global-styles/themes/${currentTheme2.stylesheet}?context=view`
|
|
});
|
|
dispatch3.__experimentalReceiveThemeBaseGlobalStyles(
|
|
currentTheme2.stylesheet,
|
|
themeGlobalStyles
|
|
);
|
|
};
|
|
var __experimentalGetCurrentThemeGlobalStylesVariations2 = () => async ({ resolveSelect: resolveSelect2, dispatch: dispatch3 }) => {
|
|
const currentTheme2 = await resolveSelect2.getCurrentTheme();
|
|
const variations = await (0, import_api_fetch9.default)({
|
|
path: `/wp/v2/global-styles/themes/${currentTheme2.stylesheet}/variations?context=view`
|
|
});
|
|
dispatch3.__experimentalReceiveThemeGlobalStyleVariations(
|
|
currentTheme2.stylesheet,
|
|
variations
|
|
);
|
|
};
|
|
var getCurrentThemeGlobalStylesRevisions2 = () => async ({ resolveSelect: resolveSelect2, dispatch: dispatch3 }) => {
|
|
const globalStylesId = await resolveSelect2.__experimentalGetCurrentGlobalStylesId();
|
|
const record = globalStylesId ? await resolveSelect2.getEntityRecord(
|
|
"root",
|
|
"globalStyles",
|
|
globalStylesId
|
|
) : void 0;
|
|
const revisionsURL = record?._links?.["version-history"]?.[0]?.href;
|
|
if (revisionsURL) {
|
|
const resetRevisions = await (0, import_api_fetch9.default)({
|
|
url: revisionsURL
|
|
});
|
|
const revisions = resetRevisions?.map(
|
|
(revision) => Object.fromEntries(
|
|
Object.entries(revision).map(([key, value]) => [
|
|
camelCase(key),
|
|
value
|
|
])
|
|
)
|
|
);
|
|
dispatch3.receiveThemeGlobalStyleRevisions(
|
|
globalStylesId,
|
|
revisions
|
|
);
|
|
}
|
|
};
|
|
getCurrentThemeGlobalStylesRevisions2.shouldInvalidate = (action) => {
|
|
return action.type === "SAVE_ENTITY_RECORD_FINISH" && action.kind === "root" && !action.error && action.name === "globalStyles";
|
|
};
|
|
var getBlockPatterns2 = () => async ({ dispatch: dispatch3 }) => {
|
|
const patterns = await fetchBlockPatterns();
|
|
dispatch3({ type: "RECEIVE_BLOCK_PATTERNS", patterns });
|
|
};
|
|
var getBlockPatternCategories2 = () => async ({ dispatch: dispatch3 }) => {
|
|
const categories = await (0, import_api_fetch9.default)({
|
|
path: "/wp/v2/block-patterns/categories"
|
|
});
|
|
dispatch3({ type: "RECEIVE_BLOCK_PATTERN_CATEGORIES", categories });
|
|
};
|
|
var getUserPatternCategories2 = () => async ({ dispatch: dispatch3, resolveSelect: resolveSelect2 }) => {
|
|
const patternCategories = await resolveSelect2.getEntityRecords(
|
|
"taxonomy",
|
|
"wp_pattern_category",
|
|
{
|
|
per_page: -1,
|
|
_fields: "id,name,description,slug",
|
|
context: "view"
|
|
}
|
|
);
|
|
const mappedPatternCategories = patternCategories?.map((userCategory) => ({
|
|
...userCategory,
|
|
label: (0, import_html_entities2.decodeEntities)(userCategory.name),
|
|
name: userCategory.slug
|
|
})) || [];
|
|
dispatch3({
|
|
type: "RECEIVE_USER_PATTERN_CATEGORIES",
|
|
patternCategories: mappedPatternCategories
|
|
});
|
|
};
|
|
var getNavigationFallbackId2 = () => async ({ dispatch: dispatch3, select: select5, registry }) => {
|
|
const fallback = await (0, import_api_fetch9.default)({
|
|
path: (0, import_url6.addQueryArgs)("/wp-block-editor/v1/navigation-fallback", {
|
|
_embed: true
|
|
})
|
|
});
|
|
const record = fallback?._embedded?.self;
|
|
registry.batch(() => {
|
|
dispatch3.receiveNavigationFallbackId(fallback?.id);
|
|
if (!record) {
|
|
return;
|
|
}
|
|
const existingFallbackEntityRecord = select5.getEntityRecord(
|
|
"postType",
|
|
"wp_navigation",
|
|
fallback.id
|
|
);
|
|
const invalidateNavigationQueries = !existingFallbackEntityRecord;
|
|
dispatch3.receiveEntityRecords(
|
|
"postType",
|
|
"wp_navigation",
|
|
record,
|
|
void 0,
|
|
invalidateNavigationQueries
|
|
);
|
|
dispatch3.finishResolution("getEntityRecord", [
|
|
"postType",
|
|
"wp_navigation",
|
|
fallback.id
|
|
]);
|
|
});
|
|
};
|
|
var getDefaultTemplateId2 = (query) => async ({ dispatch: dispatch3, registry, resolveSelect: resolveSelect2 }) => {
|
|
const template = await (0, import_api_fetch9.default)({
|
|
path: (0, import_url6.addQueryArgs)("/wp/v2/templates/lookup", query)
|
|
});
|
|
await resolveSelect2.getEntitiesConfig("postType");
|
|
const id2 = window?.__experimentalTemplateActivate ? template?.wp_id || template?.id : template?.id;
|
|
if (id2) {
|
|
template.id = id2;
|
|
registry.batch(() => {
|
|
dispatch3.receiveDefaultTemplateId(query, id2);
|
|
dispatch3.receiveEntityRecords(
|
|
"postType",
|
|
template.type,
|
|
template
|
|
);
|
|
dispatch3.finishResolution("getEntityRecord", [
|
|
"postType",
|
|
template.type,
|
|
id2
|
|
]);
|
|
});
|
|
}
|
|
};
|
|
getDefaultTemplateId2.shouldInvalidate = (action) => {
|
|
return action.type === "RECEIVE_ITEMS" && action.kind === "root" && action.name === "site";
|
|
};
|
|
var getRevisions2 = (kind, name, recordKey, query = {}) => async ({ dispatch: dispatch3, registry, resolveSelect: resolveSelect2 }) => {
|
|
const configs = await resolveSelect2.getEntitiesConfig(kind);
|
|
const entityConfig = configs.find(
|
|
(config) => config.name === name && config.kind === kind
|
|
);
|
|
if (!entityConfig) {
|
|
return;
|
|
}
|
|
if (query._fields) {
|
|
query = {
|
|
...query,
|
|
_fields: [
|
|
.../* @__PURE__ */ new Set([
|
|
...get_normalized_comma_separable_default(query._fields) || [],
|
|
entityConfig.revisionKey || DEFAULT_ENTITY_KEY
|
|
])
|
|
].join()
|
|
};
|
|
}
|
|
const path = (0, import_url6.addQueryArgs)(
|
|
entityConfig.getRevisionsUrl(recordKey),
|
|
query
|
|
);
|
|
let records, response;
|
|
const meta = {};
|
|
const isPaginated = entityConfig.supportsPagination && query.per_page !== -1;
|
|
try {
|
|
response = await (0, import_api_fetch9.default)({ path, parse: !isPaginated });
|
|
} catch (error) {
|
|
return;
|
|
}
|
|
if (response) {
|
|
if (isPaginated) {
|
|
records = Object.values(await response.json());
|
|
meta.totalItems = parseInt(
|
|
response.headers.get("X-WP-Total")
|
|
);
|
|
} else {
|
|
records = Object.values(response);
|
|
}
|
|
if (query._fields) {
|
|
records = records.map((record) => {
|
|
query._fields.split(",").forEach((field) => {
|
|
if (!record.hasOwnProperty(field)) {
|
|
record[field] = void 0;
|
|
}
|
|
});
|
|
return record;
|
|
});
|
|
}
|
|
registry.batch(() => {
|
|
dispatch3.receiveRevisions(
|
|
kind,
|
|
name,
|
|
recordKey,
|
|
records,
|
|
query,
|
|
false,
|
|
meta
|
|
);
|
|
if (!query?._fields && !query.context) {
|
|
const key = entityConfig.revisionKey || DEFAULT_ENTITY_KEY;
|
|
const resolutionsArgs = records.filter((record) => record[key]).map((record) => [
|
|
kind,
|
|
name,
|
|
recordKey,
|
|
record[key]
|
|
]);
|
|
dispatch3.finishResolutions(
|
|
"getRevision",
|
|
resolutionsArgs
|
|
);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
getRevisions2.shouldInvalidate = (action, kind, name, recordKey) => action.type === "SAVE_ENTITY_RECORD_FINISH" && name === action.name && kind === action.kind && !action.error && recordKey === action.recordId;
|
|
var getRevision2 = (kind, name, recordKey, revisionKey, query) => async ({ dispatch: dispatch3, resolveSelect: resolveSelect2 }) => {
|
|
const configs = await resolveSelect2.getEntitiesConfig(kind);
|
|
const entityConfig = configs.find(
|
|
(config) => config.name === name && config.kind === kind
|
|
);
|
|
if (!entityConfig) {
|
|
return;
|
|
}
|
|
if (query !== void 0 && query._fields) {
|
|
query = {
|
|
...query,
|
|
_fields: [
|
|
.../* @__PURE__ */ new Set([
|
|
...get_normalized_comma_separable_default(query._fields) || [],
|
|
entityConfig.revisionKey || DEFAULT_ENTITY_KEY
|
|
])
|
|
].join()
|
|
};
|
|
}
|
|
const path = (0, import_url6.addQueryArgs)(
|
|
entityConfig.getRevisionsUrl(recordKey, revisionKey),
|
|
query
|
|
);
|
|
let record;
|
|
try {
|
|
record = await (0, import_api_fetch9.default)({ path });
|
|
} catch (error) {
|
|
return;
|
|
}
|
|
if (record) {
|
|
dispatch3.receiveRevisions(kind, name, recordKey, record, query);
|
|
}
|
|
};
|
|
var getRegisteredPostMeta2 = (postType) => async ({ dispatch: dispatch3, resolveSelect: resolveSelect2 }) => {
|
|
let options;
|
|
try {
|
|
const {
|
|
rest_namespace: restNamespace = "wp/v2",
|
|
rest_base: restBase
|
|
} = await resolveSelect2.getPostType(postType) || {};
|
|
options = await (0, import_api_fetch9.default)({
|
|
path: `${restNamespace}/${restBase}/?context=edit`,
|
|
method: "OPTIONS"
|
|
});
|
|
} catch (error) {
|
|
return;
|
|
}
|
|
if (options) {
|
|
dispatch3.receiveRegisteredPostMeta(
|
|
postType,
|
|
options?.schema?.properties?.meta?.properties
|
|
);
|
|
}
|
|
};
|
|
var getEntitiesConfig2 = (kind) => async ({ dispatch: dispatch3 }) => {
|
|
const loader = additionalEntityConfigLoaders.find(
|
|
(l) => l.kind === kind
|
|
);
|
|
if (!loader) {
|
|
return;
|
|
}
|
|
try {
|
|
const configs = await loader.loadEntities();
|
|
if (!configs.length) {
|
|
return;
|
|
}
|
|
dispatch3.addEntities(configs);
|
|
} catch {
|
|
}
|
|
};
|
|
var getEditorSettings2 = () => async ({ dispatch: dispatch3 }) => {
|
|
const settings = await (0, import_api_fetch9.default)({
|
|
path: "/wp-block-editor/v1/settings"
|
|
});
|
|
dispatch3.receiveEditorSettings(settings);
|
|
};
|
|
var getEditorAssets2 = () => async ({ dispatch: dispatch3 }) => {
|
|
const assets = await (0, import_api_fetch9.default)({
|
|
path: "/wp-block-editor/v1/assets"
|
|
});
|
|
dispatch3.receiveEditorAssets(assets);
|
|
};
|
|
|
|
// packages/core-data/build-module/locks/utils.mjs
|
|
function deepCopyLocksTreePath(tree, path) {
|
|
const newTree = { ...tree };
|
|
let currentNode = newTree;
|
|
for (const branchName of path) {
|
|
currentNode.children = {
|
|
...currentNode.children,
|
|
[branchName]: {
|
|
locks: [],
|
|
children: {},
|
|
...currentNode.children[branchName]
|
|
}
|
|
};
|
|
currentNode = currentNode.children[branchName];
|
|
}
|
|
return newTree;
|
|
}
|
|
function getNode(tree, path) {
|
|
let currentNode = tree;
|
|
for (const branchName of path) {
|
|
const nextNode = currentNode.children[branchName];
|
|
if (!nextNode) {
|
|
return null;
|
|
}
|
|
currentNode = nextNode;
|
|
}
|
|
return currentNode;
|
|
}
|
|
function* iteratePath(tree, path) {
|
|
let currentNode = tree;
|
|
yield currentNode;
|
|
for (const branchName of path) {
|
|
const nextNode = currentNode.children[branchName];
|
|
if (!nextNode) {
|
|
break;
|
|
}
|
|
yield nextNode;
|
|
currentNode = nextNode;
|
|
}
|
|
}
|
|
function* iterateDescendants(node) {
|
|
const stack = Object.values(node.children);
|
|
while (stack.length) {
|
|
const childNode = stack.pop();
|
|
yield childNode;
|
|
stack.push(...Object.values(childNode.children));
|
|
}
|
|
}
|
|
function hasConflictingLock({ exclusive }, locks2) {
|
|
if (exclusive && locks2.length) {
|
|
return true;
|
|
}
|
|
if (!exclusive && locks2.filter((lock3) => lock3.exclusive).length) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// packages/core-data/build-module/locks/reducer.mjs
|
|
var DEFAULT_STATE = {
|
|
requests: [],
|
|
tree: {
|
|
locks: [],
|
|
children: {}
|
|
}
|
|
};
|
|
function locks(state = DEFAULT_STATE, action) {
|
|
switch (action.type) {
|
|
case "ENQUEUE_LOCK_REQUEST": {
|
|
const { request } = action;
|
|
return {
|
|
...state,
|
|
requests: [request, ...state.requests]
|
|
};
|
|
}
|
|
case "GRANT_LOCK_REQUEST": {
|
|
const { lock: lock3, request } = action;
|
|
const { store: store2, path } = request;
|
|
const storePath = [store2, ...path];
|
|
const newTree = deepCopyLocksTreePath(state.tree, storePath);
|
|
const node = getNode(newTree, storePath);
|
|
node.locks = [...node.locks, lock3];
|
|
return {
|
|
...state,
|
|
requests: state.requests.filter((r) => r !== request),
|
|
tree: newTree
|
|
};
|
|
}
|
|
case "RELEASE_LOCK": {
|
|
const { lock: lock3 } = action;
|
|
const storePath = [lock3.store, ...lock3.path];
|
|
const newTree = deepCopyLocksTreePath(state.tree, storePath);
|
|
const node = getNode(newTree, storePath);
|
|
node.locks = node.locks.filter((l) => l !== lock3);
|
|
return {
|
|
...state,
|
|
tree: newTree
|
|
};
|
|
}
|
|
}
|
|
return state;
|
|
}
|
|
|
|
// packages/core-data/build-module/locks/selectors.mjs
|
|
function getPendingLockRequests(state) {
|
|
return state.requests;
|
|
}
|
|
function isLockAvailable(state, store2, path, { exclusive }) {
|
|
const storePath = [store2, ...path];
|
|
const locks2 = state.tree;
|
|
for (const node2 of iteratePath(locks2, storePath)) {
|
|
if (hasConflictingLock({ exclusive }, node2.locks)) {
|
|
return false;
|
|
}
|
|
}
|
|
const node = getNode(locks2, storePath);
|
|
if (!node) {
|
|
return true;
|
|
}
|
|
for (const descendant of iterateDescendants(node)) {
|
|
if (hasConflictingLock({ exclusive }, descendant.locks)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// packages/core-data/build-module/locks/engine.mjs
|
|
function createLocks() {
|
|
let state = locks(void 0, { type: "@@INIT" });
|
|
function processPendingLockRequests() {
|
|
for (const request of getPendingLockRequests(state)) {
|
|
const { store: store2, path, exclusive, notifyAcquired } = request;
|
|
if (isLockAvailable(state, store2, path, { exclusive })) {
|
|
const lock3 = { store: store2, path, exclusive };
|
|
state = locks(state, {
|
|
type: "GRANT_LOCK_REQUEST",
|
|
lock: lock3,
|
|
request
|
|
});
|
|
notifyAcquired(lock3);
|
|
}
|
|
}
|
|
}
|
|
function acquire(store2, path, exclusive) {
|
|
return new Promise((resolve) => {
|
|
state = locks(state, {
|
|
type: "ENQUEUE_LOCK_REQUEST",
|
|
request: { store: store2, path, exclusive, notifyAcquired: resolve }
|
|
});
|
|
processPendingLockRequests();
|
|
});
|
|
}
|
|
function release(lock3) {
|
|
state = locks(state, {
|
|
type: "RELEASE_LOCK",
|
|
lock: lock3
|
|
});
|
|
processPendingLockRequests();
|
|
}
|
|
return { acquire, release };
|
|
}
|
|
|
|
// packages/core-data/build-module/locks/actions.mjs
|
|
function createLocksActions() {
|
|
const locks2 = createLocks();
|
|
function __unstableAcquireStoreLock(store2, path, { exclusive }) {
|
|
return () => locks2.acquire(store2, path, exclusive);
|
|
}
|
|
function __unstableReleaseStoreLock(lock3) {
|
|
return () => locks2.release(lock3);
|
|
}
|
|
return { __unstableAcquireStoreLock, __unstableReleaseStoreLock };
|
|
}
|
|
|
|
// packages/core-data/build-module/dynamic-entities.mjs
|
|
var dynamicActions;
|
|
var dynamicSelectors;
|
|
|
|
// packages/core-data/build-module/entity-provider.mjs
|
|
var import_element2 = __toESM(require_element(), 1);
|
|
|
|
// packages/core-data/build-module/entity-context.mjs
|
|
var import_element = __toESM(require_element(), 1);
|
|
var EntityContext = (0, import_element.createContext)({});
|
|
EntityContext.displayName = "EntityContext";
|
|
|
|
// packages/core-data/build-module/entity-provider.mjs
|
|
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
function EntityProvider({
|
|
kind,
|
|
type: name,
|
|
id: id2,
|
|
revisionId,
|
|
children
|
|
}) {
|
|
const parent = (0, import_element2.useContext)(EntityContext);
|
|
const childContext = (0, import_element2.useMemo)(
|
|
() => ({
|
|
...parent,
|
|
...kind && {
|
|
[kind]: {
|
|
...parent?.[kind],
|
|
[name]: id2
|
|
}
|
|
},
|
|
...revisionId !== void 0 && { revisionId }
|
|
}),
|
|
[parent, kind, name, id2, revisionId]
|
|
);
|
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EntityContext.Provider, { value: childContext, children });
|
|
}
|
|
|
|
// packages/core-data/build-module/hooks/use-entity-record.mjs
|
|
var import_data12 = __toESM(require_data(), 1);
|
|
var import_deprecated4 = __toESM(require_deprecated(), 1);
|
|
var import_element3 = __toESM(require_element(), 1);
|
|
|
|
// packages/core-data/build-module/hooks/use-query-select.mjs
|
|
var import_data11 = __toESM(require_data(), 1);
|
|
|
|
// node_modules/memize/dist/index.js
|
|
function memize(fn, options) {
|
|
var size2 = 0;
|
|
var head;
|
|
var tail;
|
|
options = options || {};
|
|
function memoized() {
|
|
var node = head, len = arguments.length, args2, i;
|
|
searchCache: while (node) {
|
|
if (node.args.length !== arguments.length) {
|
|
node = node.next;
|
|
continue;
|
|
}
|
|
for (i = 0; i < len; i++) {
|
|
if (node.args[i] !== arguments[i]) {
|
|
node = node.next;
|
|
continue searchCache;
|
|
}
|
|
}
|
|
if (node !== head) {
|
|
if (node === tail) {
|
|
tail = node.prev;
|
|
}
|
|
node.prev.next = node.next;
|
|
if (node.next) {
|
|
node.next.prev = node.prev;
|
|
}
|
|
node.next = head;
|
|
node.prev = null;
|
|
head.prev = node;
|
|
head = node;
|
|
}
|
|
return node.val;
|
|
}
|
|
args2 = new Array(len);
|
|
for (i = 0; i < len; i++) {
|
|
args2[i] = arguments[i];
|
|
}
|
|
node = {
|
|
args: args2,
|
|
// Generate the result from original function
|
|
val: fn.apply(null, args2)
|
|
};
|
|
if (head) {
|
|
head.prev = node;
|
|
node.next = head;
|
|
} else {
|
|
tail = node;
|
|
}
|
|
if (size2 === /** @type {MemizeOptions} */
|
|
options.maxSize) {
|
|
tail = /** @type {MemizeCacheNode} */
|
|
tail.prev;
|
|
tail.next = null;
|
|
} else {
|
|
size2++;
|
|
}
|
|
head = node;
|
|
return node.val;
|
|
}
|
|
memoized.clear = function() {
|
|
head = null;
|
|
tail = null;
|
|
size2 = 0;
|
|
};
|
|
return memoized;
|
|
}
|
|
|
|
// packages/core-data/build-module/hooks/memoize.mjs
|
|
var memoize_default = memize;
|
|
|
|
// packages/core-data/build-module/hooks/constants.mjs
|
|
var Status = /* @__PURE__ */ ((Status2) => {
|
|
Status2["Idle"] = "IDLE";
|
|
Status2["Resolving"] = "RESOLVING";
|
|
Status2["Error"] = "ERROR";
|
|
Status2["Success"] = "SUCCESS";
|
|
return Status2;
|
|
})(Status || {});
|
|
|
|
// packages/core-data/build-module/hooks/use-query-select.mjs
|
|
var META_SELECTORS = [
|
|
"getIsResolving",
|
|
"hasStartedResolution",
|
|
"hasFinishedResolution",
|
|
"isResolving",
|
|
"getCachedResolvers"
|
|
];
|
|
function useQuerySelect(mapQuerySelect, deps) {
|
|
return (0, import_data11.useSelect)((select5, registry) => {
|
|
const resolve = (store2) => enrichSelectors(select5(store2));
|
|
return mapQuerySelect(resolve, registry);
|
|
}, deps);
|
|
}
|
|
var enrichSelectors = memoize_default(((selectors) => {
|
|
const resolvers = {};
|
|
for (const selectorName in selectors) {
|
|
if (META_SELECTORS.includes(selectorName)) {
|
|
continue;
|
|
}
|
|
Object.defineProperty(resolvers, selectorName, {
|
|
get: () => (...args2) => {
|
|
const data = selectors[selectorName](...args2);
|
|
const resolutionStatus = selectors.getResolutionState(
|
|
selectorName,
|
|
args2
|
|
)?.status;
|
|
let status;
|
|
switch (resolutionStatus) {
|
|
case "resolving":
|
|
status = Status.Resolving;
|
|
break;
|
|
case "finished":
|
|
status = Status.Success;
|
|
break;
|
|
case "error":
|
|
status = Status.Error;
|
|
break;
|
|
case void 0:
|
|
status = Status.Idle;
|
|
break;
|
|
}
|
|
return {
|
|
data,
|
|
status,
|
|
isResolving: status === Status.Resolving,
|
|
hasStarted: status !== Status.Idle,
|
|
hasResolved: status === Status.Success || status === Status.Error
|
|
};
|
|
}
|
|
});
|
|
}
|
|
return resolvers;
|
|
}));
|
|
|
|
// packages/core-data/build-module/hooks/use-entity-record.mjs
|
|
var EMPTY_OBJECT2 = {};
|
|
function useEntityRecord(kind, name, recordId, options = { enabled: true }) {
|
|
const { editEntityRecord: editEntityRecord2, saveEditedEntityRecord: saveEditedEntityRecord2 } = (0, import_data12.useDispatch)(store);
|
|
const mutations = (0, import_element3.useMemo)(
|
|
() => ({
|
|
edit: (record2, editOptions = {}) => editEntityRecord2(kind, name, recordId, record2, editOptions),
|
|
save: (saveOptions = {}) => saveEditedEntityRecord2(kind, name, recordId, {
|
|
throwOnError: true,
|
|
...saveOptions
|
|
})
|
|
}),
|
|
[editEntityRecord2, kind, name, recordId, saveEditedEntityRecord2]
|
|
);
|
|
const { editedRecord, hasEdits, edits } = (0, import_data12.useSelect)(
|
|
(select5) => {
|
|
if (!options.enabled) {
|
|
return {
|
|
editedRecord: EMPTY_OBJECT2,
|
|
hasEdits: false,
|
|
edits: EMPTY_OBJECT2
|
|
};
|
|
}
|
|
return {
|
|
editedRecord: select5(store).getEditedEntityRecord(
|
|
kind,
|
|
name,
|
|
recordId
|
|
),
|
|
hasEdits: select5(store).hasEditsForEntityRecord(
|
|
kind,
|
|
name,
|
|
recordId
|
|
),
|
|
edits: select5(store).getEntityRecordNonTransientEdits(
|
|
kind,
|
|
name,
|
|
recordId
|
|
)
|
|
};
|
|
},
|
|
[kind, name, recordId, options.enabled]
|
|
);
|
|
const { data: record, ...querySelectRest } = useQuerySelect(
|
|
(query) => {
|
|
if (!options.enabled) {
|
|
return {
|
|
data: null
|
|
};
|
|
}
|
|
return query(store).getEntityRecord(kind, name, recordId);
|
|
},
|
|
[kind, name, recordId, options.enabled]
|
|
);
|
|
return {
|
|
record,
|
|
editedRecord,
|
|
hasEdits,
|
|
edits,
|
|
...querySelectRest,
|
|
...mutations
|
|
};
|
|
}
|
|
function __experimentalUseEntityRecord(kind, name, recordId, options) {
|
|
(0, import_deprecated4.default)(`wp.data.__experimentalUseEntityRecord`, {
|
|
alternative: "wp.data.useEntityRecord",
|
|
since: "6.1"
|
|
});
|
|
return useEntityRecord(kind, name, recordId, options);
|
|
}
|
|
|
|
// packages/core-data/build-module/hooks/use-entity-records.mjs
|
|
var import_url7 = __toESM(require_url(), 1);
|
|
var import_deprecated5 = __toESM(require_deprecated(), 1);
|
|
var import_data13 = __toESM(require_data(), 1);
|
|
var import_element4 = __toESM(require_element(), 1);
|
|
var EMPTY_ARRAY = [];
|
|
function useEntityRecords(kind, name, queryArgs = {}, options = { enabled: true }) {
|
|
const queryAsString = (0, import_url7.addQueryArgs)("", queryArgs);
|
|
const { data: records, ...rest } = useQuerySelect(
|
|
(query) => {
|
|
if (!options.enabled) {
|
|
return {
|
|
// Avoiding returning a new reference on every execution.
|
|
data: EMPTY_ARRAY
|
|
};
|
|
}
|
|
return query(store).getEntityRecords(kind, name, queryArgs);
|
|
},
|
|
[kind, name, queryAsString, options.enabled]
|
|
);
|
|
const { totalItems, totalPages } = (0, import_data13.useSelect)(
|
|
(select5) => {
|
|
if (!options.enabled) {
|
|
return {
|
|
totalItems: null,
|
|
totalPages: null
|
|
};
|
|
}
|
|
return {
|
|
totalItems: select5(store).getEntityRecordsTotalItems(
|
|
kind,
|
|
name,
|
|
queryArgs
|
|
),
|
|
totalPages: select5(store).getEntityRecordsTotalPages(
|
|
kind,
|
|
name,
|
|
queryArgs
|
|
)
|
|
};
|
|
},
|
|
[kind, name, queryAsString, options.enabled]
|
|
);
|
|
return {
|
|
records,
|
|
totalItems,
|
|
totalPages,
|
|
...rest
|
|
};
|
|
}
|
|
function __experimentalUseEntityRecords(kind, name, queryArgs, options) {
|
|
(0, import_deprecated5.default)(`wp.data.__experimentalUseEntityRecords`, {
|
|
alternative: "wp.data.useEntityRecords",
|
|
since: "6.1"
|
|
});
|
|
return useEntityRecords(kind, name, queryArgs, options);
|
|
}
|
|
function useEntityRecordsWithPermissions(kind, name, queryArgs = {}, options = { enabled: true }) {
|
|
const entityConfig = (0, import_data13.useSelect)(
|
|
(select5) => select5(store).getEntityConfig(kind, name),
|
|
[kind, name]
|
|
);
|
|
const { records: data, ...ret } = useEntityRecords(
|
|
kind,
|
|
name,
|
|
{
|
|
...queryArgs,
|
|
// If _fields is provided, we need to include _links in the request for permission caching to work.
|
|
...queryArgs._fields ? {
|
|
_fields: [
|
|
.../* @__PURE__ */ new Set([
|
|
...get_normalized_comma_separable_default(
|
|
queryArgs._fields
|
|
) || [],
|
|
"_links"
|
|
])
|
|
].join()
|
|
} : {}
|
|
},
|
|
options
|
|
);
|
|
const ids = (0, import_element4.useMemo)(
|
|
() => data?.map(
|
|
// @ts-ignore
|
|
(record) => record[entityConfig?.key ?? "id"]
|
|
) ?? [],
|
|
[data, entityConfig?.key]
|
|
);
|
|
const permissions = (0, import_data13.useSelect)(
|
|
(select5) => {
|
|
const { getEntityRecordsPermissions: getEntityRecordsPermissions2 } = unlock2(
|
|
select5(store)
|
|
);
|
|
return getEntityRecordsPermissions2(kind, name, ids);
|
|
},
|
|
[ids, kind, name]
|
|
);
|
|
const dataWithPermissions = (0, import_element4.useMemo)(
|
|
() => data?.map((record, index) => ({
|
|
// @ts-ignore
|
|
...record,
|
|
permissions: permissions[index]
|
|
})) ?? [],
|
|
[data, permissions]
|
|
);
|
|
return { records: dataWithPermissions, ...ret };
|
|
}
|
|
|
|
// packages/core-data/build-module/hooks/use-resource-permissions.mjs
|
|
var import_deprecated6 = __toESM(require_deprecated(), 1);
|
|
var import_warning = __toESM(require_warning(), 1);
|
|
function useResourcePermissions(resource, id2) {
|
|
const isEntity = typeof resource === "object";
|
|
const resourceAsString = isEntity ? JSON.stringify(resource) : resource;
|
|
if (isEntity && typeof id2 !== "undefined") {
|
|
(0, import_warning.default)(
|
|
`When 'resource' is an entity object, passing 'id' as a separate argument isn't supported.`
|
|
);
|
|
}
|
|
return useQuerySelect(
|
|
(resolve) => {
|
|
const hasId = isEntity ? !!resource.id : !!id2;
|
|
const { canUser: canUser3 } = resolve(store);
|
|
const create9 = canUser3(
|
|
"create",
|
|
isEntity ? { kind: resource.kind, name: resource.name } : resource
|
|
);
|
|
if (!hasId) {
|
|
const read2 = canUser3("read", resource);
|
|
const isResolving2 = create9.isResolving || read2.isResolving;
|
|
const hasResolved2 = create9.hasResolved && read2.hasResolved;
|
|
let status2 = Status.Idle;
|
|
if (isResolving2) {
|
|
status2 = Status.Resolving;
|
|
} else if (hasResolved2) {
|
|
status2 = Status.Success;
|
|
}
|
|
return {
|
|
status: status2,
|
|
isResolving: isResolving2,
|
|
hasResolved: hasResolved2,
|
|
canCreate: create9.hasResolved && create9.data,
|
|
canRead: read2.hasResolved && read2.data
|
|
};
|
|
}
|
|
const read = canUser3("read", resource, id2);
|
|
const update = canUser3("update", resource, id2);
|
|
const _delete = canUser3("delete", resource, id2);
|
|
const isResolving = read.isResolving || create9.isResolving || update.isResolving || _delete.isResolving;
|
|
const hasResolved = read.hasResolved && create9.hasResolved && update.hasResolved && _delete.hasResolved;
|
|
let status = Status.Idle;
|
|
if (isResolving) {
|
|
status = Status.Resolving;
|
|
} else if (hasResolved) {
|
|
status = Status.Success;
|
|
}
|
|
return {
|
|
status,
|
|
isResolving,
|
|
hasResolved,
|
|
canRead: hasResolved && read.data,
|
|
canCreate: hasResolved && create9.data,
|
|
canUpdate: hasResolved && update.data,
|
|
canDelete: hasResolved && _delete.data
|
|
};
|
|
},
|
|
[resourceAsString, id2]
|
|
);
|
|
}
|
|
var use_resource_permissions_default = useResourcePermissions;
|
|
function __experimentalUseResourcePermissions(resource, id2) {
|
|
(0, import_deprecated6.default)(`wp.data.__experimentalUseResourcePermissions`, {
|
|
alternative: "wp.data.useResourcePermissions",
|
|
since: "6.1"
|
|
});
|
|
return useResourcePermissions(resource, id2);
|
|
}
|
|
|
|
// packages/core-data/build-module/hooks/use-entity-block-editor.mjs
|
|
var import_element6 = __toESM(require_element(), 1);
|
|
var import_data14 = __toESM(require_data(), 1);
|
|
var import_blocks5 = __toESM(require_blocks(), 1);
|
|
|
|
// packages/core-data/build-module/hooks/use-entity-id.mjs
|
|
var import_element5 = __toESM(require_element(), 1);
|
|
function useEntityId(kind, name) {
|
|
const context = (0, import_element5.useContext)(EntityContext);
|
|
return context?.[kind]?.[name];
|
|
}
|
|
|
|
// packages/core-data/build-module/footnotes/index.mjs
|
|
var import_rich_text4 = __toESM(require_rich_text(), 1);
|
|
|
|
// packages/core-data/build-module/footnotes/get-rich-text-values-cached.mjs
|
|
var import_block_editor5 = __toESM(require_block_editor(), 1);
|
|
var unlockedApis;
|
|
var cache = /* @__PURE__ */ new WeakMap();
|
|
function getRichTextValuesCached(block) {
|
|
if (!unlockedApis) {
|
|
unlockedApis = unlock2(import_block_editor5.privateApis);
|
|
}
|
|
if (!cache.has(block)) {
|
|
const values = unlockedApis.getRichTextValues([block]);
|
|
cache.set(block, values);
|
|
}
|
|
return cache.get(block);
|
|
}
|
|
|
|
// packages/core-data/build-module/footnotes/get-footnotes-order.mjs
|
|
var cache2 = /* @__PURE__ */ new WeakMap();
|
|
function getBlockFootnotesOrder(block) {
|
|
if (!cache2.has(block)) {
|
|
const order = [];
|
|
for (const value of getRichTextValuesCached(block)) {
|
|
if (!value) {
|
|
continue;
|
|
}
|
|
value.replacements.forEach(({ type, attributes }) => {
|
|
if (type === "core/footnote") {
|
|
order.push(attributes["data-fn"]);
|
|
}
|
|
});
|
|
}
|
|
cache2.set(block, order);
|
|
}
|
|
return cache2.get(block);
|
|
}
|
|
function getFootnotesOrder(blocks) {
|
|
return blocks.flatMap(getBlockFootnotesOrder);
|
|
}
|
|
|
|
// packages/core-data/build-module/footnotes/index.mjs
|
|
var oldFootnotes = {};
|
|
function updateFootnotesFromMeta(blocks, meta) {
|
|
const output = { blocks };
|
|
if (!meta) {
|
|
return output;
|
|
}
|
|
if (meta.footnotes === void 0) {
|
|
return output;
|
|
}
|
|
const newOrder = getFootnotesOrder(blocks);
|
|
const footnotes = meta.footnotes ? JSON.parse(meta.footnotes) : [];
|
|
const currentOrder = footnotes.map((fn) => fn.id);
|
|
if (currentOrder.join("") === newOrder.join("")) {
|
|
return output;
|
|
}
|
|
const newFootnotes = newOrder.map(
|
|
(fnId) => footnotes.find((fn) => fn.id === fnId) || oldFootnotes[fnId] || {
|
|
id: fnId,
|
|
content: ""
|
|
}
|
|
);
|
|
function updateAttributes(attributes) {
|
|
if (!attributes || Array.isArray(attributes) || typeof attributes !== "object") {
|
|
return attributes;
|
|
}
|
|
attributes = { ...attributes };
|
|
for (const key in attributes) {
|
|
const value = attributes[key];
|
|
if (Array.isArray(value)) {
|
|
attributes[key] = value.map(updateAttributes);
|
|
continue;
|
|
}
|
|
if (typeof value !== "string" && !(value instanceof import_rich_text4.RichTextData)) {
|
|
continue;
|
|
}
|
|
const richTextValue = typeof value === "string" ? import_rich_text4.RichTextData.fromHTMLString(value) : new import_rich_text4.RichTextData(value);
|
|
let hasFootnotes = false;
|
|
richTextValue.replacements.forEach((replacement) => {
|
|
if (replacement.type === "core/footnote") {
|
|
const id2 = replacement.attributes["data-fn"];
|
|
const index = newOrder.indexOf(id2);
|
|
const countValue = (0, import_rich_text4.create)({
|
|
html: replacement.innerHTML
|
|
});
|
|
countValue.text = String(index + 1);
|
|
countValue.formats = Array.from(
|
|
{ length: countValue.text.length },
|
|
() => countValue.formats[0]
|
|
);
|
|
countValue.replacements = Array.from(
|
|
{ length: countValue.text.length },
|
|
() => countValue.replacements[0]
|
|
);
|
|
replacement.innerHTML = (0, import_rich_text4.toHTMLString)({
|
|
value: countValue
|
|
});
|
|
hasFootnotes = true;
|
|
}
|
|
});
|
|
if (hasFootnotes) {
|
|
attributes[key] = typeof value === "string" ? richTextValue.toHTMLString() : richTextValue;
|
|
}
|
|
}
|
|
return attributes;
|
|
}
|
|
function updateBlocksAttributes(__blocks) {
|
|
return __blocks.map((block) => {
|
|
return {
|
|
...block,
|
|
attributes: updateAttributes(block.attributes),
|
|
innerBlocks: updateBlocksAttributes(block.innerBlocks)
|
|
};
|
|
});
|
|
}
|
|
const newBlocks = updateBlocksAttributes(blocks);
|
|
oldFootnotes = {
|
|
...oldFootnotes,
|
|
...footnotes.reduce((acc, fn) => {
|
|
if (!newOrder.includes(fn.id)) {
|
|
acc[fn.id] = fn;
|
|
}
|
|
return acc;
|
|
}, {})
|
|
};
|
|
return {
|
|
meta: {
|
|
...meta,
|
|
footnotes: JSON.stringify(newFootnotes)
|
|
},
|
|
blocks: newBlocks
|
|
};
|
|
}
|
|
|
|
// packages/core-data/build-module/hooks/use-entity-block-editor.mjs
|
|
var EMPTY_ARRAY2 = [];
|
|
var parsedBlocksCache = /* @__PURE__ */ new Map();
|
|
function useEntityBlockEditor(kind, name, { id: _id } = {}) {
|
|
const providerId = useEntityId(kind, name);
|
|
const id2 = _id ?? providerId;
|
|
const { content, editedBlocks, meta } = (0, import_data14.useSelect)(
|
|
(select5) => {
|
|
if (!id2) {
|
|
return {};
|
|
}
|
|
const { getEditedEntityRecord: getEditedEntityRecord3 } = select5(STORE_NAME);
|
|
const editedRecord = getEditedEntityRecord3(kind, name, id2);
|
|
return {
|
|
editedBlocks: editedRecord.blocks,
|
|
content: editedRecord.content,
|
|
meta: editedRecord.meta
|
|
};
|
|
},
|
|
[kind, name, id2]
|
|
);
|
|
const { __unstableCreateUndoLevel: __unstableCreateUndoLevel2, editEntityRecord: editEntityRecord2 } = (0, import_data14.useDispatch)(STORE_NAME);
|
|
const blocks = (0, import_element6.useMemo)(() => {
|
|
if (!id2) {
|
|
return void 0;
|
|
}
|
|
if (editedBlocks) {
|
|
return editedBlocks;
|
|
}
|
|
if (!content || typeof content !== "string") {
|
|
return EMPTY_ARRAY2;
|
|
}
|
|
const cacheKey = `${kind}:${name}:${id2}`;
|
|
const cached = parsedBlocksCache.get(cacheKey);
|
|
let _blocks;
|
|
if (cached && cached.content === content) {
|
|
_blocks = cached.blocks;
|
|
} else {
|
|
_blocks = (0, import_blocks5.parse)(content);
|
|
parsedBlocksCache.set(cacheKey, { content, blocks: _blocks });
|
|
}
|
|
return _blocks;
|
|
}, [kind, name, id2, editedBlocks, content]);
|
|
const onChange = (0, import_element6.useCallback)(
|
|
(newBlocks, options) => {
|
|
const noChange = blocks === newBlocks;
|
|
if (noChange) {
|
|
return __unstableCreateUndoLevel2(kind, name, id2);
|
|
}
|
|
const { selection, ...rest } = options;
|
|
const edits = {
|
|
selection,
|
|
content: ({ blocks: blocksForSerialization = [] }) => (0, import_blocks5.__unstableSerializeAndClean)(blocksForSerialization),
|
|
...updateFootnotesFromMeta(newBlocks, meta)
|
|
};
|
|
editEntityRecord2(kind, name, id2, edits, {
|
|
isCached: false,
|
|
...rest
|
|
});
|
|
},
|
|
[
|
|
kind,
|
|
name,
|
|
id2,
|
|
blocks,
|
|
meta,
|
|
__unstableCreateUndoLevel2,
|
|
editEntityRecord2
|
|
]
|
|
);
|
|
const onInput = (0, import_element6.useCallback)(
|
|
(newBlocks, options) => {
|
|
const { selection, ...rest } = options;
|
|
const edits = {
|
|
selection,
|
|
...updateFootnotesFromMeta(newBlocks, meta)
|
|
};
|
|
editEntityRecord2(kind, name, id2, edits, {
|
|
isCached: true,
|
|
...rest
|
|
});
|
|
},
|
|
[kind, name, id2, meta, editEntityRecord2]
|
|
);
|
|
return [blocks, onInput, onChange];
|
|
}
|
|
|
|
// packages/core-data/build-module/hooks/use-entity-prop.mjs
|
|
var import_element7 = __toESM(require_element(), 1);
|
|
var import_data15 = __toESM(require_data(), 1);
|
|
function useEntityProp(kind, name, prop, _id) {
|
|
const providerId = useEntityId(kind, name);
|
|
const id2 = _id ?? providerId;
|
|
const context = (0, import_element7.useContext)(EntityContext);
|
|
const revisionId = context?.revisionId;
|
|
const { value, fullValue } = (0, import_data15.useSelect)(
|
|
(select5) => {
|
|
if (revisionId) {
|
|
const revisions = select5(STORE_NAME).getRevisions(
|
|
kind,
|
|
name,
|
|
id2,
|
|
{
|
|
per_page: -1,
|
|
context: "edit",
|
|
_fields: "id,date,author,meta,title.raw,excerpt.raw,content.raw"
|
|
}
|
|
);
|
|
const entityConfig = select5(STORE_NAME).getEntityConfig(
|
|
kind,
|
|
name
|
|
);
|
|
const revKey = entityConfig?.revisionKey || DEFAULT_ENTITY_KEY;
|
|
const revision = revisions?.find(
|
|
(r) => r[revKey] === revisionId
|
|
);
|
|
return revision ? {
|
|
value: revision[prop]?.raw ?? revision[prop],
|
|
fullValue: revision[prop]
|
|
} : {};
|
|
}
|
|
const { getEntityRecord: getEntityRecord3, getEditedEntityRecord: getEditedEntityRecord3 } = select5(STORE_NAME);
|
|
const record = getEntityRecord3(kind, name, id2);
|
|
const editedRecord = getEditedEntityRecord3(kind, name, id2);
|
|
return record && editedRecord ? {
|
|
value: editedRecord[prop],
|
|
fullValue: record[prop]
|
|
} : {};
|
|
},
|
|
[kind, name, id2, prop, revisionId]
|
|
);
|
|
const { editEntityRecord: editEntityRecord2 } = (0, import_data15.useDispatch)(STORE_NAME);
|
|
const setValue = (0, import_element7.useCallback)(
|
|
(newValue) => {
|
|
if (revisionId) {
|
|
return;
|
|
}
|
|
editEntityRecord2(kind, name, id2, {
|
|
[prop]: newValue
|
|
});
|
|
},
|
|
[editEntityRecord2, kind, name, id2, prop, revisionId]
|
|
);
|
|
return [value, setValue, fullValue];
|
|
}
|
|
|
|
// packages/core-data/build-module/hooks/use-post-editor-awareness-state.mjs
|
|
var import_compose3 = __toESM(require_compose(), 1);
|
|
var import_element8 = __toESM(require_element(), 1);
|
|
var defaultResolvedSelection = {
|
|
richTextOffset: null,
|
|
localClientId: null
|
|
};
|
|
var defaultState = {
|
|
activeCollaborators: [],
|
|
resolveSelection: () => defaultResolvedSelection,
|
|
getDebugData: () => ({
|
|
doc: {},
|
|
clients: {},
|
|
collaboratorMap: {}
|
|
}),
|
|
isCurrentCollaboratorDisconnected: false
|
|
};
|
|
function getAwarenessState(awareness, newState) {
|
|
const activeCollaborators = newState ?? awareness.getCurrentState();
|
|
return {
|
|
activeCollaborators,
|
|
resolveSelection: (selection) => awareness.convertSelectionStateToAbsolute(selection),
|
|
getDebugData: () => awareness.getDebugData(),
|
|
isCurrentCollaboratorDisconnected: activeCollaborators.find((collaborator) => collaborator.isMe)?.isConnected === false
|
|
};
|
|
}
|
|
function usePostEditorAwarenessState(postId, postType) {
|
|
const [state, setState] = (0, import_element8.useState)(defaultState);
|
|
(0, import_element8.useEffect)(() => {
|
|
if (null === postId || null === postType) {
|
|
setState(defaultState);
|
|
return;
|
|
}
|
|
const objectType = `postType/${postType}`;
|
|
const objectId = postId.toString();
|
|
const awareness = getSyncManager()?.getAwareness(
|
|
objectType,
|
|
objectId
|
|
);
|
|
if (!awareness) {
|
|
setState(defaultState);
|
|
return;
|
|
}
|
|
awareness.setUp();
|
|
setState(getAwarenessState(awareness));
|
|
const unsubscribe = awareness?.onStateChange(
|
|
(newState) => {
|
|
setState(getAwarenessState(awareness, newState));
|
|
}
|
|
);
|
|
return unsubscribe;
|
|
}, [postId, postType]);
|
|
return state;
|
|
}
|
|
function useActiveCollaborators(postId, postType) {
|
|
return usePostEditorAwarenessState(postId, postType).activeCollaborators;
|
|
}
|
|
function useResolvedSelection(postId, postType) {
|
|
return usePostEditorAwarenessState(postId, postType).resolveSelection;
|
|
}
|
|
function useLastPostSave(postId, postType) {
|
|
const [lastSave, setLastSave] = (0, import_element8.useState)(null);
|
|
(0, import_element8.useEffect)(() => {
|
|
if (null === postId || null === postType) {
|
|
setLastSave(null);
|
|
return;
|
|
}
|
|
const awareness = getSyncManager()?.getAwareness(
|
|
`postType/${postType}`,
|
|
postId.toString()
|
|
);
|
|
if (!awareness) {
|
|
setLastSave(null);
|
|
return;
|
|
}
|
|
awareness.setUp();
|
|
const stateMap = awareness.doc.getMap("state");
|
|
const recordMap = awareness.doc.getMap("document");
|
|
const setupTime = Date.now();
|
|
const observer = (event) => {
|
|
if (event.keysChanged.has("savedAt")) {
|
|
const savedAt = stateMap.get("savedAt");
|
|
const savedByClientId = stateMap.get("savedBy");
|
|
if (typeof savedAt === "number" && typeof savedByClientId === "number" && savedAt > setupTime) {
|
|
const postStatus = recordMap.get("status");
|
|
setLastSave({ savedAt, savedByClientId, postStatus });
|
|
}
|
|
}
|
|
};
|
|
stateMap.observe(observer);
|
|
return () => {
|
|
stateMap.unobserve(observer);
|
|
};
|
|
}, [postId, postType]);
|
|
return lastSave;
|
|
}
|
|
function useOnCollaboratorJoin(postId, postType, callback) {
|
|
const { activeCollaborators } = usePostEditorAwarenessState(
|
|
postId,
|
|
postType
|
|
);
|
|
const prevCollaborators = (0, import_compose3.usePrevious)(activeCollaborators);
|
|
(0, import_element8.useEffect)(() => {
|
|
if (!prevCollaborators || prevCollaborators.length === 0) {
|
|
return;
|
|
}
|
|
const prevMap = new Map(
|
|
prevCollaborators.map((collaborator) => [
|
|
collaborator.clientId,
|
|
collaborator
|
|
])
|
|
);
|
|
const me = activeCollaborators.find(
|
|
(collaborator) => collaborator.isMe
|
|
);
|
|
for (const collaborator of activeCollaborators) {
|
|
if (!prevMap.has(collaborator.clientId) && !collaborator.isMe) {
|
|
callback(collaborator, me);
|
|
}
|
|
}
|
|
}, [activeCollaborators, prevCollaborators, callback]);
|
|
}
|
|
function useOnCollaboratorLeave(postId, postType, callback) {
|
|
const { activeCollaborators } = usePostEditorAwarenessState(
|
|
postId,
|
|
postType
|
|
);
|
|
const prevCollaborators = (0, import_compose3.usePrevious)(activeCollaborators);
|
|
(0, import_element8.useEffect)(() => {
|
|
if (!prevCollaborators || prevCollaborators.length === 0) {
|
|
return;
|
|
}
|
|
const newMap = new Map(
|
|
activeCollaborators.map((collaborator) => [
|
|
collaborator.clientId,
|
|
collaborator
|
|
])
|
|
);
|
|
for (const prevCollab of prevCollaborators) {
|
|
if (prevCollab.isMe || !prevCollab.isConnected) {
|
|
continue;
|
|
}
|
|
const newCollab = newMap.get(prevCollab.clientId);
|
|
if (!newCollab?.isConnected) {
|
|
callback(prevCollab);
|
|
}
|
|
}
|
|
}, [activeCollaborators, prevCollaborators, callback]);
|
|
}
|
|
function useOnPostSave(postId, postType, callback) {
|
|
const { activeCollaborators } = usePostEditorAwarenessState(
|
|
postId,
|
|
postType
|
|
);
|
|
const lastPostSave = useLastPostSave(postId, postType);
|
|
const prevPostSave = (0, import_compose3.usePrevious)(lastPostSave);
|
|
(0, import_element8.useEffect)(() => {
|
|
if (!lastPostSave) {
|
|
return;
|
|
}
|
|
if (prevPostSave && lastPostSave.savedAt === prevPostSave.savedAt) {
|
|
return;
|
|
}
|
|
const saver = activeCollaborators.find(
|
|
(collaborator) => collaborator.clientId === lastPostSave.savedByClientId && !collaborator.isMe
|
|
);
|
|
if (!saver) {
|
|
return;
|
|
}
|
|
callback(lastPostSave, saver, prevPostSave ?? null);
|
|
}, [lastPostSave, prevPostSave, activeCollaborators, callback]);
|
|
}
|
|
|
|
// packages/core-data/build-module/private-apis.mjs
|
|
var lockedApis = {
|
|
useEntityRecordsWithPermissions,
|
|
RECEIVE_INTERMEDIATE_RESULTS,
|
|
retrySyncConnection,
|
|
useActiveCollaborators,
|
|
useResolvedSelection,
|
|
useOnCollaboratorJoin,
|
|
useOnCollaboratorLeave,
|
|
useOnPostSave,
|
|
SelectionType,
|
|
SelectionDirection
|
|
};
|
|
var privateApis2 = {};
|
|
lock2(privateApis2, lockedApis);
|
|
|
|
// packages/core-data/build-module/index.mjs
|
|
var entitiesConfig2 = [
|
|
...rootEntitiesConfig,
|
|
...additionalEntityConfigLoaders.filter((config) => !!config.name)
|
|
];
|
|
var entitySelectors = entitiesConfig2.reduce((result, entity2) => {
|
|
const { kind, name, plural } = entity2;
|
|
const getEntityRecordMethodName = getMethodName(kind, name);
|
|
result[getEntityRecordMethodName] = (state, key, query) => {
|
|
logEntityDeprecation(kind, name, getEntityRecordMethodName, {
|
|
isShorthandSelector: true,
|
|
alternativeFunctionName: "getEntityRecord"
|
|
});
|
|
return getEntityRecord(state, kind, name, key, query);
|
|
};
|
|
if (plural) {
|
|
const getEntityRecordsMethodName = getMethodName(kind, plural, "get");
|
|
result[getEntityRecordsMethodName] = (state, query) => {
|
|
logEntityDeprecation(kind, name, getEntityRecordsMethodName, {
|
|
isShorthandSelector: true,
|
|
alternativeFunctionName: "getEntityRecords"
|
|
});
|
|
return getEntityRecords(state, kind, name, query);
|
|
};
|
|
}
|
|
return result;
|
|
}, {});
|
|
var entityResolvers = entitiesConfig2.reduce((result, entity2) => {
|
|
const { kind, name, plural } = entity2;
|
|
const getEntityRecordMethodName = getMethodName(kind, name);
|
|
result[getEntityRecordMethodName] = (key, query) => {
|
|
logEntityDeprecation(kind, name, getEntityRecordMethodName, {
|
|
isShorthandSelector: true,
|
|
alternativeFunctionName: "getEntityRecord"
|
|
});
|
|
return getEntityRecord2(kind, name, key, query);
|
|
};
|
|
if (plural) {
|
|
const getEntityRecordsMethodName = getMethodName(kind, plural, "get");
|
|
result[getEntityRecordsMethodName] = (...args2) => {
|
|
logEntityDeprecation(kind, plural, getEntityRecordsMethodName, {
|
|
isShorthandSelector: true,
|
|
alternativeFunctionName: "getEntityRecords"
|
|
});
|
|
return getEntityRecords2(kind, name, ...args2);
|
|
};
|
|
result[getEntityRecordsMethodName].shouldInvalidate = (action) => getEntityRecords2.shouldInvalidate(action, kind, name);
|
|
}
|
|
return result;
|
|
}, {});
|
|
var entityActions = entitiesConfig2.reduce((result, entity2) => {
|
|
const { kind, name } = entity2;
|
|
const saveEntityRecordMethodName = getMethodName(kind, name, "save");
|
|
result[saveEntityRecordMethodName] = (record, options) => {
|
|
logEntityDeprecation(kind, name, saveEntityRecordMethodName, {
|
|
isShorthandSelector: true,
|
|
alternativeFunctionName: "saveEntityRecord"
|
|
});
|
|
return saveEntityRecord(kind, name, record, options);
|
|
};
|
|
const deleteEntityRecordMethodName = getMethodName(kind, name, "delete");
|
|
result[deleteEntityRecordMethodName] = (key, query, options) => {
|
|
logEntityDeprecation(kind, name, deleteEntityRecordMethodName, {
|
|
isShorthandSelector: true,
|
|
alternativeFunctionName: "deleteEntityRecord"
|
|
});
|
|
return deleteEntityRecord(kind, name, key, query, options);
|
|
};
|
|
return result;
|
|
}, {});
|
|
var storeConfig = () => ({
|
|
reducer: reducer_default2,
|
|
actions: {
|
|
...dynamicActions,
|
|
...actions_exports,
|
|
...entityActions,
|
|
...createLocksActions()
|
|
},
|
|
selectors: {
|
|
...dynamicSelectors,
|
|
...selectors_exports,
|
|
...entitySelectors
|
|
},
|
|
resolvers: { ...resolvers_exports, ...entityResolvers }
|
|
});
|
|
var store = (0, import_data16.createReduxStore)(STORE_NAME, storeConfig());
|
|
unlock2(store).registerPrivateSelectors(private_selectors_exports);
|
|
unlock2(store).registerPrivateActions(private_actions_exports);
|
|
(0, import_data16.register)(store);
|
|
return __toCommonJS(index_exports);
|
|
})();
|