@braze/sanity-plugin-ai-translation 0.2.71 → 0.2.73

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.
package/dist/index.d.ts CHANGED
@@ -476,6 +476,7 @@ declare type TelemetryEvent =
476
476
  | TranslationBatchStartedEvent
477
477
  | TranslationBatchCompletedEvent
478
478
  | TranslationJobCompletedEvent
479
+ | TranslationJobFailedEvent
479
480
  | OpenAIApiCallEvent;
480
481
 
481
482
  /**
@@ -678,6 +679,28 @@ declare interface TranslationJobCompletedEvent extends TelemetryEventBase {
678
679
  };
679
680
  }
680
681
 
682
+ /**
683
+ * Event emitted when an individual translation job fails.
684
+ * This is emitted alongside the detailed error bundle (via onError) to enable
685
+ * easy error rate calculations by comparing job_completed vs job_failed counts.
686
+ * @public
687
+ */
688
+ declare interface TranslationJobFailedEvent extends TelemetryEventBase {
689
+ name: "translation.job_failed";
690
+ data: {
691
+ documentId: string;
692
+ documentType: string;
693
+ sourceLanguage: string;
694
+ targetLanguage: string;
695
+ translationMode: TranslationMode;
696
+ durationMs: number;
697
+ /** Categorized error type for filtering/grouping */
698
+ errorType: TranslationErrorType;
699
+ /** Error message (kept short for metrics; full details in error bundle) */
700
+ errorMessage: string;
701
+ };
702
+ }
703
+
681
704
  declare type TranslationMode = "sanity-only" | "openai-only";
682
705
 
683
706
  /**
package/dist/index.js CHANGED
@@ -482,8 +482,8 @@ function isNetworkError(error) {
482
482
  return response.status >= 500 || response.status === 429;
483
483
  }
484
484
  if (error instanceof Error) {
485
- const message = error.message;
486
- if (/\b(50[0-9]|429)\b/.test(message))
485
+ const message = error.message.toLowerCase();
486
+ if (/\b(50[0-9]|429)\b/.test(message) || message.includes("rate limit"))
487
487
  return !0;
488
488
  }
489
489
  return !1;
@@ -870,7 +870,14 @@ const fetchTranslationPrompts = async ({
870
870
  languageFieldName
871
871
  }) => {
872
872
  try {
873
- const doc = await client.fetch("*[_id == $documentId][0]", { documentId });
873
+ const doc = await retryWithBackoff(
874
+ () => client.fetch("*[_id == $documentId][0]", { documentId }),
875
+ {
876
+ maxAttempts: 3,
877
+ initialDelayMs: 1e3,
878
+ context: { operation: "localizeReferences.fetchDocument", documentId }
879
+ }
880
+ );
874
881
  if (!doc) {
875
882
  devLog.error("Document not found", new Error("Document not found"), {
876
883
  documentId,
@@ -900,17 +907,24 @@ const fetchTranslationPrompts = async ({
900
907
  }, updateReferences = async (documentId, client, refs, sourceLanguage, targetLanguage) => {
901
908
  if (!refs?.length)
902
909
  return [];
903
- const refIds = refs.map((r) => getPublishedId(r.value)), translations = await client.fetch(
904
- `*[_type == $translationMetadataSchemaType && array::intersects(translations[].value._ref, $refIds)] {
910
+ const refIds = refs.map((r) => getPublishedId(r.value)), translations = await retryWithBackoff(
911
+ () => client.fetch(
912
+ `*[_type == $translationMetadataSchemaType && array::intersects(translations[].value._ref, $refIds)] {
905
913
  "sourceId": translations[_key == $sourceLanguage][0].value._ref,
906
914
  "targetId": translations[_key == $targetLanguage][0].value._ref,
907
915
  "targetType": translations[_key == $sourceLanguage][0].value -> _type
908
916
  }`,
917
+ {
918
+ translationMetadataSchemaType: TRANSLATION_METADATA_TYPE,
919
+ refIds,
920
+ sourceLanguage,
921
+ targetLanguage
922
+ }
923
+ ),
909
924
  {
910
- translationMetadataSchemaType: TRANSLATION_METADATA_TYPE,
911
- refIds,
912
- sourceLanguage,
913
- targetLanguage
925
+ maxAttempts: 3,
926
+ initialDelayMs: 1e3,
927
+ context: { operation: "localizeReferences.fetchTranslations", refCount: refIds.length }
914
928
  }
915
929
  ), translationMap = /* @__PURE__ */ new Map();
916
930
  for (const t of translations)
