From 3bf44b98505abadd8b73db7492d2079484c38733 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 4 Oct 2024 21:07:19 +0200 Subject: [PATCH] #667 - add sanity messages when data is read before type is added to a document. --- src/types/AbstractType.js | 21 +++++++++++++++++++-- src/types/YArray.js | 2 ++ src/types/YMap.js | 11 +++++++---- src/types/YText.js | 4 ++++ src/types/YXmlFragment.js | 3 +++ 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 3dff240c..24fa8802 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -17,6 +17,12 @@ import * as map from 'lib0/map' import * as iterator from 'lib0/iterator' import * as error from 'lib0/error' import * as math from 'lib0/math' +import * as log from 'lib0/logging' + +/** + * https://docs.yjs.dev/getting-started/working-with-shared-types#caveats + */ +export const warnPrematureAccess = () => { log.warn('Invalid access: Add Yjs type to a document before reading data.') } const maxSearchMarker = 80 @@ -215,6 +221,7 @@ export const updateMarkerChanges = (searchMarker, index, len) => { * @return {Array} */ export const getTypeChildren = t => { + t.doc ?? warnPrematureAccess() let s = t._start const arr = [] while (s) { @@ -408,6 +415,7 @@ export class AbstractType { * @function */ export const typeListSlice = (type, start, end) => { + type.doc ?? warnPrematureAccess() if (start < 0) { start = type._length + start } @@ -443,6 +451,7 @@ export const typeListSlice = (type, start, end) => { * @function */ export const typeListToArray = type => { + type.doc ?? warnPrematureAccess() const cs = [] let n = type._start while (n !== null) { @@ -492,6 +501,7 @@ export const typeListToArraySnapshot = (type, snapshot) => { export const 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() @@ -606,6 +616,7 @@ export const typeListForEachSnapshot = (type, f, snapshot) => { * @function */ export const typeListGet = (type, index) => { + type.doc ?? warnPrematureAccess() const marker = findMarker(type, index) let n = type._start if (marker !== null) { @@ -874,6 +885,7 @@ export const typeMapSet = (transaction, parent, key, value) => { * @function */ export const typeMapGet = (parent, key) => { + parent.doc ?? warnPrematureAccess() const val = parent._map.get(key) return val !== undefined && !val.deleted ? val.content.getContent()[val.length - 1] : undefined } @@ -890,6 +902,7 @@ export const typeMapGetAll = (parent) => { * @type {Object} */ const res = {} + parent.doc ?? warnPrematureAccess() parent._map.forEach((value, key) => { if (!value.deleted) { res[key] = value.content.getContent()[value.length - 1] @@ -907,6 +920,7 @@ export const typeMapGetAll = (parent) => { * @function */ export const typeMapHas = (parent, key) => { + parent.doc ?? warnPrematureAccess() const val = parent._map.get(key) return val !== undefined && !val.deleted } @@ -957,10 +971,13 @@ export const typeMapGetAllSnapshot = (parent, snapshot) => { } /** - * @param {Map} map + * @param {AbstractType & { _map: Map }} type * @return {IterableIterator>} * * @private * @function */ -export const createMapIterator = map => iterator.iteratorFilter(map.entries(), /** @param {any} entry */ entry => !entry[1].deleted) +export const createMapIterator = type => { + type.doc ?? warnPrematureAccess() + return iterator.iteratorFilter(type._map.entries(), /** @param {any} entry */ entry => !entry[1].deleted) +} diff --git a/src/types/YArray.js b/src/types/YArray.js index c70f2cda..8fd5c215 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -16,6 +16,7 @@ import { YArrayRefID, callTypeObservers, transact, + warnPrematureAccess, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line } from '../internals.js' import { typeListSlice } from './AbstractType.js' @@ -104,6 +105,7 @@ export class YArray extends AbstractType { } get length () { + this.doc ?? warnPrematureAccess() return this._length } diff --git a/src/types/YMap.js b/src/types/YMap.js index 974e7316..22b94afb 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -13,6 +13,7 @@ import { YMapRefID, callTypeObservers, transact, + warnPrematureAccess, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line } from '../internals.js' @@ -121,6 +122,7 @@ export class YMap extends AbstractType { * @return {Object} */ toJSON () { + this.doc ?? warnPrematureAccess() /** * @type {Object} */ @@ -140,7 +142,7 @@ export class YMap extends AbstractType { * @return {number} */ get size () { - return [...createMapIterator(this._map)].length + return [...createMapIterator(this)].length } /** @@ -149,7 +151,7 @@ export class YMap extends AbstractType { * @return {IterableIterator} */ keys () { - return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => v[0]) + return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => v[0]) } /** @@ -158,7 +160,7 @@ export class YMap extends AbstractType { * @return {IterableIterator} */ values () { - return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => v[1].content.getContent()[v[1].length - 1]) + return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => v[1].content.getContent()[v[1].length - 1]) } /** @@ -167,7 +169,7 @@ export class YMap extends AbstractType { * @return {IterableIterator<[string, MapType]>} */ entries () { - return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => /** @type {any} */ ([v[0], v[1].content.getContent()[v[1].length - 1]])) + return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => /** @type {any} */ ([v[0], v[1].content.getContent()[v[1].length - 1]])) } /** @@ -176,6 +178,7 @@ export class YMap extends AbstractType { * @param {function(MapType,string,YMap):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) diff --git a/src/types/YText.js b/src/types/YText.js index 8919b009..d4c59f03 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -26,6 +26,7 @@ import { typeMapGetAll, updateMarkerChanges, ContentType, + warnPrematureAccess, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction // eslint-disable-line } from '../internals.js' @@ -875,6 +876,7 @@ export class YText extends AbstractType { * @type {number} */ get length () { + this.doc ?? warnPrematureAccess() return this._length } @@ -931,6 +933,7 @@ export class YText extends AbstractType { * @public */ toString () { + this.doc ?? warnPrematureAccess() let str = '' /** * @type {Item|null} @@ -1004,6 +1007,7 @@ export class YText extends AbstractType { * @public */ toDelta (snapshot, prevSnapshot, computeYChange) { + this.doc ?? warnPrematureAccess() /** * @type{Array} */ diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 1445139c..2c0e9c5b 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -17,6 +17,7 @@ import { transact, typeListGet, typeListSlice, + warnPrematureAccess, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook // eslint-disable-line } from '../internals.js' @@ -66,6 +67,7 @@ export class YXmlTreeWalker { */ this._currentNode = /** @type {Item} */ (root._start) this._firstCall = true + root.doc ?? warnPrematureAccess() } [Symbol.iterator] () { @@ -177,6 +179,7 @@ export class YXmlFragment extends AbstractType { } get length () { + this.doc ?? warnPrematureAccess() return this._prelimContent === null ? this._length : this._prelimContent.length }