@purposeinplay/payload-ai-translate 0.1.0

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 (301) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +714 -0
  3. package/dist/alerts-collection.d.ts +21 -0
  4. package/dist/alerts-collection.js +159 -0
  5. package/dist/api.d.ts +4 -0
  6. package/dist/api.js +918 -0
  7. package/dist/bulk-translate-batches-collection.d.ts +29 -0
  8. package/dist/bulk-translate-batches-collection.js +404 -0
  9. package/dist/bulk-translate-units-collection.d.ts +35 -0
  10. package/dist/bulk-translate-units-collection.js +310 -0
  11. package/dist/client/estimated-cost-cell.d.ts +6 -0
  12. package/dist/client/estimated-cost-cell.js +12 -0
  13. package/dist/client/excluded-fields-field.d.ts +45 -0
  14. package/dist/client/excluded-fields-field.js +553 -0
  15. package/dist/client/field-translate-button.d.ts +6 -0
  16. package/dist/client/field-translate-button.js +199 -0
  17. package/dist/client/index.d.ts +6 -0
  18. package/dist/client/index.js +6 -0
  19. package/dist/client/lib/use-global-kill-switches.d.ts +20 -0
  20. package/dist/client/lib/use-global-kill-switches.js +58 -0
  21. package/dist/client/translate-button.d.ts +2 -0
  22. package/dist/client/translate-button.js +228 -0
  23. package/dist/client/translate-modal.d.ts +16 -0
  24. package/dist/client/translate-modal.js +549 -0
  25. package/dist/client/translation-progress.d.ts +10 -0
  26. package/dist/client/translation-progress.js +297 -0
  27. package/dist/components/TranslationNavGroup.d.ts +45 -0
  28. package/dist/components/TranslationNavGroup.js +104 -0
  29. package/dist/defaults.d.ts +11 -0
  30. package/dist/defaults.js +16 -0
  31. package/dist/endpoints/client-config.d.ts +44 -0
  32. package/dist/endpoints/client-config.js +145 -0
  33. package/dist/endpoints/estimate.d.ts +5 -0
  34. package/dist/endpoints/estimate.js +237 -0
  35. package/dist/endpoints/progress.d.ts +2 -0
  36. package/dist/endpoints/progress.js +314 -0
  37. package/dist/endpoints/translate.d.ts +11 -0
  38. package/dist/endpoints/translate.js +376 -0
  39. package/dist/endpoints/translation-hub/_helpers.d.ts +140 -0
  40. package/dist/endpoints/translation-hub/_helpers.js +297 -0
  41. package/dist/endpoints/translation-hub/active.d.ts +21 -0
  42. package/dist/endpoints/translation-hub/active.js +220 -0
  43. package/dist/endpoints/translation-hub/cancel.d.ts +22 -0
  44. package/dist/endpoints/translation-hub/cancel.js +233 -0
  45. package/dist/endpoints/translation-hub/enqueue.d.ts +70 -0
  46. package/dist/endpoints/translation-hub/enqueue.js +529 -0
  47. package/dist/endpoints/translation-hub/failures.d.ts +12 -0
  48. package/dist/endpoints/translation-hub/failures.js +67 -0
  49. package/dist/endpoints/translation-hub/force-reset.d.ts +20 -0
  50. package/dist/endpoints/translation-hub/force-reset.js +144 -0
  51. package/dist/endpoints/translation-hub/index.d.ts +21 -0
  52. package/dist/endpoints/translation-hub/index.js +20 -0
  53. package/dist/endpoints/translation-hub/list.d.ts +40 -0
  54. package/dist/endpoints/translation-hub/list.js +182 -0
  55. package/dist/endpoints/translation-hub/preflight.d.ts +19 -0
  56. package/dist/endpoints/translation-hub/preflight.js +141 -0
  57. package/dist/endpoints/translation-hub/retry-failed.d.ts +38 -0
  58. package/dist/endpoints/translation-hub/retry-failed.js +235 -0
  59. package/dist/endpoints/translation-hub/revert.d.ts +88 -0
  60. package/dist/endpoints/translation-hub/revert.js +405 -0
  61. package/dist/endpoints/translation-hub/status.d.ts +45 -0
  62. package/dist/endpoints/translation-hub/status.js +391 -0
  63. package/dist/endpoints/translation-hub/usage-summary.d.ts +114 -0
  64. package/dist/endpoints/translation-hub/usage-summary.js +481 -0
  65. package/dist/exports/client.d.ts +6 -0
  66. package/dist/exports/client.js +6 -0
  67. package/dist/exports/components.d.ts +6 -0
  68. package/dist/exports/components.js +5 -0
  69. package/dist/exports/index.d.ts +8 -0
  70. package/dist/exports/index.js +7 -0
  71. package/dist/exports/providers.d.ts +9 -0
  72. package/dist/exports/providers.js +5 -0
  73. package/dist/exports/views-client.d.ts +23 -0
  74. package/dist/exports/views-client.js +22 -0
  75. package/dist/exports/views.d.ts +30 -0
  76. package/dist/exports/views.js +29 -0
  77. package/dist/hooks/after-change-global.d.ts +4 -0
  78. package/dist/hooks/after-change-global.js +109 -0
  79. package/dist/hooks/after-change.d.ts +16 -0
  80. package/dist/hooks/after-change.js +205 -0
  81. package/dist/hooks/after-delete.d.ts +30 -0
  82. package/dist/hooks/after-delete.js +95 -0
  83. package/dist/index.d.ts +5 -0
  84. package/dist/index.js +5 -0
  85. package/dist/jobs-collection.d.ts +17 -0
  86. package/dist/jobs-collection.js +139 -0
  87. package/dist/lexical/classifier.d.ts +3 -0
  88. package/dist/lexical/classifier.js +108 -0
  89. package/dist/lexical/deserializer.d.ts +4 -0
  90. package/dist/lexical/deserializer.js +263 -0
  91. package/dist/lexical/placeholder-integrity.d.ts +6 -0
  92. package/dist/lexical/placeholder-integrity.js +21 -0
  93. package/dist/lexical/placeholders.d.ts +21 -0
  94. package/dist/lexical/placeholders.js +117 -0
  95. package/dist/lexical/serializer.d.ts +21 -0
  96. package/dist/lexical/serializer.js +233 -0
  97. package/dist/lexical/types.d.ts +32 -0
  98. package/dist/lexical/types.js +1 -0
  99. package/dist/lib/auth-diagnostics.d.ts +14 -0
  100. package/dist/lib/auth-diagnostics.js +19 -0
  101. package/dist/lib/batch-counts.d.ts +58 -0
  102. package/dist/lib/batch-counts.js +105 -0
  103. package/dist/lib/bulk-translate-migrations.d.ts +92 -0
  104. package/dist/lib/bulk-translate-migrations.js +153 -0
  105. package/dist/lib/coalescing-queue.d.ts +38 -0
  106. package/dist/lib/coalescing-queue.js +69 -0
  107. package/dist/lib/content-extractor.d.ts +16 -0
  108. package/dist/lib/content-extractor.js +410 -0
  109. package/dist/lib/content-hash.d.ts +1 -0
  110. package/dist/lib/content-hash.js +19 -0
  111. package/dist/lib/content-patcher.d.ts +15 -0
  112. package/dist/lib/content-patcher.js +293 -0
  113. package/dist/lib/cost-guards.d.ts +2 -0
  114. package/dist/lib/cost-guards.js +18 -0
  115. package/dist/lib/daily-spend-cap.d.ts +58 -0
  116. package/dist/lib/daily-spend-cap.js +233 -0
  117. package/dist/lib/effective-locales.d.ts +181 -0
  118. package/dist/lib/effective-locales.js +302 -0
  119. package/dist/lib/error-messages.d.ts +245 -0
  120. package/dist/lib/error-messages.js +626 -0
  121. package/dist/lib/events.d.ts +39 -0
  122. package/dist/lib/events.js +146 -0
  123. package/dist/lib/exclude-fields.d.ts +3 -0
  124. package/dist/lib/exclude-fields.js +64 -0
  125. package/dist/lib/field-breadcrumb.d.ts +31 -0
  126. package/dist/lib/field-breadcrumb.js +227 -0
  127. package/dist/lib/field-diff.d.ts +1 -0
  128. package/dist/lib/field-diff.js +25 -0
  129. package/dist/lib/field-empty.d.ts +2 -0
  130. package/dist/lib/field-empty.js +68 -0
  131. package/dist/lib/field-resolver.d.ts +3 -0
  132. package/dist/lib/field-resolver.js +164 -0
  133. package/dist/lib/group-soft-skips.d.ts +39 -0
  134. package/dist/lib/group-soft-skips.js +45 -0
  135. package/dist/lib/locale-merge.d.ts +44 -0
  136. package/dist/lib/locale-merge.js +357 -0
  137. package/dist/lib/locale-row-check.d.ts +30 -0
  138. package/dist/lib/locale-row-check.js +64 -0
  139. package/dist/lib/logger.d.ts +74 -0
  140. package/dist/lib/logger.js +97 -0
  141. package/dist/lib/manual-edit-guard.d.ts +128 -0
  142. package/dist/lib/manual-edit-guard.js +393 -0
  143. package/dist/lib/output-validation.d.ts +48 -0
  144. package/dist/lib/output-validation.js +148 -0
  145. package/dist/lib/payload-read.d.ts +16 -0
  146. package/dist/lib/payload-read.js +51 -0
  147. package/dist/lib/per-doc-claim.d.ts +90 -0
  148. package/dist/lib/per-doc-claim.js +140 -0
  149. package/dist/lib/per-doc-lock.d.ts +94 -0
  150. package/dist/lib/per-doc-lock.js +119 -0
  151. package/dist/lib/persist-usage.d.ts +91 -0
  152. package/dist/lib/persist-usage.js +116 -0
  153. package/dist/lib/progress-store.d.ts +103 -0
  154. package/dist/lib/progress-store.js +314 -0
  155. package/dist/lib/rate-limiter.d.ts +3 -0
  156. package/dist/lib/rate-limiter.js +53 -0
  157. package/dist/lib/snapshot-select.d.ts +43 -0
  158. package/dist/lib/snapshot-select.js +108 -0
  159. package/dist/lib/translate-prompt.d.ts +31 -0
  160. package/dist/lib/translate-prompt.js +66 -0
  161. package/dist/lib/translation-token-bucket.d.ts +57 -0
  162. package/dist/lib/translation-token-bucket.js +365 -0
  163. package/dist/lib/truncate-source-value.d.ts +1 -0
  164. package/dist/lib/truncate-source-value.js +27 -0
  165. package/dist/manual-edit-collection.d.ts +22 -0
  166. package/dist/manual-edit-collection.js +124 -0
  167. package/dist/plugin.d.ts +3 -0
  168. package/dist/plugin.js +934 -0
  169. package/dist/providers/ai-sdk-adapter.d.ts +35 -0
  170. package/dist/providers/ai-sdk-adapter.js +100 -0
  171. package/dist/providers/anthropic.d.ts +31 -0
  172. package/dist/providers/anthropic.js +66 -0
  173. package/dist/providers/custom.d.ts +36 -0
  174. package/dist/providers/custom.js +24 -0
  175. package/dist/providers/gemini.d.ts +20 -0
  176. package/dist/providers/gemini.js +48 -0
  177. package/dist/providers/mock.d.ts +2 -0
  178. package/dist/providers/mock.js +29 -0
  179. package/dist/providers/openai.d.ts +28 -0
  180. package/dist/providers/openai.js +69 -0
  181. package/dist/settings-global.d.ts +74 -0
  182. package/dist/settings-global.js +216 -0
  183. package/dist/tasks/bulk-translate-coordinator.d.ts +115 -0
  184. package/dist/tasks/bulk-translate-coordinator.js +708 -0
  185. package/dist/tasks/bulk-translate-doc-task.d.ts +142 -0
  186. package/dist/tasks/bulk-translate-doc-task.js +1000 -0
  187. package/dist/tasks/bulk-translate-janitor.d.ts +87 -0
  188. package/dist/tasks/bulk-translate-janitor.js +311 -0
  189. package/dist/tasks/translate-job-task.d.ts +51 -0
  190. package/dist/tasks/translate-job-task.js +154 -0
  191. package/dist/translate.d.ts +113 -0
  192. package/dist/translate.js +911 -0
  193. package/dist/translation-daily-spend-collection.d.ts +24 -0
  194. package/dist/translation-daily-spend-collection.js +133 -0
  195. package/dist/translation-rate-limits-collection.d.ts +30 -0
  196. package/dist/translation-rate-limits-collection.js +144 -0
  197. package/dist/types.d.ts +672 -0
  198. package/dist/types.js +1 -0
  199. package/dist/usage-collection.d.ts +14 -0
  200. package/dist/usage-collection.js +377 -0
  201. package/dist/views/BulkRunsHub/BatchRow.d.ts +32 -0
  202. package/dist/views/BulkRunsHub/BatchRow.js +1222 -0
  203. package/dist/views/BulkRunsHub/BucketRow.d.ts +62 -0
  204. package/dist/views/BulkRunsHub/BucketRow.js +982 -0
  205. package/dist/views/BulkRunsHub/BulkRunsHub.client.d.ts +18 -0
  206. package/dist/views/BulkRunsHub/BulkRunsHub.client.js +331 -0
  207. package/dist/views/BulkRunsHub/EmptyState.d.ts +6 -0
  208. package/dist/views/BulkRunsHub/EmptyState.js +64 -0
  209. package/dist/views/BulkRunsHub/FilterBar.d.ts +16 -0
  210. package/dist/views/BulkRunsHub/FilterBar.js +284 -0
  211. package/dist/views/BulkRunsHub/InFlightBanner.d.ts +14 -0
  212. package/dist/views/BulkRunsHub/InFlightBanner.js +59 -0
  213. package/dist/views/BulkRunsHub/StatusBadge.d.ts +64 -0
  214. package/dist/views/BulkRunsHub/StatusBadge.js +248 -0
  215. package/dist/views/BulkRunsHub/SummaryStrip.d.ts +22 -0
  216. package/dist/views/BulkRunsHub/SummaryStrip.js +249 -0
  217. package/dist/views/BulkRunsHub/bucket-grouping.d.ts +200 -0
  218. package/dist/views/BulkRunsHub/bucket-grouping.js +344 -0
  219. package/dist/views/BulkRunsHub/bucketFailureSummary.d.ts +9 -0
  220. package/dist/views/BulkRunsHub/bucketFailureSummary.js +36 -0
  221. package/dist/views/BulkRunsHub/dedupedStatusFetch.d.ts +5 -0
  222. package/dist/views/BulkRunsHub/dedupedStatusFetch.js +45 -0
  223. package/dist/views/BulkRunsHub/index.d.ts +17 -0
  224. package/dist/views/BulkRunsHub/index.js +80 -0
  225. package/dist/views/BulkRunsHub/urlFilters.d.ts +14 -0
  226. package/dist/views/BulkRunsHub/urlFilters.js +50 -0
  227. package/dist/views/BulkRunsHub/useBulkRunsList.d.ts +26 -0
  228. package/dist/views/BulkRunsHub/useBulkRunsList.js +204 -0
  229. package/dist/views/BulkRunsHub/useUrlFilters.d.ts +10 -0
  230. package/dist/views/BulkRunsHub/useUrlFilters.js +88 -0
  231. package/dist/views/TranslationHub/ActiveJobs.d.ts +6 -0
  232. package/dist/views/TranslationHub/ActiveJobs.js +320 -0
  233. package/dist/views/TranslationHub/AdvancedPanel.d.ts +17 -0
  234. package/dist/views/TranslationHub/AdvancedPanel.js +996 -0
  235. package/dist/views/TranslationHub/AlertBanner.d.ts +6 -0
  236. package/dist/views/TranslationHub/AlertBanner.js +568 -0
  237. package/dist/views/TranslationHub/AuditPanel.d.ts +6 -0
  238. package/dist/views/TranslationHub/AuditPanel.helpers.d.ts +44 -0
  239. package/dist/views/TranslationHub/AuditPanel.helpers.js +71 -0
  240. package/dist/views/TranslationHub/AuditPanel.js +1367 -0
  241. package/dist/views/TranslationHub/BulkTranslate.types.d.ts +242 -0
  242. package/dist/views/TranslationHub/BulkTranslate.types.js +36 -0
  243. package/dist/views/TranslationHub/BulkTranslateFailureDrawer.d.ts +19 -0
  244. package/dist/views/TranslationHub/BulkTranslateFailureDrawer.js +332 -0
  245. package/dist/views/TranslationHub/BulkTranslateMonitor.d.ts +28 -0
  246. package/dist/views/TranslationHub/BulkTranslateMonitor.js +305 -0
  247. package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.d.ts +3 -0
  248. package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.js +42 -0
  249. package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.d.ts +26 -0
  250. package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.js +95 -0
  251. package/dist/views/TranslationHub/BulkTranslatePreflightModal.d.ts +22 -0
  252. package/dist/views/TranslationHub/BulkTranslatePreflightModal.js +879 -0
  253. package/dist/views/TranslationHub/BulkTranslateTerminalCard.d.ts +29 -0
  254. package/dist/views/TranslationHub/BulkTranslateTerminalCard.js +445 -0
  255. package/dist/views/TranslationHub/BulkTranslateTrigger.d.ts +66 -0
  256. package/dist/views/TranslationHub/BulkTranslateTrigger.js +161 -0
  257. package/dist/views/TranslationHub/EditorRecentRunsPanel.d.ts +33 -0
  258. package/dist/views/TranslationHub/EditorRecentRunsPanel.js +290 -0
  259. package/dist/views/TranslationHub/Hub.client.d.ts +74 -0
  260. package/dist/views/TranslationHub/Hub.client.js +357 -0
  261. package/dist/views/TranslationHub/ModelCombobox.d.ts +14 -0
  262. package/dist/views/TranslationHub/ModelCombobox.js +415 -0
  263. package/dist/views/TranslationHub/PerCollectionConfig.d.ts +10 -0
  264. package/dist/views/TranslationHub/PerCollectionConfig.helpers.d.ts +16 -0
  265. package/dist/views/TranslationHub/PerCollectionConfig.helpers.js +19 -0
  266. package/dist/views/TranslationHub/PerCollectionConfig.js +759 -0
  267. package/dist/views/TranslationHub/SettingsRail.d.ts +11 -0
  268. package/dist/views/TranslationHub/SettingsRail.js +382 -0
  269. package/dist/views/TranslationHub/StatusStrip.d.ts +6 -0
  270. package/dist/views/TranslationHub/StatusStrip.js +451 -0
  271. package/dist/views/TranslationHub/UsageTable.d.ts +6 -0
  272. package/dist/views/TranslationHub/UsageTable.helpers.d.ts +69 -0
  273. package/dist/views/TranslationHub/UsageTable.helpers.js +49 -0
  274. package/dist/views/TranslationHub/UsageTable.js +1240 -0
  275. package/dist/views/TranslationHub/alertGrouping.d.ts +70 -0
  276. package/dist/views/TranslationHub/alertGrouping.js +99 -0
  277. package/dist/views/TranslationHub/index.d.ts +20 -0
  278. package/dist/views/TranslationHub/index.js +109 -0
  279. package/dist/views/TranslationHub/tabNavigation.d.ts +53 -0
  280. package/dist/views/TranslationHub/tabNavigation.js +74 -0
  281. package/dist/views/TranslationHub/terminalBannerVisibility.d.ts +33 -0
  282. package/dist/views/TranslationHub/terminalBannerVisibility.js +124 -0
  283. package/dist/views/TranslationHub/useBulkTranslateActive.d.ts +49 -0
  284. package/dist/views/TranslationHub/useBulkTranslateActive.js +251 -0
  285. package/dist/views/TranslationHub/useFocusTrap.d.ts +6 -0
  286. package/dist/views/TranslationHub/useFocusTrap.js +81 -0
  287. package/dist/views/TranslationHub/useTranslationHubUsageSummary.d.ts +77 -0
  288. package/dist/views/TranslationHub/useTranslationHubUsageSummary.js +267 -0
  289. package/dist/views/shared/EditorError.d.ts +97 -0
  290. package/dist/views/shared/EditorError.js +205 -0
  291. package/dist/views/shared/ModelCell.d.ts +18 -0
  292. package/dist/views/shared/ModelCell.js +31 -0
  293. package/dist/views/shared/docHref.d.ts +16 -0
  294. package/dist/views/shared/docHref.js +26 -0
  295. package/dist/views/shared/fetch-error-body.d.ts +25 -0
  296. package/dist/views/shared/fetch-error-body.js +42 -0
  297. package/dist/views/shared/filterPillStyle.d.ts +35 -0
  298. package/dist/views/shared/filterPillStyle.js +40 -0
  299. package/dist/views/shared/format.d.ts +75 -0
  300. package/dist/views/shared/format.js +131 -0
  301. package/package.json +141 -0
