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

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
@@ -332,6 +332,26 @@ export declare interface PluginConfig {
332
332
  * ```
333
333
  */
334
334
  excludeFields?: string[];
335
+ /**
336
+ * Fields to completely remove from the translated document.
337
+ * Unlike `excludeFields` (which preserves fields with their original values),
338
+ * `unsetFields` strips matched fields entirely so they are absent from the output.
339
+ * Supports the same pattern syntax as `excludeFields`: field names (e.g., 'courseId'),
340
+ * nested paths (e.g., 'meta.internalId'), wildcard arrays (e.g., 'sections[*].legacyId'),
341
+ * and hierarchical matches (e.g., '**.tmpField').
342
+ *
343
+ * Each entry can be a plain string (always unset) or an object with `preserveExisting: true`
344
+ * to retain the field's value from the published target document during re-translation.
345
+ *
346
+ * @example
347
+ * ```typescript
348
+ * unsetFields: [
349
+ * 'legacyId',
350
+ * { field: 'skilljarCourseId', preserveExisting: true }
351
+ * ]
352
+ * ```
353
+ */
354
+ unsetFields?: UnsetFieldEntry[];
335
355
  /**
336
356
  * Optional telemetry configuration for monitoring translation operations.
337
357
  * Provides callbacks for lightweight events and detailed error bundles.
@@ -703,6 +723,28 @@ declare interface TranslationJobFailedEvent extends TelemetryEventBase {
703
723
 
704
724
  declare type TranslationMode = "sanity-only" | "openai-only";
705
725
 
726
+ /**
727
+ * Configuration for a field that should be unset with additional options.
728
+ * @public
729
+ */
730
+ declare interface UnsetFieldConfig {
731
+ /** Field pattern to unset (same syntax as `excludeFields`) */
732
+ field: string;
733
+ /**
734
+ * When true, preserves the field's value from the published target document
735
+ * during re-translation. Has no effect on first translation (no existing target).
736
+ * Defaults to false.
737
+ */
738
+ preserveExisting?: boolean;
739
+ }
740
+
741
+ /**
742
+ * An entry in the `unsetFields` array: either a plain field pattern string
743
+ * or an object with additional configuration.
744
+ * @public
745
+ */
746
+ declare type UnsetFieldEntry = string | UnsetFieldConfig;
747
+
706
748
  /**
707
749
  * Represents a VTT caption cue with timing and text content.
708
750
  */
package/dist/index.js CHANGED
@@ -1360,6 +1360,22 @@ const COMMON_ENUM_FIELDS = [
1360
1360
  /** SEO and URL-related fields that often shouldn't be translated */
1361
1361
  seo: ["slug", "handle", "permalink", "canonical", "redirect", "robots", "sitemap"]
1362
1362
  };
1363
+ function normalizeUnsetFields(entries) {
1364
+ const allFieldPatterns = [], preserveFieldPatterns = [];
1365
+ for (const entry of entries)
1366
+ typeof entry == "string" ? allFieldPatterns.push(entry) : (allFieldPatterns.push(entry.field), entry.preserveExisting && preserveFieldPatterns.push(entry.field));
1367
+ return { allFieldPatterns, preserveFieldPatterns };
1368
+ }
1369
+ function unsetDocumentFields(document, unsetFields) {
1370
+ if (!unsetFields.length)
1371
+ return document;
1372
+ const { allFieldPatterns } = normalizeUnsetFields(unsetFields), { filteredDocument, excludedValues } = filterExcludedFields(document, allFieldPatterns);
1373
+ return Object.keys(excludedValues).length > 0 && devLog.info("Unset fields from document (permanently removed)", {
1374
+ documentId: document._id || "unknown",
1375
+ unsetFieldCount: Object.keys(excludedValues).length,
1376
+ unsetFields: Object.keys(excludedValues)
1377
+ }), filteredDocument;
1378
+ }
1363
1379
  function applyTransforms({
1364
1380
  document,
1365
1381
  registry,
@@ -2630,11 +2646,44 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2630
2646
  openaiConfig,
2631
2647
  releaseId,
2632
2648
  excludeFields = [],
2649
+ unsetFields = [],
2633
2650
  replaceExisting = !1,
2634
2651
  mainSourceDocumentId,
2635
2652
  telemetryConfig
2636
2653
  }) => {
2637
- const startTime = Date.now(), sourceLanguage = sourceDocument[languageFieldName], safeExcludeFields = excludeFields.filter((field) => field !== languageFieldName);
2654
+ const startTime = Date.now(), sourceLanguage = sourceDocument[languageFieldName], safeUnsetFields = unsetFields.filter((entry) => (typeof entry == "string" ? entry : entry.field) !== languageFieldName);
2655
+ unsetFields.length !== safeUnsetFields.length && devLog.warn("Language field was in unsetFields - removing it to prevent translation errors", {
2656
+ documentId: sourceDocument._id,
2657
+ languageFieldName
2658
+ });
2659
+ const documentAfterUnset = unsetDocumentFields(sourceDocument, safeUnsetFields), { preserveFieldPatterns } = normalizeUnsetFields(safeUnsetFields);
2660
+ let documentWithPreserved = documentAfterUnset;
2661
+ if (preserveFieldPatterns.length > 0 && existingTranslationId)
2662
+ try {
2663
+ const publishedTarget = await client.getDocument(existingTranslationId);
2664
+ if (publishedTarget) {
2665
+ const { excludedValues: preserveValues } = filterExcludedFields(
2666
+ publishedTarget,
2667
+ preserveFieldPatterns
2668
+ );
2669
+ Object.keys(preserveValues).length > 0 && (documentWithPreserved = restoreExcludedFields(documentAfterUnset, preserveValues), devLog.info("Preserved fields from published target during re-translation", {
2670
+ documentId: sourceDocument._id,
2671
+ existingTranslationId,
2672
+ preservedFields: Object.keys(preserveValues)
2673
+ }));
2674
+ } else
2675
+ devLog.debug("Published target not found for preserveExisting fields", {
2676
+ documentId: sourceDocument._id,
2677
+ existingTranslationId
2678
+ });
2679
+ } catch (error) {
2680
+ devLog.warn("Failed to fetch published target for preserveExisting fields", {
2681
+ documentId: sourceDocument._id,
2682
+ existingTranslationId,
2683
+ error: error instanceof Error ? error.message : String(error)
2684
+ });
2685
+ }
2686
+ const safeExcludeFields = excludeFields.filter((field) => field !== languageFieldName);
2638
2687
  excludeFields.length !== safeExcludeFields.length && devLog.warn("Language field was in excludeFields - removing it to prevent translation errors", {
2639
2688
  documentId: sourceDocument._id,
2640
2689
  languageFieldName,
@@ -2645,10 +2694,10 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2645
2694
  documentType: sourceDocument._type,
2646
2695
  excludeFields: safeExcludeFields,
2647
2696
  excludeFieldsLength: safeExcludeFields?.length ?? 0,
2648
- sourceHasTitle: "title" in sourceDocument,
2649
- sourceTitleValue: sourceDocument.title
2697
+ sourceHasTitle: "title" in documentWithPreserved,
2698
+ sourceTitleValue: documentWithPreserved.title
2650
2699
  });
2651
- const { filteredDocument, excludedValues } = filterExcludedFields(sourceDocument, safeExcludeFields);
2700
+ const { filteredDocument, excludedValues } = filterExcludedFields(documentWithPreserved, safeExcludeFields);
2652
2701
  devLog.info("After filtering excluded fields", {
2653
2702
  documentId: sourceDocument._id,
2654
2703
  filteredHasTitle: "title" in filteredDocument,
@@ -2871,6 +2920,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2871
2920
  openaiConfig,
2872
2921
  releaseId,
2873
2922
  excludeFields,
2923
+ unsetFields,
2874
2924
  replaceExisting,
2875
2925
  mainSourceDocumentId,
2876
2926
  telemetryConfig
@@ -2900,6 +2950,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
2900
2950
  openaiConfig,
2901
2951
  releaseId,
2902
2952
  excludeFields,
2953
+ unsetFields,
2903
2954
  replaceExisting,
2904
2955
  mainSourceDocumentId,
2905
2956
  telemetryConfig
@@ -3015,6 +3066,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3015
3066
  translationMode,
3016
3067
  openaiConfig,
3017
3068
  excludeFields,
3069
+ unsetFields,
3018
3070
  releaseConfig,
3019
3071
  telemetryConfig
3020
3072
  }) => {
@@ -3059,7 +3111,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3059
3111
  metadata: {
3060
3112
  title: releaseTitle,
3061
3113
  description: releaseDescription,
3062
- releaseType: "undecided"
3114
+ releaseType: "asap"
3063
3115
  }
3064
3116
  }),
3065
3117
  {
@@ -3135,6 +3187,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3135
3187
  openaiConfig,
3136
3188
  releaseId,
3137
3189
  excludeFields,
3190
+ unsetFields,
3138
3191
  replaceExisting: releaseConfig.replaceExisting,
3139
3192
  // Pass main source document ID for active translations tracking
3140
3193
  mainSourceDocumentId: sourceDocument._id,
@@ -3319,6 +3372,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3319
3372
  translationMode,
3320
3373
  openaiConfig: config.openai,
3321
3374
  excludeFields: config.excludeFields,
3375
+ unsetFields: config.unsetFields,
3322
3376
  releaseConfig: {
3323
3377
  ...releaseConfig,
3324
3378
  replaceExisting,
@@ -3341,6 +3395,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
3341
3395
  config.maxReferenceDepth,
3342
3396
  config.openai,
3343
3397
  config.excludeFields,
3398
+ config.unsetFields,
3344
3399
  config.telemetry,
3345
3400
  filteredReferences,
3346
3401
  translationMode,