UNPKG

synapse-storage

Version:

Набор инструментов для управления состоянием и апи-запросами

116 lines (111 loc) 4.17 kB
import { StorageKey } from "../utils/storage-key.js"; function parsePath(path) { // Если это StorageKey и он помечен как непарсируемый if (path instanceof StorageKey && path.isUnparseable()) { return [ path.toString() ]; } const pathStr = path.toString(); return pathStr.replace(/\[/g, '.').replace(/\]/g, '').split('.').filter(Boolean); } function getValueByPath(obj, path) { const parts = parsePath(path); return parts.reduce((curr, key)=>curr === undefined ? undefined : curr[key], obj); } const cloneNode = (node)=>Array.isArray(node) ? [ ...node ] : { ...node }; /** * Иммутабельная запись значения по пути. * * НЕ мутирует `obj`: клонирует корень и только узлы НА пути записи (structural * sharing — соседние ветки сохраняют ссылки). Возвращает новое состояние. * * Это важно для `MemoryStorage`, который держит состояние по ссылке: запись по * месту портила бы переданный `initialState` и ломала `reset()`. */ function setValueByPath(obj, path, value) { if (path === '') return value; // Непарсируемый StorageKey — один ключ верхнего уровня. if (path instanceof StorageKey && path.isUnparseable()) { return { ...obj ?? {}, [path.toString()]: value }; } const parts = parsePath(path); if (parts.length === 0) return value; const root = obj == null ? {} : cloneNode(obj); let curr = root; for(let i = 0; i < parts.length - 1; i++){ const key = parts[i]; const nextKey = parts[i + 1]; const shouldBeArray = nextKey !== undefined && !Number.isNaN(Number(nextKey)); const child = curr[key]; if (child == null || typeof child !== 'object') { // узла нет (или это примитив посреди пути) — создаём контейнер curr[key] = shouldBeArray ? [] : {}; } else { // существующий узел — клонируем, чтобы не мутировать исходный curr[key] = cloneNode(child); } curr = curr[key]; } curr[parts[parts.length - 1]] = value; return root; } /** * Иммутабельное удаление значения по пути. НЕ мутирует `obj`. * Возвращает `{ state, removed }`: `removed=false`, если пути не существовало * (состояние тогда возвращается прежним по ссылке). */ function removeValueByPath(obj, path) { if (path instanceof StorageKey && path.isUnparseable()) { const rawKey = path.toString(); if (obj == null || !(rawKey in obj)) return { state: obj, removed: false }; const clone = { ...obj }; delete clone[rawKey]; return { state: clone, removed: true }; } const parts = parsePath(path); if (parts.length === 0 || obj == null) return { state: obj, removed: false }; const root = cloneNode(obj); let curr = root; for(let i = 0; i < parts.length - 1; i++){ const key = parts[i]; const child = curr[key]; if (child == null || typeof child !== 'object') { return { state: obj, removed: false } // пути не существует ; } curr[key] = cloneNode(child); curr = curr[key]; } const lastKey = parts[parts.length - 1]; if (!(lastKey in curr)) return { state: obj, removed: false }; delete curr[lastKey]; return { state: root, removed: true }; } export { getValueByPath, parsePath, removeValueByPath, setValueByPath }; //# sourceMappingURL=path.utils.js.map