gt-sanity 2.0.8 → 2.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/index.js +257 -89
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +258 -90
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/src/components/TranslationsProvider.tsx +86 -28
  7. package/src/components/page/TranslationsTable.tsx +12 -4
  8. package/src/components/shared/LanguageStatus.tsx +3 -2
  9. package/src/components/shared/SingleDocumentView.tsx +12 -4
  10. package/src/components/tab/TranslationView.tsx +35 -6
  11. package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.test.ts +76 -0
  12. package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.ts +25 -1
  13. package/src/configuration/baseDocumentLevelConfig/helpers/createI18nDocAndPatchMetadata.ts +9 -4
  14. package/src/configuration/baseDocumentLevelConfig/helpers/createTranslationMetadata.ts +2 -1
  15. package/src/configuration/baseDocumentLevelConfig/helpers/getOrCreateTranslationMetadata.ts +7 -4
  16. package/src/configuration/baseDocumentLevelConfig/helpers/getTranslationMetadata.ts +2 -1
  17. package/src/configuration/utils/findLatestDraft.ts +3 -1
  18. package/src/sanity-api/findDocuments.ts +11 -5
  19. package/src/sanity-api/publishDocuments.ts +2 -1
  20. package/src/sanity-api/resolveRefs.ts +5 -4
  21. package/src/translation/checkTranslationStatus.ts +25 -6
  22. package/src/utils/__tests__/batchProcessor.test.ts +44 -0
  23. package/src/utils/__tests__/documentIds.test.ts +42 -0
  24. package/src/utils/__tests__/importUtils.test.ts +66 -0
  25. package/src/utils/batchProcessor.ts +40 -1
  26. package/src/utils/documentIds.ts +49 -0
  27. package/src/utils/importUtils.ts +15 -7
  28. package/src/utils/serialize.ts +2 -1
package/dist/index.js CHANGED
@@ -663,6 +663,28 @@ function createLocaleSuffix(localeId) {
663
663
  const normalized = localeId.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
664
664
  return normalized ? `-${normalized}` : "";
665
665
  }