@@ -0,0 +1,672 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ export type TranslationItemKind = 'plain' | 'inline' | 'block';
3
+ export type TranslationItem = {
4
+ id: string;
5
+ text: string;
6
+ kind: TranslationItemKind;
7
+ };
8
+ export type TranslationContext = {
9
+ collectionSlug: string;
10
+ fieldPath: string;
11
+ documentTitle?: string;
12
+ formality?: 'formal' | 'informal';
13
+ hints?: Record<string, string>;
14
+ };
15
+ export type TranslateRequest = {
16
+ items: TranslationItem[];
17
+ sourceLocale: string;
18
+ targetLocale: string;
19
+ context: TranslationContext;
20
+ glossary?: Record<string, string>;
21
+ signal?: AbortSignal;
22
+ };
23
+ export type TranslationUsage = {
24
+ inputTokens: number;
25
+ outputTokens: number;
26
+ estimatedCostUsd?: number;
27
+ /**
28
+ * Provider-reported model id (e.g. `gpt-4o-mini`). Bubbled up from the
29
+ * provider's TranslateResponse so persistence sinks can record which
30
+ * model billed the work. Optional because some custom providers may
31
+ * not return a model name.
32
+ */
33
+ model?: string;
34
+ };
35
+ export type TranslatedItem = {
36
+ id: string;
37
+ text: string;
38
+ };
39
+ export type TranslateResponse = {
40
+ items: TranslatedItem[];
41
+ usage: TranslationUsage;
42
+ model: string;
43
+ latencyMs: number;
44
+ };
45
+ export type TranslationEstimate = {
46
+ inputTokens: number;
47
+ estimatedCostUsd?: number;
48
+ };
49
+ export type TranslationProvider = {
50
+ translate(request: TranslateRequest): Promise<TranslateResponse>;
51
+ estimate?(request: TranslateRequest): Promise<TranslationEstimate>;
52
+ /**
53
+ * Configured model identifier. Used by usage tracking to record what
54
+ * model would have run, even when `translate()` throws before the
55
+ * provider gets a response back. Optional for backwards compatibility
56
+ * with custom providers; built-in providers always set it.
57
+ */
58
+ model?: string;
59
+ };
60
+ export type TargetPolicy = 'mirror' | 'preserve';
61
+ export type CostLimits = {
62
+ perCallCharLimit: number;
63
+ perDocCharCeiling: number;
64
+ bulkConfirmUsdThreshold: number;
65
+ /**
66
+ * Maximum translation units per provider call, independent of the char
67
+ * cap. Defaults to 500 (see `DEFAULT_PER_CALL_ITEM_LIMIT` in
68
+ * `translate.ts`). Exists because some providers' structured-output
69
+ * mode (notably Gemini 2.5 Flash via OpenRouter) silently stalls on
70
+ * response generation when the response items array grows past
71
+ * roughly 800-1000 entries — HTTP 200 returns in <2s but the body
72
+ * never finishes streaming, hitting the per-call timeout and failing
73
+ * the whole batch. The char cap doesn't catch this because short
74
+ * strings (nav labels, JSON keys) pack many items into modest byte
75
+ * counts: a translation-keys global with 1107 short strings fits
76
+ * comfortably in a 50k-char batch but trips the stall every time.
77
+ *
78
+ * Raise this only if you've verified your chosen model + structured
79
+ * output mode handles larger response arrays reliably.
80
+ */
81
+ perCallItemLimit?: number;
82
+ };
83
+ export type ConcurrencyLimits = {
84
+ perDocument: number;
85
+ perProvider: number;
86
+ };
87
+ export type RetryConfig = {
88
+ attempts: number;
89
+ backoffMs: number;
90
+ };
91
+ export type LexicalNodeRegistration = {
92
+ type: string;
93
+ translatable: boolean;
94
+ translatableAttributes?: string[];
95
+ };
96
+ export type AutomationConfig = {
97
+ trigger?: 'on-change' | 'on-publish';
98
+ mode?: 'async' | 'inline';
99
+ targetPolicy?: TargetPolicy;
100
+ diffOnly?: boolean;
101
+ coalescingWindowMs?: number;
102
+ };
103
+ export type AITranslatePluginConfig = {
104
+ collections?: string[];
105
+ globals?: string[];
106
+ sourceLocale: string;
107
+ targetLocales: string[];
108
+ /**
109
+ * Default provider used when no per-runtime override is configured.
110
+ * Required so the plugin can always translate, even before an admin
111
+ * has touched the `translation-settings` global.
112
+ */
113
+ provider: TranslationProvider;
114
+ /**
115
+ * Optional: a map of named providers that admins can choose between
116
+ * at runtime via the auto-registered `translation-settings` global.
117
+ *
118
+ * Each key becomes an option in the admin's "Active provider" select.
119
+ * Each value is a fully-built `TranslationProvider` (vendor + model
120
+ * baked in) — to expose multiple models for the same vendor, register
121
+ * them under different keys (e.g. `openai-mini`, `openai-4o`).
122
+ *
123
+ * When this map is non-empty, the plugin auto-registers the
124
+ * `translation-settings` global. When the global's `activeProvider`
125
+ * matches a key, that provider is used. Otherwise the plugin falls
126
+ * back to `provider`.
127
+ */
128
+ providers?: Record<string, TranslationProvider>;
129
+ costLimits: CostLimits;
130
+ excludeFields?: string[];
131
+ /**
132
+ * When true (default), URL-shaped paths (`**.url`) are appended to
133
+ * `excludeFields` so URL strings never reach the LLM. Sending URLs to
134
+ * the model wastes tokens and trips the verbatim-echo validator (a
135
+ * URL "translation" is the same URL). Set to `false` to translate
136
+ * URL fields anyway (rare — useful for locale-specific deep links).
137
+ */
138
+ excludeUrlFields?: boolean;
139
+ concurrency?: Partial<ConcurrencyLimits>;
140
+ retry?: Partial<RetryConfig>;
141
+ access?: {
142
+ /**
143
+ * Gate the `/ai-translate` and `/ai-translate/estimate` endpoints.
144
+ * Returns `true` to allow, `false` to return HTTP 403.
145
+ *
146
+ * **IMPORTANT:** the plugin's default behavior is "any authenticated
147
+ * user" — the built-in `aiTranslateAccess` helper (when consumers
148
+ * wire it via `access: { translate: aiTranslateAccess }`) only
149
+ * enforces a per-user rate limit, NOT a role check. If your CMS
150
+ * has editors vs admins, wrap the function with your role check:
151
+ *
152
+ * ```ts
153
+ * access: {
154
+ * translate: ({ req }) => {
155
+ * if (!req.user?.roles?.includes('admin')) return false;
156
+ * return aiTranslateAccess({ req });
157
+ * },
158
+ * }
159
+ * ```
160
+ *
161
+ * Translation requests can spend real money (LLM tokens) so the
162
+ * minimum default is intentionally permissive only because most
163
+ * consumers run a single-role admin setup. Multi-role consumers
164
+ * MUST add a role gate.
165
+ */
166
+ translate?: (args: {
167
+ req: PayloadRequest;
168
+ }) => boolean | Promise<boolean>;
169
+ };
170
+ onEvent?: (event: TranslationEvent) => void | Promise<void>;
171
+ onAlert?: (alert: TranslationAlert) => void | Promise<void>;
172
+ lexicalNodes?: LexicalNodeRegistration[];
173
+ enabled?: boolean;
174
+ automation?: AutomationConfig;
175
+ /** Show a translate button next to each localized field in the edit view. */
176
+ perFieldButton?: boolean;
177
+ /**
178
+ * Role names that count as "admin" for the plugin's admin-only field
179
+ * gates (the `_aiTranslateOptOut` checkbox and `_aiTranslateAutoLocales`
180
+ * select injected on every tracked surface). A user whose `roles`
181
+ * array intersects this list can edit those fields; others get
182
+ * read-only widgets.
183
+ *
184
+ * Defaults to `['admin']`. Set to your consumer's actual admin role
185
+ * value(s) if you use a different naming convention (e.g.
186
+ * `['super-admin', 'cms-admin']`). Pass `[]` to make the fields
187
+ * editable by everyone (NOT recommended — these fields gate LLM
188
+ * spend on a per-document basis).
189
+ *
190
+ * Note: this option controls FIELD-LEVEL admin gating only. The
191
+ * separate `access.translate` callback controls who can hit the
192
+ * manual translate endpoint — those concerns are intentionally
193
+ * decoupled so consumers can grant translate-button access to
194
+ * editors while keeping per-doc opt-out admin-only.
195
+ */
196
+ adminRoles?: string[];
197
+ quality?: QualityConfig;
198
+ /**
199
+ * Persist a row per translation job (succeeded or failed) into an
200
+ * auto-registered collection. Mirrors how `auditLogPlugin` registers its
201
+ * own `audit-logs` collection. Off by default — opt in via `enabled: true`.
202
+ */
203
+ usageTracking?: UsageTrackingConfig;
204
+ /**
205
+ * Override the auto-registered `translation-alerts` collection slug.
206
+ * The alerts collection is auto-registered when `usageTracking.enabled`
207
+ * is true (they're sibling features powering the Translation Hub).
208
+ * Defaults to `'translation-alerts'`. Holds rows for every
209
+ * `cost-guard-abort`, `persistent-failure`, and `provider-outage`
210
+ * event so the Hub can surface them in-app (consumer's `onAlert`
211
+ * callback continues to fire regardless).
212
+ */
213
+ alertsCollectionSlug?: string;
214
+ /**
215
+ * Detect manual edits to target-locale rows and skip overwriting them
216
+ * on the next translation pass. When enabled, the plugin auto-
217
+ * registers an `ai-translate-meta` sidecar collection holding a
218
+ * per-(collection, doc, locale, field) hash of the value it last
219
+ * wrote. Before each subsequent write, the plugin compares the
220
+ * current target value's hash to the stored one — divergence ⇒ a
221
+ * human edited the row, the field is skipped.
222
+ *
223
+ * Off by default to preserve the existing "always overwrite" behavior.
224
+ * Recommended for sites where editors hand-fix machine translations
225
+ * and want their edits preserved across publishes.
226
+ */
227
+ preserveManualEdits?: boolean;
228
+ /**
229
+ * Override the auto-registered sidecar collection slug used by
230
+ * `preserveManualEdits`. Defaults to `ai-translate-meta`. Only
231
+ * meaningful when `preserveManualEdits: true`.
232
+ */
233
+ manualEditCollectionSlug?: string;
234
+ /**
235
+ * Persist the in-memory job progress store to an auto-registered
236
+ * `ai-translate-jobs` sidecar collection. When enabled, every
237
+ * `createJob` / `updateJob` / `cleanup` operation mirrors to the
238
+ * database so admins can see what was in flight after a server
239
+ * restart.
240
+ *
241
+ * **NOT a full work-queue.** The translation closures themselves
242
+ * (req, doc, previousDoc) still live in process memory. A restart
243
+ * still drops the in-flight LLM call — this option just preserves
244
+ * the STATUS so admins can spot the ghost job and re-trigger.
245
+ *
246
+ * Off by default.
247
+ */
248
+ persistJobs?: boolean;
249
+ /**
250
+ * Override the auto-registered jobs-collection slug used by
251
+ * `persistJobs`. Defaults to `ai-translate-jobs`. Only meaningful
252
+ * when `persistJobs: true`.
253
+ */
254
+ jobsCollectionSlug?: string;
255
+ /**
256
+ * Override defaults for the auto-registered `translation-settings`
257
+ * global. Only meaningful when `providers` is configured.
258
+ */
259
+ settings?: import('./settings-global.js').TranslationSettingsConfig;
260
+ /**
261
+ * Bulk-translate engine configuration (v1.2.0+). Presence of this
262
+ * key auto-registers the bulk-translate sidecar collections
263
+ * (`bulk-translate-batches`, `bulk-translate-units`,
264
+ * `translation-daily-spend`, `translation-rate-limits`), exposes
265
+ * the `/api/translation-hub/bulk-translate` endpoints, and runs the
266
+ * `ensureBulkTranslateSchema` helper at boot to materialize the
267
+ * partial unique index that backs the F-DA-TOCTOU dedup invariant.
268
+ *
269
+ * Defaults to enabled when a `bulk` block is provided. Pass
270
+ * `enabled: false` to opt out explicitly (e.g. for environments
271
+ * where bulk translation should be disabled at the runtime layer).
272
+ *
273
+ * The real cost guards are `dailyUsdCap` + `requireTotp` — not
274
+ * this flag. See Design-2026-05-27-bulk-translate.md for the full
275
+ * design rationale.
276
+ */
277
+ bulk?: BulkTranslateConfig;
278
+ /** @internal Set by plugin factory — do not configure directly. */
279
+ _rateLimiter?: {
280
+ acquire(): Promise<void>;
281
+ };
282
+ };
283
+ export type UsageTrackingConfig = {
284
+ /** Master switch. Default: false (opt-in). */
285
+ enabled?: boolean;
286
+ /** Override the auto-registered collection slug. Default: 'translation-usage'. */
287
+ collectionSlug?: string;
288
+ /**
289
+ * Override the default admin-only read access. The default keeps cost data
290
+ * out of editors' hands by gating on a user role of `admin`.
291
+ */
292
+ access?: {
293
+ read?: import('payload').Access;
294
+ };
295
+ };
296
+ export type WriteMode = 'full' | 'minimal';
297
+ export type TranslateDocumentOptions = {
298
+ collection: string;
299
+ id: string | number;
300
+ targetLocales?: string[];
301
+ targetPolicy?: TargetPolicy;
302
+ fields?: string[];
303
+ previousDoc?: Record<string, unknown>;
304
+ signal?: AbortSignal;
305
+ /**
306
+ * Locale-write scope. Default `'full'` includes all localized top-level
307
+ * fields in the update body so Payload's required-localized-field
308
+ * validation passes on a fresh target row. `'minimal'` writes only the
309
+ * fields that actually got translated — caller accepts that Payload
310
+ * may reject the write when other localized required fields are empty
311
+ * in the target locale. Useful for "translate one field, leave the
312
+ * rest exactly as the user has them" workflows where the target locale
313
+ * row is known to be fully populated already.
314
+ */
315
+ writeMode?: WriteMode;
316
+ /**
317
+ * Reuse an existing job in the progress store instead of creating a new one.
318
+ * Used by the after-change hook to avoid duplicate jobs (one phantom from the
319
+ * hook for instant UI feedback, one real from the API call).
320
+ */
321
+ jobId?: string;
322
+ /**
323
+ * Originating PayloadRequest from the user-facing operation. When passed,
324
+ * locale writes inherit `req.user`/`req.transactionID` so downstream hooks
325
+ * (e.g. audit-log) can attribute the changes correctly. Optional because
326
+ * background jobs and tests may invoke `translateDocument` without a req.
327
+ */
328
+ req?: PayloadRequest;
329
+ /**
330
+ * Read the latest draft when the collection has drafts enabled. Defaults
331
+ * to true (the typical caller is the admin UI editing a draft). No-op for
332
+ * non-versioned collections.
333
+ */
334
+ draft?: boolean;
335
+ /**
336
+ * Per-call override of the field-level guards that normally short-circuit
337
+ * a translate. When `true`:
338
+ * - The hash-skip path (BUG-21) — which skips fields whose source AND
339
+ * target hashes match the last successful write — is DISABLED. Every
340
+ * translatable field is sent to the LLM.
341
+ * - The manual-edit preserve guard — which drops fields whose current
342
+ * target value diverges from the stored hash (assumed to be a
343
+ * human-edited target) — is DISABLED. The new translation overwrites
344
+ * the editor's manual change.
345
+ * Defaults to `false`. The bulk-translate worker passes `true` when the
346
+ * batch was enqueued with `mode: 'force'` so the UI's "Force re-translate
347
+ * (includes docs already up to date)" checkbox semantically matches what
348
+ * actually happens. Hash recording after a successful write stays active
349
+ * regardless — the next non-force run still benefits from the updated
350
+ * hashes.
351
+ */
352
+ force?: boolean;
353
+ };
354
+ export type TranslateGlobalOptions = {
355
+ global: string;
356
+ targetLocales?: string[];
357
+ targetPolicy?: TargetPolicy;
358
+ fields?: string[];
359
+ signal?: AbortSignal;
360
+ /** See `TranslateDocumentOptions.writeMode`. */
361
+ writeMode?: WriteMode;
362
+ /**
363
+ * Previous version of the global doc (typically the value before the save
364
+ * that triggered translation). When provided, fields whose hashed content
365
+ * matches the previous version are skipped — same diffOnly behavior the
366
+ * collections API has.
367
+ */
368
+ previousDoc?: Record<string, unknown>;
369
+ /**
370
+ * Reuse an existing job from the progress store. The on-change hook seeds
371
+ * one for instant UI feedback and threads it through here.
372
+ */
373
+ jobId?: string;
374
+ /** See `TranslateDocumentOptions.req`. */
375
+ req?: PayloadRequest;
376
+ /**
377
+ * When `true`, bypass the BUG-21 hash-skip path so every translatable
378
+ * top-level field is sent to the LLM regardless of cached hashes. Used
379
+ * by `bulk-translate mode: 'force'` and equivalent admin actions where
380
+ * the editor explicitly asks for a re-translate. Hash bookkeeping
381
+ * after the write still runs so the next non-force call benefits from
382
+ * fresh hashes. Mirrors `TranslateDocumentOptions.force`.
383
+ */
384
+ force?: boolean;
385
+ };
386
+ export type FieldLocaleResult = {
387
+ fieldPath: string;
388
+ locale: string;
389
+ status: 'success' | 'failed' | 'skipped';
390
+ error?: string;
391
+ /**
392
+ * Source-locale text for this field-locale entry. Populated on
393
+ * `status: 'skipped'` results so the persistence layer can surface the
394
+ * raw value to editors via `softSkippedFields[].sourceValue`. Optional
395
+ * on success/failed (renderer doesn't need it there) and on legacy code
396
+ * paths that haven't been updated to forward it.
397
+ */
398
+ sourceValue?: string;
399
+ /**
400
+ * Editor-facing code (see `EditorErrorCode` in `lib/error-messages.ts`)
401
+ * classifying the failure or soft-skip reason. The UI keys off this
402
+ * to render a friendly message; `error` is kept as the raw provider /
403
+ * validator string for the "Show technical details" disclosure.
404
+ * Set on every entry with `status !== 'success'`. Optional on legacy
405
+ * data — UI falls back to a generic message when absent.
406
+ */
407
+ errorCode?: string;
408
+ characterCount?: number;
409
+ durationMs?: number;
410
+ /**
411
+ * Final value written to the target locale for this field. Returned only
412
+ * for plain `text`/`textarea` results so the client can patch the form
413
+ * state without a round-trip. Omitted for richText/blocks where the
414
+ * structure can't be applied via a flat `setValue`.
415
+ */
416
+ translatedText?: string;
417
+ };
418
+ export type TranslateDocumentResult = {
419
+ jobId?: string;
420
+ documentId: string | number;
421
+ collection: string;
422
+ sourceLocale: string;
423
+ /**
424
+ * Cumulative provider-call latency for this translation in milliseconds,
425
+ * summed across every batch across every target locale. Measures ONLY
426
+ * actual LLM round-trip time — excludes token-bucket wait, provider
427
+ * rate-limit backoff, and queue scheduling. Used by the bulk worker
428
+ * to populate `bulk_translate_units.processingDurationMs`; usable by
429
+ * any caller that wants honest "how long did the AI take" signal.
430
+ */
431
+ providerLatencyMs?: number;
432
+ /**
433
+ * Field-locale entries that were actually persisted to the database.
434
+ * Excludes fields the plugin preserved as manual edits — those land
435
+ * in `preserved` instead. Pre-1.2.0 builds put preserved entries here
436
+ * too, which misled downstream automation about what was written.
437
+ */
438
+ succeeded: FieldLocaleResult[];
439
+ /**
440
+ * Field-locale entries the manual-edit guard kept out of the write
441
+ * because the target value diverged from the plugin's last-written
442
+ * hash. Distinct from `succeeded` so callers can tell "translated and
443
+ * written" from "translated but skipped to preserve editor work."
444
+ *
445
+ * Empty when `preserveManualEdits` is off.
446
+ */
447
+ preserved: FieldLocaleResult[];
448
+ failed: FieldLocaleResult[];
449
+ usage: TranslationUsage;
450
+ };
451
+ export type TranslationEventType = 'translation.started' | 'translation.field' | 'translation.succeeded' | 'translation.failed';
452
+ export type SkippedFieldReport = {
453
+ fieldPath: string;
454
+ locale: string;
455
+ /**
456
+ * Validator reason (e.g. "Translation matches source verbatim — model
457
+ * likely echoed input for ..."). Surfaced as the raw "Show technical
458
+ * details" text — UI renders the friendly version via `reasonCode`.
459
+ */
460
+ reason?: string;
461
+ /**
462
+ * Editor-facing code (one of the `soft-skip.*` values from
463
+ * `lib/error-messages.ts`). UI keys off this to render a friendly
464
+ * message via `editorMessageFor(code, ctx)`. Optional on legacy data.
465
+ */
466
+ reasonCode?: string;
467
+ /**
468
+ * The source-locale text the AI declined to translate. Surfaced in the
469
+ * Hub so editors can see WHAT was kept (e.g. "Wheel Of") without opening
470
+ * the doc. Optional on legacy rows; capped at 500 chars so a rogue
471
+ * richText body can't bloat the row.
472
+ */
473
+ sourceValue?: string;
474
+ };
475
+ export type TranslationEvent = {
476
+ type: TranslationEventType;
477
+ documentId: string | number;
478
+ collection: string;
479
+ sourceLocale: string;
480
+ targetLocales: string[];
481
+ timestamp: Date;
482
+ fields?: FieldLocaleResult[];
483
+ usage?: TranslationUsage;
484
+ error?: string;
485
+ canary?: boolean;
486
+ /**
487
+ * Per-field soft skips (verbatim echo, validator misses). Doc-level
488
+ * `type` may still be `translation.succeeded` when the only failures are
489
+ * skips — soft skips don't drive doc-level status to failed, but the
490
+ * editor needs to know which fields silently stayed in source.
491
+ */
492
+ skippedFields?: SkippedFieldReport[];
493
+ };
494
+ export type TranslationAlertType = 'translation.persistent-failure' | 'translation.cost-guard-abort' | 'translation.provider-outage';
495
+ export type TranslationAlert = {
496
+ type: TranslationAlertType;
497
+ message: string;
498
+ documentId?: string | number;
499
+ collection?: string;
500
+ timestamp: Date;
501
+ metadata?: Record<string, unknown>;
502
+ /**
503
+ * Editor-facing code (one of the `alert.*` or `cost-guard.*` values
504
+ * from `lib/error-messages.ts`). When set, AlertBanner renders the
505
+ * friendly title/body via `editorMessageFor(code, context)` instead
506
+ * of the raw `message` string. `message` is still written for
507
+ * back-compat with the consumer `onAlert` callback (Slack/email).
508
+ * Flattened into the persisted row's `metadata.code`.
509
+ */
510
+ code?: string;
511
+ /**
512
+ * Context for templating the code's message (count, locale, spent,
513
+ * cap, etc.). Flattened into the persisted row's `metadata.context`.
514
+ */
515
+ context?: Record<string, unknown>;
516
+ };
517
+ export type ValidationConfig = {
518
+ minLengthRatio?: number;
519
+ maxLengthRatio?: number;
520
+ minSourceLength?: number;
521
+ extraRefusalPatterns?: RegExp[];
522
+ extraInjectionPatterns?: RegExp[];
523
+ };
524
+ export type TranslationSample = {
525
+ documentId: string | number;
526
+ collection: string;
527
+ fieldPath: string;
528
+ sourceLocale: string;
529
+ targetLocale: string;
530
+ sourceText: string;
531
+ translatedText: string;
532
+ model: string;
533
+ timestamp: Date;
534
+ };
535
+ export type SamplingConfig = {
536
+ rate: number;
537
+ onSample?: (sample: TranslationSample) => void | Promise<void>;
538
+ };
539
+ export type QualityConfig = {
540
+ validation?: ValidationConfig;
541
+ sampling?: SamplingConfig;
542
+ canaryLocale?: string;
543
+ };
544
+ export type TranslatableFieldType = 'text' | 'textarea' | 'richText' | 'json' | 'group' | 'array' | 'blocks';
545
+ export type TranslatableField = {
546
+ path: string;
547
+ type: TranslatableFieldType;
548
+ localized: boolean;
549
+ };
550
+ export type TranslationUnit = {
551
+ id: string;
552
+ fieldPath: string;
553
+ text: string;
554
+ kind: TranslationItemKind;
555
+ };
556
+ export type CostGuardError = Error & {
557
+ code: 'PER_CALL_LIMIT' | 'PER_DOC_CEILING';
558
+ characterCount: number;
559
+ limit: number;
560
+ };
561
+ /**
562
+ * Bulk-translate batch lifecycle event. Fires on terminal state
563
+ * transitions (completed / partial / failed / cancelled / reverted).
564
+ * Consumer-supplied callbacks can use this to plug into webhook
565
+ * notification surfaces (Slack, email, etc).
566
+ */
567
+ export type BulkTranslateBatchEvent = {
568
+ batchId: string;
569
+ status: 'success' | 'partial' | 'failed' | 'cancelled' | 'reverted';
570
+ scope: {
571
+ collections?: string[];
572
+ globals?: string[];
573
+ locales?: string[];
574
+ mode: 'changed' | 'force' | 'canary';
575
+ };
576
+ counts: {
577
+ total: number;
578
+ completed: number;
579
+ failed: number;
580
+ skipped: number;
581
+ };
582
+ costUsd: {
583
+ estimated: number;
584
+ actual: number;
585
+ };
586
+ triggeredByUserId: string;
587
+ triggeredByEmail?: string;
588
+ durationMs: number;
589
+ };
590
+ export type BulkTranslateConfig = {
591
+ /**
592
+ * Master switch for the bulk-translate engine. Defaults to `true`
593
+ * when the `bulk` block is present in plugin config — pass
594
+ * `enabled: false` to opt out at the runtime layer (e.g. in
595
+ * environments where bulk operations should be disabled but the
596
+ * surrounding plugin config stays the same). The real cost guards
597
+ * are `dailyUsdCap` + `requireTotp`.
598
+ */
599
+ enabled?: boolean;
600
+ /**
601
+ * Collection slugs to exclude from bulk operations even if they're
602
+ * in the plugin's `collections` list. Decision #32: `users` is
603
+ * recommended here for any consumer where `users.bio` carries
604
+ * personally-identifying content (employee bios etc).
605
+ */
606
+ excludeCollections?: string[];
607
+ /**
608
+ * Hard ceiling on aggregate USD spend per UTC day (Decision #14 +
609
+ * F-SEC-TOTP-BYPASS). Applies to bulk-translate, per-doc retry,
610
+ * AND the plugin coalesce path. The env var
611
+ * `BULK_TRANSLATE_DAILY_USD_CAP` overrides this if set. Default
612
+ * is $50.
613
+ */
614
+ dailyUsdCap?: number;
615
+ /**
616
+ * Require a TOTP code on the bulk-translate POST endpoint
617
+ * (Decision #13 v2). Friction, not the primary security boundary
618
+ * — the daily USD cap is the real enforcement. Defaults to `true`
619
+ * when a TOTP plugin is detected at boot.
620
+ */
621
+ requireTotp?: boolean;
622
+ /**
623
+ * Default canary sample size when `mode: 'canary'` is selected
624
+ * without an explicit limit (Decision #26 + F-DA-CANARY).
625
+ * Selection is random-stratified across configured collections.
626
+ * Defaults to 10.
627
+ */
628
+ canaryDefaultSize?: number;
629
+ /**
630
+ * Fires on every terminal batch transition. Use for Slack /
631
+ * email notifications. Best-effort — exceptions are caught and
632
+ * logged, never block batch completion.
633
+ */
634
+ onBatchComplete?: (event: BulkTranslateBatchEvent) => void | Promise<void>;
635
+ /**
636
+ * Fires when a batch enters `failed` terminal state (zero
637
+ * successes). Distinct from `onBatchComplete` (which fires on all
638
+ * terminal states) so consumers can wire different alerting
639
+ * priorities.
640
+ */
641
+ onBatchFailed?: (event: BulkTranslateBatchEvent) => void | Promise<void>;
642
+ /**
643
+ * Fires when the daily USD cap rejects a request. Lets consumers
644
+ * page on-call before the cap silently locks out a day's work.
645
+ */
646
+ onCapExceeded?: (info: {
647
+ todaySpentUsd: number;
648
+ capUsd: number;
649
+ rejectedEstimateUsd: number;
650
+ requestPath: 'bulk-endpoint' | 'per-doc-retry' | 'coalesce';
651
+ }) => void | Promise<void>;
652
+ /**
653
+ * Slug override for the bulk-translate units collection. Reads default
654
+ * from `DEFAULT_BULK_TRANSLATE_UNITS_COLLECTION_SLUG`. Only set if the
655
+ * consumer renamed the collection.
656
+ */
657
+ unitsCollectionSlug?: string;
658
+ /**
659
+ * Interval (ms) between janitor sweeps that reclaim stuck `running`
660
+ * units back to `pending` when the worker crashes or the process
661
+ * restarts. Default 5 minutes. Set to `0` to disable the periodic
662
+ * sweep (the boot sweep still runs on every onInit).
663
+ *
664
+ * The janitor mechanism (see `tasks/bulk-translate-janitor.ts`) is
665
+ * dynamic-threshold: it only resets rows older than
666
+ * `max(10min, 2 × p99-LLM-latency)`, so legitimately slow
667
+ * translations are never interrupted. Lowering this interval below
668
+ * 1 minute is a foot-gun — the sweep walks the units table; do not
669
+ * set < 30_000.
670
+ */
671
+ janitorIntervalMs?: number;
672
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,14 @@
1
+ import type { Access, CollectionConfig } from 'payload';
2
+ /**
3
+ * Factory for the auto-registered `translation-usage` collection.
4
+ *
5
+ * The plugin pushes this collection onto `config.collections` when
6
+ * `usageTracking.enabled` is true. Every translation job — succeeded or
7
+ * failed — writes one row from inside `lib/persist-usage.ts`. Cost data
8
+ * is treated as admin-only by default: `admin.hidden` keeps the entry
9
+ * out of the editor nav, and `access.read` blocks API reads even via
10
+ * direct URL.
11
+ *
12
+ * Mirrors the audit-log plugin's `createAuditLogsCollection` pattern.
13
+ */
14
+ export declare function createUsageCollection(readAccess?: Access, collectionSlug?: string): CollectionConfig;