import { isArrayIndexPropertyName } from "@commonfabric/utils/arrays"; import type { FabricValue } from "@commonfabric/api"; import { isRecord } from "@commonfabric/utils/types"; export type ReadPathOptions = { allowArrayLength?: boolean; }; export const isArrayIndexSegment = (segment: string): boolean => isArrayIndexPropertyName(segment); export const createPathContainer = (nextSegment: string): FabricValue => isArrayIndexSegment(nextSegment) ? [] : {}; const hasOwnPathSegment = ( value: Record | unknown[], segment: string | number, ): boolean => Object.hasOwn(value, segment); export const hasValueAtPath = ( root: FabricValue | undefined, path: readonly string[], options: ReadPathOptions = {}, ): boolean => { let current: unknown = root; for (const segment of path) { if (Array.isArray(current)) { if (options.allowArrayLength === true && segment === "length") { current = current.length; continue; } if (!isArrayIndexSegment(segment)) { return false; } const index = Number(segment); if (!hasOwnPathSegment(current, index)) { return false; } current = current[index]; continue; } if (!isRecord(current)) { return false; } const record = current as Record; if (!hasOwnPathSegment(record, segment)) { return false; } current = record[segment]; } return true; }; export const readValueAtPath = ( root: FabricValue | undefined, path: readonly string[], options: ReadPathOptions = {}, ): FabricValue | undefined => { let current: unknown = root; for (const segment of path) { if (Array.isArray(current)) { if (options.allowArrayLength === true && segment === "length") { current = current.length; continue; } if (!isArrayIndexSegment(segment)) { return undefined; } const index = Number(segment); if (!hasOwnPathSegment(current, index)) { return undefined; } current = current[index]; continue; } if (!isRecord(current)) { return undefined; } const record = current as Record; if (!hasOwnPathSegment(record, segment)) { return undefined; } current = record[segment]; } return current as FabricValue | undefined; };