@braze/sanity-plugin-ai-translation 0.2.2 → 0.2.4

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
@@ -1,6 +1,7 @@
1
1
  import { ArrayDefinition } from "sanity";
2
2
  import { FieldDefinitionBase } from "sanity";
3
3
  import { Plugin as Plugin_2 } from "sanity";
4
+ import type { SanityDocument } from "sanity";
4
5
  import { WidenInitialValue } from "sanity";
5
6
  import { WidenValidation } from "sanity";
6
7
 
@@ -12,13 +13,269 @@ import { WidenValidation } from "sanity";
12
13
  */
13
14
  export declare const aiTranslationPlugin: Plugin_2<PluginConfig>;
14
15
 
16
+ /**
17
+ * Type-erased version of FieldTransform for use in collections.
18
+ * Uses `any` to allow variance - this is necessary because function parameters
19
+ * are contravariant, and using `unknown` would prevent assigning specific
20
+ * transforms like `FieldTransform<VTTCaption[], TranslatableVTT>` to the array.
21
+ *
22
+ * This type is used internally by the plugin for storing transforms
23
+ * in arrays and registries where the specific types aren't known.
24
+ *
25
+ * @public
26
+ *
27
+ * @example Usage in plugin config
28
+ * ```typescript
29
+ * const config: PluginConfig = {
30
+ * // ... other config
31
+ * fieldTransforms: [
32
+ * vttTransform, // FieldTransform<VTTCaption[], TranslatableVTT>
33
+ * richTextTransform, // FieldTransform<PortableTextBlock[], TranslatableRichText>
34
+ * customTransform, // FieldTransform<CustomType, TranslatableCustom>
35
+ * ] // All coerced to AnyFieldTransform[]
36
+ * }
37
+ * ```
38
+ */
39
+ export declare type AnyFieldTransform = FieldTransform<any, any>;
40
+
41
+ /**
42
+ * Common enum field names that are typically excluded from translation
43
+ * @internal
44
+ */
45
+ export declare const COMMON_ENUM_FIELDS: readonly [
46
+ "status",
47
+ "state",
48
+ "type",
49
+ "category",
50
+ "priority",
51
+ "visibility",
52
+ "role",
53
+ "permission",
54
+ "level",
55
+ "mode",
56
+ "format",
57
+ "variant",
58
+ "size",
59
+ "theme",
60
+ "kind",
61
+ "phase",
62
+ "stage",
63
+ ];
64
+
65
+ /**
66
+ * Criteria for matching fields that should be transformed.
67
+ * @public
68
+ * @typeParam TOriginal - Type of the original field value
69
+ */
70
+ export declare interface FieldMatcher<TOriginal = unknown> {
71
+ /**
72
+ * Schema type name to match (e.g., 'video')
73
+ */
74
+ schemaType?: string;
75
+ /**
76
+ * Field name to match (e.g., 'captions')
77
+ */
78
+ fieldName?: string;
79
+ /**
80
+ * Full field path pattern to match (e.g., 'captions[].text' or RegExp)
81
+ */
82
+ fieldPath?: string | RegExp;
83
+ /**
84
+ * Optional function to match based on field value.
85
+ * This is called after other matcher criteria pass (or if no other criteria specified).
86
+ * Useful for detecting field types by examining their content (e.g., Portable Text detection).
87
+ * @param value - The field value to examine
88
+ * @param context - Transform context with field path, schema type, and document
89
+ * @returns true if this field should be transformed, false otherwise
90
+ */
91
+ valueMatcher?: (value: TOriginal, context: TransformContext) => boolean;
92
+ }
93
+
94
+ /**
95
+ * Transform definition for custom field handling.
96
+ *
97
+ * Transforms allow you to convert complex field values to simpler formats
98
+ * for translation, then restore the original structure after translation.
99
+ *
100
+ * @public
101
+ * @typeParam TOriginal - Type of the original field value
102
+ * @typeParam TTranslatable - Type of the translatable format (what gets sent to translation)
103
+ *
104
+ * @example Creating a type-safe transform for video captions
105
+ * ```typescript
106
+ * interface VTTCaption {
107
+ * startTime: number;
108
+ * endTime: number;
109
+ * text: string;
110
+ * }
111
+ *
112
+ * interface TranslatableCaption {
113
+ * _type: 'translatable.vtt';
114
+ * vtt: string;
115
+ * }
116
+ *
117
+ * const vttTransform: FieldTransform<VTTCaption[], TranslatableCaption> = {
118
+ * matcher: {
119
+ * schemaType: 'video',
120
+ * fieldName: 'captions',
121
+ * },
122
+ * toTranslatable: (captions) => ({
123
+ * _type: 'translatable.vtt',
124
+ * vtt: captions.map(c => `${c.startTime} --> ${c.endTime}\n${c.text}`).join('\n\n')
125
+ * }),
126
+ * fromTranslatable: (translated, original) => {
127
+ * // Parse VTT and restore original structure with translated text
128
+ * const lines = translated.vtt.split('\n\n');
129
+ * return original.map((caption, i) => ({
130
+ * ...caption,
131
+ * text: lines[i]?.split('\n')[1] || caption.text
132
+ * }));
133
+ * }
134
+ * };
135
+ * ```
136
+ */
137
+ export declare interface FieldTransform<
138
+ TOriginal = unknown,
139
+ TTranslatable = unknown,
140
+ > {
141
+ /**
142
+ * Criteria for matching fields that should be transformed
143
+ */
144
+ matcher: FieldMatcher<TOriginal>;
145
+ /**
146
+ * Transform complex field value to translatable format.
147
+ * This method is called before translation to simplify the field structure.
148
+ *
149
+ * @param value - Original field value (typed as TOriginal)
150
+ * @param context - Transform context with field path, schema type, and document
151
+ * @returns Transformed value for translation (typed as TTranslatable)
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * toTranslatable: (value: CustomField[], context) => {
156
+ * return {
157
+ * _type: 'translatable.custom',
158
+ * text: value.map(item => item.content).join('\n'),
159
+ * _metadata: value.map(item => ({ id: item.id }))
160
+ * };
161
+ * }
162
+ * ```
163
+ */
164
+ toTranslatable: (
165
+ value: TOriginal,
166
+ context: TransformContext,
167
+ ) => TTranslatable;
168
+ /**
169
+ * Transform translated value back to original format.
170
+ * This method is called after translation to restore the original structure
171
+ * while preserving the translated content.
172
+ *
173
+ * @param value - Translated value (typed as TTranslatable)
174
+ * @param originalValue - Original value before translation (typed as TOriginal)
175
+ * @param context - Transform context with field path, schema type, and document
176
+ * @returns Restored value with translated content (must match TOriginal type)
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * fromTranslatable: (translated: TranslatableCustom, original: CustomField[], context) => {
181
+ * const translatedLines = translated.text.split('\n');
182
+ * return original.map((item, index) => ({
183
+ * ...item, // Preserve original structure and metadata
184
+ * content: translatedLines[index] || item.content // Apply translated text
185
+ * }));
186
+ * }
187
+ * ```
188
+ */
189
+ fromTranslatable: (
190
+ value: TTranslatable,
191
+ originalValue: TOriginal,
192
+ context: TransformContext,
193
+ ) => TOriginal;
194
+ }
195
+
196
+ /**
197
+ * Result of filtering excluded fields from a document
198
+ * @internal
199
+ */
200
+ export declare interface FilteredDocument<T = any> {
201
+ /** Document with excluded fields removed */
202
+ filteredDocument: T;
203
+ /** Excluded field values that were removed */
204
+ excludedValues: Record<string, any>;
205
+ }
206
+
207
+ /**
208
+ * Recursively filters out excluded fields from a document
209
+ * @param document - The document to filter
210
+ * @param excludeFields - Array of field patterns to exclude. Supports:
211
+ * - Field names: 'status' (matches any field named 'status' anywhere)
212
+ * - Exact paths: 'meta.category' (matches only 'category' under 'meta')
213
+ * - Array paths: 'body[0].imageType' (matches 'imageType' in first array item)
214
+ * - Wildcard arrays: 'body[*].imageType' (matches 'imageType' in any array item)
215
+ * - Hierarchical: '**.status' (explicitly matches 'status' anywhere - same as just 'status')
216
+ * @param currentPath - Current path in the document (used internally)
217
+ * @returns Filtered document and excluded values
218
+ * @internal
219
+ */
220
+ export declare function filterExcludedFields<T extends Record<string, any>>(
221
+ document: T,
222
+ excludeFields?: string[],
223
+ currentPath?: string,
224
+ ): FilteredDocument<T>;
225
+
226
+ /**
227
+ * Helper type to infer the original type from a FieldTransform.
228
+ * Useful for type checking and creating wrapper functions.
229
+ *
230
+ * @public
231
+ * @typeParam T - The FieldTransform type to extract from
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * const myTransform: FieldTransform<MyType, TranslatableType> = { ... };
236
+ *
237
+ * // Inferred type will be MyType
238
+ * type OriginalType = InferOriginalType<typeof myTransform>;
239
+ *
240
+ * // Use in function signature
241
+ * function processOriginal(value: InferOriginalType<typeof myTransform>) {
242
+ * // value is typed as MyType
243
+ * }
244
+ * ```
245
+ */
246
+ export declare type InferOriginalType<T> =
247
+ T extends FieldTransform<infer O, unknown> ? O : never;
248
+
249
+ /**
250
+ * Helper type to infer the translatable type from a FieldTransform.
251
+ * Useful for type checking intermediate translation formats.
252
+ *
253
+ * @public
254
+ * @typeParam T - The FieldTransform type to extract from
255
+ *
256
+ * @example
257
+ * ```typescript
258
+ * const myTransform: FieldTransform<MyType, TranslatableType> = { ... };
259
+ *
260
+ * // Inferred type will be TranslatableType
261
+ * type TransType = InferTranslatableType<typeof myTransform>;
262
+ *
263
+ * // Use in function signature
264
+ * function processTranslatable(value: InferTranslatableType<typeof myTransform>) {
265
+ * // value is typed as TranslatableType
266
+ * }
267
+ * ```
268
+ */
269
+ export declare type InferTranslatableType<T> =
270
+ T extends FieldTransform<unknown, infer R> ? R : never;
271
+
15
272
  /**
16
273
  *
17
274
  * Configuration for the Sanity AI Translation plugin.
18
275
  * @public
19
276
  *
20
277
  */
