UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

1 lines • 32.8 kB
{"version":3,"file":"LiveQueries.mjs","sources":["../../src/presentation/loader/useLiveEvents.ts","../../src/presentation/loader/utils.ts","../../src/presentation/loader/useLiveQueries.ts","../../src/presentation/loader/LiveQueries.tsx"],"sourcesContent":["import {type LiveEvent, type LiveEventMessage} from '@sanity/client'\nimport {useDeferredValue, useEffect, useReducer, useState} from 'react'\nimport {type SanityClient} from 'sanity'\n\ntype State = {\n /**\n * Growing list over live events with Sync Tags,\n * that can be used to refetch with Sanity Client, using the id as the lastLiveEventId parameter\n */\n messages: LiveEventMessage[]\n /**\n * If the connection experiences a reconnect, or a restart event is received, the counter is incremented.\n * This counter is suitable as a `key` on React Components as a way to reset its internal state and refetch.\n */\n resets: number\n}\n\nexport function reducer(state: State, event: LiveEvent): State {\n switch (event.type) {\n case 'message':\n return {\n ...state,\n messages: [...state.messages, event],\n }\n case 'reconnect':\n case 'restart':\n return {\n ...state,\n messages: [],\n resets: state.resets + 1,\n }\n case 'welcome':\n // no-op\n return state\n default:\n throw Error(\n `Unknown event: ${\n // oxlint-disable-next-line no-explicit-any\n (event as any).type\n }`,\n {cause: event},\n )\n }\n}\n\nexport const initialState: State = {\n messages: [],\n resets: 0,\n}\n\nexport function useLiveEvents(client: SanityClient): State {\n const [state, dispatch] = useReducer(reducer, initialState)\n const [error, setError] = useState<unknown>(null)\n if (error !== null) {\n // Push error to nearest error boundary\n throw error\n }\n\n useEffect(() => {\n const subscription = client.live\n .events({includeDrafts: true, tag: 'presentation-loader'})\n .subscribe({\n next: dispatch,\n error: (err) =>\n setError(\n err instanceof Error\n ? err\n : new Error('Unexpected error in useLiveEvents', {cause: err}),\n ),\n })\n return () => subscription.unsubscribe()\n }, [client.live])\n\n return useDeferredValue(state)\n}\n","import {toPlainText} from '@portabletext/react'\nimport {isPortableTextBlock} from '@portabletext/toolkit'\nimport {type ClientPerspective, type QueryParams} from '@sanity/client'\nimport {type ApplySourceDocumentsUpdateFunction} from '@sanity/client/csm'\nimport {type FIXME} from 'sanity'\n\n/**\n * Used by `applySourceDocuments`\n * @internal\n */\nexport const mapChangedValue: ApplySourceDocumentsUpdateFunction = (\n changedValue: FIXME,\n {previousValue},\n) => {\n if (typeof previousValue === 'string') {\n if (typeof changedValue === 'number') {\n // If the string() function was used in the query, we need to convert the source value to a string as well\n return `${changedValue}`\n }\n // If it's an array in the source, but a string in the query response, it could be pt::text\n if (Array.isArray(changedValue)) {\n if (changedValue.length === 0) {\n // If it's empty assume it's PT and return an empty string\n return ''\n }\n // If the array contains any valid block type, assume the GROQ initially used pt::text on it and do the same conversion\n if (changedValue.some((node) => typeof node === 'object' && isPortableTextBlock(node))) {\n return toPlainText(changedValue)\n }\n }\n }\n\n return changedValue\n}\n\n/**\n * @internal\n */\nexport type QueryCacheKey = `${string}:${string}:${string}`\n/**\n * @internal\n */\nexport function getQueryCacheKey(\n perspective: ClientPerspective,\n query: string,\n params: QueryParams,\n): QueryCacheKey {\n return `${perspective}:${query}:${JSON.stringify(params)}`\n}\n","import {type ClientPerspective} from '@sanity/client'\nimport isEqual from 'fast-deep-equal'\nimport {useDeferredValue, useEffect, useReducer} from 'react'\nimport {type QueryParams} from 'sanity'\n\nimport {LOADER_QUERY_GC_INTERVAL} from '../constants'\nimport {getQueryCacheKey, type QueryCacheKey} from './utils'\n\ntype LiveQueriesState = Map<\n QueryCacheKey,\n {\n query: string\n params: QueryParams\n perspective: ClientPerspective\n }\n>\n\ntype State = {\n queries: LiveQueriesState\n heartbeats: Map<\n QueryCacheKey,\n {\n receivedAt: number\n /**\n * If false it means the query can't safely be garbage collected,\n * as older versions of \\@sanity/core-loader doesn't fire listen events\n * on an interval.\n */\n heartbeat: number | false\n }\n >\n}\n\ntype QueryListenAction = {\n type: 'query-listen'\n payload: {\n perspective: ClientPerspective\n query: string\n params: QueryParams\n heartbeat: number | false\n }\n}\ntype GarbageCollectAction = {type: 'gc'}\ntype Action = QueryListenAction | GarbageCollectAction\n\nfunction gc(state: State): State {\n if (state.queries.size < 1) {\n return state\n }\n\n const now = Date.now()\n const hasAnyExpired = Array.from(state.heartbeats.values()).some(\n (entry) => entry.heartbeat !== false && now > entry.receivedAt + entry.heartbeat,\n )\n if (!hasAnyExpired) {\n return state\n }\n const nextHeartbeats = new Map()\n const nextQueries = new Map()\n for (const [key, entry] of state.heartbeats.entries()) {\n if (entry.heartbeat !== false && now > entry.receivedAt + entry.heartbeat) {\n continue\n }\n nextHeartbeats.set(key, entry)\n nextQueries.set(key, state.queries.get(key))\n }\n\n return {...state, queries: nextQueries, heartbeats: nextHeartbeats}\n}\nfunction queryListen(state: State, {payload}: QueryListenAction): State {\n const key = getQueryCacheKey(payload.perspective, payload.query, payload.params)\n const data = {query: payload.query, params: payload.params, perspective: payload.perspective}\n\n const nextHeartbeats = new Map(state.heartbeats)\n nextHeartbeats.set(key, {\n receivedAt: Date.now(),\n heartbeat: payload.heartbeat,\n })\n\n let nextQueries = state.queries\n /**\n * The data comes from a postMessage event, which uses the structured clone algorithm to serialize state (https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#message).\n * This impacts `params`, which is an object, as it will be a new object every time even if the sender is sending the same object instance on their end.\n * It also impacts `perspective`, as it's no longer just a string, but can also be an array of strings.\n * Both cases are handled by fast-deep-equal, which is used to compare the data before deciding wether the state should be updated.\n */\n if (!state.queries.has(key) || !isEqual(state.queries.get(key), data)) {\n nextQueries = new Map(state.queries)\n nextQueries.set(key, data)\n }\n\n return {heartbeats: nextHeartbeats, queries: nextQueries}\n}\n\nexport function reducer(state: State, action: Action): State {\n switch (action.type) {\n case 'query-listen':\n return queryListen(state, action)\n case 'gc':\n return gc(state)\n default:\n throw Error(\n `Unknown action: ${\n // oxlint-disable-next-line no-explicit-any\n (action as any).type\n }`,\n {cause: action},\n )\n }\n}\n\nexport const initialState: State = {\n queries: new Map(),\n heartbeats: new Map(),\n}\n\nexport function useLiveQueries(): [LiveQueriesState, React.ActionDispatch<[action: Action]>] {\n const [state, dispatch] = useReducer(reducer, initialState)\n\n useEffect(() => {\n const interval = setInterval(() => dispatch({type: 'gc'}), LOADER_QUERY_GC_INTERVAL)\n return () => clearInterval(interval)\n }, [])\n\n const queries = useDeferredValue(state.queries)\n return [queries, dispatch]\n}\n","import {\n type ClientPerspective,\n type ContentSourceMap,\n type LiveEventMessage,\n type QueryParams,\n type SyncTag,\n} from '@sanity/client'\nimport {applySourceDocuments, getPublishedId} from '@sanity/client/csm'\nimport {\n type ChannelInstance,\n type Controller,\n createConnectionMachine,\n type StatusEvent,\n} from '@sanity/comlink'\nimport {\n createCompatibilityActors,\n type LoaderControllerMsg,\n type LoaderNodeMsg,\n} from '@sanity/presentation-comlink'\nimport isEqual from 'fast-deep-equal'\nimport {memo, startTransition, useDeferredValue, useEffect, useMemo, useState} from 'react'\nimport {\n isReleasePerspective,\n RELEASES_STUDIO_CLIENT_OPTIONS,\n type SanityClient,\n type SanityDocument,\n useClient,\n useDataset,\n useProjectId,\n} from 'sanity'\nimport {useEffectEvent} from 'use-effect-event'\n\nimport {API_VERSION, MIN_LOADER_QUERY_LISTEN_HEARTBEAT_INTERVAL} from '../constants'\nimport {type LoaderConnection, type PresentationPerspective} from '../types'\nimport {type DocumentOnPage} from '../useDocumentsOnPage'\nimport {useLiveEvents} from './useLiveEvents'\nimport {useLiveQueries} from './useLiveQueries'\nimport {mapChangedValue} from './utils'\n\nexport interface LiveQueriesProps {\n liveDocument: Partial<SanityDocument> | null | undefined\n controller: Controller | undefined\n perspective: ClientPerspective\n onLoadersConnection: (event: StatusEvent) => void\n onDocumentsOnPage: (\n key: string,\n perspective: PresentationPerspective,\n state: DocumentOnPage[],\n ) => void\n}\n\nexport default function LiveQueries(props: LiveQueriesProps): React.JSX.Element {\n const {controller, perspective: activePerspective, onLoadersConnection, onDocumentsOnPage} = props\n\n const [comlink, setComlink] = useState<ChannelInstance<LoaderControllerMsg, LoaderNodeMsg>>()\n const [liveQueries, liveQueriesDispatch] = useLiveQueries()\n\n const projectId = useProjectId()\n const dataset = useDataset()\n\n useEffect((): (() => void) => {\n if (controller) {\n const nextComlink = controller.createChannel<LoaderControllerMsg, LoaderNodeMsg>(\n {\n name: 'presentation',\n connectTo: 'loaders',\n heartbeat: true,\n },\n createConnectionMachine<LoaderControllerMsg, LoaderNodeMsg>().provide({\n actors: createCompatibilityActors<LoaderControllerMsg>(),\n }),\n )\n setComlink(nextComlink)\n\n nextComlink.onStatus(onLoadersConnection)\n\n nextComlink.on('loader/documents', (data) => {\n if (data.projectId === projectId && data.dataset === dataset) {\n onDocumentsOnPage(\n 'loaders',\n // oxlint-disable-next-line no-explicit-any\n data.perspective as unknown as any,\n data.documents,\n )\n }\n })\n\n nextComlink.on('loader/query-listen', (data) => {\n if (data.projectId === projectId && data.dataset === dataset) {\n if (\n typeof data.heartbeat === 'number' &&\n data.heartbeat < MIN_LOADER_QUERY_LISTEN_HEARTBEAT_INTERVAL\n ) {\n throw new Error(\n `Loader query listen heartbeat interval must be at least ${MIN_LOADER_QUERY_LISTEN_HEARTBEAT_INTERVAL}ms`,\n )\n }\n liveQueriesDispatch({\n type: 'query-listen',\n payload: {\n perspective: data.perspective,\n query: data.query,\n params: data.params,\n heartbeat: data.heartbeat ?? false,\n },\n })\n }\n })\n\n return nextComlink.start()\n }\n return () => undefined\n }, [controller, dataset, liveQueriesDispatch, onDocumentsOnPage, onLoadersConnection, projectId])\n\n const studioClient = useClient(\n isReleasePerspective(activePerspective)\n ? RELEASES_STUDIO_CLIENT_OPTIONS\n : {apiVersion: API_VERSION},\n )\n const client = useMemo(\n () =>\n studioClient.withConfig({\n resultSourceMap: 'withKeyArraySelector',\n }),\n [studioClient],\n )\n useEffect(() => {\n if (comlink) {\n comlink.post('loader/perspective', {\n projectId,\n dataset,\n perspective: activePerspective,\n })\n }\n }, [comlink, activePerspective, projectId, dataset])\n\n /**\n * Defer the liveDocument to avoid unnecessary rerenders on rapid edits\n */\n const liveDocument = useDeferredValue(props.liveDocument)\n\n const liveEvents = useLiveEvents(client)\n\n return (\n <>\n {[...liveQueries.entries()].map(([key, {query, params, perspective}]) => (\n <QuerySubscription\n key={`${liveEvents.resets}:${key}`}\n projectId={projectId}\n dataset={dataset}\n perspective={perspective}\n query={query}\n params={params}\n comlink={comlink}\n client={client}\n liveDocument={liveDocument}\n liveEventsMessages={liveEvents.messages}\n />\n ))}\n </>\n )\n}\n\ninterface SharedProps {\n /**\n * The Sanity client to use for fetching data and listening to mutations.\n */\n client: SanityClient\n}\n\ninterface QuerySubscriptionProps\n extends Pick<UseQuerySubscriptionProps, 'client' | 'liveDocument' | 'liveEventsMessages'> {\n projectId: string\n dataset: string\n perspective: ClientPerspective\n query: string\n params: QueryParams\n comlink: LoaderConnection | undefined\n}\nfunction QuerySubscriptionComponent(props: QuerySubscriptionProps) {\n const {\n projectId,\n dataset,\n perspective,\n query,\n client,\n liveDocument,\n params,\n comlink,\n liveEventsMessages,\n } = props\n\n const {\n result,\n resultSourceMap,\n syncTags: tags,\n } = useQuerySubscription({\n client,\n liveDocument,\n params,\n perspective,\n query,\n liveEventsMessages,\n }) || {}\n\n /* eslint-disable @typescript-eslint/no-shadow,max-params */\n const handleQueryChange = useEffectEvent(\n (\n comlink: LoaderConnection | undefined,\n perspective: ClientPerspective,\n query: string,\n params: QueryParams,\n result: unknown,\n resultSourceMap: ContentSourceMap | undefined,\n tags: `s1:${string}`[] | undefined,\n ) => {\n comlink?.post('loader/query-change', {\n projectId,\n dataset,\n perspective,\n query,\n params,\n result,\n resultSourceMap,\n tags,\n })\n },\n )\n /* eslint-enable @typescript-eslint/no-shadow,max-params */\n\n useEffect(() => {\n if (resultSourceMap) {\n handleQueryChange(comlink, perspective, query, params, result, resultSourceMap, tags)\n }\n return undefined\n }, [comlink, params, perspective, query, result, resultSourceMap, tags])\n\n return null\n}\nconst QuerySubscription = memo(QuerySubscriptionComponent)\nQuerySubscription.displayName = 'Memo(QuerySubscription)'\n\ninterface UseQuerySubscriptionProps extends Required<Pick<SharedProps, 'client'>> {\n liveDocument: Partial<SanityDocument> | null | undefined\n query: string\n params: QueryParams\n perspective: ClientPerspective\n liveEventsMessages: LiveEventMessage[]\n}\nfunction useQuerySubscription(props: UseQuerySubscriptionProps) {\n const {liveDocument, client, query, params, perspective, liveEventsMessages} = props\n const [result, setResult] = useState<unknown>(null)\n const [resultSourceMap, setResultSourceMap] = useState<ContentSourceMap | null | undefined>(null)\n const [syncTags, setSyncTags] = useState<SyncTag[] | undefined>(undefined)\n const [skipEventIds] = useState(() => new Set(liveEventsMessages.map((msg) => msg.id)))\n const recentLiveEvents = liveEventsMessages.filter((msg) => !skipEventIds.has(msg.id))\n const lastLiveEvent = recentLiveEvents.findLast((msg) =>\n msg.tags.some((tag) => syncTags?.includes(tag)),\n )\n const lastLiveEventId = lastLiveEvent?.id\n\n // Make sure any async errors bubble up to the nearest error boundary\n const [error, setError] = useState<unknown>(null)\n if (error) throw error\n\n /* eslint-disable max-nested-callbacks */\n useEffect(() => {\n const controller = new AbortController()\n\n client\n .fetch(query, params, {\n lastLiveEventId,\n tag: 'presentation-loader',\n signal: controller.signal,\n perspective,\n filterResponse: false,\n returnQuery: false,\n })\n .then((response) => {\n startTransition(() => {\n setResult((prev: unknown) => (isEqual(prev, response.result) ? prev : response.result))\n setResultSourceMap((prev) =>\n isEqual(prev, response.resultSourceMap) ? prev : response.resultSourceMap,\n )\n setSyncTags((prev) => (isEqual(prev, response.syncTags) ? prev : response.syncTags))\n })\n })\n .catch((err) => {\n if (typeof err !== 'object' || err?.name !== 'AbortError') {\n setError(err)\n }\n })\n\n return () => {\n controller.abort()\n }\n }, [client, lastLiveEventId, params, perspective, query])\n /* eslint-enable max-nested-callbacks */\n\n return useMemo(() => {\n if (liveDocument && resultSourceMap) {\n return {\n result: turboChargeResultIfSourceMap(liveDocument, result, perspective, resultSourceMap),\n resultSourceMap,\n syncTags,\n }\n }\n return {result, resultSourceMap, syncTags}\n }, [liveDocument, perspective, result, resultSourceMap, syncTags])\n}\n\nexport function turboChargeResultIfSourceMap<T = unknown>(\n liveDocument: Partial<SanityDocument> | null | undefined,\n result: T,\n perspective: ClientPerspective,\n resultSourceMap?: ContentSourceMap,\n): T {\n if (perspective === 'raw') {\n throw new Error('turboChargeResultIfSourceMap does not support raw perspective')\n }\n return applySourceDocuments(\n result,\n resultSourceMap,\n (sourceDocument) => {\n // If there's a displayed document, always prefer it\n if (\n // If _projectId is set, it's a cross dataset reference and we should skip it\n !sourceDocument._projectId &&\n liveDocument?._id &&\n getPublishedId(liveDocument._id) === getPublishedId(sourceDocument._id)\n ) {\n if (typeof liveDocument._id === 'string' && typeof sourceDocument._type === 'string') {\n return liveDocument as unknown as Required<Pick<SanityDocument, '_id' | '_type'>>\n }\n return {\n ...liveDocument,\n _id: liveDocument._id || sourceDocument._id,\n _type: liveDocument._type || sourceDocument._type,\n }\n }\n return null\n },\n mapChangedValue,\n perspective,\n )\n}\n"],"names":["reducer","state","event","type","messages","resets","Error","cause","initialState","useLiveEvents","client","$","_c","dispatch","useReducer","error","setError","useState","t0","t1","live","subscription","events","includeDrafts","tag","subscribe","next","err","unsubscribe","useEffect","useDeferredValue","mapChangedValue","changedValue","previousValue","Array","isArray","length","some","node","isPortableTextBlock","toPlainText","getQueryCacheKey","perspective","query","params","JSON","stringify","gc","queries","size","now","Date","from","heartbeats","values","entry","heartbeat","receivedAt","nextHeartbeats","Map","nextQueries","key","entries","set","get","queryListen","payload","data","has","isEqual","action","useLiveQueries","Symbol","for","interval","setInterval","LOADER_QUERY_GC_INTERVAL","clearInterval","t2","LiveQueries","props","controller","activePerspective","onLoadersConnection","onDocumentsOnPage","comlink","setComlink","liveQueries","liveQueriesDispatch","projectId","useProjectId","dataset","useDataset","nextComlink","createChannel","name","connectTo","createConnectionMachine","provide","actors","createCompatibilityActors","onStatus","on","documents","data_0","MIN_LOADER_QUERY_LISTEN_HEARTBEAT_INTERVAL","start","_temp","isReleasePerspective","RELEASES_STUDIO_CLIENT_OPTIONS","apiVersion","API_VERSION","studioClient","useClient","t3","t4","withConfig","resultSourceMap","t5","t6","post","liveDocument","liveEvents","t7","t8","map","t9","t10","QuerySubscriptionComponent","liveEventsMessages","result","syncTags","tags","useQuerySubscription","comlink_0","perspective_0","query_0","params_0","result_0","resultSourceMap_0","tags_0","handleQueryChange","useEffectEvent","QuerySubscription","memo","displayName","setResult","setResultSourceMap","setSyncTags","undefined","Set","_temp2","skipEventIds","msg_0","msg","id","recentLiveEvents","filter","msg_1","includes","findLast","lastLiveEventId","AbortController","fetch","signal","filterResponse","returnQuery","then","response","startTransition","prev","prev_0","prev_1","catch","abort","bb0","turboChargeResultIfSourceMap","applySourceDocuments","sourceDocument","_projectId","_id","getPublishedId","_type"],"mappings":";;;;;;;;;;;;AAiBO,SAASA,UAAQC,OAAcC,OAAyB;AAC7D,UAAQA,MAAMC,MAAAA;AAAAA,IACZ,KAAK;AACH,aAAO;AAAA,QACL,GAAGF;AAAAA,QACHG,UAAU,CAAC,GAAGH,MAAMG,UAAUF,KAAK;AAAA,MAAA;AAAA,IAEvC,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,QACL,GAAGD;AAAAA,QACHG,UAAU,CAAA;AAAA,QACVC,QAAQJ,MAAMI,SAAS;AAAA,MAAA;AAAA,IAE3B,KAAK;AAEH,aAAOJ;AAAAA,IACT;AACE,YAAMK,MACJ;AAAA,MAEGJ,MAAcC,IAAI,IAErB;AAAA,QAACI,OAAOL;AAAAA,MAAAA,CACV;AAAA,EAAA;AAEN;AAEO,MAAMM,iBAAsB;AAAA,EACjCJ,UAAU,CAAA;AAAA,EACVC,QAAQ;AACV;AAEO,SAAAI,cAAAC,QAAA;AAAA,QAAAC,IAAAC,EAAA,CAAA,GACL,CAAAX,OAAAY,QAAA,IAA0BC,WAAAd,WAAAQ,cAAgC,GAC1D,CAAAO,OAAAC,QAAA,IAA0BC,aAAsB;AAAC,MAC7CF,UAAK;AAAS,UAEVA;AAAK,MAAAG,IAAAC;AAAA,SAAAR,EAAA,CAAA,MAAAD,OAAAU,QAGHF,KAAAA,MAAA;AACR,UAAAG,eAAqBX,OAAMU,KAAAE,OAAA;AAAA,MAAAC,eAAA;AAAA,MAAAC,KACU;AAAA,IAAA,CAAsB,EAACC,UAAA;AAAA,MAAAC,MAElDb;AAAAA,MAAQE,OAAAY,SAEZX,SACEW,eAAGrB,QACCqB,MAAG,IAAArB,MACO,qCAAmC;AAAA,QAAAC,OAAUoB;AAAAA,MAAAA,CAAG,CAChE;AAAA,IAAA,CACH;AAAC,WAAA,MACSN,aAAYO,YAAAA;AAAAA,EAAc,GACtCT,KAAA,CAACT,OAAMU,IAAA,GAAMT,EAAA,CAAA,IAAAD,OAAAU,MAAAT,OAAAO,IAAAP,OAAAQ,OAAAD,KAAAP,EAAA,CAAA,GAAAQ,KAAAR,EAAA,CAAA,IAbhBkB,UAAUX,IAaPC,EAAa,GAETW,iBAAiB7B,KAAK;AAAC;AC/DzB,MAAM8B,kBAAsDA,CACjEC,cACA;AAAA,EAACC;AAAa,MACX;AACH,MAAI,OAAOA,iBAAkB,UAAU;AACrC,QAAI,OAAOD,gBAAiB;AAE1B,aAAO,GAAGA,YAAY;AAGxB,QAAIE,MAAMC,QAAQH,YAAY,GAAG;AAC/B,UAAIA,aAAaI,WAAW;AAE1B,eAAO;AAGT,UAAIJ,aAAaK,KAAMC,CAAAA,SAAS,OAAOA,QAAS,YAAYC,oBAAoBD,IAAI,CAAC;AACnF,eAAOE,YAAYR,YAAY;AAAA,IAEnC;AAAA,EACF;AAEA,SAAOA;AACT;AASO,SAASS,iBACdC,aACAC,OACAC,QACe;AACf,SAAO,GAAGF,WAAW,IAAIC,KAAK,IAAIE,KAAKC,UAAUF,MAAM,CAAC;AAC1D;ACHA,SAASG,GAAG9C,OAAqB;AAC/B,MAAIA,MAAM+C,QAAQC,OAAO;AACvB,WAAOhD;AAGT,QAAMiD,MAAMC,KAAKD,IAAAA;AAIjB,MAAI,CAHkBhB,MAAMkB,KAAKnD,MAAMoD,WAAWC,QAAQ,EAAEjB,KACzDkB,CAAAA,UAAUA,MAAMC,cAAc,MAASN,MAAMK,MAAME,aAAaF,MAAMC,SACzE;AAEE,WAAOvD;AAET,QAAMyD,iBAAiB,oBAAIC,IAAAA,GACrBC,kCAAkBD,IAAAA;AACxB,aAAW,CAACE,KAAKN,KAAK,KAAKtD,MAAMoD,WAAWS,QAAAA;AACtCP,UAAMC,cAAc,MAASN,MAAMK,MAAME,aAAaF,MAAMC,cAGhEE,eAAeK,IAAIF,KAAKN,KAAK,GAC7BK,YAAYG,IAAIF,KAAK5D,MAAM+C,QAAQgB,IAAIH,GAAG,CAAC;AAG7C,SAAO;AAAA,IAAC,GAAG5D;AAAAA,IAAO+C,SAASY;AAAAA,IAAaP,YAAYK;AAAAA,EAAAA;AACtD;AACA,SAASO,YAAYhE,OAAc;AAAA,EAACiE;AAA0B,GAAU;AACtE,QAAML,MAAMpB,iBAAiByB,QAAQxB,aAAawB,QAAQvB,OAAOuB,QAAQtB,MAAM,GACzEuB,OAAO;AAAA,IAACxB,OAAOuB,QAAQvB;AAAAA,IAAOC,QAAQsB,QAAQtB;AAAAA,IAAQF,aAAawB,QAAQxB;AAAAA,EAAAA,GAE3EgB,iBAAiB,IAAIC,IAAI1D,MAAMoD,UAAU;AAC/CK,iBAAeK,IAAIF,KAAK;AAAA,IACtBJ,YAAYN,KAAKD,IAAAA;AAAAA,IACjBM,WAAWU,QAAQV;AAAAA,EAAAA,CACpB;AAED,MAAII,cAAc3D,MAAM+C;AAOxB,UAAI,CAAC/C,MAAM+C,QAAQoB,IAAIP,GAAG,KAAK,CAACQ,QAAQpE,MAAM+C,QAAQgB,IAAIH,GAAG,GAAGM,IAAI,OAClEP,cAAc,IAAID,IAAI1D,MAAM+C,OAAO,GACnCY,YAAYG,IAAIF,KAAKM,IAAI,IAGpB;AAAA,IAACd,YAAYK;AAAAA,IAAgBV,SAASY;AAAAA,EAAAA;AAC/C;AAEO,SAAS5D,QAAQC,OAAcqE,QAAuB;AAC3D,UAAQA,OAAOnE,MAAAA;AAAAA,IACb,KAAK;AACH,aAAO8D,YAAYhE,OAAOqE,MAAM;AAAA,IAClC,KAAK;AACH,aAAOvB,GAAG9C,KAAK;AAAA,IACjB;AACE,YAAMK,MACJ;AAAA,MAEGgE,OAAenE,IAAI,IAEtB;AAAA,QAACI,OAAO+D;AAAAA,MAAAA,CACV;AAAA,EAAA;AAEN;AAEO,MAAM9D,eAAsB;AAAA,EACjCwC,6BAAaW,IAAAA;AAAAA,EACbN,gCAAgBM,IAAAA;AAClB;AAEO,SAAAY,iBAAA;AAAA,QAAA5D,IAAAC,EAAA,CAAA,GACL,CAAAX,OAAAY,QAAA,IAA0BC,WAAAd,SAAAQ,YAAgC;AAAC,MAAAU,IAAAC;AAAAR,IAAA,CAAA,MAAA6D,OAAAC,IAAA,2BAAA,KAEjDvD,KAAAA,MAAA;AACR,UAAAwD,WAAiBC,YAAA,MAAkB9D,SAAQ;AAAA,MAAAV,MAAQ;AAAA,IAAA,CAAK,GAACyE,wBAA0B;AAAC,WAAA,MACvEC,cAAcH,QAAQ;AAAA,EAAC,GACnCvD,KAAA,CAAA,GAAER,OAAAO,IAAAP,OAAAQ,OAAAD,KAAAP,EAAA,CAAA,GAAAQ,KAAAR,EAAA,CAAA,IAHLkB,UAAUX,IAGPC,EAAE;AAEL,QAAA6B,UAAgBlB,iBAAiB7B,MAAK+C,OAAQ;AAAC,MAAA8B;AAAA,SAAAnE,SAAAqC,WACxC8B,KAAA,CAAC9B,SAASnC,QAAQ,GAACF,OAAAqC,SAAArC,OAAAmE,MAAAA,KAAAnE,EAAA,CAAA,GAAnBmE;AAAmB;AC1E5B,SAAeC,YAAAC,OAAA;AAAA,QAAArE,IAAAC,EAAA,EAAA,GACb;AAAA,IAAAqE;AAAAA,IAAAvC,aAAAwC;AAAAA,IAAAC;AAAAA,IAAAC;AAAAA,EAAAA,IAA6FJ,OAE7F,CAAAK,SAAAC,UAAA,IAA8BrE,YAC9B,CAAAsE,aAAAC,mBAAA,IAA2CjB,eAAAA,GAE3CkB,YAAkBC,aAAAA,GAClBC,UAAgBC,WAAAA;AAAY,MAAA1E,IAAAC;AAAAR,WAAAsE,cAAAtE,EAAA,CAAA,MAAAgF,WAAAhF,EAAA,CAAA,MAAA6E,uBAAA7E,EAAA,CAAA,MAAAyE,qBAAAzE,SAAAwE,uBAAAxE,EAAA,CAAA,MAAA8E,aAElBvE,KAAAA,MAAA;AAAA,QACJ+D,YAAU;AACZ,YAAAY,cAAoBZ,WAAUa,cAAA;AAAA,QAAAC,MAEpB;AAAA,QAAcC,WACT;AAAA,QAASxC,WAAA;AAAA,MAAA,GAGtByC,wBAAAA,EAA6DC,QAAA;AAAA,QAAAC,QACnDC,0BAAAA;AAAAA,MAA+C,CACxD,CACH;AACAd,aAAAA,WAAWO,WAAW,GAEtBA,YAAWQ,SAAUlB,mBAAmB,GAExCU,YAAWS,GAAI,oBAAkBnC,CAAAA,SAAA;AAC3BA,aAAIsB,cAAeA,aAAatB,KAAIwB,YAAaA,WACnDP,kBACE,WAEAjB,KAAIzB,aACJyB,KAAIoC,SACN;AAAA,MAAC,CAEJ,GAEDV,YAAWS,GAAI,uBAAqBE,CAAAA,WAAA;AAAA,YAC9BrC,OAAIsB,cAAeA,aAAatB,OAAIwB,YAAaA,SAAO;AAAA,cAExD,OAAOxB,OAAIX,aAAe,YAC1BW,OAAIX,YAAAiD;AAAuD,kBAAA,IAAAnG,MAGzD,2DAAAmG,0CAAA,IAAyG;AAG7GjB,8BAAmB;AAAA,YAAArF,MACX;AAAA,YAAc+D,SAAA;AAAA,cAAAxB,aAELyB,OAAIzB;AAAAA,cAAAC,OACVwB,OAAIxB;AAAAA,cAAAC,QACHuB,OAAIvB;AAAAA,cAAAY,WACDW,OAAIX,aAAA;AAAA,YAAA;AAAA,UAAmB,CAErC;AAAA,QAAC;AAAA,MAAA,CAEL,GAEMqC,YAAWa,MAAAA;AAAAA,IAAQ;AAAA,WAAAC;AAAAA,EAAA,GAG3BxF,KAAA,CAAC8D,YAAYU,SAASH,qBAAqBJ,mBAAmBD,qBAAqBM,SAAS,GAAC9E,OAAAsE,YAAAtE,OAAAgF,SAAAhF,OAAA6E,qBAAA7E,OAAAyE,mBAAAzE,OAAAwE,qBAAAxE,OAAA8E,WAAA9E,OAAAO,IAAAP,OAAAQ,OAAAD,KAAAP,EAAA,CAAA,GAAAQ,KAAAR,EAAA,CAAA,IApDhGkB,UAAUX,IAoDPC,EAA6F;AAAC,MAAA2D;AAAAnE,WAAAuE,qBAG/FJ,KAAA8B,qBAAqB1B,iBAAiB,IAAC2B,iCAAA;AAAA,IAAAC,YAAAC;AAAAA,EAAAA,GAEVpG,OAAAuE,mBAAAvE,OAAAmE,MAAAA,KAAAnE,EAAA,CAAA;AAH/B,QAAAqG,eAAqBC,UACnBnC,EAGF;AAAC,MAAAoC,IAAAC;AAAAxG,YAAAqG,gBAGGG,KAAAH,aAAYI,WAAA;AAAA,IAAAC,iBACO;AAAA,EAAA,CAClB,GAAC1G,QAAAqG,cAAArG,QAAAwG,MAAAA,KAAAxG,EAAA,EAAA,GAAAuG,KAFFC;AAFJ,QAAAzG,SAAewG;AAMd,MAAAI,IAAAC;AAAA5G,IAAA,EAAA,MAAAuE,qBAAAvE,EAAA,EAAA,MAAA0E,WAAA1E,EAAA,EAAA,MAAAgF,WAAAhF,UAAA8E,aACS6B,KAAAA,MAAA;AACJjC,eACFA,QAAOmC,KAAM,sBAAoB;AAAA,MAAA/B;AAAAA,MAAAE;AAAAA,MAAAjD,aAGlBwC;AAAAA,IAAAA,CACd;AAAA,EAAC,GAEHqC,MAAClC,SAASH,mBAAmBO,WAAWE,OAAO,GAAChF,QAAAuE,mBAAAvE,QAAA0E,SAAA1E,QAAAgF,SAAAhF,QAAA8E,WAAA9E,QAAA2G,IAAA3G,QAAA4G,OAAAD,KAAA3G,EAAA,EAAA,GAAA4G,KAAA5G,EAAA,EAAA,IARnDkB,UAAUyF,IAQPC,EAAgD;AAKnD,QAAAE,eAAqB3F,iBAAiBkD,MAAKyC,YAAa,GAExDC,aAAmBjH,cAAcC,MAAM;AAAC,MAAAiH;AAAAhH,YAAA4E,eAInCoC,KAAA,CAAA,GAAIpC,YAAWzB,QAAAA,CAAU,GAACnD,QAAA4E,aAAA5E,QAAAgH,MAAAA,KAAAhH,EAAA,EAAA;AAAA,MAAAiH;AAAA,SAAAjH,EAAA,EAAA,MAAAD,UAAAC,EAAA,EAAA,MAAA0E,WAAA1E,EAAA,EAAA,MAAAgF,WAAAhF,UAAA8G,gBAAA9G,EAAA,EAAA,MAAA+G,cAAA/G,EAAA,EAAA,MAAA8E,aAAA9E,EAAA,EAAA,MAAAgH,MAD7BC,qCACGD,UAAAA,GAA0BE,IAAAC,CAAAA,OAAA;AAAM,UAAA,CAAAjE,KAAAkE,GAAA,IAAAD,IAAM;AAAA,MAAAnF;AAAAA,MAAAC;AAAAA,MAAAF;AAAAA,IAAAA,IAAAqF;AAA4B,+BAChE,mBAAA,EAEYtC,WACFE,SACIjD,aACNC,OACCC,QACCyC,SACD3E,QACM+G,cACM,oBAAAC,WAAUtH,SAAAA,GATzB,GAAGsH,WAAUrH,MAAA,IAAWwD,GAAG,EASO;AAAA,EACvC,CACH,EAAA,CAAC,GACDlD,QAAAD,QAAAC,QAAA0E,SAAA1E,QAAAgF,SAAAhF,QAAA8G,cAAA9G,QAAA+G,YAAA/G,QAAA8E,WAAA9E,QAAAgH,IAAAhH,QAAAiH,MAAAA,KAAAjH,EAAA,EAAA,GAfHiH;AAeG;AA5GQ,SAAAjB,QAAA;AAAA;AAgIf,SAAAqB,2BAAAhD,OAAA;AAAA,QAAArE,IAAAC,EAAA,EAAA,GACE;AAAA,IAAA6E;AAAAA,IAAAE;AAAAA,IAAAjD;AAAAA,IAAAC;AAAAA,IAAAjC;AAAAA,IAAA+G;AAAAA,IAAA7E;AAAAA,IAAAyC;AAAAA,IAAA4C;AAAAA,EAAAA,IAUIjD,OAEJ;AAAA,IAAAkD;AAAAA,IAAAb;AAAAA,IAAAc,UAAAC;AAAAA,EAAAA,IAIIC,qBAAA;AAAA,IAAA3H;AAAAA,IAAA+G;AAAAA,IAAA7E;AAAAA,IAAAF;AAAAA,IAAAC;AAAAA,IAAAsF;AAAAA,EAAAA,CAOH,KAAC,CAAA;AAAM,MAAA/G;AAAAP,IAAA,CAAA,MAAAgF,WAAAhF,SAAA8E,aAINvE,KAAAA,CAAAoH,WAAAC,eAAAC,SAAAC,UAAAC,UAAAC,mBAAAC,WAAA;AASEvD,eAAOmC,KAAO,uBAAqB;AAAA,MAAA/B;AAAAA,MAAAE;AAAAA,MAAAjD,aAGjCA;AAAAA,MAAWC,OACXA;AAAAA,MAAKC,QACLA;AAAAA,MAAMsF,QACNA;AAAAA,MAAMb,iBACNA;AAAAA,MAAee,MACfA;AAAAA,IAAAA,CAAI;AAAA,EAAA,GAEPzH,OAAAgF,SAAAhF,OAAA8E,WAAA9E,OAAAO,MAAAA,KAAAP,EAAA,CAAA;AApBH,QAAAkI,oBAA0BC,eACxB5H,EAoBF;AAAC,MAAAC;AAAAR,IAAA,CAAA,MAAA0E,WAAA1E,EAAA,CAAA,MAAAkI,qBAAAlI,EAAA,CAAA,MAAAiC,UAAAjC,EAAA,CAAA,MAAA+B,eAAA/B,EAAA,CAAA,MAAAgC,SAAAhC,EAAA,CAAA,MAAAuH,UAAAvH,EAAA,CAAA,MAAA0G,mBAAA1G,UAAAyH,QAGSjH,KAAAA,MAAA;AACJkG,uBACFwB,kBAAkBxD,SAAS3C,aAAaC,OAAOC,QAAQsF,QAAQb,iBAAiBe,IAAI;AAAA,EAAC,GAGxFzH,OAAA0E,SAAA1E,OAAAkI,mBAAAlI,OAAAiC,QAAAjC,OAAA+B,aAAA/B,OAAAgC,OAAAhC,OAAAuH,QAAAvH,OAAA0G,iBAAA1G,QAAAyH,MAAAzH,QAAAQ,MAAAA,KAAAR,EAAA,EAAA;AAAA,MAAAmE;AAAA,SAAAnE,EAAA,EAAA,MAAA0E,WAAA1E,EAAA,EAAA,MAAAiC,UAAAjC,EAAA,EAAA,MAAA+B,eAAA/B,UAAAgC,SAAAhC,EAAA,EAAA,MAAAuH,UAAAvH,EAAA,EAAA,MAAA0G,mBAAA1G,EAAA,EAAA,MAAAyH,QAAEtD,KAAA,CAACO,SAASzC,QAAQF,aAAaC,OAAOuF,QAAQb,iBAAiBe,IAAI,GAACzH,QAAA0E,SAAA1E,QAAAiC,QAAAjC,QAAA+B,aAAA/B,QAAAgC,OAAAhC,QAAAuH,QAAAvH,QAAA0G,iBAAA1G,QAAAyH,MAAAzH,QAAAmE,MAAAA,KAAAnE,EAAA,EAAA,GALvEkB,UAAUV,IAKP2D,EAAoE,GAAC;AAAA;AAI1E,MAAMiE,oBAAoBC,KAAKhB,0BAA0B;AACzDe,kBAAkBE,cAAc;AAShC,SAAAZ,qBAAArD,OAAA;AAAA,QAAArE,IAAAC,EAAA,EAAA,GACE;AAAA,IAAA6G;AAAAA,IAAA/G;AAAAA,IAAAiC;AAAAA,IAAAC;AAAAA,IAAAF;AAAAA,IAAAuF;AAAAA,EAAAA,IAA+EjD,OAC/E,CAAAkD,QAAAgB,SAAA,IAA4BjI,aAAsB,GAClD,CAAAoG,iBAAA8B,kBAAA,IAA8ClI,aAAkD,GAChG,CAAAkH,UAAAiB,WAAA,IAAgCnI,SAAAoI,MAAyC;AAAC,MAAAnI;AAAAP,WAAAsH,sBAC1C/G,KAAAA,MAAA,IAAAoI,IAAcrB,mBAAkBJ,IAAA0B,MAAoB,CAAC,GAAC5I,OAAAsH,oBAAAtH,OAAAO,MAAAA,KAAAP,EAAA,CAAA;AAAtF,QAAA,CAAA6I,YAAA,IAAuBvI,SAASC,EAAsD;AAAC,MAAAC;AAAA,MAAAR,EAAA,CAAA,MAAAsH,sBAAAtH,SAAA6I,gBAAA7I,EAAA,CAAA,MAAAwH,UAAA;AAAA,QAAArD;AAAAnE,aAAA6I,gBACpC1E,MAAA2E,CAAAA,UAAA,CAAUD,aAAYpF,IAAKsF,MAAGC,EAAG,GAAChJ,OAAA6I,cAAA7I,OAAAmE,OAAAA,MAAAnE,EAAA,CAAA;AAArF,UAAAiJ,mBAAyB3B,mBAAkB4B,OAAQ/E,GAAkC;AAAC,QAAAoC;AAAAvG,aAAAwH,YACtCjB,MAAA4C,CAAAA,UAC9CJ,MAAGtB,KAAA/F,KAAAb,CAAAA,QAAoB2G,UAAQ4B,SAAWvI,GAAG,CAAC,GAACb,OAAAwH,UAAAxH,OAAAuG,OAAAA,MAAAvG,EAAA,CAAA,GAD3BQ,KAAAyI,iBAAgBI,SAAU9C,GAEhD,GAACvG,OAAAsH,oBAAAtH,OAAA6I,cAAA7I,OAAAwH,UAAAxH,OAAAQ;AAAAA,EAAA;AAAAA,SAAAR,EAAA,CAAA;AACD,QAAAsJ,kBAHsB9I,IAGewI,IAGrC,CAAA5I,OAAAC,QAAA,IAA0BC,aAAsB;AAAC,MAC7CF;AAAK,UAAQA;AAAK,MAAA+D,IAAAoC;AAAAvG,IAAA,EAAA,MAAAD,UAAAC,EAAA,EAAA,MAAAsJ,mBAAAtJ,EAAA,EAAA,MAAAiC,UAAAjC,EAAA,EAAA,MAAA+B,eAAA/B,UAAAgC,SAGZmC,KAAAA,MAAA;AACR,UAAAG,iBAAAiF,gBAAAA;AAEAxJ,WAAAA,OAAMyJ,MACGxH,OAAOC,QAAM;AAAA,MAAAqH;AAAAA,MAAAzI,KAEb;AAAA,MAAqB4I,QAClBnF,WAAUmF;AAAAA,MAAA1H;AAAAA,MAAA2H,gBAAA;AAAA,MAAAC,aAAA;AAAA,IAAA,CAInB,EAACC,KAAAC,CAAAA,aAAA;AAEAC,sBAAA,MAAA;AACEvB,kBAASwB,CAAAA,SAAqBrG,QAAQqG,MAAMF,SAAQtC,MAAO,IAAIwC,OAAOF,SAAQtC,MAAQ,GACtFiB,mBAAkBwB,CAAAA,WAChBtG,QAAQqG,QAAMF,SAAQnD,eAAgB,IAAIqD,SAAOF,SAAQnD,eAC3D,GACA+B,YAAWwB,CAAAA,WAAYvG,QAAQqG,QAAMF,SAAQrC,QAAS,IAAIuC,SAAOF,SAAQrC,QAAU;AAAA,MAAC,CACrF;AAAA,IAAC,CACH,EAAC0C,MAAAlJ,CAAAA,QAAA;AAAA,OAEI,OAAOA,OAAQ,YAAYA,KAAGoE,SAAW,iBAC3C/E,SAASW,GAAG;AAAA,IAAC,CAEhB,GAAC,MAAA;AAGFsD,iBAAU6F,MAAAA;AAAAA,IAAQ;AAAA,EAAA,GAEnB5D,KAAA,CAACxG,QAAQuJ,iBAAiBrH,QAAQF,aAAaC,KAAK,GAAChC,QAAAD,QAAAC,QAAAsJ,iBAAAtJ,QAAAiC,QAAAjC,QAAA+B,aAAA/B,QAAAgC,OAAAhC,QAAAmE,IAAAnE,QAAAuG,OAAApC,KAAAnE,EAAA,EAAA,GAAAuG,KAAAvG,EAAA,EAAA,IA9BxDkB,UAAUiD,IA8BPoC,EAAqD;AAAC,MAAAC;AAAA4D,OAAA;AAAA,QAInDtD,gBAAgBJ,iBAAe;AAAA,UAAAC;AAAA3G,QAAA,EAAA,MAAA8G,gBAAA9G,EAAA,EAAA,MAAA+B,eAAA/B,EAAA,EAAA,MAAAuH,UAAAvH,UAAA0G,mBAEvBC,MAAA0D,6BAA6BvD,cAAcS,QAAQxF,aAAa2E,eAAe,GAAC1G,QAAA8G,cAAA9G,QAAA+B,aAAA/B,QAAAuH,QAAAvH,QAAA0G,iBAAA1G,QAAA2G,OAAAA,MAAA3G,EAAA,EAAA;AAAA,UAAA4G;AAAA5G,QAAA,EAAA,MAAA0G,mBAAA1G,UAAAwH,YAAAxH,EAAA,EAAA,MAAA2G,OADnFC,KAAA;AAAA,QAAAW,QACGZ;AAAAA,QAAgFD;AAAAA,QAAAc;AAAAA,MAAAA,GAGzFxH,QAAA0G,iBAAA1G,QAAAwH,UAAAxH,QAAA2G,KAAA3G,QAAA4G,MAAAA,KAAA5G,EAAA,EAAA,GAJDwG,KAAOI;AAIN,YAAAwD;AAAAA,IAAA;AAAA,QAAAzD;AAAA3G,MAAA,EAAA,MAAAuH,UAAAvH,UAAA0G,mBAAA1G,EAAA,EAAA,MAAAwH,YAEIb,KAAA;AAAA,MAAAY;AAAAA,MAAAb;AAAAA,MAAAc;AAAAA,IAAAA,GAAmCxH,QAAAuH,QAAAvH,QAAA0G,iBAAA1G,QAAAwH,UAAAxH,QAAA2G,MAAAA,KAAA3G,EAAA,EAAA,GAA1CwG,KAAOG;AAAAA,EAAmC;AAAA,SARrCH;AAS2D;AA3DpE,SAAAoC,OAAAG,KAAA;AAAA,SAKgFA,IAAGC;AAAA;AAyD5E,SAASqB,6BACdvD,cACAS,QACAxF,aACA2E,iBACG;AACH,MAAI3E,gBAAgB;AAClB,UAAM,IAAIpC,MAAM,+DAA+D;AAEjF,SAAO2K,qBACL/C,QACAb,iBACC6D,CAAAA;AAAAA;AAAAA,IAIG,CAACA,eAAeC,cAChB1D,cAAc2D,OACdC,eAAe5D,aAAa2D,GAAG,MAAMC,eAAeH,eAAeE,GAAG,IAElE,OAAO3D,aAAa2D,OAAQ,YAAY,OAAOF,eAAeI,SAAU,WACnE7D,eAEF;AAAA,MACL,GAAGA;AAAAA,MACH2D,KAAK3D,aAAa2D,OAAOF,eAAeE;AAAAA,MACxCE,OAAO7D,aAAa6D,SAASJ,eAAeI;AAAAA,IAAAA,IAGzC;AAAA,KAETvJ,iBACAW,WACF;AACF;"}