sanity-plugin-transifex

5.1.215.1.22
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":[],"sources":["../src/transifexAdapter/helpers.ts","../src/transifexAdapter/getLocales.ts","../src/transifexAdapter/getTranslationTask.ts","../src/transifexAdapter/createTask.ts","../src/transifexAdapter/getTranslation.ts","../src/transifexAdapter/index.ts","../src/index.ts"],"sourcesContent":["import type {Secrets} from 'sanity-translations-tab'\n\nexport const baseTransifexUrl = 'https://rest.api.transifex.com'\n\nexport const getHeaders = (secrets: Secrets | null): Record<string, string> => ({\n  'Authorization': `Bearer ${secrets?.token}`,\n  'Content-Type': 'application/vnd.api+json',\n})\n\nexport const projOrgSlug = (secrets: Secrets | null): string =>\n  `o:${secrets?.organization}:p:${secrets?.project}`\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nexport const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {\n  let locales = []\n  if (secrets) {\n    const response = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {\n      headers: getHeaders(secrets),\n    })\n\n    if (!response.ok) {\n      throw Error(`Failed to retrieve locales from Transifex. Status: ${response.status}`)\n    }\n\n    locales = await response.json().then((res) =>\n      res.data.map((lang: Record<string, any>) => ({\n        enabled: true,\n        description: lang['attributes']['name'],\n        localeId: lang['attributes']['code'],\n      })),\n    )\n  }\n  return locales\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {getLocales} from './getLocales'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nexport const getTranslationTask: Adapter['getTranslationTask'] = async (\n  documentId: string,\n  secrets: Secrets | null,\n) => {\n  if (!documentId || !secrets) {\n    return {\n      taskId: documentId,\n      documentId: documentId,\n      locales: [],\n    }\n  }\n  const projectFilter = `filter[project]=${projOrgSlug(secrets)}`\n  const resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`\n  const task = await fetch(\n    `${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`,\n    {headers: getHeaders(secrets)},\n  )\n    .then((res) => {\n      if (res.ok) {\n        return res.json()\n      }\n      //normal -- just means that this task doesn't exist yet.\n      else if (res.status === 404) {\n        return {data: []}\n      }\n      throw Error(`Failed to retrieve tasks from Transifex. Status: ${res.status}`)\n    })\n    .then((res) => ({\n      taskId: `${projOrgSlug(secrets)}:r:${documentId}`,\n      documentId: documentId,\n      locales: res.data.map((locale: Record<string, any>) => {\n        const reviewedStrings = Number(locale['attributes']['reviewed_strings'])\n        const totalStrings = Number(locale['attributes']['total_strings'])\n\n        return {\n          localeId: locale['relationships']['language']['data']['id'].split(':')[1],\n          progress:\n            Number.isFinite(reviewedStrings) && Number.isFinite(totalStrings) && totalStrings > 0\n              ? Math.floor((100 * reviewedStrings) / totalStrings)\n              : 0,\n        }\n      }),\n    }))\n\n  const locales = await getLocales(secrets)\n  const localeIds = locales.map((l: Record<string, any>) => l['localeId'])\n  const validLocales = task.locales.filter((locale: Record<string, any>) =>\n    localeIds.find((id: string) => id === locale['localeId']),\n  )\n  task.locales = validLocales\n\n  return task\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {getTranslationTask} from './getTranslationTask'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nconst createResource = async (\n  doc: Record<string, any>,\n  documentId: string,\n  secrets: Secrets | null,\n) => {\n  const resourceCreateBody = {\n    data: {\n      attributes: {\n        accept_translations: true,\n        name: doc['name'],\n        slug: documentId,\n      },\n      relationships: {\n        i18n_format: {\n          data: {\n            id: 'HTML_FRAGMENT',\n            type: 'i18n_formats',\n          },\n        },\n        project: {\n          data: {\n            id: projOrgSlug(secrets),\n            type: 'projects',\n          },\n        },\n      },\n      type: 'resources',\n    },\n  }\n\n  const response = await fetch(`${baseTransifexUrl}/resources`, {\n    headers: getHeaders(secrets),\n    method: 'POST',\n    body: JSON.stringify(resourceCreateBody),\n  })\n\n  if (!response.ok) {\n    throw Error(`Failed to create Transifex resource. Status: ${response.status}`)\n  }\n\n  const res = await response.json()\n  const resourceId = res?.data?.id\n\n  if (!resourceId) {\n    throw Error('Failed to create Transifex resource. Missing resource id in response.')\n  }\n\n  return resourceId\n}\n\nexport const createTask: Adapter['createTask'] = async (\n  documentId: string,\n  document: Record<string, any>,\n  _localeIds: string[],\n  secrets: Secrets | null,\n) => {\n  if (!documentId || !secrets) {\n    throw Error('Missing documentId or Transifex secrets.')\n  }\n\n  const resourceResponse = await fetch(\n    `${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`,\n    {headers: getHeaders(secrets)},\n  )\n  let resourceId: string | null = null\n\n  if (resourceResponse.status === 404) {\n    resourceId = null\n  } else if (!resourceResponse.ok) {\n    throw Error(`Failed to retrieve Transifex resource. Status: ${resourceResponse.status}`)\n  } else {\n    const resource = await resourceResponse.json()\n    resourceId = resource.data ? resource.data.id : null\n  }\n\n  if (!resourceId) {\n    resourceId = await createResource(document, documentId, secrets)\n  }\n\n  const resourceUploadUrl = `${baseTransifexUrl}/resource_strings_async_uploads`\n  const resourceUploadBody = {\n    data: {\n      attributes: {\n        content: document['content'],\n        content_encoding: 'text',\n      },\n      relationships: {\n        resource: {\n          data: {\n            id: resourceId,\n            type: 'resources',\n          },\n        },\n      },\n      type: 'resource_strings_async_uploads',\n    },\n  }\n\n  const uploadResponse = await fetch(resourceUploadUrl, {\n    method: 'POST',\n    body: JSON.stringify(resourceUploadBody),\n    headers: getHeaders(secrets),\n  })\n\n  if (!uploadResponse.ok) {\n    throw Error(`Failed to upload resource strings to Transifex. Status: ${uploadResponse.status}`)\n  }\n\n  return getTranslationTask(documentId, secrets)\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {baseTransifexUrl, getHeaders} from './helpers'\n\nconst pollForFileDownloadLocation = async (\n  resourceDownloadUrl: string,\n  translationDownloadId: string,\n  headers: Record<string, any>,\n  retryCount = 0,\n  maxRetries = 20,\n): Promise<string> => {\n  const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {\n    headers: headers,\n  })\n\n  if (retryCount >= maxRetries) {\n    throw Error(\n      `Failed to retrieve download location for translation download ID ${translationDownloadId} after ${maxRetries} retries.`,\n    )\n  }\n\n  if (response.status === 500) {\n    //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n    console.info(\n      `Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`,\n    )\n    await new Promise((resolve) => setTimeout(resolve, 3000))\n    return pollForFileDownloadLocation(\n      resourceDownloadUrl,\n      translationDownloadId,\n      headers,\n      retryCount + 1,\n      maxRetries,\n    )\n  } else if (response.redirected) {\n    //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n    console.info(\n      `Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`,\n    )\n    return response.url\n  } else if (response.status === 200) {\n    //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n    console.info(\n      `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`,\n    )\n    await new Promise((resolve) => setTimeout(resolve, 3000))\n    return pollForFileDownloadLocation(\n      resourceDownloadUrl,\n      translationDownloadId,\n      headers,\n      retryCount + 1,\n      maxRetries,\n    )\n  } else if (response.status === 401 || response.status === 403) {\n    throw Error(\n      `Failed to retrieve download location for translation download ID ${translationDownloadId}. Status: ${response.status}`,\n    )\n  }\n  console.error(\n    `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`,\n  )\n  await new Promise((resolve) => setTimeout(resolve, 3000))\n  return pollForFileDownloadLocation(\n    resourceDownloadUrl,\n    translationDownloadId,\n    headers,\n    retryCount + 1,\n    maxRetries,\n  )\n}\n\nconst handleFileDownload = (url: string) => {\n  return fetch(url).then((res) => res.text())\n}\n\nexport const getTranslation: Adapter['getTranslation'] = async (\n  taskId: string,\n  localeId: string,\n  secrets: Secrets | null,\n) => {\n  if (!secrets) {\n    throw Error('Missing Transifex secrets.')\n  }\n\n  const resourceDownloadBody = {\n    data: {\n      attributes: {\n        content_encoding: 'text',\n      },\n      relationships: {\n        language: {\n          data: {\n            id: `l:${localeId}`,\n            type: 'languages',\n          },\n        },\n        resource: {\n          data: {\n            id: taskId,\n            type: 'resources',\n          },\n        },\n      },\n      type: 'resource_translations_async_downloads',\n    },\n  }\n\n  const resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`\n  const downloadResponse = await fetch(resourceDownloadUrl, {\n    headers: getHeaders(secrets),\n    method: 'POST',\n    body: JSON.stringify(resourceDownloadBody),\n  })\n\n  if (!downloadResponse.ok) {\n    throw Error(\n      `Failed to create translation download request in Transifex. Status: ${downloadResponse.status}`,\n    )\n  }\n\n  const download = await downloadResponse.json()\n  const translationDownloadId = download?.data?.id\n\n  if (!translationDownloadId) {\n    throw Error('Failed to create translation download request in Transifex. Missing download id.')\n  }\n\n  const headers = getHeaders(secrets)\n  const location = await pollForFileDownloadLocation(\n    resourceDownloadUrl,\n    translationDownloadId,\n    headers,\n  )\n  return handleFileDownload(location)\n}\n","import type {Adapter} from 'sanity-translations-tab'\n\nimport {createTask} from './createTask'\nimport {getLocales} from './getLocales'\nimport {getTranslation} from './getTranslation'\nimport {getTranslationTask} from './getTranslationTask'\n\nexport const TransifexAdapter: Adapter = {\n  getLocales,\n  getTranslationTask,\n  createTask,\n  getTranslation,\n}\n","import {\n  baseDocumentLevelConfig,\n  legacyDocumentLevelConfig as baseLegacyDocumentLevelConfig,\n  baseFieldLevelConfig,\n  baseI18nArrayConfig,\n} from 'sanity-translations-tab'\nimport type {Adapter, TranslationFunctionContext} from 'sanity-translations-tab'\n\nimport {TransifexAdapter} from './transifexAdapter'\n\nexport {\n  findLatestDraft,\n  BaseDocumentDeserializer,\n  BaseDocumentSerializer,\n  BaseDocumentMerger,\n  defaultStopTypes,\n  customSerializers,\n  legacyDocumentLevelPatch,\n  documentLevelPatch,\n  fieldLevelPatch,\n  i18nArrayPatch,\n  TranslationsTab,\n} from 'sanity-translations-tab'\n\ninterface ConfigOptions {\n  adapter: Adapter\n  secretsNamespace: string | null\n  exportForTranslation: (\n    id: string,\n    context: TranslationFunctionContext,\n  ) => Promise<Record<string, any>>\n  importTranslation: (\n    id: string,\n    localeId: string,\n    doc: string,\n    context: TranslationFunctionContext,\n  ) => Promise<void>\n}\nconst defaultDocumentLevelConfig: ConfigOptions = {\n  ...baseDocumentLevelConfig,\n  adapter: TransifexAdapter,\n  secretsNamespace: 'transifex',\n}\n\nconst legacyDocumentLevelConfig: ConfigOptions = {\n  ...baseLegacyDocumentLevelConfig,\n  adapter: TransifexAdapter,\n  secretsNamespace: 'transifex',\n}\n\nconst defaultFieldLevelConfig: ConfigOptions = {\n  ...baseFieldLevelConfig,\n  adapter: TransifexAdapter,\n  secretsNamespace: 'transifex',\n}\n\n//for documents using sanity-plugin-internationalized-array fields\nconst defaultI18nArrayConfig: ConfigOptions = {\n  ...baseI18nArrayConfig,\n  adapter: TransifexAdapter,\n  secretsNamespace: 'transifex',\n}\n\nexport {\n  TransifexAdapter,\n  defaultDocumentLevelConfig,\n  defaultFieldLevelConfig,\n  defaultI18nArrayConfig,\n  legacyDocumentLevelConfig,\n}\n"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"index.js","names":["baseLegacyDocumentLevelConfig","\"Authorization\"","\"attributes\"","\"name\"","\"code\"","\"reviewed_strings\"","\"total_strings\"","\"relationships\"","\"language\"","\"data\"","\"id\"","\"localeId\"","\"content\""],"sources":["../src/transifexAdapter/helpers.ts","../src/transifexAdapter/getLocales.ts","../src/transifexAdapter/getTranslationTask.ts","../src/transifexAdapter/createTask.ts","../src/transifexAdapter/getTranslation.ts","../src/transifexAdapter/index.ts","../src/index.ts"],"sourcesContent":["import type {Secrets} from 'sanity-translations-tab'\n\nexport const baseTransifexUrl = 'https://rest.api.transifex.com'\n\nexport const getHeaders = (secrets: Secrets | null): Record<string, string> => ({\n  'Authorization': `Bearer ${secrets?.token}`,\n  'Content-Type': 'application/vnd.api+json',\n})\n\nexport const projOrgSlug = (secrets: Secrets | null): string =>\n  `o:${secrets?.organization}:p:${secrets?.project}`\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nexport const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {\n  let locales = []\n  if (secrets) {\n    const response = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {\n      headers: getHeaders(secrets),\n    })\n\n    if (!response.ok) {\n      throw Error(`Failed to retrieve locales from Transifex. Status: ${response.status}`)\n    }\n\n    locales = await response.json().then((res) =>\n      res.data.map((lang: Record<string, any>) => ({\n        enabled: true,\n        description: lang['attributes']['name'],\n        localeId: lang['attributes']['code'],\n      })),\n    )\n  }\n  return locales\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {getLocales} from './getLocales'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nexport const getTranslationTask: Adapter['getTranslationTask'] = async (\n  documentId: string,\n  secrets: Secrets | null,\n) => {\n  if (!documentId || !secrets) {\n    return {\n      taskId: documentId,\n      documentId: documentId,\n      locales: [],\n    }\n  }\n  const projectFilter = `filter[project]=${projOrgSlug(secrets)}`\n  const resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`\n  const task = await fetch(\n    `${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`,\n    {headers: getHeaders(secrets)},\n  )\n    .then((res) => {\n      if (res.ok) {\n        return res.json()\n      }\n      //normal -- just means that this task doesn't exist yet.\n      else if (res.status === 404) {\n        return {data: []}\n      }\n      throw Error(`Failed to retrieve tasks from Transifex. Status: ${res.status}`)\n    })\n    .then((res) => ({\n      taskId: `${projOrgSlug(secrets)}:r:${documentId}`,\n      documentId: documentId,\n      locales: res.data.map((locale: Record<string, any>) => {\n        const reviewedStrings = Number(locale['attributes']['reviewed_strings'])\n        const totalStrings = Number(locale['attributes']['total_strings'])\n\n        return {\n          localeId: locale['relationships']['language']['data']['id'].split(':')[1],\n          progress:\n            Number.isFinite(reviewedStrings) && Number.isFinite(totalStrings) && totalStrings > 0\n              ? Math.floor((100 * reviewedStrings) / totalStrings)\n              : 0,\n        }\n      }),\n    }))\n\n  const locales = await getLocales(secrets)\n  const localeIds = locales.map((l: Record<string, any>) => l['localeId'])\n  const validLocales = task.locales.filter((locale: Record<string, any>) =>\n    localeIds.find((id: string) => id === locale['localeId']),\n  )\n  task.locales = validLocales\n\n  return task\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {getTranslationTask} from './getTranslationTask'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nconst createResource = async (\n  doc: Record<string, any>,\n  documentId: string,\n  secrets: Secrets | null,\n) => {\n  const resourceCreateBody = {\n    data: {\n      attributes: {\n        accept_translations: true,\n        name: doc['name'],\n        slug: documentId,\n      },\n      relationships: {\n        i18n_format: {\n          data: {\n            id: 'HTML_FRAGMENT',\n            type: 'i18n_formats',\n          },\n        },\n        project: {\n          data: {\n            id: projOrgSlug(secrets),\n            type: 'projects',\n          },\n        },\n      },\n      type: 'resources',\n    },\n  }\n\n  const response = await fetch(`${baseTransifexUrl}/resources`, {\n    headers: getHeaders(secrets),\n    method: 'POST',\n    body: JSON.stringify(resourceCreateBody),\n  })\n\n  if (!response.ok) {\n    throw Error(`Failed to create Transifex resource. Status: ${response.status}`)\n  }\n\n  const res = await response.json()\n  const resourceId = res?.data?.id\n\n  if (!resourceId) {\n    throw Error('Failed to create Transifex resource. Missing resource id in response.')\n  }\n\n  return resourceId\n}\n\nexport const createTask: Adapter['createTask'] = async (\n  documentId: string,\n  document: Record<string, any>,\n  _localeIds: string[],\n  secrets: Secrets | null,\n) => {\n  if (!documentId || !secrets) {\n    throw Error('Missing documentId or Transifex secrets.')\n  }\n\n  const resourceResponse = await fetch(\n    `${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`,\n    {headers: getHeaders(secrets)},\n  )\n  let resourceId: string | null = null\n\n  if (resourceResponse.status === 404) {\n    resourceId = null\n  } else if (!resourceResponse.ok) {\n    throw Error(`Failed to retrieve Transifex resource. Status: ${resourceResponse.status}`)\n  } else {\n    const resource = await resourceResponse.json()\n    resourceId = resource.data ? resource.data.id : null\n  }\n\n  if (!resourceId) {\n    resourceId = await createResource(document, documentId, secrets)\n  }\n\n  const resourceUploadUrl = `${baseTransifexUrl}/resource_strings_async_uploads`\n  const resourceUploadBody = {\n    data: {\n      attributes: {\n        content: document['content'],\n        content_encoding: 'text',\n      },\n      relationships: {\n        resource: {\n          data: {\n            id: resourceId,\n            type: 'resources',\n          },\n        },\n      },\n      type: 'resource_strings_async_uploads',\n    },\n  }\n\n  const uploadResponse = await fetch(resourceUploadUrl, {\n    method: 'POST',\n    body: JSON.stringify(resourceUploadBody),\n    headers: getHeaders(secrets),\n  })\n\n  if (!uploadResponse.ok) {\n    throw Error(`Failed to upload resource strings to Transifex. Status: ${uploadResponse.status}`)\n  }\n\n  return getTranslationTask(documentId, secrets)\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {baseTransifexUrl, getHeaders} from './helpers'\n\nconst pollForFileDownloadLocation = async (\n  resourceDownloadUrl: string,\n  translationDownloadId: string,\n  headers: Record<string, any>,\n  retryCount = 0,\n  maxRetries = 20,\n): Promise<string> => {\n  const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {\n    headers: headers,\n  })\n\n  if (retryCount >= maxRetries) {\n    throw Error(\n      `Failed to retrieve download location for translation download ID ${translationDownloadId} after ${maxRetries} retries.`,\n    )\n  }\n\n  if (response.status === 500) {\n    //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n    console.info(\n      `Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`,\n    )\n    await new Promise((resolve) => setTimeout(resolve, 3000))\n    return pollForFileDownloadLocation(\n      resourceDownloadUrl,\n      translationDownloadId,\n      headers,\n      retryCount + 1,\n      maxRetries,\n    )\n  } else if (response.redirected) {\n    //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n    console.info(\n      `Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`,\n    )\n    return response.url\n  } else if (response.status === 200) {\n    //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n    console.info(\n      `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`,\n    )\n    await new Promise((resolve) => setTimeout(resolve, 3000))\n    return pollForFileDownloadLocation(\n      resourceDownloadUrl,\n      translationDownloadId,\n      headers,\n      retryCount + 1,\n      maxRetries,\n    )\n  } else if (response.status === 401 || response.status === 403) {\n    throw Error(\n      `Failed to retrieve download location for translation download ID ${translationDownloadId}. Status: ${response.status}`,\n    )\n  }\n  console.error(\n    `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`,\n  )\n  await new Promise((resolve) => setTimeout(resolve, 3000))\n  return pollForFileDownloadLocation(\n    resourceDownloadUrl,\n    translationDownloadId,\n    headers,\n    retryCount + 1,\n    maxRetries,\n  )\n}\n\nconst handleFileDownload = (url: string) => {\n  return fetch(url).then((res) => res.text())\n}\n\nexport const getTranslation: Adapter['getTranslation'] = async (\n  taskId: string,\n  localeId: string,\n  secrets: Secrets | null,\n) => {\n  if (!secrets) {\n    throw Error('Missing Transifex secrets.')\n  }\n\n  const resourceDownloadBody = {\n    data: {\n      attributes: {\n        content_encoding: 'text',\n      },\n      relationships: {\n        language: {\n          data: {\n            id: `l:${localeId}`,\n            type: 'languages',\n          },\n        },\n        resource: {\n          data: {\n            id: taskId,\n            type: 'resources',\n          },\n        },\n      },\n      type: 'resource_translations_async_downloads',\n    },\n  }\n\n  const resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`\n  const downloadResponse = await fetch(resourceDownloadUrl, {\n    headers: getHeaders(secrets),\n    method: 'POST',\n    body: JSON.stringify(resourceDownloadBody),\n  })\n\n  if (!downloadResponse.ok) {\n    throw Error(\n      `Failed to create translation download request in Transifex. Status: ${downloadResponse.status}`,\n    )\n  }\n\n  const download = await downloadResponse.json()\n  const translationDownloadId = download?.data?.id\n\n  if (!translationDownloadId) {\n    throw Error('Failed to create translation download request in Transifex. Missing download id.')\n  }\n\n  const headers = getHeaders(secrets)\n  const location = await pollForFileDownloadLocation(\n    resourceDownloadUrl,\n    translationDownloadId,\n    headers,\n  )\n  return handleFileDownload(location)\n}\n","import type {Adapter} from 'sanity-translations-tab'\n\nimport {createTask} from './createTask'\nimport {getLocales} from './getLocales'\nimport {getTranslation} from './getTranslation'\nimport {getTranslationTask} from './getTranslationTask'\n\nexport const TransifexAdapter: Adapter = {\n  getLocales,\n  getTranslationTask,\n  createTask,\n  getTranslation,\n}\n","import {\n  baseDocumentLevelConfig,\n  legacyDocumentLevelConfig as baseLegacyDocumentLevelConfig,\n  baseFieldLevelConfig,\n  baseI18nArrayConfig,\n} from 'sanity-translations-tab'\nimport type {Adapter, TranslationFunctionContext} from 'sanity-translations-tab'\n\nimport {TransifexAdapter} from './transifexAdapter'\n\nexport {\n  findLatestDraft,\n  BaseDocumentDeserializer,\n  BaseDocumentSerializer,\n  BaseDocumentMerger,\n  defaultStopTypes,\n  customSerializers,\n  legacyDocumentLevelPatch,\n  documentLevelPatch,\n  fieldLevelPatch,\n  i18nArrayPatch,\n  TranslationsTab,\n} from 'sanity-translations-tab'\n\ninterface ConfigOptions {\n  adapter: Adapter\n  secretsNamespace: string | null\n  exportForTranslation: (\n    id: string,\n    context: TranslationFunctionContext,\n  ) => Promise<Record<string, any>>\n  importTranslation: (\n    id: string,\n    localeId: string,\n    doc: string,\n    context: TranslationFunctionContext,\n  ) => Promise<void>\n}\nconst defaultDocumentLevelConfig: ConfigOptions = {\n  ...baseDocumentLevelConfig,\n  adapter: TransifexAdapter,\n  secretsNamespace: 'transifex',\n}\n\nconst legacyDocumentLevelConfig: ConfigOptions = {\n  ...baseLegacyDocumentLevelConfig,\n  adapter: TransifexAdapter,\n  secretsNamespace: 'transifex',\n}\n\nconst defaultFieldLevelConfig: ConfigOptions = {\n  ...baseFieldLevelConfig,\n  adapter: TransifexAdapter,\n  secretsNamespace: 'transifex',\n}\n\n//for documents using sanity-plugin-internationalized-array fields\nconst defaultI18nArrayConfig: ConfigOptions = {\n  ...baseI18nArrayConfig,\n  adapter: TransifexAdapter,\n  secretsNamespace: 'transifex',\n}\n\nexport {\n  TransifexAdapter,\n  defaultDocumentLevelConfig,\n  defaultFieldLevelConfig,\n  defaultI18nArrayConfig,\n  legacyDocumentLevelConfig,\n}\n"],"mappings":"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"}
\ No newline at end of file