666
+ function getPublishedId(documentId) {
667
+ return documentId.startsWith("drafts.") ? documentId.slice(7) : documentId;
668
+ }
669
+ function getDocumentPublishedId(document2) {
670
+ return getPublishedId(document2._id);
671
+ }
672
+ function dedupeDocumentsPreferDraft(documents) {
673
+ const byPublishedId = /* @__PURE__ */ new Map();
674
+ for (const document2 of documents) {
675
+ const publishedId = getDocumentPublishedId(document2);
676
+ (!byPublishedId.get(publishedId) || document2._id.startsWith("drafts.")) && byPublishedId.set(publishedId, document2);
677
+ }
678
+ return Array.from(byPublishedId.values());
679
+ }
680
+ function createTranslationStatusKey(branchId, documentId, versionId, localeId) {
681
+ const publishedId = getPublishedId(documentId);
682
+ return branchId ? `${branchId}:${publishedId}:${versionId}:${localeId}` : `${publishedId}:${versionId}:${localeId}`;
683
+ }
684
+ function createStableTranslationKey(branchId, documentId, localeId) {
685
+ const publishedId = getPublishedId(documentId);
686
+ return branchId ? `${branchId}:${publishedId}:${localeId}` : `${publishedId}:${localeId}`;
687
+ }
666
688
  function deserializeDocument(document2) {
667
689
  const deserializers = merge__default.default(
668
690
  { types: {} },
@@ -702,7 +724,7 @@ function stripIgnoredFields(document2, ignoreFields, dedupeFields) {
702
724
  if (fieldsToStrip.length === 0) return document2;
703
725
  const strippedDoc = JSON.parse(JSON.stringify(document2));
704
726
  return deleteMatchingFields(
705
- document2._id.replace("drafts.", ""),
727
+ getPublishedId(document2._id),
706
728
  strippedDoc,
707
729
  fieldsToStrip
708
730
  ), strippedDoc;
@@ -783,9 +805,19 @@ async function downloadTranslations(files, secrets, maxRetries = 3, retryDelay =
783
805
  async function checkTranslationStatus(fileQueryData, downloadStatus, secrets) {
784
806
  overrideConfig(secrets);
785
807
  try {
786
- const currentQueryData = fileQueryData.filter(
787
- (item) => !downloadStatus.downloaded.has(`${item.fileId}:${item.locale}`) && !downloadStatus.failed.has(`${item.fileId}:${item.locale}`) && !downloadStatus.skipped.has(`${item.fileId}:${item.locale}`)
788
- );
808
+ const currentQueryData = fileQueryData.filter((item) => {
809
+ const statusKey = createTranslationStatusKey(
810
+ item.branchId,
811
+ item.fileId,
812
+ item.versionId,
813
+ item.locale
814
+ ), stableKey = createStableTranslationKey(
815
+ item.branchId,
816
+ item.fileId,
817
+ item.locale
818
+ );
819
+ return !downloadStatus.downloaded.has(statusKey) && !downloadStatus.downloaded.has(stableKey) && !downloadStatus.failed.has(statusKey) && !downloadStatus.failed.has(stableKey) && !downloadStatus.skipped.has(statusKey) && !downloadStatus.skipped.has(stableKey);
820
+ });
789
821
  return currentQueryData.length === 0 ? !0 : ((await gt.queryFileData({
790
822
  translatedFiles: currentQueryData
791
823
  })).translatedFiles || []).filter(
@@ -796,7 +828,7 @@ async function checkTranslationStatus(fileQueryData, downloadStatus, secrets) {
796
828
  }
797
829
  }
798
830
  const findLatestDraft = (documentId, client) => {
799
- const query = "*[_id == $id || _id == $draftId]", params = { id: documentId, draftId: `drafts.${documentId}` };
831
+ const publishedId = getPublishedId(documentId), query = "*[_id == $id || _id == $draftId]", params = { id: publishedId, draftId: `drafts.${publishedId}` };
800
832
  return client.fetch(query, params).then(
801
833
  (docs) => docs.find((doc) => doc._id.startsWith("drafts.")) ?? docs[0]
802
834
  );
@@ -808,21 +840,25 @@ function randomKey() {
808
840
  return Math.random().toString(36).slice(2, 10);
809
841
  }
810
842
  async function createI18nDocAndPatchMetadata(sourceDocument, translatedDoc, localeId, client, translationMetadata, sourceDocumentId, languageField = "language") {
843
+ const publishedSourceDocumentId = getPublishedId(sourceDocumentId);
811
844
  translatedDoc[languageField] = localeId;
812
845
  const existingLocaleKey = translationMetadata.translations.find(
813
846
  (translation) => translation.language === localeId
814
847
  ), operation = existingLocaleKey ? "replace" : "after", location = existingLocaleKey ? `translations[language == "${localeId}"]` : "translations[-1]", { _updatedAt, _createdAt, ...rest } = translatedDoc, appliedDocument = applyDocuments(
815
- sourceDocumentId,
848
+ publishedSourceDocumentId,
816
849
  sourceDocument,
817
850
  rest,
818
851
  pluginConfig.getIgnoreFields(),
819
852
  pluginConfig.getSkipFields(),
820
853
  pluginConfig.getDedupeFields(),
821
854
  localeId
822
- ), isSingleton = pluginConfig.getSingletons().includes(sourceDocumentId);
855
+ ), isSingleton = pluginConfig.getSingletons().includes(publishedSourceDocumentId);
823
856
  let createDocumentPromise;
824
857
  if (isSingleton) {
825
- const translatedDocId = pluginConfig.getSingletonMapping()(sourceDocumentId, localeId);
858
+ const translatedDocId = pluginConfig.getSingletonMapping()(
859
+ publishedSourceDocumentId,
860
+ localeId
861
+ );
826
862
  createDocumentPromise = client.create({
827
863
  ...appliedDocument,
828
864
  _type: rest._type,
@@ -834,7 +870,7 @@ async function createI18nDocAndPatchMetadata(sourceDocument, translatedDoc, loca
834
870
  _type: rest._type,
835
871
  _id: "drafts."
836
872
  });
837
- const doc = await createDocumentPromise, _ref = doc._id.replace("drafts.", "");
873
+ const doc = await createDocumentPromise, _ref = getPublishedId(doc._id);
838
874
  await client.transaction().patch(
839
875
  translationMetadata._id,
840
876
  (p) => p.insert(operation, location, [
@@ -855,12 +891,12 @@ async function createI18nDocAndPatchMetadata(sourceDocument, translatedDoc, loca
855
891
  ).commit();
856
892
  }
857
893
  const getOrCreateTranslationMetadata = async (documentId, baseDocument, client, baseLanguage) => {
858
- const existingMetadata = await client.fetch(
894
+ const publishedId = getPublishedId(documentId), existingMetadata = await client.fetch(
859
895
  `*[
860
896
  _type == 'translation.metadata' &&
861
897
  translations[language == $baseLanguage][0].value._ref == $id
862
898
  ][0]`,
863
- { baseLanguage, id: documentId.replace("drafts.", "") }
899
+ { baseLanguage, id: publishedId }
864
900
  );
865
901
  if (existingMetadata)
866
902
  return existingMetadata;
@@ -870,7 +906,7 @@ const getOrCreateTranslationMetadata = async (documentId, baseDocument, client,
870
906
  _type: "internationalizedArrayReferenceValue",
871
907
  value: {
872
908
  _type: "reference",
873
- _ref: baseDocument._id.replace("drafts.", "")
909
+ _ref: getPublishedId(baseDocument._id)
874
910
  }
875
911
  };
876
912
  baseDocument._id.startsWith("drafts.") && (baseLangEntry.value = {
@@ -884,7 +920,7 @@ const getOrCreateTranslationMetadata = async (documentId, baseDocument, client,
884
920
  });
885
921
  try {
886
922
  return await client.createIfNotExists({
887
- _id: `translation.metadata.${documentId.replace("drafts.", "")}`,
923
+ _id: `translation.metadata.${publishedId}`,
888
924
  _type: "translation.metadata",
889
925
  translations: [baseLangEntry]
890
926
  });
@@ -894,7 +930,7 @@ const getOrCreateTranslationMetadata = async (documentId, baseDocument, client,
894
930
  _type == 'translation.metadata' &&
895
931
  translations[language == $baseLanguage][0].value._ref == $id
896
932
  ][0]`,
897
- { baseLanguage, id: documentId.replace("drafts.", "") }
933
+ { baseLanguage, id: publishedId }
898
934
  );
899
935
  if (metadata)
900
936
  return metadata;
@@ -935,12 +971,12 @@ const documentLevelPatch = async (docInfo, translatedFields, localeId, client, l
935
971
  docInfo.versionId,
936
972
  client
937
973
  )), baseDoc || (baseDoc = await findLatestDraft(docInfo.documentId, client));
938
- const translationMetadata = await getOrCreateTranslationMetadata(
974
+ const i18nDocId = (await getOrCreateTranslationMetadata(
939
975
  docInfo.documentId,
940
976
  baseDoc,
941
977
  client,
942
978
  baseLanguage
943
- ), i18nDocId = translationMetadata.translations.find((translation) => translation.language === localeId)?.value?._ref;
979
+ )).translations.find((translation) => translation.language === localeId)?.value?._ref;
944
980
  i18nDocId && (i18nDoc = await findLatestDraft(i18nDocId, client)), mergeWithTargetLocale && i18nDoc ? baseDoc = i18nDoc : docInfo.documentId && docInfo.versionId && (baseDoc = await findDocumentAtRevision(
945
981
  docInfo.documentId,
946
982
  docInfo.versionId,
@@ -950,23 +986,46 @@ const documentLevelPatch = async (docInfo, translatedFields, localeId, client, l
950
986
  translatedFields,
951
987
  baseDoc
952
988
  );
953
- i18nDoc ? await patchI18nDoc(
954
- docInfo.documentId,
955
- i18nDoc._id,
956
- baseDoc,
957
- merged,
958
- translatedFields,
959
- client,
960
- i18nDoc
961
- ) : await createI18nDocAndPatchMetadata(
962
- baseDoc,
963
- merged,
964
- localeId,
965
- client,
966
- translationMetadata,
967
- docInfo.documentId,
968
- languageField
969
- );
989
+ if (i18nDoc)
990
+ await patchI18nDoc(
991
+ docInfo.documentId,
992
+ i18nDoc._id,
993
+ baseDoc,
994
+ merged,
995
+ translatedFields,
996
+ client,
997
+ i18nDoc
998
+ );
999
+ else {
1000
+ const freshTranslationMetadata = await getOrCreateTranslationMetadata(
1001
+ docInfo.documentId,
1002
+ baseDoc,
1003
+ client,
1004
+ baseLanguage
1005
+ ), freshI18nDocId = freshTranslationMetadata.translations.find((translation) => translation.language === localeId)?.value?._ref;
1006
+ if (freshI18nDocId) {
1007
+ const freshI18nDoc = await findLatestDraft(freshI18nDocId, client);
1008
+ await patchI18nDoc(
1009
+ docInfo.documentId,
1010
+ freshI18nDoc._id,
1011
+ baseDoc,
1012
+ merged,
1013
+ translatedFields,
1014
+ client,
1015
+ freshI18nDoc
1016
+ );
1017
+ return;
1018
+ }
1019
+ await createI18nDocAndPatchMetadata(
1020
+ baseDoc,
1021
+ merged,
1022
+ localeId,
1023
+ client,
1024
+ freshTranslationMetadata,
1025
+ docInfo.documentId,
1026
+ languageField
1027
+ );
1028
+ }
970
1029
  };
971
1030
  async function importDocument(docInfo, localeId, document2, context, mergeWithTargetLocale = !1) {
972
1031
  const { client } = context, deserialized = deserializeDocument(document2);
@@ -1007,10 +1066,12 @@ async function resolveTranslatedReferences(references, locale, client) {
1007
1066
  return translatedRefs;
1008
1067
  const sourceLocale = pluginConfig.getSourceLocale(), translationPairs = await client.fetch(`*[_type == "translation.metadata" && count(translations[language == $sourceLocale && value._ref in $refIds]) > 0] {
1009
1068
  "originalRef": translations[language == $sourceLocale][0].value._ref,
1010
- "translatedRef": translations[language == $locale][0].value._ref
1011
- }[defined(originalRef) && defined(translatedRef)]`, { refIds, sourceLocale, locale });
1012
- for (const { originalRef, translatedRef } of translationPairs)
1069
+ "translatedRefs": translations[language == $locale].value._ref
1070
+ }[defined(originalRef) && count(translatedRefs) > 0]`, { refIds, sourceLocale, locale });
1071
+ for (const { originalRef, translatedRefs: localeRefs } of translationPairs) {
1072
+ const translatedRef = localeRefs[localeRefs.length - 1];
1013
1073
  translatedRefs.set(originalRef, translatedRef);
1074
+ }
1014
1075
  return translatedRefs;
1015
1076
  }
1016
1077
  function updateDocumentReferences(doc, translatedRefs) {
@@ -1032,34 +1093,50 @@ function updateReferencesRecursive(obj, translatedRefs) {
1032
1093
  }), updated;
1033
1094
  }
1034
1095
  async function findTranslatedDocumentForLocale(sourceDocumentId, localeId, client) {
1035
- const cleanDocId = sourceDocumentId.replace("drafts.", "");
1036
- return await client.fetch(`*[
1096
+ const cleanDocId = getPublishedId(sourceDocumentId), translatedDocIds = await client.fetch(`*[
1037
1097
  _type == "translation.metadata" &&
1038
1098
  (
1039
1099
  translations[language == $sourceLocale][0].value._ref == $cleanDocId
1040
1100
  ) &&
1041
1101
  defined(translations[language == $localeId])
1042
- ][0].translations[language == $localeId][0].value->`, {
1102
+ ][0].translations[language == $localeId].value._ref`, {
1043
1103
  sourceLocale: pluginConfig.getSourceLocale(),
1044
1104
  cleanDocId,
1045
1105
  localeId
1046
- }) || null;
1106
+ }), translatedDocId = translatedDocIds?.[translatedDocIds.length - 1];
1107
+ return translatedDocId && await findLatestDraft(translatedDocId, client) || null;
1047
1108
  }
1048
1109
  async function findDocument(documentId, client) {
1049
1110
  const query = "*[_id == $id]", params = { id: documentId };
1050
1111
  return (await client.fetch(query, params))[0] || null;
1051
1112
  }
1052
1113
  async function processBatch(items, processor, options = {}) {
1053
- const { batchSize = 20, onProgress, onItemSuccess, onItemFailure } = options;
1114
+ const {
1115
+ batchSize = 20,
1116
+ getConcurrencyKey,
1117
+ onProgress,
1118
+ onItemSuccess,
1119
+ onItemFailure
1120
+ } = options;
1054
1121
  let successCount = 0, failureCount = 0;
1055
1122
  const successfulItems = [], failedItems = [];
1056
1123
  for (let i = 0; i < items.length; i += batchSize) {
1057
- const batch = items.slice(i, i + batchSize), batchPromises = batch.map(async (item) => {
1124
+ const batch = items.slice(i, i + batchSize), pendingByKey = /* @__PURE__ */ new Map(), batchPromises = batch.map(async (item) => {
1125
+ const concurrencyKey = getConcurrencyKey?.(item), pending = concurrencyKey ? pendingByKey.get(concurrencyKey) : void 0;
1126
+ let release = () => {
1127
+ };
1128
+ const current = new Promise((resolve) => {
1129
+ release = resolve;
1130
+ });
1131
+ concurrencyKey && pendingByKey.set(concurrencyKey, current);
1058
1132
  try {
1133
+ pending && await pending;
1059
1134
  const result = await processor(item);
1060
1135
  return onItemSuccess?.(item, result), { success: !0, item, result };
1061
1136
  } catch (error) {
1062
1137
  return onItemFailure?.(item, error), { success: !1, item, error };
1138
+ } finally {
1139
+ release(), concurrencyKey && pendingByKey.get(concurrencyKey) === current && pendingByKey.delete(concurrencyKey);
1063
1140
  }
1064
1141
  });
1065
1142
  (await Promise.all(batchPromises)).forEach((result) => {
@@ -1082,6 +1159,11 @@ async function processImportBatch(items, options = {}) {
1082
1159
  ), item.key),
1083
1160
  {
1084
1161
  ...options,
1162
+ getConcurrencyKey: (item) => createStableTranslationKey(
1163
+ void 0,
1164
+ item.docInfo.documentId,
1165
+ item.locale
1166
+ ),
1085
1167
  onItemSuccess: (item, key) => {
1086
1168
  successfulImports.push(key), options.onItemSuccess?.(item, key);
1087
1169
  },
@@ -1097,15 +1179,25 @@ async function processImportBatch(items, options = {}) {
1097
1179
  };
1098
1180
  }
1099
1181
  async function getReadyFilesForImport(translationStatuses, options = {}) {
1100
- const { filterReadyFiles = () => !0 } = options, readyFiles = [];
1182
+ const { filterReadyFiles = () => !0 } = options, readyFilesByDocumentLocale = /* @__PURE__ */ new Map();
1101
1183
  for (const [key, status] of translationStatuses.entries())
1102
- status.isReady && filterReadyFiles(key, status) && readyFiles.push({
1103
- fileId: status.fileData.fileId,
1104
- versionId: status.fileData.versionId,
1105
- branchId: status.fileData.branchId,
1106
- locale: status.fileData.locale
1107
- });
1108
- return readyFiles;
1184
+ if (status.isReady && status.fileData && filterReadyFiles(key, status)) {
1185
+ const fileData = {
1186
+ fileId: getPublishedId(status.fileData.fileId),
1187
+ versionId: status.fileData.versionId,
1188
+ branchId: status.fileData.branchId,
1189
+ locale: status.fileData.locale
1190
+ };
1191
+ readyFilesByDocumentLocale.set(
1192
+ createStableTranslationKey(
1193
+ fileData.branchId,
1194
+ fileData.fileId,
1195
+ fileData.locale
1196
+ ),
1197
+ fileData
1198
+ );
1199
+ }
1200
+ return Array.from(readyFilesByDocumentLocale.values());
1109
1201
  }
1110
1202
  async function importTranslations(readyFiles, secrets, translationContext, options = {}) {
1111
1203
  if (readyFiles.length === 0)
@@ -1137,7 +1229,7 @@ async function publishDocument(documentId, client) {
1137
1229
  {
1138
1230
  actionType: "sanity.action.document.publish",
1139
1231
  draftId: documentId,
1140
- publishedId: documentId.replace("drafts.", "")
1232
+ publishedId: getPublishedId(documentId)
1141
1233
  },
1142
1234
  {}
1143
1235
  );
@@ -1185,9 +1277,13 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1185
1277
  downloaded: /* @__PURE__ */ new Set(),
1186
1278
  failed: /* @__PURE__ */ new Set(),
1187
1279
  skipped: /* @__PURE__ */ new Set()
1188
- }), [translationStatuses, setTranslationStatuses] = o.useState(/* @__PURE__ */ new Map()), [isRefreshing, setIsRefreshing] = o.useState(!1), client = useClient(), schema2 = sanity.useSchema(), translationContext = { client, schema: schema2 }, toast = ui.useToast(), { loading: loadingSecrets, secrets } = useSecrets(
1280
+ }), downloadStatusRef = o.useRef(downloadStatus), [translationStatuses, setTranslationStatuses] = o.useState(/* @__PURE__ */ new Map()), [isRefreshing, setIsRefreshing] = o.useState(!1), client = useClient(), schema2 = sanity.useSchema(), translationContext = { client, schema: schema2 }, toast = ui.useToast(), { loading: loadingSecrets, secrets } = useSecrets(
1189
1281
  pluginConfig.getSecretsNamespace()
1190
- ), [branchId, setBranchId] = o.useState(void 0), fetchDocuments = o.useCallback(async () => {
1282
+ ), [branchId, setBranchId] = o.useState(void 0);
1283
+ o.useEffect(() => {
1284
+ downloadStatusRef.current = downloadStatus;
1285
+ }, [downloadStatus]);
1286
+ const fetchDocuments = o.useCallback(async () => {
1191
1287
  setLoadingDocuments(!0);
1192
1288
  try {
1193
1289
  if (singleDocument) {
@@ -1198,7 +1294,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1198
1294
  let query;
1199
1295
  filterConditions.length === 0 ? query = `*[!(_type in ["system.group"]) && !(_id in path("_.**")) && ${languageFilter}]` : query = `*[!(_type in ["system.group"]) && !(_id in path("_.**")) && (${filterConditions.join(" || ")}) && ${languageFilter}]`;
1200
1296
  const docs = await client.fetch(query);
1201
- setDocuments(docs);
1297
+ setDocuments(dedupeDocumentsPreferDraft(docs));
1202
1298
  } catch {
1203
1299
  toast.push({
1204
1300
  title: "Error fetching documents",
@@ -1223,9 +1319,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1223
1319
  }, [secrets]), fetchExistingTranslations = o.useCallback(async () => {
1224
1320
  if (!(!documents.length || !locales.length))
1225
1321
  try {
1226
- const sourceLocale = pluginConfig.getSourceLocale(), availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), documentIds = documents.map(
1227
- (doc) => doc._id?.replace("drafts.", "") || doc._id
1228
- ), existingMetadata = await client.fetch(`*[
1322
+ const sourceLocale = pluginConfig.getSourceLocale(), availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), documentIds = documents.map(getDocumentPublishedId), existingMetadata = await client.fetch(`*[
1229
1323
  _type == 'translation.metadata' &&
1230
1324
  translations[language == $sourceLocale][0].value._ref in $documentIds
1231
1325
  ] {
@@ -1238,7 +1332,13 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1238
1332
  }), existing = /* @__PURE__ */ new Set();
1239
1333
  existingMetadata.forEach((metadata) => {
1240
1334
  metadata.existingTranslations?.forEach((localeId) => {
1241
- localeId !== sourceLocale && existing.add(`${metadata.sourceDocId}:${localeId}`);
1335
+ localeId !== sourceLocale && existing.add(
1336
+ createStableTranslationKey(
1337
+ void 0,
1338
+ metadata.sourceDocId,
1339
+ localeId
1340
+ )
1341
+ );
1242
1342
  });
1243
1343
  }), setExistingTranslations(existing);
1244
1344
  } catch (error) {
@@ -1262,7 +1362,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1262
1362
  );
1263
1363
  return {
1264
1364
  info: {
1265
- documentId: doc._id?.replace("drafts.", "") || doc._id,
1365
+ documentId: getDocumentPublishedId(doc),
1266
1366
  versionId: doc._rev
1267
1367
  },
1268
1368
  serializedDocument: serialized
@@ -1367,7 +1467,11 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1367
1467
  return existingMetadata.forEach((metadata) => {
1368
1468
  metadata.existingTranslations?.forEach((localeId) => {
1369
1469
  localeId !== sourceLocale && existing.add(
1370
- `${branchId2}:${metadata.sourceDocId}:${metadata._rev}:${localeId}`
1470
+ createStableTranslationKey(
1471
+ branchId2,
1472
+ metadata.sourceDocId,
1473
+ localeId
1474
+ )
1371
1475
  );
1372
1476
  });
1373
1477
  }), existing;
@@ -1377,14 +1481,18 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1377
1481
  if (!(!secrets || documents.length === 0 || !branchId)) {
1378
1482
  setIsBusy(!0);
1379
1483
  try {
1380
- const availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), documentIds = documents.map(
1381
- (doc) => doc._id?.replace("drafts.", "") || doc._id
1382
- ), existingTranslations2 = await getExistingTranslations(
1484
+ const availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), documentIds = documents.map(getDocumentPublishedId), existingTranslations2 = await getExistingTranslations(
1383
1485
  documentIds,
1384
1486
  availableLocaleIds,
1385
1487
  branchId
1386
1488
  ), readyFiles = await getReadyFilesForImport(translationStatuses, {
1387
- filterReadyFiles: (key) => !existingTranslations2.has(key)
1489
+ filterReadyFiles: (_key, status) => !existingTranslations2.has(
1490
+ createStableTranslationKey(
1491
+ branchId,
1492
+ status.fileData.fileId,
1493
+ status.fileData.locale
1494
+ )
1495
+ )
1388
1496
  });
1389
1497
  if (readyFiles.length === 0) {
1390
1498
  toast.push({
@@ -1463,7 +1571,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1463
1571
  const availableLocaleIds = locales.filter((locale) => locale.enabled !== !1).map((locale) => locale.localeId), fileQueryData = [];
1464
1572
  for (const doc of documents)
1465
1573
  for (const localeId of availableLocaleIds) {
1466
- const documentId = doc._id?.replace("drafts.", "") || doc._id;
1574
+ const documentId = getDocumentPublishedId(doc);
1467
1575
  fileQueryData.push({
1468
1576
  versionId: doc._rev,
1469
1577
  fileId: documentId,
@@ -1473,19 +1581,29 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1473
1581
  }
1474
1582
  const readyTranslations = await checkTranslationStatus(
1475
1583
  fileQueryData,
1476
- downloadStatus,
1584
+ downloadStatusRef.current,
1477
1585
  secrets
1478
1586
  );
1479
1587
  setTranslationStatuses((prevStatuses) => {
1480
1588
  const newStatuses = /* @__PURE__ */ new Map();
1481
1589
  for (const doc of documents)
1482
1590
  for (const localeId of availableLocaleIds) {
1483
- const documentId = doc._id?.replace("drafts.", "") || doc._id, versionId = doc._rev, key = `${branchId}:${documentId}:${versionId}:${localeId}`;
1591
+ const documentId = getDocumentPublishedId(doc), versionId = doc._rev, key = createTranslationStatusKey(
1592
+ branchId,
1593
+ documentId,
1594
+ versionId,
1595
+ localeId
1596
+ );
1484
1597
  newStatuses.set(key, { progress: 0, isReady: !1 });
1485
1598
  }
1486
1599
  if (Array.isArray(readyTranslations))
1487
1600
  for (const translation of readyTranslations) {
1488
- const key = `${branchId}:${translation.fileId}:${translation.versionId}:${translation.locale}`;
1601
+ const key = createTranslationStatusKey(
1602
+ branchId,
1603
+ translation.fileId,
1604
+ translation.versionId,
1605
+ translation.locale
1606
+ );
1489
1607
  newStatuses.set(key, {
1490
1608
  progress: 100,
1491
1609
  isReady: !0,
@@ -1516,7 +1634,12 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1516
1634
  }, [secrets, documents, locales, branchId]), handleImportDocument = o.useCallback(
1517
1635
  async (documentId, versionId, localeId) => {
1518
1636
  if (!secrets) return;
1519
- const key = `${branchId}:${documentId}:${versionId}:${localeId}`, status = translationStatuses.get(key);
1637
+ const key = createTranslationStatusKey(
1638
+ branchId,
1639
+ documentId,
1640
+ versionId,
1641
+ localeId
1642
+ ), status = translationStatuses.get(key);
1520
1643
  if (!status?.isReady || !status.fileData) {
1521
1644
  toast.push({
1522
1645
  title: `Translation not ready for ${documentId} (${localeId})`,
@@ -1526,7 +1649,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1526
1649
  return;
1527
1650
  }
1528
1651
  const document2 = documents.find(
1529
- (doc) => (doc._id?.replace("drafts.", "") || doc._id) === documentId
1652
+ (doc) => getDocumentPublishedId(doc) === getPublishedId(documentId)
1530
1653
  );
1531
1654
  if (!document2) {
1532
1655
  toast.push({
@@ -1551,7 +1674,7 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1551
1674
  if (downloadedFiles.length > 0)
1552
1675
  try {
1553
1676
  const docInfo = {
1554
- documentId,
1677
+ documentId: getPublishedId(documentId),
1555
1678
  versionId: document2._rev
1556
1679
  };
1557
1680
  await importDocument(
@@ -1563,7 +1686,12 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1563
1686
  ), setDownloadStatus((prev2) => ({
1564
1687
  ...prev2,
1565
1688
  downloaded: /* @__PURE__ */ new Set([...prev2.downloaded, key])
1566
- })), setImportedTranslations((prev2) => /* @__PURE__ */ new Set([...prev2, key])), toast.push({
1689
+ })), setImportedTranslations((prev2) => /* @__PURE__ */ new Set([...prev2, key])), setExistingTranslations(
1690
+ (prev2) => /* @__PURE__ */ new Set([
1691
+ ...prev2,
1692
+ createStableTranslationKey(branchId, documentId, localeId)
1693
+ ])
1694
+ ), toast.push({
1567
1695
  title: `Successfully imported translation for ${documentId} (${localeId})`,
1568
1696
  status: "success",
1569
1697
  closable: !0
@@ -1668,7 +1796,10 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1668
1796
  if (!secrets || documents.length === 0) return 0;
1669
1797
  setIsBusy(!0);
1670
1798
  try {
1671
- const sourceLocale = pluginConfig.getSourceLocale(), publishedDocumentIds = documents.filter((doc) => !doc._id.startsWith("drafts.")).map((doc) => doc._id);
1799
+ const sourceLocale = pluginConfig.getSourceLocale(), sourceDocumentIds = documents.map(getDocumentPublishedId), publishedDocumentIds = await client.fetch(
1800
+ "*[_id in $sourceDocumentIds]._id",
1801
+ { sourceDocumentIds }
1802
+ );
1672
1803
  if (publishedDocumentIds.length === 0)
1673
1804
  return toast.push({
1674
1805
  title: "No published source documents found to publish translations for",
@@ -1687,19 +1818,19 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1687
1818
  }`, {
1688
1819
  sourceLocale,
1689
1820
  publishedDocumentIds
1690
- }), translationDocIds = [];
1821
+ }), translationDocIds = /* @__PURE__ */ new Set();
1691
1822
  if (translationMetadata.forEach((metadata) => {
1692
1823
  metadata.translationDocs?.forEach((translation) => {
1693
- translation.docId && translationDocIds.push(translation.docId);
1824
+ translation.docId && translationDocIds.add(translation.docId);
1694
1825
  });
1695
- }), translationDocIds.length === 0)
1826
+ }), translationDocIds.size === 0)
1696
1827
  return toast.push({
1697
1828
  title: "No translation documents found to publish",
1698
1829
  status: "warning",
1699
1830
  closable: !0
1700
1831
  }), 0;
1701
1832
  const translatedDocumentIds = await publishTranslations(
1702
- translationDocIds,
1833
+ Array.from(translationDocIds),
1703
1834
  client
1704
1835
  );
1705
1836
  return toast.push({
@@ -1724,8 +1855,15 @@ const getLocales = async (secrets) => pluginConfig.getLocales().map((locale) =>
1724
1855
  }, [fetchLocales, secrets]), o.useEffect(() => {
1725
1856
  documents.length > 0 && locales.length > 0 && fetchExistingTranslations();
1726
1857
  }, [fetchExistingTranslations]), o.useEffect(() => {
1727
- documents.length > 0 && locales.length > 0 && secrets && !loadingDocuments && handleRefreshAll();
1728
- }, [documents]), o.useEffect(() => {
1858
+ documents.length > 0 && locales.length > 0 && secrets && !loadingDocuments && branchId && handleRefreshAll();
1859
+ }, [
1860
+ documents,
1861
+ locales,
1862
+ secrets,
1863
+ loadingDocuments,
1864
+ branchId,
1865
+ handleRefreshAll
1866
+ ]), o.useEffect(() => {
1729
1867
  if (!autoRefresh || documents.length === 0 || !secrets) return;
1730
1868
  const interval = setInterval(async () => {
1731
1869
  await handleRefreshAll();
@@ -1814,7 +1952,7 @@ const LanguageStatus = ({
1814
1952
  importFile,
1815
1953
  isImported = !1
1816
1954
  }) => {
1817
- const [isBusy, setIsBusy] = o.useState(!1), handleImport = o.useCallback(async () => {
1955
+ const [isBusy, setIsBusy] = o.useState(!1), displayedProgress = isImported && progress < 100 ? 100 : progress, handleImport = o.useCallback(async () => {
1818
1956
  setIsBusy(!0);
1819
1957
  try {
1820
1958
  await importFile();
@@ -1824,7 +1962,7 @@ const LanguageStatus = ({
1824
1962
  }, [importFile, setIsBusy]);
1825
1963
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { shadow: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Grid, { columns: 5, gap: 3, padding: 3, children: [
1826
1964
  /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { columnStart: 1, columnEnd: 3, align: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", size: 1, children: title }) }),
1827
- typeof progress == "number" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { columnStart: 3, columnEnd: 5, align: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { progress }) }) : null,
1965
+ typeof displayedProgress == "number" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { columnStart: 3, columnEnd: 5, align: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { progress: displayedProgress }) }) : null,
1828
1966
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { columnStart: 5, columnEnd: 6, children: isImported ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", justify: "center", style: { color: "green" }, children: [
1829
1967
  /* @__PURE__ */ jsxRuntime.jsx(icons.CheckmarkCircleIcon, {}),
1830
1968
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, style: { marginLeft: "4px" }, children: "Imported" })
@@ -3051,12 +3189,17 @@ const WrapText = dt(ui.Box)`
3051
3189
  return locales.filter(
3052
3190
  (locale) => locale.enabled !== !1 && locale.localeId !== sourceLocale
3053
3191
  );
3054
- }, [locales]), documentId = o.useMemo(() => document2 ? document2._id?.replace("drafts.", "") || document2._id : null, [document2]), handleImportTranslations = o.useCallback(
3192
+ }, [locales]), documentId = o.useMemo(() => document2 ? getDocumentPublishedId(document2) : null, [document2]), handleImportTranslations = o.useCallback(
3055
3193
  async (options = {}) => {
3056
3194
  const { autoOnly = !1 } = options;
3057
3195
  if (isImporting || !documentId || autoOnly && !autoImport) return;
3058
3196
  const readyTranslations = availableLocales.filter((locale) => {
3059
- const key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`;
3197
+ const key = createTranslationStatusKey(
3198
+ branchId,
3199
+ documentId,
3200
+ document2._rev,
3201
+ locale.localeId
3202
+ );
3060
3203
  return translationStatuses.get(key)?.isReady && !importedTranslations.has(key);
3061
3204
  });
3062
3205
  if (readyTranslations.length !== 0) {
@@ -3177,7 +3320,12 @@ const WrapText = dt(ui.Box)`
3177
3320
  ] })
3178
3321
  ] }),
3179
3322
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { children: availableLocales.map((locale) => {
3180
- const key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`, status = translationStatuses.get(key), progress = status?.progress || 0, isImported = importedTranslations.has(key);
3323
+ const key = createTranslationStatusKey(
3324
+ branchId,
3325
+ documentId,
3326
+ document2._rev,
3327
+ locale.localeId
3328
+ ), status = translationStatuses.get(key), progress = status?.progress || 0, isImported = importedTranslations.has(key);
3181
3329
  return /* @__PURE__ */ jsxRuntime.jsx(
3182
3330
  LanguageStatus,
3183
3331
  {
@@ -3207,7 +3355,12 @@ const WrapText = dt(ui.Box)`
3207
3355
  text: isImporting ? "Importing..." : "Import All",
3208
3356
  icon: icons.DownloadIcon,
3209
3357
  disabled: isImporting || availableLocales.every((locale) => {
3210
- const key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`;
3358
+ const key = createTranslationStatusKey(
3359
+ branchId,
3360
+ documentId,
3361
+ document2._rev,
3362
+ locale.localeId
3363
+ );
3211
3364
  return !translationStatuses.get(key)?.isReady || importedTranslations.has(key);
3212
3365
  }),
3213
3366
  style: { minWidth: "180px" }
@@ -3229,12 +3382,22 @@ const WrapText = dt(ui.Box)`
3229
3382
  "Imported",
3230
3383
  " ",
3231
3384
  availableLocales.filter((locale) => {
3232
- const key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`;
3385
+ const key = createTranslationStatusKey(
3386
+ branchId,
3387
+ documentId,
3388
+ document2._rev,
3389
+ locale.localeId
3390
+ );
3233
3391
  return importedTranslations.has(key);
3234
3392
  }).length,
3235
3393
  "/",
3236
3394
  availableLocales.filter((locale) => {
3237
- const key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`;
3395
+ const key = createTranslationStatusKey(
3396
+ branchId,
3397
+ documentId,
3398
+ document2._rev,
3399
+ locale.localeId
3400
+ );
3238
3401
  return translationStatuses.get(key)?.isReady;
3239
3402
  }).length
3240
3403
  ] })
@@ -3350,11 +3513,16 @@ const WrapText = dt(ui.Box)`
3350
3513
  } = useTranslations();
3351
3514
  return loadingDocuments ? /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { align: "center", justify: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {}) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { style: { maxHeight: "60vh", overflowY: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: documents.map((document2) => /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { shadow: 1, padding: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
3352
3515
  /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { justify: "space-between", align: "flex-start", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { flex: 1, children: [
3353
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "semibold", size: 1, children: document2._id?.replace("drafts.", "") || document2._id }),
3516
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "semibold", size: 1, children: getDocumentPublishedId(document2) }),
3354
3517
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 0, muted: !0, style: { marginTop: "2px" }, children: document2._type })
3355
3518
  ] }) }),
3356
3519
  /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 2, children: locales.length > 0 ? locales.filter((locale) => locale.enabled !== !1).map((locale) => {
3357
- const documentId = document2._id?.replace("drafts.", "") || document2._id, key = `${branchId}:${documentId}:${document2._rev}:${locale.localeId}`, status = translationStatuses.get(key), isDownloaded = downloadStatus.downloaded.has(key), isImported = importedTranslations.has(key);
3520
+ const documentId = getDocumentPublishedId(document2), key = createTranslationStatusKey(
3521
+ branchId,
3522
+ documentId,
3523
+ document2._rev,
3524
+ locale.localeId
3525
+ ), status = translationStatuses.get(key), isDownloaded = downloadStatus.downloaded.has(key), isImported = importedTranslations.has(key);
3358
3526
  return /* @__PURE__ */ jsxRuntime.jsx(
3359
3527
  LanguageStatus,
3360
3528
  {