@sanity/sanity-plugin-async-list
3.0.03.0.1
dist/index.js.map~
dist/index.js.mapModified+1−1
Index: package/dist/index.js.map
===================================================================
--- package/dist/index.js.map
+++ package/dist/index.js.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"index.js","names":["ApiIcon","SearchIcon","SpinnerIcon","SettingsView","useSecrets","Autocomplete","Button","Card","Flex","Text","debounce","JSX","useCallback","useEffect","useMemo","useRef","useState","set","StringInputProps","unset","useClient","AsyncListInputOptions","LoadingIcon","$","_c","t0","Symbol","for","t1","animation","OptionsItem","value","key","validOptions","arr","Array","isArray","every","item","AsyncListInputProps","options","AsyncList","props","Element","namespace","secrets","schemaType","secretsKeys","keys","secretsNamespace","process","env","console","warn","Record","data","setData","loading","setLoading","error","setError","Error","showSettings","setShowSettings","prevQuery","setPrevQuery","client","clientOptions","apiVersion","fetchData","query","loaderData","loader","e","errorMessage","message","handleChange","onChange","handleQueryChange","handleQueryChangeRef","current","debouncedHandler","cancel","readOnlyProps","elementProps","readOnly","renderDefault","autocompleteProps","id","filterOption","loaderType","undefined","icon","openButton","onQueryChange","title","createAsyncListInput","AsyncListInput","defineType","StringDefinition","createAsyncListInput","AsyncListPluginConfig","asyncListType","config","name","schemaType","type","components","input","SchemaTypeDefinition","AsyncListPluginConfig","asyncListType","schema","config","types","definePlugin","AsyncList","createAsyncListInput","schema","AsyncListPluginConfig","AsyncListInputProps","AsyncListInputOptions","asyncList","config","schemaType","Error","name"],"sources":["../src/components/async-list.tsx","../src/schema-types/async-list.ts","../src/schema-types/index.ts","../src/index.ts"],"sourcesContent":["import {ApiIcon} from '@sanity/icons/Api'\nimport {SearchIcon} from '@sanity/icons/Search'\nimport {SpinnerIcon} from '@sanity/icons/Spinner'\nimport {SettingsView, useSecrets} from '@sanity/studio-secrets'\nimport {Autocomplete, Button, Card, Flex, Text} from '@sanity/ui'\nimport debounce from 'lodash-es/debounce.js'\nimport {type JSX, useCallback, useEffect, useMemo, useRef, useState} from 'react'\nimport {set, type StringInputProps, unset, useClient} from 'sanity'\n\nimport type {AsyncListInputOptions} from '../types'\n\n// Spinner shown in the Autocomplete `icon` slot while the loader is fetching.\n// Defined at module scope so it has a stable identity across renders.\nfunction LoadingIcon(): JSX.Element {\n return (\n <>\n <style>\n {`\n @keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n `}\n </style>\n <SpinnerIcon\n style={{\n animation: 'spin 2s linear infinite',\n }}\n />\n </>\n )\n}\n\n// Object for Autocomplete's `options` prop\ninterface OptionsItem {\n value: string\n [key: string]: unknown\n}\n\n// Autocomplete options validation\nfunction validOptions(arr: unknown): arr is OptionsItem[] {\n return (\n Array.isArray(arr) &&\n arr.every(\n (item: unknown) =>\n typeof item === 'object' &&\n item !== null &&\n 'value' in item &&\n typeof item.value === 'string',\n )\n )\n}\n\n/**\n * Props for the {@link AsyncList} input component: the standard Sanity string\n * input props plus the async-list `options`.\n *\n * @public\n */\nexport interface AsyncListInputProps extends StringInputProps {\n options: AsyncListInputOptions\n}\n\n/**\n * The async-list input component. It is a regular React component that takes a\n * single `props` argument, so it is safe under the Rules of Hooks and gets\n * optimized by the React Compiler.\n *\n * For the `components.input` slot, prefer {@link createAsyncListInput}, which\n * binds the options for you.\n *\n * TODO:\n * - Cache fetchData call w/o arguments\n *\n * @public\n */\nexport function AsyncList(props: AsyncListInputProps): JSX.Element {\n const {options} = props\n const namespace =\n options.secrets?.namespace ??\n (options.schemaType ? `async-list-${options.schemaType}` : 'async-list')\n\n // Warn (in dev) when secrets are configured but there is nothing stable to\n // derive a namespace from. Without an explicit `secrets.namespace` (or a\n // `schemaType` from the plugin), multiple component-usage fields would share\n // the same default namespace and collide.\n const secretsKeys = options.secrets?.keys\n const secretsNamespace = options.secrets?.namespace\n const schemaType = options.schemaType\n useEffect(() => {\n if (process.env['NODE_ENV'] === 'production') return\n if (secretsKeys && !secretsNamespace && !schemaType) {\n console.warn(\n 'sanity-plugin-async-list: `secrets` is configured without `schemaType` or `secrets.namespace`. ' +\n 'Set an explicit `secrets.namespace` to avoid collisions between fields.',\n )\n }\n }, [secretsKeys, secretsNamespace, schemaType])\n\n const {secrets} = useSecrets<Record<string, string> | undefined>(namespace)\n const [data, setData] = useState<OptionsItem[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n const [showSettings, setShowSettings] = useState(false)\n const [prevQuery, setPrevQuery] = useState<string | null>(null)\n const client = useClient(options.clientOptions ?? {apiVersion: '2024-12-12'})\n\n const fetchData = useCallback(\n async (query?: string) => {\n try {\n // Reset previous error state\n setError(null)\n setLoading(true)\n\n // Call user provided data loader\n const loaderData = await options.loader({secrets, query, client})\n\n // Validate and set data\n if (validOptions(loaderData)) {\n setData(loaderData)\n } else {\n console.error(\n 'sanity-plugin-async-list data error - data must match options from @sanity/ui Autocomplete https://www.sanity.io/ui/docs/component/autocomplete',\n loaderData,\n )\n setError(new Error('Error with list data. Check console for more info.'))\n }\n } catch (e) {\n const errorMessage =\n e instanceof Error ? e.message : 'An unknown error occurred while fetching data'\n\n console.error('sanity-plugin-async-list fetch error:', errorMessage)\n setError(new Error('Error fetching list, check console for more info'))\n } finally {\n setLoading(false)\n }\n },\n [options, secrets, client],\n )\n\n useEffect(() => {\n // Don't fetch if we expect API keys but secrets don't exist\n if (options?.secrets?.keys && !secrets) return\n // fetch the initial data, but only if the field doesn't have a value\n if (!props.value && !data) {\n // Kicking off the initial load intentionally sets loading/error state on\n // mount so the field shows a spinner while the loader resolves.\n // oxlint-disable-next-line react/react-compiler\n void fetchData()\n }\n }, [fetchData, data, secrets, options.secrets, props.value])\n\n // Set field value in content lake. Plain function: with React Compiler enabled\n // this is memoized automatically based on the `props.onChange` it captures.\n const handleChange = (value?: string) => props.onChange(value ? set(value) : unset())\n // Handle searching in 'search' mode\n const handleQueryChange = useCallback(\n (query: string | null) => {\n if (\n query === '' || // User hit backspace or\n (!query && !props.value && prevQuery) // they cleared out an existing value or query\n ) {\n if (!data) {\n void fetchData()\n }\n }\n if (query) {\n void fetchData(query)\n }\n setPrevQuery(query)\n },\n [fetchData, data, props.value, prevQuery],\n )\n\n // Keep a stable debounced wrapper that always calls the latest handler.\n // Depending on `handleQueryChange` in useMemo would recreate (and cancel) the\n // debounce after every search finishes (`data`/`prevQuery` change), dropping\n // keystrokes typed while a request was in flight.\n const handleQueryChangeRef = useRef(handleQueryChange)\n useEffect(() => {\n handleQueryChangeRef.current = handleQueryChange\n }, [handleQueryChange])\n\n // oxlint-disable react/react-compiler -- stable debounce instance; latest handler via ref\n const debouncedHandler = useMemo(\n () => debounce((value: string | null) => handleQueryChangeRef.current(value), 300),\n [],\n )\n // oxlint-enable react/react-compiler\n\n // Cancel only on unmount — not when the underlying handler identity changes.\n useEffect(() => () => debouncedHandler.cancel(), [debouncedHandler])\n\n // Render error state as a readonly string field\n if (error) {\n const readOnlyProps = {\n ...props,\n elementProps: {...props.elementProps, readOnly: true},\n }\n\n return (\n <Card>\n {readOnlyProps.renderDefault(readOnlyProps)}\n <Card tone=\"critical\" padding={2}>\n <Text size={1}>{error.message}</Text>\n </Card>\n </Card>\n )\n }\n if (props.readOnly) {\n return <Card>{props.renderDefault(props)}</Card>\n }\n return (\n <Card>\n <Autocomplete\n // Passthrough first; critical form/id props below always win.\n {...options.autocompleteProps}\n id={props.elementProps.id}\n filterOption={\n options.autocompleteProps?.filterOption ??\n (options.loaderType === 'search' ? () => true : undefined)\n }\n icon={loading ? LoadingIcon : (options.autocompleteProps?.icon ?? SearchIcon)}\n openButton={options.autocompleteProps?.openButton ?? true}\n onChange={handleChange}\n options={data ?? []}\n value={props.value}\n onQueryChange={\n options.loaderType === 'search'\n ? debouncedHandler\n : options.autocompleteProps?.onQueryChange\n }\n />\n\n {showSettings && options.secrets?.keys && (\n <SettingsView\n title={options.secrets.title ?? 'Secrets'}\n namespace={namespace}\n keys={options.secrets?.keys}\n onClose={() => setShowSettings(false)}\n />\n )}\n {options.secrets && (\n <Flex justify={'flex-end'} padding={2} paddingRight={0}>\n <Button\n fontSize={0}\n icon={ApiIcon}\n mode=\"ghost\"\n padding={2}\n radius=\"full\"\n text=\"Manage keys\"\n onClick={() => setShowSettings(true)}\n />\n </Flex>\n )}\n </Card>\n )\n}\n\n/**\n * Create a Sanity string input component bound to the given async-list\n * `options`. Use this for the `components.input` slot when wiring the input\n * manually (i.e. without the `asyncList()` plugin):\n *\n * ```ts\n * import {defineField} from 'sanity'\n * import {createAsyncListInput} from '@sanity/sanity-plugin-async-list'\n *\n * defineField({\n * name: 'myString',\n * type: 'string',\n * components: {\n * input: createAsyncListInput({\n * loader: async () => {\n * // ...return [{value: 'a'}, {value: 'b'}]\n * },\n * }),\n * },\n * })\n * ```\n *\n * @public\n */\nexport function createAsyncListInput(\n options: AsyncListInputOptions,\n): (props: StringInputProps) => JSX.Element {\n return function AsyncListInput(props: StringInputProps): JSX.Element {\n 'use memo'\n return <AsyncList {...props} options={options} />\n }\n}\n","import {defineType, type StringDefinition} from 'sanity'\n\nimport {createAsyncListInput} from '../components/async-list'\nimport type {AsyncListPluginConfig} from '../types'\n\nexport const asyncListType = (config: AsyncListPluginConfig): StringDefinition =>\n defineType({\n name: config?.schemaType,\n type: 'string',\n components: {\n input: createAsyncListInput(config),\n },\n })\n","import type {SchemaTypeDefinition} from 'sanity'\n\nimport type {AsyncListPluginConfig} from '../types'\nimport {asyncListType} from './async-list'\n\nexport const schema = (config: AsyncListPluginConfig): {types: SchemaTypeDefinition[]} => {\n return {types: [asyncListType(config)]}\n}\n","import {definePlugin} from 'sanity'\n\nimport {AsyncList, createAsyncListInput} from './components/async-list'\nimport {schema} from './schema-types'\nimport type {AsyncListPluginConfig} from './types'\n\nexport {AsyncList, createAsyncListInput}\nexport type {AsyncListInputProps} from './components/async-list'\nexport type {AsyncListInputOptions, AsyncListPluginConfig} from './types'\n\n/**\n * Usage in `sanity.config.ts` (or .js)\n *\n * ```ts\n * import {defineConfig} from 'sanity'\n * import {asyncList} from '@sanity/sanity-plugin-async-list'\n *\n * export default defineConfig({\n * // ...\n * plugins: [asyncList()],\n * })\n * ```\n */\nexport const asyncList = definePlugin<AsyncListPluginConfig>((config) => {\n if (!config.schemaType) {\n throw new Error('schemaType required by async-list plugin')\n }\n return {\n name: 'sanity-plugin-async-list',\n schema: schema(config),\n }\n})\n"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"index.js","names":["ApiIcon","SearchIcon","SpinnerIcon","SettingsView","useSecrets","Button","Card","Flex","Text","Autocomplete","debounce","JSX","useCallback","useEffect","useMemo","useRef","useState","set","StringInputProps","unset","useClient","AsyncListInputOptions","LoadingIcon","$","_c","t0","Symbol","for","t1","animation","OptionsItem","value","key","validOptions","arr","Array","isArray","every","item","AsyncListInputProps","options","AsyncList","props","Element","namespace","secrets","schemaType","secretsKeys","keys","secretsNamespace","process","env","console","warn","Record","data","setData","loading","setLoading","error","setError","Error","showSettings","setShowSettings","prevQuery","setPrevQuery","client","clientOptions","apiVersion","fetchData","query","loaderData","loader","e","errorMessage","message","handleChange","onChange","handleQueryChange","handleQueryChangeRef","current","debouncedHandler","cancel","readOnlyProps","elementProps","readOnly","renderDefault","autocompleteProps","id","filterOption","loaderType","undefined","icon","openButton","onQueryChange","title","createAsyncListInput","AsyncListInput","defineType","StringDefinition","createAsyncListInput","AsyncListPluginConfig","asyncListType","config","name","schemaType","type","components","input","SchemaTypeDefinition","AsyncListPluginConfig","asyncListType","schema","config","types","definePlugin","AsyncList","createAsyncListInput","schema","AsyncListPluginConfig","AsyncListInputProps","AsyncListInputOptions","asyncList","config","schemaType","Error","name"],"sources":["../src/components/async-list.tsx","../src/schema-types/async-list.ts","../src/schema-types/index.ts","../src/index.ts"],"sourcesContent":["import {ApiIcon} from '@sanity/icons/Api'\nimport {SearchIcon} from '@sanity/icons/Search'\nimport {SpinnerIcon} from '@sanity/icons/Spinner'\nimport {SettingsView, useSecrets} from '@sanity/studio-secrets'\nimport {Button, Card, Flex, Text} from '@sanity/ui'\nimport {Autocomplete} from '@sanity/ui/autocomplete'\nimport debounce from 'lodash-es/debounce.js'\nimport {type JSX, useCallback, useEffect, useMemo, useRef, useState} from 'react'\nimport {set, type StringInputProps, unset, useClient} from 'sanity'\n\nimport type {AsyncListInputOptions} from '../types'\n\n// Spinner shown in the Autocomplete `icon` slot while the loader is fetching.\n// Defined at module scope so it has a stable identity across renders.\nfunction LoadingIcon(): JSX.Element {\n return (\n <>\n <style>\n {`\n @keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n `}\n </style>\n <SpinnerIcon\n style={{\n animation: 'spin 2s linear infinite',\n }}\n />\n </>\n )\n}\n\n// Object for Autocomplete's `options` prop\ninterface OptionsItem {\n value: string\n [key: string]: unknown\n}\n\n// Autocomplete options validation\nfunction validOptions(arr: unknown): arr is OptionsItem[] {\n return (\n Array.isArray(arr) &&\n arr.every(\n (item: unknown) =>\n typeof item === 'object' &&\n item !== null &&\n 'value' in item &&\n typeof item.value === 'string',\n )\n )\n}\n\n/**\n * Props for the {@link AsyncList} input component: the standard Sanity string\n * input props plus the async-list `options`.\n *\n * @public\n */\nexport interface AsyncListInputProps extends StringInputProps {\n options: AsyncListInputOptions\n}\n\n/**\n * The async-list input component. It is a regular React component that takes a\n * single `props` argument, so it is safe under the Rules of Hooks and gets\n * optimized by the React Compiler.\n *\n * For the `components.input` slot, prefer {@link createAsyncListInput}, which\n * binds the options for you.\n *\n * TODO:\n * - Cache fetchData call w/o arguments\n *\n * @public\n */\nexport function AsyncList(props: AsyncListInputProps): JSX.Element {\n const {options} = props\n const namespace =\n options.secrets?.namespace ??\n (options.schemaType ? `async-list-${options.schemaType}` : 'async-list')\n\n // Warn (in dev) when secrets are configured but there is nothing stable to\n // derive a namespace from. Without an explicit `secrets.namespace` (or a\n // `schemaType` from the plugin), multiple component-usage fields would share\n // the same default namespace and collide.\n const secretsKeys = options.secrets?.keys\n const secretsNamespace = options.secrets?.namespace\n const schemaType = options.schemaType\n useEffect(() => {\n if (process.env['NODE_ENV'] === 'production') return\n if (secretsKeys && !secretsNamespace && !schemaType) {\n console.warn(\n 'sanity-plugin-async-list: `secrets` is configured without `schemaType` or `secrets.namespace`. ' +\n 'Set an explicit `secrets.namespace` to avoid collisions between fields.',\n )\n }\n }, [secretsKeys, secretsNamespace, schemaType])\n\n const {secrets} = useSecrets<Record<string, string> | undefined>(namespace)\n const [data, setData] = useState<OptionsItem[] | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n const [showSettings, setShowSettings] = useState(false)\n const [prevQuery, setPrevQuery] = useState<string | null>(null)\n const client = useClient(options.clientOptions ?? {apiVersion: '2024-12-12'})\n\n const fetchData = useCallback(\n async (query?: string) => {\n try {\n // Reset previous error state\n setError(null)\n setLoading(true)\n\n // Call user provided data loader\n const loaderData = await options.loader({secrets, query, client})\n\n // Validate and set data\n if (validOptions(loaderData)) {\n setData(loaderData)\n } else {\n console.error(\n 'sanity-plugin-async-list data error - data must match options from @sanity/ui Autocomplete https://www.sanity.io/ui/docs/component/autocomplete',\n loaderData,\n )\n setError(new Error('Error with list data. Check console for more info.'))\n }\n } catch (e) {\n const errorMessage =\n e instanceof Error ? e.message : 'An unknown error occurred while fetching data'\n\n console.error('sanity-plugin-async-list fetch error:', errorMessage)\n setError(new Error('Error fetching list, check console for more info'))\n } finally {\n setLoading(false)\n }\n },\n [options, secrets, client],\n )\n\n useEffect(() => {\n // Don't fetch if we expect API keys but secrets don't exist\n if (options?.secrets?.keys && !secrets) return\n // fetch the initial data, but only if the field doesn't have a value\n if (!props.value && !data) {\n // Kicking off the initial load intentionally sets loading/error state on\n // mount so the field shows a spinner while the loader resolves.\n // oxlint-disable-next-line react/react-compiler\n void fetchData()\n }\n }, [fetchData, data, secrets, options.secrets, props.value])\n\n // Set field value in content lake. Plain function: with React Compiler enabled\n // this is memoized automatically based on the `props.onChange` it captures.\n const handleChange = (value?: string) => props.onChange(value ? set(value) : unset())\n // Handle searching in 'search' mode\n const handleQueryChange = useCallback(\n (query: string | null) => {\n if (\n query === '' || // User hit backspace or\n (!query && !props.value && prevQuery) // they cleared out an existing value or query\n ) {\n if (!data) {\n void fetchData()\n }\n }\n if (query) {\n void fetchData(query)\n }\n setPrevQuery(query)\n },\n [fetchData, data, props.value, prevQuery],\n )\n\n // Keep a stable debounced wrapper that always calls the latest handler.\n // Depending on `handleQueryChange` in useMemo would recreate (and cancel) the\n // debounce after every search finishes (`data`/`prevQuery` change), dropping\n // keystrokes typed while a request was in flight.\n const handleQueryChangeRef = useRef(handleQueryChange)\n useEffect(() => {\n handleQueryChangeRef.current = handleQueryChange\n }, [handleQueryChange])\n\n // oxlint-disable react/react-compiler -- stable debounce instance; latest handler via ref\n const debouncedHandler = useMemo(\n () => debounce((value: string | null) => handleQueryChangeRef.current(value), 300),\n [],\n )\n // oxlint-enable react/react-compiler\n\n // Cancel only on unmount — not when the underlying handler identity changes.\n useEffect(() => () => debouncedHandler.cancel(), [debouncedHandler])\n\n // Render error state as a readonly string field\n if (error) {\n const readOnlyProps = {\n ...props,\n elementProps: {...props.elementProps, readOnly: true},\n }\n\n return (\n <Card>\n {readOnlyProps.renderDefault(readOnlyProps)}\n <Card tone=\"critical\" padding={2}>\n <Text size={1}>{error.message}</Text>\n </Card>\n </Card>\n )\n }\n if (props.readOnly) {\n return <Card>{props.renderDefault(props)}</Card>\n }\n return (\n <Card>\n <Autocomplete\n // Passthrough first; critical form/id props below always win.\n {...options.autocompleteProps}\n id={props.elementProps.id}\n filterOption={\n options.autocompleteProps?.filterOption ??\n (options.loaderType === 'search' ? () => true : undefined)\n }\n icon={loading ? LoadingIcon : (options.autocompleteProps?.icon ?? SearchIcon)}\n openButton={options.autocompleteProps?.openButton ?? true}\n onChange={handleChange}\n options={data ?? []}\n value={props.value}\n onQueryChange={\n options.loaderType === 'search'\n ? debouncedHandler\n : options.autocompleteProps?.onQueryChange\n }\n />\n\n {showSettings && options.secrets?.keys && (\n <SettingsView\n title={options.secrets.title ?? 'Secrets'}\n namespace={namespace}\n keys={options.secrets?.keys}\n onClose={() => setShowSettings(false)}\n />\n )}\n {options.secrets && (\n <Flex justify={'flex-end'} padding={2} paddingRight={0}>\n <Button\n fontSize={0}\n icon={ApiIcon}\n mode=\"ghost\"\n padding={2}\n radius=\"full\"\n text=\"Manage keys\"\n onClick={() => setShowSettings(true)}\n />\n </Flex>\n )}\n </Card>\n )\n}\n\n/**\n * Create a Sanity string input component bound to the given async-list\n * `options`. Use this for the `components.input` slot when wiring the input\n * manually (i.e. without the `asyncList()` plugin):\n *\n * ```ts\n * import {defineField} from 'sanity'\n * import {createAsyncListInput} from '@sanity/sanity-plugin-async-list'\n *\n * defineField({\n * name: 'myString',\n * type: 'string',\n * components: {\n * input: createAsyncListInput({\n * loader: async () => {\n * // ...return [{value: 'a'}, {value: 'b'}]\n * },\n * }),\n * },\n * })\n * ```\n *\n * @public\n */\nexport function createAsyncListInput(\n options: AsyncListInputOptions,\n): (props: StringInputProps) => JSX.Element {\n return function AsyncListInput(props: StringInputProps): JSX.Element {\n 'use memo'\n return <AsyncList {...props} options={options} />\n }\n}\n","import {defineType, type StringDefinition} from 'sanity'\n\nimport {createAsyncListInput} from '../components/async-list'\nimport type {AsyncListPluginConfig} from '../types'\n\nexport const asyncListType = (config: AsyncListPluginConfig): StringDefinition =>\n defineType({\n name: config?.schemaType,\n type: 'string',\n components: {\n input: createAsyncListInput(config),\n },\n })\n","import type {SchemaTypeDefinition} from 'sanity'\n\nimport type {AsyncListPluginConfig} from '../types'\nimport {asyncListType} from './async-list'\n\nexport const schema = (config: AsyncListPluginConfig): {types: SchemaTypeDefinition[]} => {\n return {types: [asyncListType(config)]}\n}\n","import '@sanity/ui/styles.css'\nimport {definePlugin} from 'sanity'\n\nimport {AsyncList, createAsyncListInput} from './components/async-list'\nimport {schema} from './schema-types'\nimport type {AsyncListPluginConfig} from './types'\n\nexport {AsyncList, createAsyncListInput}\nexport type {AsyncListInputProps} from './components/async-list'\nexport type {AsyncListInputOptions, AsyncListPluginConfig} from './types'\n\n/**\n * Usage in `sanity.config.ts` (or .js)\n *\n * ```ts\n * import {defineConfig} from 'sanity'\n * import {asyncList} from '@sanity/sanity-plugin-async-list'\n *\n * export default defineConfig({\n * // ...\n * plugins: [asyncList()],\n * })\n * ```\n */\nexport const asyncList = definePlugin<AsyncListPluginConfig>((config) => {\n if (!config.schemaType) {\n throw new Error('schemaType required by async-list plugin')\n }\n return {\n name: 'sanity-plugin-async-list',\n schema: schema(config),\n }\n})\n"],"mappings":"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"}
\ No newline at end of file