sanity-plugin-studio-smartling
5.0.185.0.19
~
Modified (3 files)
Index: package/package.json
===================================================================
--- package/package.json
+++ package/package.json
@@ -1,7 +1,7 @@
{
"name": "sanity-plugin-studio-smartling",
- "version": "5.0.18",
+ "version": "5.0.19",
"description": "This plugin provides an in-studio integration with Smartling. It allows your editors to send any document to Smartling with the click of a button, monitor ongoing translations, and import partial or complete translations back into the studio.",
"keywords": [
"sanity",
"sanity-plugin"
@@ -26,19 +26,19 @@
".": "./dist/index.js",
"./package.json": "./package.json"
},
"dependencies": {
- "sanity-translations-tab": "^6.1.21"
+ "sanity-translations-tab": "^6.1.22"
},
"devDependencies": {
"@sanity/tsconfig": "^2.2.1",
- "@sanity/tsdown-config": "^0.26.0",
- "@types/react": "^19.2.18",
- "@types/react-dom": "^19.2.4",
- "oxc-transform-react": "^0.145.0",
- "react": "^19.2.8",
- "react-dom": "^19.2.8",
- "sanity": "^6.10.1",
+ "@sanity/tsdown-config": "^0.27.1",
+ "@types/react": "^19.3.0",
+ "@types/react-dom": "^19.3.0",
+ "oxc-transform-react": "^0.147.0",
+ "react": "^19.3.0",
+ "react-dom": "^19.3.0",
+ "sanity": "^6.16.0",
"styled-components": "^6.5.3",
"tsdown": "^0.22.14"
},
"peerDependencies": { Index: package/dist/index.d.ts.map
===================================================================
--- package/dist/index.d.ts.map
+++ package/dist/index.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"index.d.ts","names":[],"sources":["../src/adapter/index.ts","../src/index.ts"],"mappings":";cAOa,kBAAkB;cCmBzB,4BAA4B;cAK5B,2BAA2B;cAK3B,yBAAyB"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","names":["TranslationsTabConfigOptions"],"sources":["../src/adapter/index.ts","../src/index.ts"],"mappings":";cAOa,kBAAkB;cCmBzB,4BAA4BA;cAK5B,2BAA2BA;cAK3B,yBAAyBA"}
\ No newline at end of file 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/adapter/helpers.ts","../src/adapter/getTranslationTask.ts","../src/adapter/createTask.ts","../src/adapter/getLocales.ts","../src/adapter/getTranslation.ts","../src/adapter/index.ts","../src/index.ts"],"sourcesContent":["import type {Secrets} from 'sanity-translations-tab'\n\nexport const authenticate = (secrets: Secrets): Promise<string> => {\n const url = 'https://api.smartling.com/auth-api/v2/authenticate'\n const headers = {\n 'content-type': 'application/json',\n 'X-URL': url,\n }\n const {secret, proxy} = secrets\n if (!secret || !proxy) {\n throw new Error(\n 'The Smartling adapter requires a secret key and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n // `secret` is already a JSON string (e.g. `{\"userIdentifier\":\"…\",\"userSecret\":\"…\"}`),\n // so it is sent as-is. Wrapping it in `JSON.stringify` would double-encode it into a\n // quoted string and break authentication with proxies that forward the body verbatim.\n return fetch(proxy, {\n headers,\n method: 'POST',\n body: secret,\n })\n .then((res) => res.json())\n .then((res) => {\n const accessToken = res?.response?.data?.accessToken\n if (!accessToken) {\n const errMsg =\n res?.response?.errors?.[0]?.message ||\n 'Could not authenticate with Smartling. Please check your secret key and proxy URL, per the plugin documentation.'\n throw new Error(errMsg)\n }\n return accessToken\n })\n}\n\nexport const getHeaders = (url: string, accessToken: string): Record<string, string> => ({\n 'Authorization': `Bearer ${accessToken}`,\n 'X-URL': url,\n})\n\nexport const findExistingJob = async (\n documentId: string,\n secrets: Secrets,\n accessToken: string,\n): Promise<string> => {\n const {project, proxy} = secrets\n if (!project || !proxy) {\n throw new Error(\n 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n const url = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs?jobName=${documentId}`\n //first, try fetching from name resolution\n let items = await fetch(proxy, {\n headers: getHeaders(url, accessToken),\n })\n .then((res) => res.json())\n .then((res) => res?.response?.data?.items)\n\n if (!items || !items.length) {\n //if that fails, try fetching by fileUri and check the referenceNumber\n const refUrl = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs/search`\n items = await fetch(proxy, {\n headers: {\n ...getHeaders(refUrl, accessToken),\n 'content-type': 'application/json',\n },\n method: 'POST',\n body: JSON.stringify({\n fileUris: [documentId],\n }),\n })\n .then((res) => res.json())\n .then((res) => res?.response?.data?.items)\n }\n\n if (items?.length) {\n //smartling will fuzzy match job names. We need to be precise.\n const correctJob = items\n .filter((item: {jobStatus: string}) => item.jobStatus !== 'DELETED')\n .find(\n (item: {jobName: string; referenceNumber: string}) =>\n (item.jobName && item.jobName === documentId) ||\n (item.referenceNumber && item.referenceNumber === documentId),\n )\n\n if (correctJob) {\n return correctJob.translationJobUid\n }\n }\n return ''\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {authenticate, getHeaders, findExistingJob} from './helpers'\n\ninterface WorkflowProgressItem {\n workflowStepSummaryReportItemList: {\n wordCount: number\n }[]\n}\n\ninterface SmartlingProgressItem {\n targetLocaleId: string\n progress: {\n percentComplete: number\n totalWordCount: number\n }\n workflowProgressReportList: WorkflowProgressItem[]\n}\n\nexport const getTranslationTask: Adapter['getTranslationTask'] = async (\n documentId: string,\n secrets: Secrets | null,\n) => {\n if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {\n return {\n documentId,\n taskId: documentId,\n locales: [],\n }\n }\n\n const {project, proxy} = secrets\n\n const accessToken = await authenticate(secrets)\n const taskId = await findExistingJob(documentId, secrets, accessToken)\n if (!taskId) {\n return {\n documentId,\n taskId: documentId,\n locales: [],\n }\n }\n\n const progressUrl = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs/${taskId}/progress`\n const smartlingTask = await fetch(proxy, {\n method: 'GET',\n headers: getHeaders(progressUrl, accessToken),\n })\n .then((res) => res.json())\n .then((res) => res.response.data)\n\n let locales = []\n if (smartlingTask && smartlingTask.contentProgressReport) {\n locales = smartlingTask.contentProgressReport.map((item: SmartlingProgressItem) => {\n let progress = item.progress ? item.progress.percentComplete : 0\n //default to the first workflow -- it's likely what is being used\n const progressItem = item.workflowProgressReportList?.[0]\n if (progressItem && item.progress) {\n //this is a list of the various steps in the workflow\n if (\n progressItem.workflowStepSummaryReportItemList &&\n progressItem.workflowStepSummaryReportItemList.length > 1\n ) {\n //get the last step in the workflow -- usually \"published\"\n const lastStep = progressItem.workflowStepSummaryReportItemList.at(-1)\n //get the percentage of how many words have reached the last step\n //guard against a 0 totalWordCount (e.g. empty documents) which would\n //otherwise produce NaN/Infinity and surface as an invalid percentage\n if (lastStep && lastStep.wordCount >= 0 && item.progress.totalWordCount > 0) {\n progress = Math.floor((lastStep.wordCount / item.progress.totalWordCount) * 100)\n }\n }\n }\n return {\n localeId: item.targetLocaleId,\n progress,\n }\n })\n }\n\n return {\n documentId,\n locales,\n //since our download is tied to document id for smartling, keep track of it as a task\n taskId: documentId,\n linkToVendorTask: `https://dashboard.smartling.com/app/projects/${project}/account-jobs/${project}:${taskId}`,\n }\n}\n","import type {Adapter, Secrets, SerializedDocument} from 'sanity-translations-tab'\n\nimport {getTranslationTask} from './getTranslationTask'\nimport {authenticate, getHeaders, findExistingJob} from './helpers'\n\nconst createJob = (\n jobName: string,\n secrets: Secrets,\n localeIds: string[],\n accessToken: string,\n documentId: string,\n) => {\n const {project, proxy} = secrets\n if (!project || !proxy) {\n throw new Error(\n 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n\n const url = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs`\n return fetch(proxy, {\n method: 'POST',\n headers: {\n ...getHeaders(url, accessToken),\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n jobName,\n targetLocaleIds: localeIds,\n referenceNumber: documentId,\n }),\n })\n .then((res) => res.json())\n .then((res) => res.response.data.translationJobUid)\n}\n\n/* we're using batches here because it eliminates some\n * new string authorization issues for updating existing jobs,\n * and is able to be used for new bulk\n * job functionality.\n */\n\nconst createJobBatch = (\n jobId: string,\n secrets: Secrets,\n documentId: string,\n accessToken: string,\n localeIds: string[],\n workflowUid?: string,\n) => {\n const {project, proxy} = secrets\n if (!project || !proxy) {\n throw new Error(\n 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n const url = `https://api.smartling.com/job-batches-api/v2/projects/${project}/batches`\n const reqBody: {\n authorize: boolean\n translationJobUid: string\n fileUris: string[]\n localeWorkflows?: {targetLocaleId: string; workflowUid: string}[]\n } = {\n authorize: true,\n translationJobUid: jobId,\n fileUris: [documentId],\n }\n\n if (workflowUid) {\n reqBody.localeWorkflows = localeIds.map((l) => ({\n targetLocaleId: l,\n workflowUid,\n }))\n }\n\n return fetch(proxy, {\n method: 'POST',\n headers: {\n ...getHeaders(url, accessToken),\n 'content-type': 'application/json',\n },\n body: JSON.stringify(reqBody),\n })\n .then((res) => res.json())\n .then((res) => res.response.data.batchUid)\n}\n\nconst uploadFileToBatch = (\n batchUid: string,\n documentId: string,\n document: SerializedDocument,\n secrets: Secrets,\n localeIds: string[],\n accessToken: string,\n callbackUrl?: string,\n) => {\n const {project, proxy} = secrets\n if (!project || !proxy) {\n throw new Error(\n 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n const url = `https://api.smartling.com/job-batches-api/v2/projects/${project}/batches/${batchUid}/file`\n const formData = new FormData()\n formData.append('fileUri', documentId)\n formData.append('fileType', 'html')\n formData.append('file', new Blob([document.content]), `${document.name}.html`)\n localeIds.forEach((localeId) => formData.append('localeIdsToAuthorize[]', localeId))\n if (callbackUrl) {\n formData.append('callbackUrl', callbackUrl)\n }\n\n return fetch(proxy, {\n method: 'POST',\n headers: getHeaders(url, accessToken),\n body: formData,\n }).then((res) => res.json())\n}\n\nexport const createTask: Adapter['createTask'] = async (\n documentId: string,\n document: SerializedDocument,\n localeIds: string[],\n secrets: Secrets | null,\n workflowUid?: string,\n callbackUrl?: string,\n) => {\n if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {\n throw new Error(\n 'The Smartling adapter requires a project ID, a secret key, and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n\n const accessToken = await authenticate(secrets)\n\n let taskId = await findExistingJob(document.name, secrets, accessToken)\n if (!taskId) {\n taskId = await createJob(document.name, secrets, localeIds, accessToken, documentId)\n }\n\n const batchUid = await createJobBatch(\n taskId,\n secrets,\n documentId,\n accessToken,\n localeIds,\n workflowUid,\n )\n await uploadFileToBatch(\n batchUid,\n documentId,\n document,\n secrets,\n localeIds,\n accessToken,\n callbackUrl,\n )\n\n return getTranslationTask(documentId, secrets)\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {authenticate, getHeaders} from './helpers'\n\nexport const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {\n if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {\n return []\n }\n const {project, proxy} = secrets\n const url = `https://api.smartling.com/projects-api/v2/projects/${project}`\n const accessToken = await authenticate(secrets)\n return fetch(proxy, {\n method: 'GET',\n headers: getHeaders(url, accessToken),\n })\n .then((res) => res.json())\n .then((res) => res.response.data.targetLocales)\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {authenticate, getHeaders} from './helpers'\n\nexport const getTranslation: Adapter['getTranslation'] = async (\n taskId: string,\n localeId: string,\n secrets: Secrets | null,\n) => {\n if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {\n throw new Error(\n 'The Smartling adapter requires a project ID, a secret key, and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n\n const {project, proxy} = secrets\n\n const url = `https://api.smartling.com/files-api/v2/projects/${project}/locales/${localeId}/file?fileUri=${taskId}&retrievalType=pending`\n const accessToken = await authenticate(secrets)\n const translatedHTML = await fetch(proxy, {\n method: 'GET',\n headers: getHeaders(url, accessToken),\n })\n .then((res) => res.json())\n .then((res) => {\n if (res.body) {\n return res.body\n } else if (res.response?.errors) {\n const errMsg =\n res.response.errors[0]?.message || 'Error retrieving translation from Smartling'\n throw new Error(errMsg)\n }\n return ''\n })\n\n return translatedHTML\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 SmartlingAdapter: Adapter = {\n getLocales,\n getTranslationTask,\n createTask,\n getTranslation,\n}\n","import {\n baseDocumentLevelConfig,\n baseFieldLevelConfig,\n legacyDocumentLevelConfig as baseLegacyDocumentLevelConfig,\n} from 'sanity-translations-tab'\nimport type {TranslationsTabConfigOptions} from 'sanity-translations-tab'\n\nimport {SmartlingAdapter} from './adapter'\n\nexport {\n BaseDocumentDeserializer,\n BaseDocumentMerger,\n BaseDocumentSerializer,\n customSerializers,\n defaultStopTypes,\n documentLevelPatch,\n fieldLevelPatch,\n findLatestDraft,\n legacyDocumentLevelPatch,\n TranslationsTab,\n} from 'sanity-translations-tab'\nexport type {\n TranslationFunctionContext,\n TranslationsTabConfigOptions,\n} from 'sanity-translations-tab'\n\nconst defaultDocumentLevelConfig: TranslationsTabConfigOptions = {\n ...baseDocumentLevelConfig,\n adapter: SmartlingAdapter,\n}\n\nconst legacyDocumentLevelConfig: TranslationsTabConfigOptions = {\n ...baseLegacyDocumentLevelConfig,\n adapter: SmartlingAdapter,\n}\n\nconst defaultFieldLevelConfig: TranslationsTabConfigOptions = {\n ...baseFieldLevelConfig,\n adapter: SmartlingAdapter,\n}\n\nexport {\n defaultDocumentLevelConfig,\n defaultFieldLevelConfig,\n legacyDocumentLevelConfig,\n SmartlingAdapter,\n}\n"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"index.js","names":["baseLegacyDocumentLevelConfig","\"Authorization\""],"sources":["../src/adapter/helpers.ts","../src/adapter/getTranslationTask.ts","../src/adapter/createTask.ts","../src/adapter/getLocales.ts","../src/adapter/getTranslation.ts","../src/adapter/index.ts","../src/index.ts"],"sourcesContent":["import type {Secrets} from 'sanity-translations-tab'\n\nexport const authenticate = (secrets: Secrets): Promise<string> => {\n const url = 'https://api.smartling.com/auth-api/v2/authenticate'\n const headers = {\n 'content-type': 'application/json',\n 'X-URL': url,\n }\n const {secret, proxy} = secrets\n if (!secret || !proxy) {\n throw new Error(\n 'The Smartling adapter requires a secret key and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n // `secret` is already a JSON string (e.g. `{\"userIdentifier\":\"…\",\"userSecret\":\"…\"}`),\n // so it is sent as-is. Wrapping it in `JSON.stringify` would double-encode it into a\n // quoted string and break authentication with proxies that forward the body verbatim.\n return fetch(proxy, {\n headers,\n method: 'POST',\n body: secret,\n })\n .then((res) => res.json())\n .then((res) => {\n const accessToken = res?.response?.data?.accessToken\n if (!accessToken) {\n const errMsg =\n res?.response?.errors?.[0]?.message ||\n 'Could not authenticate with Smartling. Please check your secret key and proxy URL, per the plugin documentation.'\n throw new Error(errMsg)\n }\n return accessToken\n })\n}\n\nexport const getHeaders = (url: string, accessToken: string): Record<string, string> => ({\n 'Authorization': `Bearer ${accessToken}`,\n 'X-URL': url,\n})\n\nexport const findExistingJob = async (\n documentId: string,\n secrets: Secrets,\n accessToken: string,\n): Promise<string> => {\n const {project, proxy} = secrets\n if (!project || !proxy) {\n throw new Error(\n 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n const url = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs?jobName=${documentId}`\n //first, try fetching from name resolution\n let items = await fetch(proxy, {\n headers: getHeaders(url, accessToken),\n })\n .then((res) => res.json())\n .then((res) => res?.response?.data?.items)\n\n if (!items || !items.length) {\n //if that fails, try fetching by fileUri and check the referenceNumber\n const refUrl = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs/search`\n items = await fetch(proxy, {\n headers: {\n ...getHeaders(refUrl, accessToken),\n 'content-type': 'application/json',\n },\n method: 'POST',\n body: JSON.stringify({\n fileUris: [documentId],\n }),\n })\n .then((res) => res.json())\n .then((res) => res?.response?.data?.items)\n }\n\n if (items?.length) {\n //smartling will fuzzy match job names. We need to be precise.\n const correctJob = items\n .filter((item: {jobStatus: string}) => item.jobStatus !== 'DELETED')\n .find(\n (item: {jobName: string; referenceNumber: string}) =>\n (item.jobName && item.jobName === documentId) ||\n (item.referenceNumber && item.referenceNumber === documentId),\n )\n\n if (correctJob) {\n return correctJob.translationJobUid\n }\n }\n return ''\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {authenticate, getHeaders, findExistingJob} from './helpers'\n\ninterface WorkflowProgressItem {\n workflowStepSummaryReportItemList: {\n wordCount: number\n }[]\n}\n\ninterface SmartlingProgressItem {\n targetLocaleId: string\n progress: {\n percentComplete: number\n totalWordCount: number\n }\n workflowProgressReportList: WorkflowProgressItem[]\n}\n\nexport const getTranslationTask: Adapter['getTranslationTask'] = async (\n documentId: string,\n secrets: Secrets | null,\n) => {\n if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {\n return {\n documentId,\n taskId: documentId,\n locales: [],\n }\n }\n\n const {project, proxy} = secrets\n\n const accessToken = await authenticate(secrets)\n const taskId = await findExistingJob(documentId, secrets, accessToken)\n if (!taskId) {\n return {\n documentId,\n taskId: documentId,\n locales: [],\n }\n }\n\n const progressUrl = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs/${taskId}/progress`\n const smartlingTask = await fetch(proxy, {\n method: 'GET',\n headers: getHeaders(progressUrl, accessToken),\n })\n .then((res) => res.json())\n .then((res) => res.response.data)\n\n let locales = []\n if (smartlingTask && smartlingTask.contentProgressReport) {\n locales = smartlingTask.contentProgressReport.map((item: SmartlingProgressItem) => {\n let progress = item.progress ? item.progress.percentComplete : 0\n //default to the first workflow -- it's likely what is being used\n const progressItem = item.workflowProgressReportList?.[0]\n if (progressItem && item.progress) {\n //this is a list of the various steps in the workflow\n if (\n progressItem.workflowStepSummaryReportItemList &&\n progressItem.workflowStepSummaryReportItemList.length > 1\n ) {\n //get the last step in the workflow -- usually \"published\"\n const lastStep = progressItem.workflowStepSummaryReportItemList.at(-1)\n //get the percentage of how many words have reached the last step\n //guard against a 0 totalWordCount (e.g. empty documents) which would\n //otherwise produce NaN/Infinity and surface as an invalid percentage\n if (lastStep && lastStep.wordCount >= 0 && item.progress.totalWordCount > 0) {\n progress = Math.floor((lastStep.wordCount / item.progress.totalWordCount) * 100)\n }\n }\n }\n return {\n localeId: item.targetLocaleId,\n progress,\n }\n })\n }\n\n return {\n documentId,\n locales,\n //since our download is tied to document id for smartling, keep track of it as a task\n taskId: documentId,\n linkToVendorTask: `https://dashboard.smartling.com/app/projects/${project}/account-jobs/${project}:${taskId}`,\n }\n}\n","import type {Adapter, Secrets, SerializedDocument} from 'sanity-translations-tab'\n\nimport {getTranslationTask} from './getTranslationTask'\nimport {authenticate, getHeaders, findExistingJob} from './helpers'\n\nconst createJob = (\n jobName: string,\n secrets: Secrets,\n localeIds: string[],\n accessToken: string,\n documentId: string,\n) => {\n const {project, proxy} = secrets\n if (!project || !proxy) {\n throw new Error(\n 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n\n const url = `https://api.smartling.com/jobs-api/v3/projects/${project}/jobs`\n return fetch(proxy, {\n method: 'POST',\n headers: {\n ...getHeaders(url, accessToken),\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n jobName,\n targetLocaleIds: localeIds,\n referenceNumber: documentId,\n }),\n })\n .then((res) => res.json())\n .then((res) => res.response.data.translationJobUid)\n}\n\n/* we're using batches here because it eliminates some\n * new string authorization issues for updating existing jobs,\n * and is able to be used for new bulk\n * job functionality.\n */\n\nconst createJobBatch = (\n jobId: string,\n secrets: Secrets,\n documentId: string,\n accessToken: string,\n localeIds: string[],\n workflowUid?: string,\n) => {\n const {project, proxy} = secrets\n if (!project || !proxy) {\n throw new Error(\n 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n const url = `https://api.smartling.com/job-batches-api/v2/projects/${project}/batches`\n const reqBody: {\n authorize: boolean\n translationJobUid: string\n fileUris: string[]\n localeWorkflows?: {targetLocaleId: string; workflowUid: string}[]\n } = {\n authorize: true,\n translationJobUid: jobId,\n fileUris: [documentId],\n }\n\n if (workflowUid) {\n reqBody.localeWorkflows = localeIds.map((l) => ({\n targetLocaleId: l,\n workflowUid,\n }))\n }\n\n return fetch(proxy, {\n method: 'POST',\n headers: {\n ...getHeaders(url, accessToken),\n 'content-type': 'application/json',\n },\n body: JSON.stringify(reqBody),\n })\n .then((res) => res.json())\n .then((res) => res.response.data.batchUid)\n}\n\nconst uploadFileToBatch = (\n batchUid: string,\n documentId: string,\n document: SerializedDocument,\n secrets: Secrets,\n localeIds: string[],\n accessToken: string,\n callbackUrl?: string,\n) => {\n const {project, proxy} = secrets\n if (!project || !proxy) {\n throw new Error(\n 'The Smartling adapter requires a Smartling project identifier and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n const url = `https://api.smartling.com/job-batches-api/v2/projects/${project}/batches/${batchUid}/file`\n const formData = new FormData()\n formData.append('fileUri', documentId)\n formData.append('fileType', 'html')\n formData.append('file', new Blob([document.content]), `${document.name}.html`)\n localeIds.forEach((localeId) => formData.append('localeIdsToAuthorize[]', localeId))\n if (callbackUrl) {\n formData.append('callbackUrl', callbackUrl)\n }\n\n return fetch(proxy, {\n method: 'POST',\n headers: getHeaders(url, accessToken),\n body: formData,\n }).then((res) => res.json())\n}\n\nexport const createTask: Adapter['createTask'] = async (\n documentId: string,\n document: SerializedDocument,\n localeIds: string[],\n secrets: Secrets | null,\n workflowUid?: string,\n callbackUrl?: string,\n) => {\n if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {\n throw new Error(\n 'The Smartling adapter requires a project ID, a secret key, and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n\n const accessToken = await authenticate(secrets)\n\n let taskId = await findExistingJob(document.name, secrets, accessToken)\n if (!taskId) {\n taskId = await createJob(document.name, secrets, localeIds, accessToken, documentId)\n }\n\n const batchUid = await createJobBatch(\n taskId,\n secrets,\n documentId,\n accessToken,\n localeIds,\n workflowUid,\n )\n await uploadFileToBatch(\n batchUid,\n documentId,\n document,\n secrets,\n localeIds,\n accessToken,\n callbackUrl,\n )\n\n return getTranslationTask(documentId, secrets)\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {authenticate, getHeaders} from './helpers'\n\nexport const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {\n if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {\n return []\n }\n const {project, proxy} = secrets\n const url = `https://api.smartling.com/projects-api/v2/projects/${project}`\n const accessToken = await authenticate(secrets)\n return fetch(proxy, {\n method: 'GET',\n headers: getHeaders(url, accessToken),\n })\n .then((res) => res.json())\n .then((res) => res.response.data.targetLocales)\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {authenticate, getHeaders} from './helpers'\n\nexport const getTranslation: Adapter['getTranslation'] = async (\n taskId: string,\n localeId: string,\n secrets: Secrets | null,\n) => {\n if (!secrets?.project || !secrets?.secret || !secrets?.proxy) {\n throw new Error(\n 'The Smartling adapter requires a project ID, a secret key, and a proxy URL. Please check your secrets document in this dataset, per the plugin documentation.',\n )\n }\n\n const {project, proxy} = secrets\n\n const url = `https://api.smartling.com/files-api/v2/projects/${project}/locales/${localeId}/file?fileUri=${taskId}&retrievalType=pending`\n const accessToken = await authenticate(secrets)\n const translatedHTML = await fetch(proxy, {\n method: 'GET',\n headers: getHeaders(url, accessToken),\n })\n .then((res) => res.json())\n .then((res) => {\n if (res.body) {\n return res.body\n } else if (res.response?.errors) {\n const errMsg =\n res.response.errors[0]?.message || 'Error retrieving translation from Smartling'\n throw new Error(errMsg)\n }\n return ''\n })\n\n return translatedHTML\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 SmartlingAdapter: Adapter = {\n getLocales,\n getTranslationTask,\n createTask,\n getTranslation,\n}\n","import {\n baseDocumentLevelConfig,\n baseFieldLevelConfig,\n legacyDocumentLevelConfig as baseLegacyDocumentLevelConfig,\n} from 'sanity-translations-tab'\nimport type {TranslationsTabConfigOptions} from 'sanity-translations-tab'\n\nimport {SmartlingAdapter} from './adapter'\n\nexport {\n BaseDocumentDeserializer,\n BaseDocumentMerger,\n BaseDocumentSerializer,\n customSerializers,\n defaultStopTypes,\n documentLevelPatch,\n fieldLevelPatch,\n findLatestDraft,\n legacyDocumentLevelPatch,\n TranslationsTab,\n} from 'sanity-translations-tab'\nexport type {\n TranslationFunctionContext,\n TranslationsTabConfigOptions,\n} from 'sanity-translations-tab'\n\nconst defaultDocumentLevelConfig: TranslationsTabConfigOptions = {\n ...baseDocumentLevelConfig,\n adapter: SmartlingAdapter,\n}\n\nconst legacyDocumentLevelConfig: TranslationsTabConfigOptions = {\n ...baseLegacyDocumentLevelConfig,\n adapter: SmartlingAdapter,\n}\n\nconst defaultFieldLevelConfig: TranslationsTabConfigOptions = {\n ...baseFieldLevelConfig,\n adapter: SmartlingAdapter,\n}\n\nexport {\n defaultDocumentLevelConfig,\n defaultFieldLevelConfig,\n legacyDocumentLevelConfig,\n SmartlingAdapter,\n}\n"],"mappings":"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"}
\ No newline at end of file