@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.
- package/LICENSE +21 -0
- package/README.md +714 -0
- package/dist/alerts-collection.d.ts +21 -0
- package/dist/alerts-collection.js +159 -0
- package/dist/api.d.ts +4 -0
- package/dist/api.js +918 -0
- package/dist/bulk-translate-batches-collection.d.ts +29 -0
- package/dist/bulk-translate-batches-collection.js +404 -0
- package/dist/bulk-translate-units-collection.d.ts +35 -0
- package/dist/bulk-translate-units-collection.js +310 -0
- package/dist/client/estimated-cost-cell.d.ts +6 -0
- package/dist/client/estimated-cost-cell.js +12 -0
- package/dist/client/excluded-fields-field.d.ts +45 -0
- package/dist/client/excluded-fields-field.js +553 -0
- package/dist/client/field-translate-button.d.ts +6 -0
- package/dist/client/field-translate-button.js +199 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +6 -0
- package/dist/client/lib/use-global-kill-switches.d.ts +20 -0
- package/dist/client/lib/use-global-kill-switches.js +58 -0
- package/dist/client/translate-button.d.ts +2 -0
- package/dist/client/translate-button.js +228 -0
- package/dist/client/translate-modal.d.ts +16 -0
- package/dist/client/translate-modal.js +549 -0
- package/dist/client/translation-progress.d.ts +10 -0
- package/dist/client/translation-progress.js +297 -0
- package/dist/components/TranslationNavGroup.d.ts +45 -0
- package/dist/components/TranslationNavGroup.js +104 -0
- package/dist/defaults.d.ts +11 -0
- package/dist/defaults.js +16 -0
- package/dist/endpoints/client-config.d.ts +44 -0
- package/dist/endpoints/client-config.js +145 -0
- package/dist/endpoints/estimate.d.ts +5 -0
- package/dist/endpoints/estimate.js +237 -0
- package/dist/endpoints/progress.d.ts +2 -0
- package/dist/endpoints/progress.js +314 -0
- package/dist/endpoints/translate.d.ts +11 -0
- package/dist/endpoints/translate.js +376 -0
- package/dist/endpoints/translation-hub/_helpers.d.ts +140 -0
- package/dist/endpoints/translation-hub/_helpers.js +297 -0
- package/dist/endpoints/translation-hub/active.d.ts +21 -0
- package/dist/endpoints/translation-hub/active.js +220 -0
- package/dist/endpoints/translation-hub/cancel.d.ts +22 -0
- package/dist/endpoints/translation-hub/cancel.js +233 -0
- package/dist/endpoints/translation-hub/enqueue.d.ts +70 -0
- package/dist/endpoints/translation-hub/enqueue.js +529 -0
- package/dist/endpoints/translation-hub/failures.d.ts +12 -0
- package/dist/endpoints/translation-hub/failures.js +67 -0
- package/dist/endpoints/translation-hub/force-reset.d.ts +20 -0
- package/dist/endpoints/translation-hub/force-reset.js +144 -0
- package/dist/endpoints/translation-hub/index.d.ts +21 -0
- package/dist/endpoints/translation-hub/index.js +20 -0
- package/dist/endpoints/translation-hub/list.d.ts +40 -0
- package/dist/endpoints/translation-hub/list.js +182 -0
- package/dist/endpoints/translation-hub/preflight.d.ts +19 -0
- package/dist/endpoints/translation-hub/preflight.js +141 -0
- package/dist/endpoints/translation-hub/retry-failed.d.ts +38 -0
- package/dist/endpoints/translation-hub/retry-failed.js +235 -0
- package/dist/endpoints/translation-hub/revert.d.ts +88 -0
- package/dist/endpoints/translation-hub/revert.js +405 -0
- package/dist/endpoints/translation-hub/status.d.ts +45 -0
- package/dist/endpoints/translation-hub/status.js +391 -0
- package/dist/endpoints/translation-hub/usage-summary.d.ts +114 -0
- package/dist/endpoints/translation-hub/usage-summary.js +481 -0
- package/dist/exports/client.d.ts +6 -0
- package/dist/exports/client.js +6 -0
- package/dist/exports/components.d.ts +6 -0
- package/dist/exports/components.js +5 -0
- package/dist/exports/index.d.ts +8 -0
- package/dist/exports/index.js +7 -0
- package/dist/exports/providers.d.ts +9 -0
- package/dist/exports/providers.js +5 -0
- package/dist/exports/views-client.d.ts +23 -0
- package/dist/exports/views-client.js +22 -0
- package/dist/exports/views.d.ts +30 -0
- package/dist/exports/views.js +29 -0
- package/dist/hooks/after-change-global.d.ts +4 -0
- package/dist/hooks/after-change-global.js +109 -0
- package/dist/hooks/after-change.d.ts +16 -0
- package/dist/hooks/after-change.js +205 -0
- package/dist/hooks/after-delete.d.ts +30 -0
- package/dist/hooks/after-delete.js +95 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/jobs-collection.d.ts +17 -0
- package/dist/jobs-collection.js +139 -0
- package/dist/lexical/classifier.d.ts +3 -0
- package/dist/lexical/classifier.js +108 -0
- package/dist/lexical/deserializer.d.ts +4 -0
- package/dist/lexical/deserializer.js +263 -0
- package/dist/lexical/placeholder-integrity.d.ts +6 -0
- package/dist/lexical/placeholder-integrity.js +21 -0
- package/dist/lexical/placeholders.d.ts +21 -0
- package/dist/lexical/placeholders.js +117 -0
- package/dist/lexical/serializer.d.ts +21 -0
- package/dist/lexical/serializer.js +233 -0
- package/dist/lexical/types.d.ts +32 -0
- package/dist/lexical/types.js +1 -0
- package/dist/lib/auth-diagnostics.d.ts +14 -0
- package/dist/lib/auth-diagnostics.js +19 -0
- package/dist/lib/batch-counts.d.ts +58 -0
- package/dist/lib/batch-counts.js +105 -0
- package/dist/lib/bulk-translate-migrations.d.ts +92 -0
- package/dist/lib/bulk-translate-migrations.js +153 -0
- package/dist/lib/coalescing-queue.d.ts +38 -0
- package/dist/lib/coalescing-queue.js +69 -0
- package/dist/lib/content-extractor.d.ts +16 -0
- package/dist/lib/content-extractor.js +410 -0
- package/dist/lib/content-hash.d.ts +1 -0
- package/dist/lib/content-hash.js +19 -0
- package/dist/lib/content-patcher.d.ts +15 -0
- package/dist/lib/content-patcher.js +293 -0
- package/dist/lib/cost-guards.d.ts +2 -0
- package/dist/lib/cost-guards.js +18 -0
- package/dist/lib/daily-spend-cap.d.ts +58 -0
- package/dist/lib/daily-spend-cap.js +233 -0
- package/dist/lib/effective-locales.d.ts +181 -0
- package/dist/lib/effective-locales.js +302 -0
- package/dist/lib/error-messages.d.ts +245 -0
- package/dist/lib/error-messages.js +626 -0
- package/dist/lib/events.d.ts +39 -0
- package/dist/lib/events.js +146 -0
- package/dist/lib/exclude-fields.d.ts +3 -0
- package/dist/lib/exclude-fields.js +64 -0
- package/dist/lib/field-breadcrumb.d.ts +31 -0
- package/dist/lib/field-breadcrumb.js +227 -0
- package/dist/lib/field-diff.d.ts +1 -0
- package/dist/lib/field-diff.js +25 -0
- package/dist/lib/field-empty.d.ts +2 -0
- package/dist/lib/field-empty.js +68 -0
- package/dist/lib/field-resolver.d.ts +3 -0
- package/dist/lib/field-resolver.js +164 -0
- package/dist/lib/group-soft-skips.d.ts +39 -0
- package/dist/lib/group-soft-skips.js +45 -0
- package/dist/lib/locale-merge.d.ts +44 -0
- package/dist/lib/locale-merge.js +357 -0
- package/dist/lib/locale-row-check.d.ts +30 -0
- package/dist/lib/locale-row-check.js +64 -0
- package/dist/lib/logger.d.ts +74 -0
- package/dist/lib/logger.js +97 -0
- package/dist/lib/manual-edit-guard.d.ts +128 -0
- package/dist/lib/manual-edit-guard.js +393 -0
- package/dist/lib/output-validation.d.ts +48 -0
- package/dist/lib/output-validation.js +148 -0
- package/dist/lib/payload-read.d.ts +16 -0
- package/dist/lib/payload-read.js +51 -0
- package/dist/lib/per-doc-claim.d.ts +90 -0
- package/dist/lib/per-doc-claim.js +140 -0
- package/dist/lib/per-doc-lock.d.ts +94 -0
- package/dist/lib/per-doc-lock.js +119 -0
- package/dist/lib/persist-usage.d.ts +91 -0
- package/dist/lib/persist-usage.js +116 -0
- package/dist/lib/progress-store.d.ts +103 -0
- package/dist/lib/progress-store.js +314 -0
- package/dist/lib/rate-limiter.d.ts +3 -0
- package/dist/lib/rate-limiter.js +53 -0
- package/dist/lib/snapshot-select.d.ts +43 -0
- package/dist/lib/snapshot-select.js +108 -0
- package/dist/lib/translate-prompt.d.ts +31 -0
- package/dist/lib/translate-prompt.js +66 -0
- package/dist/lib/translation-token-bucket.d.ts +57 -0
- package/dist/lib/translation-token-bucket.js +365 -0
- package/dist/lib/truncate-source-value.d.ts +1 -0
- package/dist/lib/truncate-source-value.js +27 -0
- package/dist/manual-edit-collection.d.ts +22 -0
- package/dist/manual-edit-collection.js +124 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.js +934 -0
- package/dist/providers/ai-sdk-adapter.d.ts +35 -0
- package/dist/providers/ai-sdk-adapter.js +100 -0
- package/dist/providers/anthropic.d.ts +31 -0
- package/dist/providers/anthropic.js +66 -0
- package/dist/providers/custom.d.ts +36 -0
- package/dist/providers/custom.js +24 -0
- package/dist/providers/gemini.d.ts +20 -0
- package/dist/providers/gemini.js +48 -0
- package/dist/providers/mock.d.ts +2 -0
- package/dist/providers/mock.js +29 -0
- package/dist/providers/openai.d.ts +28 -0
- package/dist/providers/openai.js +69 -0
- package/dist/settings-global.d.ts +74 -0
- package/dist/settings-global.js +216 -0
- package/dist/tasks/bulk-translate-coordinator.d.ts +115 -0
- package/dist/tasks/bulk-translate-coordinator.js +708 -0
- package/dist/tasks/bulk-translate-doc-task.d.ts +142 -0
- package/dist/tasks/bulk-translate-doc-task.js +1000 -0
- package/dist/tasks/bulk-translate-janitor.d.ts +87 -0
- package/dist/tasks/bulk-translate-janitor.js +311 -0
- package/dist/tasks/translate-job-task.d.ts +51 -0
- package/dist/tasks/translate-job-task.js +154 -0
- package/dist/translate.d.ts +113 -0
- package/dist/translate.js +911 -0
- package/dist/translation-daily-spend-collection.d.ts +24 -0
- package/dist/translation-daily-spend-collection.js +133 -0
- package/dist/translation-rate-limits-collection.d.ts +30 -0
- package/dist/translation-rate-limits-collection.js +144 -0
- package/dist/types.d.ts +672 -0
- package/dist/types.js +1 -0
- package/dist/usage-collection.d.ts +14 -0
- package/dist/usage-collection.js +377 -0
- package/dist/views/BulkRunsHub/BatchRow.d.ts +32 -0
- package/dist/views/BulkRunsHub/BatchRow.js +1222 -0
- package/dist/views/BulkRunsHub/BucketRow.d.ts +62 -0
- package/dist/views/BulkRunsHub/BucketRow.js +982 -0
- package/dist/views/BulkRunsHub/BulkRunsHub.client.d.ts +18 -0
- package/dist/views/BulkRunsHub/BulkRunsHub.client.js +331 -0
- package/dist/views/BulkRunsHub/EmptyState.d.ts +6 -0
- package/dist/views/BulkRunsHub/EmptyState.js +64 -0
- package/dist/views/BulkRunsHub/FilterBar.d.ts +16 -0
- package/dist/views/BulkRunsHub/FilterBar.js +284 -0
- package/dist/views/BulkRunsHub/InFlightBanner.d.ts +14 -0
- package/dist/views/BulkRunsHub/InFlightBanner.js +59 -0
- package/dist/views/BulkRunsHub/StatusBadge.d.ts +64 -0
- package/dist/views/BulkRunsHub/StatusBadge.js +248 -0
- package/dist/views/BulkRunsHub/SummaryStrip.d.ts +22 -0
- package/dist/views/BulkRunsHub/SummaryStrip.js +249 -0
- package/dist/views/BulkRunsHub/bucket-grouping.d.ts +200 -0
- package/dist/views/BulkRunsHub/bucket-grouping.js +344 -0
- package/dist/views/BulkRunsHub/bucketFailureSummary.d.ts +9 -0
- package/dist/views/BulkRunsHub/bucketFailureSummary.js +36 -0
- package/dist/views/BulkRunsHub/dedupedStatusFetch.d.ts +5 -0
- package/dist/views/BulkRunsHub/dedupedStatusFetch.js +45 -0
- package/dist/views/BulkRunsHub/index.d.ts +17 -0
- package/dist/views/BulkRunsHub/index.js +80 -0
- package/dist/views/BulkRunsHub/urlFilters.d.ts +14 -0
- package/dist/views/BulkRunsHub/urlFilters.js +50 -0
- package/dist/views/BulkRunsHub/useBulkRunsList.d.ts +26 -0
- package/dist/views/BulkRunsHub/useBulkRunsList.js +204 -0
- package/dist/views/BulkRunsHub/useUrlFilters.d.ts +10 -0
- package/dist/views/BulkRunsHub/useUrlFilters.js +88 -0
- package/dist/views/TranslationHub/ActiveJobs.d.ts +6 -0
- package/dist/views/TranslationHub/ActiveJobs.js +320 -0
- package/dist/views/TranslationHub/AdvancedPanel.d.ts +17 -0
- package/dist/views/TranslationHub/AdvancedPanel.js +996 -0
- package/dist/views/TranslationHub/AlertBanner.d.ts +6 -0
- package/dist/views/TranslationHub/AlertBanner.js +568 -0
- package/dist/views/TranslationHub/AuditPanel.d.ts +6 -0
- package/dist/views/TranslationHub/AuditPanel.helpers.d.ts +44 -0
- package/dist/views/TranslationHub/AuditPanel.helpers.js +71 -0
- package/dist/views/TranslationHub/AuditPanel.js +1367 -0
- package/dist/views/TranslationHub/BulkTranslate.types.d.ts +242 -0
- package/dist/views/TranslationHub/BulkTranslate.types.js +36 -0
- package/dist/views/TranslationHub/BulkTranslateFailureDrawer.d.ts +19 -0
- package/dist/views/TranslationHub/BulkTranslateFailureDrawer.js +332 -0
- package/dist/views/TranslationHub/BulkTranslateMonitor.d.ts +28 -0
- package/dist/views/TranslationHub/BulkTranslateMonitor.js +305 -0
- package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.d.ts +3 -0
- package/dist/views/TranslationHub/BulkTranslateNarrowViewportBanner.js +42 -0
- package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.d.ts +26 -0
- package/dist/views/TranslationHub/BulkTranslatePostEnqueueTransition.js +95 -0
- package/dist/views/TranslationHub/BulkTranslatePreflightModal.d.ts +22 -0
- package/dist/views/TranslationHub/BulkTranslatePreflightModal.js +879 -0
- package/dist/views/TranslationHub/BulkTranslateTerminalCard.d.ts +29 -0
- package/dist/views/TranslationHub/BulkTranslateTerminalCard.js +445 -0
- package/dist/views/TranslationHub/BulkTranslateTrigger.d.ts +66 -0
- package/dist/views/TranslationHub/BulkTranslateTrigger.js +161 -0
- package/dist/views/TranslationHub/EditorRecentRunsPanel.d.ts +33 -0
- package/dist/views/TranslationHub/EditorRecentRunsPanel.js +290 -0
- package/dist/views/TranslationHub/Hub.client.d.ts +74 -0
- package/dist/views/TranslationHub/Hub.client.js +357 -0
- package/dist/views/TranslationHub/ModelCombobox.d.ts +14 -0
- package/dist/views/TranslationHub/ModelCombobox.js +415 -0
- package/dist/views/TranslationHub/PerCollectionConfig.d.ts +10 -0
- package/dist/views/TranslationHub/PerCollectionConfig.helpers.d.ts +16 -0
- package/dist/views/TranslationHub/PerCollectionConfig.helpers.js +19 -0
- package/dist/views/TranslationHub/PerCollectionConfig.js +759 -0
- package/dist/views/TranslationHub/SettingsRail.d.ts +11 -0
- package/dist/views/TranslationHub/SettingsRail.js +382 -0
- package/dist/views/TranslationHub/StatusStrip.d.ts +6 -0
- package/dist/views/TranslationHub/StatusStrip.js +451 -0
- package/dist/views/TranslationHub/UsageTable.d.ts +6 -0
- package/dist/views/TranslationHub/UsageTable.helpers.d.ts +69 -0
- package/dist/views/TranslationHub/UsageTable.helpers.js +49 -0
- package/dist/views/TranslationHub/UsageTable.js +1240 -0
- package/dist/views/TranslationHub/alertGrouping.d.ts +70 -0
- package/dist/views/TranslationHub/alertGrouping.js +99 -0
- package/dist/views/TranslationHub/index.d.ts +20 -0
- package/dist/views/TranslationHub/index.js +109 -0
- package/dist/views/TranslationHub/tabNavigation.d.ts +53 -0
- package/dist/views/TranslationHub/tabNavigation.js +74 -0
- package/dist/views/TranslationHub/terminalBannerVisibility.d.ts +33 -0
- package/dist/views/TranslationHub/terminalBannerVisibility.js +124 -0
- package/dist/views/TranslationHub/useBulkTranslateActive.d.ts +49 -0
- package/dist/views/TranslationHub/useBulkTranslateActive.js +251 -0
- package/dist/views/TranslationHub/useFocusTrap.d.ts +6 -0
- package/dist/views/TranslationHub/useFocusTrap.js +81 -0
- package/dist/views/TranslationHub/useTranslationHubUsageSummary.d.ts +77 -0
- package/dist/views/TranslationHub/useTranslationHubUsageSummary.js +267 -0
- package/dist/views/shared/EditorError.d.ts +97 -0
- package/dist/views/shared/EditorError.js +205 -0
- package/dist/views/shared/ModelCell.d.ts +18 -0
- package/dist/views/shared/ModelCell.js +31 -0
- package/dist/views/shared/docHref.d.ts +16 -0
- package/dist/views/shared/docHref.js +26 -0
- package/dist/views/shared/fetch-error-body.d.ts +25 -0
- package/dist/views/shared/fetch-error-body.js +42 -0
- package/dist/views/shared/filterPillStyle.d.ts +35 -0
- package/dist/views/shared/filterPillStyle.js +40 -0
- package/dist/views/shared/format.d.ts +75 -0
- package/dist/views/shared/format.js +131 -0
- package/package.json +141 -0
package/dist/types.d.ts
ADDED
|
@@ -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;
|