diff --git a/src/vanilla/utils/proxyMap.ts b/src/vanilla/utils/proxyMap.ts index d41220d1..a06afee3 100644 --- a/src/vanilla/utils/proxyMap.ts +++ b/src/vanilla/utils/proxyMap.ts @@ -14,6 +14,7 @@ export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { const initialData: Array = [] let initialIndex = 0 const indexMap = new Map() + const snapMapCache = new WeakMap>() const registerSnapMap = () => { const cache = snapCache.get(vObject) @@ -33,7 +34,7 @@ export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { } const getSnapMap = (x: any) => snapMapCache.get(x) - if (entries !== null && typeof entries !== 'undefined') { + if (entries) { if (typeof entries[Symbol.iterator] !== 'function') { throw new TypeError( 'proxyMap:\n\tinitial state must be iterable\n\t\ttip: structure should be [[key, value]]', @@ -97,7 +98,6 @@ export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { this.data[nextIndex++] = v this.index = nextIndex } - return this }, delete(key: K) { @@ -109,7 +109,6 @@ export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { if (index === undefined) { return false } - delete this.data[index] delete this.data[index + 1] indexMap.delete(k) @@ -119,9 +118,9 @@ export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { if (!isProxy(this)) { throw new Error('Cannot perform mutations on a snapshot') } - indexMap.clear() + this.data.length = 0 // empty array this.index = 0 - this.data.length = 0 + indexMap.clear() }, forEach(cb: (value: V, key: K, map: Map) => void) { const map = getSnapMap(this) || indexMap @@ -154,20 +153,17 @@ export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { return 'Map' }, toJSON(): Map { - const map = getSnapMap(this) || indexMap - return new Map([...map].map(([k, i]) => [k, this.data[i + 1] as V])) + return new Map(this.entries()) }, } const proxiedObject = proxy(vObject) - Object.defineProperties(proxiedObject, { size: { enumerable: false }, index: { enumerable: false }, data: { enumerable: false }, toJSON: { enumerable: false }, }) - Object.seal(proxiedObject) return proxiedObject as unknown as Map & { diff --git a/src/vanilla/utils/proxySet.ts b/src/vanilla/utils/proxySet.ts index 0416096f..249178ac 100644 --- a/src/vanilla/utils/proxySet.ts +++ b/src/vanilla/utils/proxySet.ts @@ -20,6 +20,7 @@ export function proxySet(initialValues?: Iterable | null) { const initialData: T[] = [] const indexMap = new Map() let initialIndex = 0 + const snapMapCache = new WeakMap>() const registerSnapMap = () => { const cache = snapCache.get(vObject) @@ -33,7 +34,7 @@ export function proxySet(initialValues?: Iterable | null) { } const getSnapMap = (x: any) => snapMapCache.get(x) - if (initialValues !== null && typeof initialValues !== 'undefined') { + if (initialValues) { if (typeof initialValues[Symbol.iterator] !== 'function') { throw new TypeError('not iterable') } @@ -55,6 +56,16 @@ export function proxySet(initialValues?: Iterable | null) { } return indexMap.size }, + has(v: T) { + const map = getSnapMap(this) || indexMap + const value = maybeProxify(v) + const exists = map.has(value) + if (!exists && !isProxy(this)) { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + this.index + } + return exists + }, add(value: T) { if (!isProxy(this)) { throw new Error('Cannot perform mutations on a snapshot') @@ -64,6 +75,7 @@ export function proxySet(initialValues?: Iterable | null) { let nextIndex = this.index indexMap.set(v, nextIndex) this.data[nextIndex++] = v + this.index = nextIndex } return this }, @@ -76,7 +88,6 @@ export function proxySet(initialValues?: Iterable | null) { if (index === undefined) { return false } - delete this.data[index] indexMap.delete(v) return true @@ -85,29 +96,19 @@ export function proxySet(initialValues?: Iterable | null) { if (!isProxy(this)) { throw new Error('Cannot perform mutations on a snapshot') } - this.data.length = 0 + this.data.length = 0 // empty array this.index = 0 indexMap.clear() }, forEach(cb) { - const set = getSnapMap(this) || indexMap - set.forEach((index) => { + const map = getSnapMap(this) || indexMap + map.forEach((index) => { cb(this.data[index]!, this.data[index]!, this) }) }, - has(v: T) { - const iMap = getSnapMap(this) || indexMap - const value = maybeProxify(v) - const exists = iMap.has(value) - if (!exists && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.index - } - return exists - }, *values(): IterableIterator { - const iMap = getSnapMap(this) || indexMap - for (const index of iMap.values()) { + const map = getSnapMap(this) || indexMap + for (const index of map.values()) { yield this.data[index]! } }, @@ -115,16 +116,14 @@ export function proxySet(initialValues?: Iterable | null) { return this.values() }, *entries(): IterableIterator<[T, T]> { - const iMap = getSnapMap(this) || indexMap - for (const index of iMap.values()) { + const map = getSnapMap(this) || indexMap + for (const index of map.values()) { const value = this.data[index]! yield [value, value] } }, toJSON(): Set { - // filtering is about twice as fast as creating a new set and deleting - // the undefined value because filter actually skips empty slots - return new Set(this.data.filter((v) => v !== undefined) as T[]) + return new Set(this.values()) }, [Symbol.iterator]() { return this.values() @@ -135,13 +134,11 @@ export function proxySet(initialValues?: Iterable | null) { intersection(other: Set): Set { const otherSet = proxySet(other) const resultSet = proxySet() - for (const value of this.values()) { if (otherSet.has(value)) { resultSet.add(value) } } - return proxySet(resultSet) }, isDisjointFrom(other: Set): boolean { @@ -168,38 +165,32 @@ export function proxySet(initialValues?: Iterable | null) { symmetricDifference(other: Set) { const resultSet = proxySet() const otherSet = proxySet(other) - for (const value of this.values()) { if (!otherSet.has(value)) { resultSet.add(value) } } - return proxySet(resultSet) }, union(other: Set) { const resultSet = proxySet() const otherSet = proxySet(other) - for (const value of this.values()) { resultSet.add(value) } for (const value of otherSet) { resultSet.add(value) } - return proxySet(resultSet) }, } const proxiedObject = proxy(vObject) - Object.defineProperties(proxiedObject, { size: { enumerable: false }, data: { enumerable: false }, toJSON: { enumerable: false }, }) - Object.seal(proxiedObject) return proxiedObject as unknown as InternalProxySet & {