@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,216 @@
1
+ import { invalidateSettingsCache } from './lib/effective-locales.js';
2
+ export const DEFAULT_SETTINGS_GLOBAL_SLUG = 'translation-settings';
3
+ export function createSettingsGlobal(providerKeys, targetLocales, options, trackedSurfaces = []) {
4
+ const slug = options?.globalSlug ?? DEFAULT_SETTINGS_GLOBAL_SLUG;
5
+ const defaultAdminAccess = ({ req })=>req?.user?.roles?.includes('admin') ?? false;
6
+ const providerOptions = providerKeys.map((key)=>({
7
+ label: key,
8
+ value: key
9
+ }));
10
+ const localeOptions = targetLocales.map((locale)=>({
11
+ label: locale,
12
+ value: locale
13
+ }));
14
+ const surfaceOptions = trackedSurfaces.map((slugName)=>({
15
+ label: slugName,
16
+ value: slugName
17
+ }));
18
+ const fields = [];
19
+ // Provider selector — only meaningful when there's something to switch
20
+ // between. Single-provider consumers see only the locale toggle.
21
+ if (providerKeys.length > 0) {
22
+ fields.push({
23
+ name: 'activeProvider',
24
+ type: 'select',
25
+ required: false,
26
+ options: providerOptions,
27
+ admin: {
28
+ description: 'The provider used for new translations. When unset, the plugin falls back to the default `provider` configured in code.'
29
+ }
30
+ });
31
+ }
32
+ // Per-locale auto-translate toggle. Empty = "all enabled" so that
33
+ // existing consumers see no behavior change after upgrading.
34
+ fields.push({
35
+ name: 'enabledTargetLocales',
36
+ type: 'select',
37
+ hasMany: true,
38
+ required: false,
39
+ options: localeOptions,
40
+ admin: {
41
+ description: '⚠️ EMPTY = ALL LOCALES ENABLED (back-compat default). Select locales here to NARROW auto-translate to that subset; unselected locales are skipped by automation. Clearing the list does NOT disable auto-translate — to fully stop automation, uncheck "Enabled" or "Auto on publish" on every per-collection row below. Manual Translate from the dialog is never gated by this field.'
42
+ }
43
+ });
44
+ // Three independent plugin-wide kill switches. Each one supersedes
45
+ // every per-collection setting below — when off, no surface receives
46
+ // work via that subsystem regardless of the per-surface `enabled` /
47
+ // `autoOnPublish` flags. Defaults are `true` so upgrades don't break
48
+ // existing consumers; admins flip them off when they need an
49
+ // emergency stop without redeploying.
50
+ //
51
+ // Storage shape: three top-level checkboxes (not nested in a group)
52
+ // so Payload's drizzle generator emits flat boolean columns —
53
+ // `global_auto_translate_enabled` etc. on the `translation_settings`
54
+ // table — instead of a join table or a JSON blob. Flat columns make
55
+ // the migration a no-op `ADD COLUMN IF NOT EXISTS ... DEFAULT true`
56
+ // on both consumer repos.
57
+ //
58
+ // Read path: every gate uses `getGlobalKillSwitches` in
59
+ // `lib/effective-locales.ts`, which shares the 30s-TTL settings
60
+ // cache with the per-collection helpers. The settings global's own
61
+ // `afterChange` already invalidates the cache, so admin edits
62
+ // propagate on the next read.
63
+ fields.push({
64
+ name: 'globalAutoTranslateEnabled',
65
+ type: 'checkbox',
66
+ defaultValue: true,
67
+ admin: {
68
+ description: '🛑 GLOBAL KILL SWITCH — auto-translate. When unchecked, the after-change hook does nothing on every save. Per-collection `Enabled` / `Auto on publish` still apply WITHIN this switch — flipping it back on resumes whatever per-surface config was already in place. Manual Translate and Bulk Translate are unaffected by this flag.'
69
+ }
70
+ }, {
71
+ name: 'globalManualTranslateEnabled',
72
+ type: 'checkbox',
73
+ defaultValue: true,
74
+ admin: {
75
+ description: '🛑 GLOBAL KILL SWITCH — manual Translate dialog + per-field button. When unchecked, the dialog and the per-field button hide in the admin UI, and the underlying endpoint returns 403. Per-collection `Enabled` still applies WITHIN this switch. Auto-translate and Bulk Translate are unaffected by this flag.'
76
+ }
77
+ }, {
78
+ name: 'globalBulkTranslateEnabled',
79
+ type: 'checkbox',
80
+ defaultValue: true,
81
+ admin: {
82
+ description: '🛑 GLOBAL KILL SWITCH — Bulk Translate (new runs). When unchecked, enqueueing a new bulk run is rejected and the BulkTranslate trigger in the Hub renders disabled. In-flight bulk runs that were already enqueued continue to completion — flipping this switch does NOT cancel running work. Manual Translate and auto-translate are unaffected by this flag.'
83
+ }
84
+ });
85
+ // Per-collection / per-global configuration. Each tracked surface
86
+ // gets a row; admins can disable a surface entirely, turn off
87
+ // auto-on-publish, or narrow the target locales for just that
88
+ // surface. Empty rows / missing entries fall back to the site-wide
89
+ // defaults — so consumers upgrading without admin action see no
90
+ // behavior change.
91
+ if (trackedSurfaces.length > 0) {
92
+ fields.push({
93
+ name: 'perCollection',
94
+ type: 'array',
95
+ required: false,
96
+ labels: {
97
+ singular: 'Collection / Global setting',
98
+ plural: 'Per-collection settings'
99
+ },
100
+ // Postgres identifier limit is 63 chars; the default-generated
101
+ // table / enum names from `translation_settings_per_collection_*`
102
+ // overflow. Pin compact db identifiers so generated migrations
103
+ // are portable across adapters.
104
+ dbName: 'tr_st_pc',
105
+ admin: {
106
+ description: 'Override site-wide settings for a specific collection or global. Rows that are missing or have all defaults inherit the site-wide config above.',
107
+ initCollapsed: true
108
+ },
109
+ fields: [
110
+ {
111
+ name: 'slug',
112
+ type: 'select',
113
+ required: true,
114
+ options: surfaceOptions,
115
+ enumName: 'enum_tr_st_pc_slug',
116
+ admin: {
117
+ description: 'The collection or global this row configures.'
118
+ }
119
+ },
120
+ {
121
+ name: 'enabled',
122
+ type: 'checkbox',
123
+ defaultValue: true,
124
+ admin: {
125
+ description: 'KILL SWITCH. Unchecked = translation is fully disabled for this surface — auto-on-publish AND manual Translate dialog AND per-field button. Use the "Auto on publish" toggle below if you want to disable ONLY automation while keeping manual translate available.'
126
+ }
127
+ },
128
+ {
129
+ name: 'autoOnPublish',
130
+ type: 'checkbox',
131
+ defaultValue: true,
132
+ admin: {
133
+ description: 'AUTOMATION ONLY. When checked, publishing a doc in this surface fires auto-translate. Uncheck to keep this collection manual-only — editors can still trigger Translate… on demand. Has no effect when "Enabled" above is unchecked (the kill switch takes precedence).'
134
+ }
135
+ },
136
+ {
137
+ name: 'targetLocalesOverride',
138
+ type: 'select',
139
+ hasMany: true,
140
+ required: false,
141
+ options: localeOptions,
142
+ enumName: 'enum_tr_st_pc_tlo',
143
+ admin: {
144
+ description: '⚠️ EMPTY = INHERIT site-wide locales (back-compat default). Select locales here to NARROW auto-translate for THIS surface specifically. A locale must be enabled at BOTH the site-wide level AND here to fan out. Clearing the list does NOT opt this surface out — uncheck "Enabled" above to do that.'
145
+ }
146
+ },
147
+ {
148
+ // Opt this surface's `slug` field into translation. Defaults
149
+ // to false because the plugin's `excludeFields` config
150
+ // typically lists `slug` to prevent accidental URL churn
151
+ // across locales. Editors who want per-locale SEO-friendly
152
+ // URLs check this on a per-collection basis.
153
+ //
154
+ // Pre-requisite: the slug field on the collection schema
155
+ // must be `localized: true` — otherwise the resolver can't
156
+ // see it and this toggle has no effect. The widget below
157
+ // surfaces the path only when localized; the description
158
+ // makes this explicit.
159
+ name: 'translateSlug',
160
+ type: 'checkbox',
161
+ defaultValue: false,
162
+ admin: {
163
+ description: 'Include the slug field in translation runs for this surface. The slug field must also be `localized: true` on the collection schema for this to take effect — otherwise the toggle is a no-op. Applies to BOTH automation and the manual Translate… dialog.'
164
+ }
165
+ },
166
+ {
167
+ // Storage shape: `text hasMany` => Postgres join table.
168
+ // Parent array's `dbName: 'tr_st_pc'` keeps the generated
169
+ // join-table name under the 63-char identifier limit.
170
+ // Stored as opaque strings — the plugin doesn't enforce
171
+ // that listed paths currently exist, so renaming a field
172
+ // doesn't auto-clean stale entries. Stale entries are
173
+ // harmless downstream: the filter is a Set membership
174
+ // check; paths absent from the resolver don't match
175
+ // anything and pass through unchanged.
176
+ name: 'excludedFieldPaths',
177
+ type: 'text',
178
+ hasMany: true,
179
+ required: false,
180
+ admin: {
181
+ description: 'Fields excluded from translation for this surface. Applies to BOTH automation and the Translate… dialog — different from the AUTOMATION-ONLY flags above. Renders a checkbox list driven by the current "Slug" value: uncheck a field to opt it out of every translation path (auto + manual). Leave all checked to translate every detected field (back-compat default).',
182
+ components: {
183
+ Field: '@purposeinplay/payload-ai-translate/client#ExcludedFieldsField'
184
+ }
185
+ }
186
+ }
187
+ ]
188
+ });
189
+ }
190
+ return {
191
+ slug,
192
+ label: 'Translation Settings',
193
+ admin: {
194
+ group: 'System',
195
+ description: 'Configure runtime translation behaviour: which provider runs new translations and which locales auto-translate on publish.',
196
+ // Hide from non-admins — same posture as `translation-usage`.
197
+ hidden: ({ user })=>!user?.roles?.includes('admin')
198
+ },
199
+ access: {
200
+ read: options?.access?.read ?? defaultAdminAccess,
201
+ update: options?.access?.update ?? defaultAdminAccess
202
+ },
203
+ hooks: {
204
+ // Invalidate the `effective-locales` cache whenever the settings
205
+ // global is saved so admin edits propagate to the next publish
206
+ // cycle without waiting for the 30s TTL.
207
+ afterChange: [
208
+ ()=>{
209
+ invalidateSettingsCache(slug);
210
+ }
211
+ ]
212
+ },
213
+ graphQL: false,
214
+ fields
215
+ };
216
+ }
@@ -0,0 +1,115 @@
1
+ import type { Payload, TaskConfig } from 'payload';
2
+ export declare const BULK_TRANSLATE_COORDINATOR_SLUG = "bulk-translate-coordinator";
3
+ export declare const BULK_TRANSLATE_DOC_TASK_SLUG = "bulk-translate-doc";
4
+ /**
5
+ * Coordinator persists this shape into `bulk-translate-batches
6
+ * .lastEnumerationCursor` so a crashed-mid-enumeration run can resume
7
+ * from where it left off rather than re-scan every doc on restart.
8
+ *
9
+ * Phase ordering: collections → globals → (canary sampling, when
10
+ * mode='canary') → done. Each phase is independently resumable.
11
+ */
12
+ export type EnumerationCursor = {
13
+ phase: 'collections' | 'globals' | 'canary' | 'done';
14
+ collectionIndex: number;
15
+ page: number;
16
+ globalIndex: number;
17
+ };
18
+ export type BulkBatchScope = {
19
+ /**
20
+ * Collection slugs to enumerate. Required when targeting collection
21
+ * docs. Empty + `globals: []` is a no-op (the coordinator immediately
22
+ * transitions the batch to `completed` with `totalUnits: 0`).
23
+ */
24
+ collections?: string[];
25
+ /** Global slugs to enumerate. */
26
+ globals?: string[];
27
+ /**
28
+ * Target locales the batch should translate to. Coordinator creates
29
+ * one unit per (doc, locale). Must be non-empty.
30
+ */
31
+ locales: string[];
32
+ /**
33
+ * Collections to skip even when listed in `collections`. Honors
34
+ * `BulkTranslateConfig.excludeCollections` (e.g. `users` per
35
+ * Decision #32 / F-SEC-USERS-BIO).
36
+ */
37
+ excludeCollections?: string[];
38
+ /** Optional whitelist of specific doc IDs (scopes enumeration). */
39
+ documentIds?: string[];
40
+ /** Source locale snapshotted at enqueue. Used to read source docs. */
41
+ sourceLocale: string;
42
+ /** Batch mode. `canary` mode caps the unit count via `canaryLimit`. */
43
+ mode: 'changed' | 'force' | 'canary';
44
+ /** Canary fan-out cap (only meaningful when `mode === 'canary'`). */
45
+ canaryLimit?: number;
46
+ };
47
+ export interface BulkCoordinatorOptions {
48
+ /** Slug override for the batches collection. */
49
+ batchesCollectionSlug?: string;
50
+ /** Slug override for the units collection. */
51
+ unitsCollectionSlug?: string;
52
+ /**
53
+ * Per-tick wall-clock budget. When the coordinator hits this limit
54
+ * mid-enumeration it persists the cursor and re-enqueues itself
55
+ * instead of running until Payload's job-runner times out.
56
+ *
57
+ * Default 25_000ms — well under Vercel Pro's 300s function ceiling
58
+ * and Payload's default `runJobs` async-function timeout.
59
+ */
60
+ tickBudgetMs?: number;
61
+ /**
62
+ * Per-collection pagination size. Default 100 — Payload's local API
63
+ * defaults to 10, which would over-trample the jobs queue with
64
+ * coordinator re-enqueues.
65
+ */
66
+ pageSize?: number;
67
+ /** Slug for the worker task. Mirrors `BULK_TRANSLATE_DOC_TASK_SLUG`. */
68
+ workerTaskSlug?: string;
69
+ }
70
+ type CoordinatorTaskInput = {
71
+ batchId: string;
72
+ };
73
+ /**
74
+ * Coordinator task that enumerates every (doc, locale) under
75
+ * `batch.scope`, inserts a `bulk-translate-units` row per unit and
76
+ * queues a `bulk-translate-doc` task per unit. Both ops are wrapped
77
+ * in a single transaction per (doc, locale) tuple to close F-DA-TOCTOU
78
+ * scenario A — the worker can never dequeue before the unit row has
79
+ * committed.
80
+ *
81
+ * Long enumerations trampoline: the task self-re-enqueues with the
82
+ * same `batchId` and a persisted `lastEnumerationCursor` once it hits
83
+ * `tickBudgetMs`. Resume from the cursor is exact (collection +
84
+ * pagination page), not approximate.
85
+ *
86
+ * For `mode: 'canary'`, the coordinator performs full enumeration
87
+ * (so the candidate pool is the same as `mode: 'changed'` would
88
+ * scope) and selects N units via random-stratified sampling keyed
89
+ * on `batchId` for reproducibility.
90
+ */
91
+ export declare function buildBulkTranslateCoordinator(options?: BulkCoordinatorOptions): TaskConfig<{
92
+ input: CoordinatorTaskInput;
93
+ output: {
94
+ ok: true;
95
+ cursor: EnumerationCursor;
96
+ };
97
+ }>;
98
+ export type CoordinatorTickParams = {
99
+ payload: Payload;
100
+ batchId: string;
101
+ batchesSlug: string;
102
+ unitsSlug: string;
103
+ tickBudgetMs: number;
104
+ pageSize: number;
105
+ workerSlug: string;
106
+ /** Test override — injects current time for deterministic budget checks. */
107
+ now?: () => number;
108
+ };
109
+ export type CoordinatorTickResult = {
110
+ cursor: EnumerationCursor;
111
+ unitsCreated: number;
112
+ finished: boolean;
113
+ };
114
+ export declare function runCoordinatorTick(params: CoordinatorTickParams): Promise<CoordinatorTickResult>;
115
+ export {};