UNPKG

valtio

Version:

🧙 Valtio makes proxy-state simple for React and Vanilla

584 lines (574 loc) • 17.6 kB
'use strict'; var vanilla = require('valtio/vanilla'); function subscribeKey(proxyObject, key, callback, notifyInSync) { let prevValue = proxyObject[key]; return vanilla.subscribe( proxyObject, () => { const nextValue = proxyObject[key]; if (!Object.is(prevValue, nextValue)) { callback(prevValue = nextValue); } }, notifyInSync ); } let currentCleanups; let didWarnDeprecation = false; function watch(callback, options) { if (process.env.NODE_ENV !== "production" && !didWarnDeprecation) { console.warn( "[DEPRECATED] The `watch` util is no longer maintained. Please migrate to [valtio-reactive](https://github.com/valtiojs/valtio-reactive)." ); didWarnDeprecation = true; } let alive = true; const cleanups = /* @__PURE__ */ new Set(); const subscriptions = /* @__PURE__ */ new Map(); const cleanup = () => { if (alive) { alive = false; cleanups.forEach((clean) => clean()); cleanups.clear(); subscriptions.forEach((unsubscribe) => unsubscribe()); subscriptions.clear(); } }; const revalidate = async () => { if (!alive) { return; } cleanups.forEach((clean) => clean()); cleanups.clear(); const proxiesToSubscribe = /* @__PURE__ */ new Set(); const parent = currentCleanups; currentCleanups = cleanups; try { const promiseOrPossibleCleanup = callback((proxyObject) => { proxiesToSubscribe.add(proxyObject); if (alive && !subscriptions.has(proxyObject)) { const unsubscribe = vanilla.subscribe(proxyObject, revalidate, options == null ? void 0 : options.sync); subscriptions.set(proxyObject, unsubscribe); } return proxyObject; }); const couldBeCleanup = promiseOrPossibleCleanup && promiseOrPossibleCleanup instanceof Promise ? await promiseOrPossibleCleanup : promiseOrPossibleCleanup; if (couldBeCleanup) { if (alive) { cleanups.add(couldBeCleanup); } else { cleanup(); } } } finally { currentCleanups = parent; } subscriptions.forEach((unsubscribe, proxyObject) => { if (!proxiesToSubscribe.has(proxyObject)) { subscriptions.delete(proxyObject); unsubscribe(); } }); }; if (currentCleanups) { currentCleanups.add(cleanup); } revalidate(); return cleanup; } const DEVTOOLS = /* @__PURE__ */ Symbol(); function devtools(proxyObject, options) { const { enabled, name = "", ...rest } = options || {}; let extension; try { extension = (enabled != null ? enabled : process.env.NODE_ENV !== "production") && window.__REDUX_DEVTOOLS_EXTENSION__; } catch (e) { } if (!extension) { if (process.env.NODE_ENV !== "production" && enabled) { console.warn("[Warning] Please install/enable Redux devtools extension"); } return; } vanilla.unstable_enableOp(); let isTimeTraveling = false; const devtools2 = extension.connect({ name, ...rest }); const unsub1 = vanilla.subscribe(proxyObject, (unstable_ops) => { const action = unstable_ops.filter(([_, path]) => path[0] !== DEVTOOLS).map(([op, path]) => `${op}:${path.map(String).join(".")}`).join(", "); if (!action) { return; } if (isTimeTraveling) { isTimeTraveling = false; } else { const snapWithoutDevtools = Object.assign({}, vanilla.snapshot(proxyObject)); delete snapWithoutDevtools[DEVTOOLS]; devtools2.send( { type: action, updatedAt: (/* @__PURE__ */ new Date()).toLocaleString() }, snapWithoutDevtools ); } }); const unsub2 = devtools2.subscribe((message) => { var _a, _b, _c, _d, _e, _f; if (message.type === "ACTION" && message.payload) { try { Object.assign(proxyObject, JSON.parse(message.payload)); } catch (e) { console.error( "please dispatch a serializable value that JSON.parse() and proxy() support\n", e ); } } if (message.type === "DISPATCH" && message.state) { if (((_a = message.payload) == null ? void 0 : _a.type) === "JUMP_TO_ACTION" || ((_b = message.payload) == null ? void 0 : _b.type) === "JUMP_TO_STATE") { isTimeTraveling = true; const state = JSON.parse(message.state); Object.assign(proxyObject, state); } proxyObject[DEVTOOLS] = message; } else if (message.type === "DISPATCH" && ((_c = message.payload) == null ? void 0 : _c.type) === "COMMIT") { devtools2.init(vanilla.snapshot(proxyObject)); } else if (message.type === "DISPATCH" && ((_d = message.payload) == null ? void 0 : _d.type) === "IMPORT_STATE") { const actions = (_e = message.payload.nextLiftedState) == null ? void 0 : _e.actionsById; const computedStates = ((_f = message.payload.nextLiftedState) == null ? void 0 : _f.computedStates) || []; isTimeTraveling = true; computedStates.forEach(({ state }, index) => { const action = actions[index] || "No action found"; Object.assign(proxyObject, state); if (index === 0) { devtools2.init(vanilla.snapshot(proxyObject)); } else { devtools2.send(action, vanilla.snapshot(proxyObject)); } }); } }); devtools2.init(vanilla.snapshot(proxyObject)); return () => { unsub1(); unsub2 == null ? void 0 : unsub2(); }; } const isObject$1 = (x) => typeof x === "object" && x !== null; let defaultRefSet$1; const getDefaultRefSet$1 = () => { if (!defaultRefSet$1) { defaultRefSet$1 = vanilla.unstable_getInternalStates().refSet; } return defaultRefSet$1; }; function deepClone(obj, getRefSet = getDefaultRefSet$1) { if (!isObject$1(obj) || getRefSet().has(obj)) { return obj; } const baseObject = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj)); Reflect.ownKeys(obj).forEach((key) => { baseObject[key] = deepClone(obj[key], getRefSet); }); return baseObject; } const { proxyStateMap: proxyStateMap$1, snapCache: snapCache$1 } = vanilla.unstable_getInternalStates(); const isProxy$1 = (x) => proxyStateMap$1.has(x); const isProxyMap = (obj) => { return Symbol.toStringTag in obj && obj[Symbol.toStringTag] === "Map" && proxyStateMap$1.has(obj); }; function proxyMap(entries) { const initialData = []; let initialIndex = 0; const indexMap = /* @__PURE__ */ new Map(); const snapMapCache = /* @__PURE__ */ new WeakMap(); const registerSnapMap = () => { const cache = snapCache$1.get(vObject); const latestSnap = cache == null ? void 0 : cache[1]; if (latestSnap && !snapMapCache.has(latestSnap)) { const clonedMap = new Map(indexMap); snapMapCache.set(latestSnap, clonedMap); } }; const getMapForThis = (x) => snapMapCache.get(x) || indexMap; if (entries) { if (typeof entries[Symbol.iterator] !== "function") { throw new TypeError( "proxyMap:\n initial state must be iterable\n tip: structure should be [[key, value]]" ); } for (const [key, value] of entries) { indexMap.set(key, initialIndex); initialData[initialIndex++] = value; } } const vObject = { data: initialData, index: initialIndex, epoch: 0, get size() { if (!isProxy$1(this)) { registerSnapMap(); } const map = getMapForThis(this); return map.size; }, get(key) { const map = getMapForThis(this); const index = map.get(key); if (index === void 0) { this.epoch; return void 0; } return this.data[index]; }, has(key) { const map = getMapForThis(this); this.epoch; return map.has(key); }, set(key, value) { if (!isProxy$1(this)) { throw new Error("Cannot perform mutations on a snapshot"); } const index = indexMap.get(key); if (index === void 0) { indexMap.set(key, this.index); this.data[this.index++] = value; } else { this.data[index] = value; } this.epoch++; return this; }, delete(key) { if (!isProxy$1(this)) { throw new Error("Cannot perform mutations on a snapshot"); } const index = indexMap.get(key); if (index === void 0) { return false; } delete this.data[index]; indexMap.delete(key); this.epoch++; return true; }, clear() { if (!isProxy$1(this)) { throw new Error("Cannot perform mutations on a snapshot"); } this.data.length = 0; this.index = 0; this.epoch++; indexMap.clear(); }, forEach(cb) { this.epoch; const map = getMapForThis(this); map.forEach((index, key) => { cb(this.data[index], key, this); }); }, *entries() { this.epoch; const map = getMapForThis(this); for (const [key, index] of map) { yield [key, this.data[index]]; } }, *keys() { this.epoch; const map = getMapForThis(this); for (const key of map.keys()) { yield key; } }, *values() { this.epoch; const map = getMapForThis(this); for (const index of map.values()) { yield this.data[index]; } }, [Symbol.iterator]() { return this.entries(); }, get [Symbol.toStringTag]() { return "Map"; }, toJSON() { return new Map(this.entries()); }, // [ONLY-TS-5.9.3] [ONLY-TS-5.8.3] [ONLY-TS-5.7.3] [ONLY-TS-5.6.3] @ts-expect-error ignore getOrInsert() { throw new Error("not implemented"); }, getOrInsertComputed() { throw new Error("not implemented"); } }; const proxiedObject = vanilla.proxy(vObject); Object.defineProperties(proxiedObject, { size: { enumerable: false }, index: { enumerable: false }, epoch: { enumerable: false }, data: { enumerable: false }, toJSON: { enumerable: false } }); Object.seal(proxiedObject); return proxiedObject; } const { proxyStateMap, snapCache } = vanilla.unstable_getInternalStates(); const maybeProxify = (x) => typeof x === "object" ? vanilla.proxy({ x }).x : x; const isProxy = (x) => proxyStateMap.has(x); const isProxySet = (obj) => { return Symbol.toStringTag in obj && obj[Symbol.toStringTag] === "Set" && proxyStateMap.has(obj); }; function proxySet(initialValues) { const initialData = []; const indexMap = /* @__PURE__ */ new Map(); let initialIndex = 0; const snapMapCache = /* @__PURE__ */ new WeakMap(); const registerSnapMap = () => { const cache = snapCache.get(vObject); const latestSnap = cache == null ? void 0 : cache[1]; if (latestSnap && !snapMapCache.has(latestSnap)) { const clonedMap = new Map(indexMap); snapMapCache.set(latestSnap, clonedMap); } }; const getMapForThis = (x) => snapMapCache.get(x) || indexMap; if (initialValues) { if (typeof initialValues[Symbol.iterator] !== "function") { throw new TypeError("not iterable"); } for (const value of initialValues) { if (!indexMap.has(value)) { const v = maybeProxify(value); indexMap.set(v, initialIndex); initialData[initialIndex++] = v; } } } const isIterable = (o) => typeof o === "object" && o !== null && Symbol.iterator in o; const hasForEach = (o) => typeof o.forEach === "function"; const asIterable = (other) => { if (isIterable(other)) return other; if (hasForEach(other)) { const acc = []; other.forEach((v) => acc.push(v)); return acc; } throw new TypeError("Expected an iterable"); }; function intersectionImpl(other) { this.epoch; const otherSet = proxySet(asIterable(other)); const result = proxySet(); for (const value of this.values()) { if (otherSet.has(value)) { result.add(value); } } return proxySet(result); } function unionImpl(other) { this.epoch; const otherSet = proxySet(asIterable(other)); const result = proxySet(); for (const v of this.values()) result.add(v); for (const v of otherSet.values()) result.add(v); return proxySet(result); } function differenceImpl(other) { this.epoch; const otherSet = proxySet(asIterable(other)); const result = proxySet(); for (const v of this.values()) if (!otherSet.has(v)) result.add(v); return proxySet(result); } function symmetricDifferenceImpl(other) { this.epoch; const otherSet = proxySet(asIterable(other)); const result = proxySet(); for (const v of this.values()) if (!otherSet.has(v)) result.add(v); for (const v of otherSet.values()) if (!this.has(v)) result.add(v); return proxySet(result); } const vObject = { data: initialData, index: initialIndex, epoch: 0, get size() { if (!isProxy(this)) { registerSnapMap(); } return indexMap.size; }, has(value) { const map = getMapForThis(this); const v = maybeProxify(value); this.epoch; return map.has(v); }, add(value) { if (!isProxy(this)) { throw new Error("Cannot perform mutations on a snapshot"); } const v = maybeProxify(value); if (!indexMap.has(v)) { indexMap.set(v, this.index); this.data[this.index++] = v; this.epoch++; } return this; }, delete(value) { if (!isProxy(this)) { throw new Error("Cannot perform mutations on a snapshot"); } const v = maybeProxify(value); const index = indexMap.get(v); if (index === void 0) { return false; } delete this.data[index]; indexMap.delete(v); this.epoch++; return true; }, clear() { if (!isProxy(this)) { throw new Error("Cannot perform mutations on a snapshot"); } this.data.length = 0; this.index = 0; this.epoch++; indexMap.clear(); }, forEach(cb) { this.epoch; const map = getMapForThis(this); map.forEach((index) => { cb(this.data[index], this.data[index], this); }); }, *values() { this.epoch; const map = getMapForThis(this); for (const index of map.values()) { yield this.data[index]; } }, keys() { this.epoch; return this.values(); }, *entries() { this.epoch; const map = getMapForThis(this); for (const index of map.values()) { const value = this.data[index]; yield [value, value]; } }, toJSON() { return new Set(this.values()); }, [Symbol.iterator]() { return this.values(); }, get [Symbol.toStringTag]() { return "Set"; }, intersection: intersectionImpl, union: unionImpl, difference: differenceImpl, symmetricDifference: symmetricDifferenceImpl, isSubsetOf(other) { this.epoch; for (const v of this.values()) if (!other.has(v)) return false; return true; }, isSupersetOf(other) { this.epoch; const it = asIterable(other); for (const v of it) if (!this.has(v)) return false; return true; }, isDisjointFrom(other) { this.epoch; for (const v of this.values()) if (other.has(v)) return false; return true; } }; const proxiedObject = vanilla.proxy(vObject); Object.defineProperties(proxiedObject, { size: { enumerable: false }, data: { enumerable: false }, index: { enumerable: false }, epoch: { enumerable: false }, toJSON: { enumerable: false } }); Object.seal(proxiedObject); return proxiedObject; } const isObject = (x) => typeof x === "object" && x !== null; let defaultRefSet; const getDefaultRefSet = () => { if (!defaultRefSet) { defaultRefSet = vanilla.unstable_getInternalStates().refSet; } return defaultRefSet; }; const cloneContainer = (src) => { return Array.isArray(src) ? [] : Object.create(Object.getPrototypeOf(src)); }; function unstable_deepProxy(obj, getRefSet = getDefaultRefSet) { const memo = /* @__PURE__ */ new WeakMap(); const visit = (value) => { if (!isObject(value) || getRefSet().has(value)) return value; if (value instanceof Set || isProxySet(value)) { const input = value; const items = []; for (const el of input) { items.push(visit(el)); } return proxySet(items); } if (value instanceof Map || isProxyMap(value)) { const input = value; const entries = []; for (const [k, v] of input) { entries.push([visit(k), visit(v)]); } return proxyMap(entries); } const hit = memo.get(value); if (hit) return hit; const target = cloneContainer(value); memo.set(value, target); for (const key of Reflect.ownKeys(value)) { const desc = Reflect.getOwnPropertyDescriptor(value, key); if (!desc) continue; if ("value" in desc) { const next = visit(value[key]); Object.defineProperty(target, key, { ...desc, value: next }); } else { Object.defineProperty(target, key, desc); } } return vanilla.proxy(target); }; return visit(obj); } exports.deepClone = deepClone; exports.devtools = devtools; exports.isProxyMap = isProxyMap; exports.isProxySet = isProxySet; exports.proxyMap = proxyMap; exports.proxySet = proxySet; exports.subscribeKey = subscribeKey; exports.unstable_deepProxy = unstable_deepProxy; exports.watch = watch;