@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,131 @@
1
+ /**
2
+ * Shared cost / duration formatters for Translation Hub + Bulk Translate
3
+ * Runs surfaces. v1.2.6 BugIndex NEW-17 + NEW-18 + NEW-10.
4
+ *
5
+ * Before extracting these, every surface (Hub Overview, Audit, Runs,
6
+ * batch rows, drill-down) rolled its own threshold logic. The result:
7
+ * - Hub Overview "Estimated cost" showed `$0.54`
8
+ * - Audit "Total spend" showed `53.6¢`
9
+ * - Runs "Last 7 days cost" showed `$0.51`
10
+ * - Batch row Cost column showed `12.4¢`
11
+ * - Drill-down unit-detail showed `2.1¢`
12
+ * - Bucket-row duration showed `964m 6s` with no hours carryover
13
+ *
14
+ * Same underlying value (cents, ms), five different formats.
15
+ *
16
+ * The contract here is the only contract going forward. Every cost /
17
+ * duration render site MUST import `formatCost` / `formatDuration`. Do
18
+ * NOT roll new threshold rules at the call site.
19
+ *
20
+ * `formatCost` rule (1.2.8):
21
+ * - All values render as `$X.YY[YY]` (USD, dollar sign always present).
22
+ * - Sub-cent values use up to 4 decimals so editors can tell a
23
+ * 0.46¢ run from a 0.88¢ run — but the unit is consistently $,
24
+ * never the ¢ glyph (it was being mis-read as `$X.YY¢` on dense
25
+ * tables and made per-doc costs look 100× higher than they were).
26
+ * - Values ≥ $0.01 stay at the standard 2-decimal $X.YY form.
27
+ *
28
+ * Wait — that was the OLD rule. The Group F brief locks this in:
29
+ * `$X.XX` with 2 decimals; switch to `Y.Y¢` ONLY when value < $0.01
30
+ * (or, equivalently, < 1¢).
31
+ *
32
+ * So: < $0.01 → `Y.YY¢`, else `$X.XX`.
33
+ *
34
+ * `formatDuration` rule:
35
+ * ms → s → m → h → d, carrying everywhere. Show two units of
36
+ * precision when the next-down unit would otherwise be lost
37
+ * (e.g. `16h 38m`, `5m 16s`, `42s`).
38
+ */ // ---------------------------------------------------------------------------
39
+ // formatCost — single source of truth for any USD render in the UI.
40
+ // ---------------------------------------------------------------------------
41
+ /**
42
+ * Render a USD value with consistent rules.
43
+ *
44
+ * @param usd Dollars (e.g. 0.124 means 12.4 cents). `null` / `undefined`
45
+ * / non-finite → `'—'`. Negative → coerced to `0`.
46
+ * @returns
47
+ * - `'—'` when missing or non-finite
48
+ * - `'$0.00'` when exactly zero
49
+ * - `'$0.YYYY'` (USD, four decimals) when 0 < value < $0.01
50
+ * - `'$X.XX'` (USD, two decimals) otherwise
51
+ *
52
+ * Tests pin the boundaries — see `__tests__/format.test.ts`.
53
+ */ export function formatCost(usd) {
54
+ if (usd == null || !Number.isFinite(usd)) return '—';
55
+ const v = Math.max(0, usd);
56
+ if (v === 0) return '$0.00';
57
+ if (v < 0.01) {
58
+ // Sub-cent: keep the $ unit consistent (the prior `0.46¢` form
59
+ // was mis-read on dense tables as `$0.46`, making per-doc costs
60
+ // look 100× higher). 4 decimals so editors can still tell 0.0046
61
+ // from 0.0088.
62
+ return `$${v.toFixed(4)}`;
63
+ }
64
+ return `$${v.toFixed(2)}`;
65
+ }
66
+ // ---------------------------------------------------------------------------
67
+ // formatDuration — single source of truth for any ms render in the UI.
68
+ // ---------------------------------------------------------------------------
69
+ const MS_PER_SECOND = 1000;
70
+ const MS_PER_MINUTE = 60 * MS_PER_SECOND;
71
+ const MS_PER_HOUR = 60 * MS_PER_MINUTE;
72
+ const MS_PER_DAY = 24 * MS_PER_HOUR;
73
+ /**
74
+ * Render a duration in ms with full carry-over from ms → s → m → h → d.
75
+ *
76
+ * @param ms Duration in milliseconds. `null` / `undefined` / non-finite
77
+ * → `'—'`. Zero → `'—'` by convention (matches the legacy `fmtUsd`
78
+ * behaviour the call sites relied on). Negative → coerced to `0` →
79
+ * `'—'`.
80
+ * @returns
81
+ * - `'—'` when missing / non-finite / non-positive
82
+ * - `'Nms'` when < 1s
83
+ * - `'N.Ns'` when < 10s (one decimal, e.g. `3.4s`)
84
+ * - `'Ns'` when < 60s
85
+ * - `'Nm Ss'` when < 1h (e.g. `5m 16s`)
86
+ * - `'Nh Mm'` when < 24h (e.g. `16h 38m`)
87
+ * - `'Nd Hh'` when ≥ 24h (e.g. `2d 4h`)
88
+ *
89
+ * The rule is the same one BatchRow.tsx implemented for batch-level
90
+ * durations; lifting it here so BucketRow stops capping at minutes and
91
+ * rendering `964m 6s`. See NEW-18.
92
+ *
93
+ * Tests pin the boundaries — see `__tests__/format.test.ts`.
94
+ */ export function formatDuration(ms) {
95
+ if (ms == null || !Number.isFinite(ms)) return '—';
96
+ const v = Math.max(0, ms);
97
+ if (v === 0) return '—';
98
+ if (v < MS_PER_SECOND) return `${Math.round(v)}ms`;
99
+ if (v < 10 * MS_PER_SECOND) return `${(v / MS_PER_SECOND).toFixed(1)}s`;
100
+ if (v < MS_PER_MINUTE) return `${Math.round(v / MS_PER_SECOND)}s`;
101
+ if (v < MS_PER_HOUR) {
102
+ // Round to whole seconds FIRST, then decompose by integer division.
103
+ // The previous code rounded seconds independently of minutes:
104
+ // 1_799_500ms → minutes=29, seconds=Math.round(59.5)=60 → `29m 60s`.
105
+ // Decomposing from a single rounded total keeps the boundary clean.
106
+ // If the round-up tips us over the hour boundary (e.g. 3_599_500ms
107
+ // → 3600 totalSeconds → would render `60m 0s`), promote to the
108
+ // Nh Mm format so the carry cascades all the way.
109
+ const totalSeconds = Math.round(v / MS_PER_SECOND);
110
+ if (totalSeconds >= 3600) {
111
+ const hoursCarry = Math.floor(totalSeconds / 3600);
112
+ const minutesCarry = Math.floor(totalSeconds % 3600 / 60);
113
+ return `${hoursCarry}h ${minutesCarry}m`;
114
+ }
115
+ const minutes = Math.floor(totalSeconds / 60);
116
+ const seconds = totalSeconds % 60;
117
+ return `${minutes}m ${seconds}s`;
118
+ }
119
+ if (v < MS_PER_DAY) {
120
+ // Floor on minutes here (NOT round) — rounding would tip
121
+ // 23h 59m 59s up to 24h 0m, contradicting the formatter contract
122
+ // that the day branch handles ≥ 24h. The carry bug doesn't manifest
123
+ // here because there's no sub-minute display to round into.
124
+ const hours = Math.floor(v / MS_PER_HOUR);
125
+ const minutes = Math.floor(v % MS_PER_HOUR / MS_PER_MINUTE);
126
+ return `${hours}h ${minutes}m`;
127
+ }
128
+ const days = Math.floor(v / MS_PER_DAY);
129
+ const hours = Math.floor(v % MS_PER_DAY / MS_PER_HOUR);
130
+ return `${days}d ${hours}h`;
131
+ }
package/package.json ADDED
@@ -0,0 +1,141 @@
1
+ {
2
+ "name": "@purposeinplay/payload-ai-translate",
3
+ "version": "0.1.0",
4
+ "description": "AI translation plugin for Payload CMS 3 — multi-provider (OpenAI, Anthropic, Gemini, custom), bulk translation, Lexical-aware, with an admin Translation Hub.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/exports/index.js",
8
+ "types": "./dist/exports/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/exports/index.d.ts",
12
+ "import": "./dist/exports/index.js",
13
+ "default": "./dist/exports/index.js"
14
+ },
15
+ "./providers": {
16
+ "types": "./dist/exports/providers.d.ts",
17
+ "import": "./dist/exports/providers.js",
18
+ "default": "./dist/exports/providers.js"
19
+ },
20
+ "./views": {
21
+ "types": "./dist/exports/views.d.ts",
22
+ "import": "./dist/exports/views.js",
23
+ "default": "./dist/exports/views.js"
24
+ },
25
+ "./views-client": {
26
+ "types": "./dist/exports/views-client.d.ts",
27
+ "import": "./dist/exports/views-client.js",
28
+ "default": "./dist/exports/views-client.js"
29
+ },
30
+ "./client": {
31
+ "types": "./dist/exports/client.d.ts",
32
+ "import": "./dist/exports/client.js",
33
+ "default": "./dist/exports/client.js"
34
+ },
35
+ "./components": {
36
+ "types": "./dist/exports/components.d.ts",
37
+ "import": "./dist/exports/components.js",
38
+ "default": "./dist/exports/components.js"
39
+ }
40
+ },
41
+ "files": [
42
+ "dist"
43
+ ],
44
+ "peerDependencies": {
45
+ "@ai-sdk/anthropic": "^3.0.0",
46
+ "@ai-sdk/google": "^3.0.0",
47
+ "@ai-sdk/openai": "^3.0.0",
48
+ "@ai-sdk/openai-compatible": "^2.0.0",
49
+ "@payloadcms/db-postgres": "^3.0.0",
50
+ "@payloadcms/next": "^3.0.0",
51
+ "@payloadcms/ui": "^3.0.0",
52
+ "ai": "^6.0.0",
53
+ "next": "^15.0.0 || ^16.0.0",
54
+ "payload": "^3.0.0",
55
+ "react": "^18.0.0 || ^19.0.0",
56
+ "react-dom": "^18.0.0 || ^19.0.0",
57
+ "zod": "^3.0.0 || ^4.0.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "@ai-sdk/anthropic": {
61
+ "optional": true
62
+ },
63
+ "@ai-sdk/google": {
64
+ "optional": true
65
+ },
66
+ "@ai-sdk/openai": {
67
+ "optional": true
68
+ },
69
+ "@ai-sdk/openai-compatible": {
70
+ "optional": true
71
+ },
72
+ "@payloadcms/db-postgres": {
73
+ "optional": true
74
+ },
75
+ "@payloadcms/next": {
76
+ "optional": true
77
+ },
78
+ "@payloadcms/ui": {
79
+ "optional": true
80
+ }
81
+ },
82
+ "devDependencies": {
83
+ "@ai-sdk/anthropic": "^3.0.74",
84
+ "@ai-sdk/google": "^3.0.67",
85
+ "@ai-sdk/openai": "^3.0.58",
86
+ "@ai-sdk/openai-compatible": "^2.0.45",
87
+ "@biomejs/biome": "^2.2.0",
88
+ "@changesets/cli": "^2.27.9",
89
+ "@payloadcms/db-postgres": "^3.0.0",
90
+ "@payloadcms/next": "^3.85.0",
91
+ "@payloadcms/ui": "^3.0.0",
92
+ "@swc/cli": "^0.7.7",
93
+ "@swc/core": "^1.10.1",
94
+ "@types/node": "^22.0.0",
95
+ "drizzle-orm": "^0.45.0",
96
+ "@types/react": "^19.0.0",
97
+ "@types/react-dom": "^19.2.3",
98
+ "ai": "^6.0.174",
99
+ "next": "^16.0.0",
100
+ "payload": "^3.0.0",
101
+ "react": "^19.0.0",
102
+ "react-dom": "^19.0.0",
103
+ "typescript": "^5.6.3",
104
+ "vitest": "^4.1.2",
105
+ "zod": "^4.4.3"
106
+ },
107
+ "repository": {
108
+ "type": "git",
109
+ "url": "git+https://github.com/purposeinplay/payload-ai-translate.git"
110
+ },
111
+ "homepage": "https://github.com/purposeinplay/payload-ai-translate#readme",
112
+ "bugs": {
113
+ "url": "https://github.com/purposeinplay/payload-ai-translate/issues"
114
+ },
115
+ "keywords": [
116
+ "payload",
117
+ "payloadcms",
118
+ "plugin",
119
+ "translation",
120
+ "i18n",
121
+ "ai",
122
+ "openai",
123
+ "anthropic",
124
+ "gemini"
125
+ ],
126
+ "scripts": {
127
+ "build": "pnpm run build:types && pnpm run build:swc && pnpm run build:fix-esm",
128
+ "build:types": "tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist",
129
+ "build:swc": "swc ./src -d ./dist --config-file ./.swcrc --strip-leading-paths --ignore \"**/__tests__/**\"",
130
+ "build:fix-esm": "node ./scripts/fix-dist-extensions.mjs dist && node ./scripts/check-dist-esm.mjs dist",
131
+ "typecheck": "tsc -p tsconfig.json --noEmit",
132
+ "test": "vitest run",
133
+ "lint": "biome check .",
134
+ "format": "biome format --write .",
135
+ "check": "biome check . && pnpm typecheck && pnpm build && pnpm test",
136
+ "clean": "rm -rf dist *.tsbuildinfo",
137
+ "changeset": "changeset",
138
+ "version-packages": "changeset version",
139
+ "release": "pnpm build && changeset publish"
140
+ }
141
+ }