@@ -948,81 +962,80 @@ const fetchTranslationPrompts = async ({
948
962
  }, updateBackwardReferences = async ({
949
963
  client,
950
964
  releaseId,
951
- sourceDocumentIds,
952
965
  targetLanguages,
953
966
  sourceLanguage,
954
967
  languageFieldName,
955
968
  excludeDocumentIds
956
969
  }) => {
957
- if (!sourceDocumentIds.length || !targetLanguages.length)
958
- return;
959
- const normalizedSourceIds = sourceDocumentIds.map((id) => getPublishedId(id));
960
- devLog.debug("Starting backward reference update", {
961
- releaseId,
962
- sourceDocumentCount: normalizedSourceIds.length,
963
- targetLanguages,
964
- excludeCount: excludeDocumentIds.length,
965
- component: "updateBackwardReferences"
966
- });
967
- try {
968
- for (const targetLanguage of targetLanguages) {
969
- const existingDocsWithStaleRefs = await client.fetch(
970
+ if (targetLanguages.length) {
971
+ devLog.debug("Starting backward reference update", {
972
+ releaseId,
973
+ targetLanguages,
974
+ excludeCount: excludeDocumentIds.length,
975
+ component: "updateBackwardReferences"
976
+ });
977
+ try {
978
+ const existingDocs = await client.fetch(
970
979
  `*[
971
- _id match "versions." + $releaseId + ".*" &&
972
- @[$languageFieldName] == $targetLanguage &&
973
- !(_id in $excludeIds)
974
- ] {
975
- _id,
976
- "hasStaleRefs": count(*[
977
- _id in $sourceDocumentIds &&
978
- ^references(@._id)
979
- ]) > 0
980
- }[hasStaleRefs == true]`,
980
+ sanity::partOfRelease($releaseId) &&
981
+ @[$languageFieldName] in $targetLanguages &&
982
+ !(_id in $excludeIds)
983
+ ]{
984
+ _id,
985
+ "language": @[$languageFieldName]
986
+ }`,
981
987
  {
982
988
  releaseId,
983
- targetLanguage,
989
+ targetLanguages,
984
990
  languageFieldName,
985
- excludeIds: excludeDocumentIds,
986
- sourceDocumentIds: normalizedSourceIds
991
+ excludeIds: excludeDocumentIds
987
992
  }
988
993
  );
989
- if (existingDocsWithStaleRefs.length === 0) {
990
- devLog.debug("No existing documents with stale references found", {
994
+ if (existingDocs.length === 0) {
995
+ devLog.debug("No existing documents found in release for target languages", {
991
996
  releaseId,
992
- targetLanguage,
997
+ targetLanguages,
993
998
  component: "updateBackwardReferences"
994
999
  });
995
- continue;
1000
+ return;
996
1001
  }
997
- devLog.info("Found existing documents with stale references", {
1002
+ devLog.info("Processing existing documents for backward reference updates", {
998
1003
  releaseId,
999
- targetLanguage,
1000
- count: existingDocsWithStaleRefs.length,
1001
- documentIds: existingDocsWithStaleRefs.map((d) => d._id),
1004
+ targetLanguages,
1005
+ count: existingDocs.length,
1002
1006
  component: "updateBackwardReferences"
1003
- }), await Promise.all(
1004
- existingDocsWithStaleRefs.map(async ({ _id }) => {
1007
+ });
1008
+ let updated = 0, errors = 0;
1009
+ for (const { _id } of existingDocs)
1010
+ try {
1005
1011
  await localizeReferences({
1006
1012
  client,
1007
1013
  documentId: _id,
1008
1014
  sourceLanguage,
1009
1015
  languageFieldName
1016
+ }), updated++;
1017
+ } catch (error) {
1018
+ errors++, devLog.warn("Failed to update references for document", {
1019
+ documentId: _id,
1020
+ error: error instanceof Error ? error.message : String(error),
1021
+ component: "updateBackwardReferences"
1010
1022
  });
1011
- })
1012
- ), devLog.info("Updated backward references", {
1023
+ }
1024
+ devLog.info("Backward reference update complete", {
1013
1025
  releaseId,
1014
- targetLanguage,
1015
- updatedCount: existingDocsWithStaleRefs.length,
1026
+ targetLanguages,
1027
+ processed: existingDocs.length,
1028
+ updated,
1029
+ errors,
1030
+ component: "updateBackwardReferences"
1031
+ });
1032
+ } catch (error) {
1033
+ devLog.error("Error updating backward references", error instanceof Error ? error : new Error(String(error)), {
1034
+ releaseId,
1035
+ targetLanguages,
1016
1036
  component: "updateBackwardReferences"
1017
1037
  });
1018
1038
  }
1019
- } catch (error) {
1020
- devLog.error("Error updating backward references", error instanceof Error ? error : new Error(String(error)), {
1021
- releaseId,
1022
- sourceDocumentIds: normalizedSourceIds,
1023
- targetLanguages,
1024
- component: "updateBackwardReferences"
1025
- });
1026
1039
  }
1027
1040
  }, setupTranslationJobStatuses = (dispatch, sourceDocuments, targetLanguages, mainSourceDocumentId) => {
1028
1041
  for (const sourceDocument of sourceDocuments)
@@ -2892,7 +2905,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2892
2905
  telemetryConfig
2893
2906
  }), newDocument = translationResult?.document;
2894
2907
  if (!newDocument) {
2895
- devLog.error("Translation failed: no document returned", new Error("Translation returned null"), logContext), setJobStatus(
2908
+ const failureError = new Error("Translation returned null");
2909
+ devLog.error("Translation failed: no document returned", failureError, logContext), setJobStatus(
2896
2910
  dispatch,
2897
2911
  sourceDocument._id,
2898
2912
  targetLanguage,
@@ -2901,7 +2915,20 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2901
2915
  errorMessage: "Translation failed"
2902
2916
  },
2903
2917
  mainSourceDocumentId
2904
- ), devLog.timeEnd("translateDocument"), devLog.groupEnd();
2918
+ ), emitTelemetryEvent(telemetryConfig, {
2919
+ name: "translation.job_failed",
2920
+ timestamp: createTimestamp(),
2921
+ data: {
2922
+ documentId: sourceDocument._id,
2923
+ documentType: sourceDocument._type,
2924
+ sourceLanguage,
2925
+ targetLanguage,
2926
+ translationMode,
2927
+ durationMs: Date.now() - jobStartTime,
2928
+ errorType: classifyError(failureError),
2929
+ errorMessage: "Translation failed"
2930
+ }
2931
+ }), devLog.timeEnd("translateDocument"), devLog.groupEnd();
2905
2932
  return;
2906
2933
  }
2907
2934
  return setJobStatus(
@@ -2950,8 +2977,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2950
2977
  }
2951
2978
  }), devLog.timeEnd("translateDocument"), devLog.groupEnd(), newDocument._id;
2952
2979
  } catch (error) {
2953
- const errorMessage = error instanceof Error ? error.message : String(error);
2954
- devLog.error("Translation failed", error instanceof Error ? error : new Error(String(error)), logContext), setJobStatus(
2980
+ const errorMessage = error instanceof Error ? error.message : String(error), errorObj = error instanceof Error ? error : new Error(String(error));
2981
+ devLog.error("Translation failed", errorObj, logContext), setJobStatus(
2955
2982
  dispatch,
2956
2983
  sourceDocument._id,
2957
2984
  targetLanguage,
@@ -2960,7 +2987,20 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2960
2987
  errorMessage
2961
2988
  },
2962
2989
  mainSourceDocumentId
2963
- ), devLog.timeEnd("translateDocument"), devLog.groupEnd();
2990
+ ), emitTelemetryEvent(telemetryConfig, {
2991
+ name: "translation.job_failed",
2992
+ timestamp: createTimestamp(),
2993
+ data: {
2994
+ documentId: sourceDocument._id,
2995
+ documentType: sourceDocument._type,
2996
+ sourceLanguage,
2997
+ targetLanguage,
2998
+ translationMode,
2999
+ durationMs: Date.now() - jobStartTime,
3000
+ errorType: classifyError(errorObj),
3001
+ errorMessage
3002
+ }
3003
+ }), devLog.timeEnd("translateDocument"), devLog.groupEnd();
2964
3004
  return;
2965
3005
  }
2966
3006
  }, translationSemaphore = new Semaphore(2), translateDocumentSet = async ({
@@ -3149,10 +3189,14 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3149
3189
  languageFieldName
3150
3190
  });
3151
3191
  })
3152
- ), releaseConfig.mode === "existing" && releaseId && await updateBackwardReferences({
3192
+ ), devLog.debug("Checking if backward reference update is needed", {
3193
+ releaseMode: releaseConfig.mode,
3194
+ releaseId,
3195
+ willUpdate: releaseConfig.mode === "existing" && !!releaseId,
3196
+ component: "translateDocumentSet"
3197
+ }), releaseConfig.mode === "existing" && releaseId && await updateBackwardReferences({
3153
3198
  client,
3154
3199
  releaseId,
3155
- sourceDocumentIds,
3156
3200
  targetLanguages,
3157
3201
  sourceLanguage,
3158
3202
  languageFieldName,