/* eslint-disable @typescript-eslint/no-explicit-any */ import set from 'lodash/set'; export const SUBMIT_RESPONSE_MUTATION = `mutation SubmitResponseState($name: String!, $fields: JSON!, $state: JSON!) {\n submitResponseState(name: $name, fields: $fields, state: $state)\n}`; // Rebuild nested object from a flat state map (dot/bracket notation) export function unflattenState>(flat?: Record): T | undefined { if (!flat) return undefined; const out: Record = {}; for (const [path, value] of Object.entries(flat)) { if (!path) continue; set(out, path, value); } return out as T; } // Flatten nested object into dot/bracket path map compatible with lodash/set // Arrays are represented with bracket indices, e.g. `items[0].name` export function flattenState(obj?: unknown): Record | undefined { if (!obj || typeof obj !== 'object') return undefined; const out: Record = {}; const walk = (val: unknown, path: string) => { if (Array.isArray(val)) { val.forEach((v, i) => walk(v, path ? `${path}[${i}]` : `[${i}]`)); return; } if (val && typeof val === 'object') { for (const [k, v] of Object.entries(val as Record)) { const next = path ? `${path}.${k}` : k; walk(v, next); } return; } if (path) out[path] = val as unknown; }; walk(obj as Record, ''); return out; } export function buildMeta(input: { entityType?: string; entityName?: string; prefix?: string; }): Record { const { entityType, entityName, prefix } = input; const base: Record = { entityType, entityName, prefix }; if (typeof window === 'undefined') return base; const nav = (window.navigator as any) || {}; const conn = (nav?.connection as any) || {}; const url = new URL(window.location.href); const params: Record = {}; url.searchParams.forEach((v, k) => { params[k] = v; }); const colorScheme = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' : undefined; const sourceOfApply = `${window.location.origin}${window.location.pathname}`; return { ...base, sourceOfApply, isMobile: /Mobi|Android/i.test(nav?.userAgent || ''), client: { userAgent: nav?.userAgent, language: nav?.language, languages: nav?.languages, platform: nav?.platform, deviceMemory: nav?.deviceMemory, hardwareConcurrency: nav?.hardwareConcurrency, }, connection: { effectiveType: conn?.effectiveType, downlink: conn?.downlink, rtt: conn?.rtt, saveData: conn?.saveData, }, screen: { width: window.screen?.width, height: window.screen?.height, availWidth: window.screen?.availWidth, availHeight: window.screen?.availHeight, pixelRatio: window.devicePixelRatio, }, viewport: { innerWidth: window.innerWidth, innerHeight: window.innerHeight, }, timezone: Intl?.DateTimeFormat?.().resolvedOptions?.().timeZone, tzOffset: new Date().getTimezoneOffset(), referrer: document.referrer || undefined, title: document.title || undefined, location: { href: window.location.href, origin: window.location.origin, pathname: window.location.pathname, search: window.location.search, hash: window.location.hash, params, }, colorScheme, }; } // Build GraphQL request body for submitResponseState export function buildGraphQLBody(input: { name: string; rootFields?: unknown[]; state: Record | undefined; meta: Record; }): string { const { name, rootFields, state, meta } = input; const fields = { fields: rootFields || [], meta }; return JSON.stringify({ query: SUBMIT_RESPONSE_MUTATION, variables: { name, fields, state } }); }