21
- declare interface PluginConfig {
278
+ export declare interface PluginConfig {
22
279
  supportedLanguages: Array<{
23
280
  id: string;
24
281
  title: string;
@@ -30,12 +287,163 @@ declare interface PluginConfig {
30
287
  maxReferenceDepth?: number;
31
288
  openai?: {
32
289
  apiKey: string;
33
- model?: "gpt-5" | "gpt-4o" | "gpt-4-turbo" | "gpt-4";
34
- reasoningEffort?: "minimal" | "low" | "medium" | "high";
35
- verbosity?: "low" | "medium" | "high";
290
+ model?:
291
+ | "gpt-5.1"
292
+ | "gpt-5"
293
+ | "gpt-5-mini"
294
+ | "gpt-5-nano"
295
+ | "gpt-5-pro"
296
+ | "gpt-4o"
297
+ | "gpt-4-turbo"
298
+ | "gpt-4";
299
+ reasoningEffort?: "low" | "medium" | "high";
36
300
  enabled?: boolean;
37
301
  defaultMode?: TranslationMode;
38
302
  };
303
+ fieldTransforms?: AnyFieldTransform[];
304
+ /**
305
+ * Fields to exclude from translation. These fields will be preserved as-is in the translated document.
306
+ * Supports field names (e.g., 'status') and nested field paths (e.g., 'meta.category').
307
+ * Useful for enum fields, IDs, technical values, and other non-translatable content.
308
+ *
309
+ * @example
310
+ * ```typescript
311
+ * excludeFields: ['status', 'category', 'type', 'meta.priority', 'config.mode']
312
+ * ```
313
+ */
314
+ excludeFields?: string[];
315
+ }
316
+
317
+ /**
318
+ * Predefined exclusion lists for common use cases
319
+ * @internal
320
+ */
321
+ export declare const PREDEFINED_EXCLUSIONS: {
322
+ /** Common enum and status fields */
323
+ readonly enums: readonly [
324
+ "status",
325
+ "state",
326
+ "type",
327
+ "category",
328
+ "priority",
329
+ "visibility",
330
+ "role",
331
+ "permission",
332
+ "level",
333
+ "mode",
334
+ "format",
335
+ "variant",
336
+ "size",
337
+ "theme",
338
+ "kind",
339
+ "phase",
340
+ "stage",
341
+ ];
342
+ /** All metadata and system fields (starting with _) */
343
+ readonly metadata: readonly [
344
+ "_id",
345
+ "_type",
346
+ "_rev",
347
+ "_createdAt",
348
+ "_updatedAt",
349
+ ];
350
+ /** Common technical and configuration fields */
351
+ readonly technical: readonly [
352
+ "slug",
353
+ "handle",
354
+ "key",
355
+ "code",
356
+ "id",
357
+ "uuid",
358
+ "token",
359
+ "hash",
360
+ "checksum",
361
+ "version",
362
+ "config",
363
+ "settings",
364
+ ];
365
+ /** SEO and URL-related fields that often shouldn't be translated */
366
+ readonly seo: readonly [
367
+ "slug",
368
+ "handle",
369
+ "permalink",
370
+ "canonical",
371
+ "redirect",
372
+ "robots",
373
+ "sitemap",
374
+ ];
375
+ };
376
+
377
+ /**
378
+ * Restores previously excluded fields to a translated document
379
+ * @param translatedDocument - The translated document
380
+ * @param excludedValues - The excluded field values to restore
381
+ * @returns Document with excluded fields restored
382
+ * @internal
383
+ */
384
+ export declare function restoreExcludedFields<T extends Record<string, any>>(
385
+ translatedDocument: T,
386
+ excludedValues: Record<string, any>,
387
+ ): T;
388
+
389
+ /**
390
+ * Transform for handling Portable Text (rich text) content using XML-like serialization.
391
+ * This leverages the existing serialization utilities to convert complex rich text
392
+ * to a format that's easier for translation models to process.
393
+ *
394
+ * Uses value-based detection to identify Portable Text arrays (checking for _type === 'block').
395
+ * The fieldPath pattern is used as a performance optimization to avoid checking every field,
396
+ * but the valueMatcher is the definitive check.
397
+ *
398
+ * @public
399
+ */
400
+ export declare const richTextTransform: FieldTransform<
401
+ unknown[],
402
+ TranslatableRichText
403
+ >;
404
+
405
+ /**
406
+ * Context passed to transform functions.
407
+ * @public
408
+ */
409
+ export declare interface TransformContext {
410
+ /**
411
+ * Current field path
412
+ */
413
+ fieldPath: string;
414
+ /**
415
+ * Document schema type
416
+ */
417
+ schemaType: string;
418
+ /**
419
+ * Full document being processed
420
+ */
421
+ document: TranslatableDocument;
422
+ }
423
+
424
+ declare interface TranslatableDocument extends SanityDocument {
425
+ language: string;
426
+ }
427
+
428
+ /**
429
+ * Special type to track transformed rich text content.
430
+ * This separates the serialized XML content from regular strings
431
+ * so the main serialization can handle it correctly.
432
+ */
433
+ declare interface TranslatableRichText {
434
+ _type: "translatable.richtext";
435
+ translationText: string;
436
+ _originalValue: unknown[];
437
+ }
438
+
439
+ /**
440
+ * Special type to track transformed VTT captions.
441
+ * This combines metadata separation for token efficiency with XML format for translation.
442
+ */
443
+ declare interface TranslatableVTTCaptions {
444
+ _type: "translatable.vtt.captions";
445
+ translationText: string;
446
+ _originalMetadata: VTTCaptionMetadata[];
39
447
  }
40
448
 
41
449
  /**
@@ -56,4 +464,40 @@ declare type TranslationMode =
56
464
  | "openai-only"
57
465
  | "sanity-with-review";
58
466
 
467
+ /**
468
+ * Represents a VTT caption cue with timing and text content.
469
+ */
470
+ declare interface VTTCaption {
471
+ _key?: string;
472
+ _type?: string;
473
+ text?: string;
474
+ startTime?: number;
475
+ endTime?: number;
476
+ [key: string]: string | number | undefined;
477
+ }
478
+
479
+ /**
480
+ * VTT caption metadata without text content, used for preservation during translation.
481
+ */
482
+ declare interface VTTCaptionMetadata {
483
+ _key?: string;
484
+ _type?: string;
485
+ startTime?: number;
486
+ endTime?: number;
487
+ _originalIndex: number;
488
+ [key: string]: string | number | undefined;
489
+ }
490
+
491
+ /**
492
+ * Transform for handling VTT captions in video documents using a hybrid approach.
493
+ * This stores metadata separately for token efficiency while formatting
494
+ * text content as XML for better translation results.
495
+ *
496
+ * @public
497
+ */
498
+ export declare const vttCaptionTransform: FieldTransform<
499
+ VTTCaption[],
500
+ TranslatableVTTCaptions
501
+ >;
502
+
59
503
  export {};