@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
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether a given doc has any per-locale row in
|
|
3
|
+
* `<collection>_locales` for the target locale.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: Payload's `findByIdNoFallback(locale: X)` returns
|
|
6
|
+
* source-locale field values for SHARED parent tables (block parents,
|
|
7
|
+
* array parents) when the target locale has no row in `<col>_locales`.
|
|
8
|
+
* Downstream code in the translate flow then treats those source-fallback
|
|
9
|
+
* values as "real target data," merges translations on top, and writes
|
|
10
|
+
* them back with the source-locale row IDs — colliding with the source's
|
|
11
|
+
* own rows in the shared parent tables (PK violation surfacing as
|
|
12
|
+
* "Value must be unique: id" on the page-level update).
|
|
13
|
+
*
|
|
14
|
+
* The fix path: before merging, probe `<collection>_locales` directly
|
|
15
|
+
* via the pg pool. If zero rows for (parent=docId, locale=targetLocale),
|
|
16
|
+
* force the merge to take the "fresh target" branch so it strips array
|
|
17
|
+
* row ids and lets Payload generate fresh ones for the new locale.
|
|
18
|
+
*
|
|
19
|
+
* Postgres-only — Payload's other adapter (mongodb) handles this
|
|
20
|
+
* differently and doesn't hit the same race. Returns `true` (assume
|
|
21
|
+
* row exists) on non-Postgres or on any error so we never block a
|
|
22
|
+
* translation that would otherwise succeed.
|
|
23
|
+
*/
|
|
24
|
+
import type { Payload } from 'payload';
|
|
25
|
+
/**
|
|
26
|
+
* Probe `<collection>_locales` for (parent=documentId, _locale=locale).
|
|
27
|
+
* Returns `true` when at least one row exists, `false` when zero rows,
|
|
28
|
+
* and `true` (defensive) when the check can't run.
|
|
29
|
+
*/
|
|
30
|
+
export declare function checkLocaleRowExists(payload: Payload, collection: string, documentId: string | number, locale: string): Promise<boolean>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether a given doc has any per-locale row in
|
|
3
|
+
* `<collection>_locales` for the target locale.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists: Payload's `findByIdNoFallback(locale: X)` returns
|
|
6
|
+
* source-locale field values for SHARED parent tables (block parents,
|
|
7
|
+
* array parents) when the target locale has no row in `<col>_locales`.
|
|
8
|
+
* Downstream code in the translate flow then treats those source-fallback
|
|
9
|
+
* values as "real target data," merges translations on top, and writes
|
|
10
|
+
* them back with the source-locale row IDs — colliding with the source's
|
|
11
|
+
* own rows in the shared parent tables (PK violation surfacing as
|
|
12
|
+
* "Value must be unique: id" on the page-level update).
|
|
13
|
+
*
|
|
14
|
+
* The fix path: before merging, probe `<collection>_locales` directly
|
|
15
|
+
* via the pg pool. If zero rows for (parent=docId, locale=targetLocale),
|
|
16
|
+
* force the merge to take the "fresh target" branch so it strips array
|
|
17
|
+
* row ids and lets Payload generate fresh ones for the new locale.
|
|
18
|
+
*
|
|
19
|
+
* Postgres-only — Payload's other adapter (mongodb) handles this
|
|
20
|
+
* differently and doesn't hit the same race. Returns `true` (assume
|
|
21
|
+
* row exists) on non-Postgres or on any error so we never block a
|
|
22
|
+
* translation that would otherwise succeed.
|
|
23
|
+
*/ /**
|
|
24
|
+
* Probe `<collection>_locales` for (parent=documentId, _locale=locale).
|
|
25
|
+
* Returns `true` when at least one row exists, `false` when zero rows,
|
|
26
|
+
* and `true` (defensive) when the check can't run.
|
|
27
|
+
*/ export async function checkLocaleRowExists(payload, collection, documentId, locale) {
|
|
28
|
+
const dbHandle = payload.db;
|
|
29
|
+
const pool = dbHandle?.pool;
|
|
30
|
+
if (!pool || typeof pool.connect !== 'function') {
|
|
31
|
+
// Non-Postgres or no pool — fall back to "assume present" so we
|
|
32
|
+
// don't block writes. The id-collision bug this guards against is
|
|
33
|
+
// Postgres-specific anyway.
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
// Sanitize the table name. Payload's collection slug → table name
|
|
37
|
+
// conversion replaces `-` with `_`; same here. Single underscore-only
|
|
38
|
+
// alphanumeric guard prevents injection even though we control all
|
|
39
|
+
// call sites today.
|
|
40
|
+
const tableBase = collection.replace(/-/g, '_');
|
|
41
|
+
if (!/^[a-zA-Z0-9_]+$/.test(tableBase)) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
const localesTable = `${tableBase}_locales`;
|
|
45
|
+
let client;
|
|
46
|
+
try {
|
|
47
|
+
client = await pool.connect();
|
|
48
|
+
const res = await client.query(// `_locale` is the per-locale row's discriminator column;
|
|
49
|
+
// `_parent_id` joins back to the parent doc.
|
|
50
|
+
`SELECT 1 FROM "${localesTable}" WHERE "_parent_id" = $1 AND "_locale" = $2 LIMIT 1`, [
|
|
51
|
+
documentId,
|
|
52
|
+
locale
|
|
53
|
+
]);
|
|
54
|
+
return res.rows.length > 0;
|
|
55
|
+
} catch (err) {
|
|
56
|
+
// Table doesn't exist (no localized fields at all), pool failure,
|
|
57
|
+
// SQL error — any of these should not block a translate. Return
|
|
58
|
+
// `true` so the merge uses its existing targetValue branch.
|
|
59
|
+
payload.logger?.debug?.(`[ai-translate] checkLocaleRowExists(${collection}, ${String(documentId)}, ${locale}) probe failed (assuming present): ${String(err)}`);
|
|
60
|
+
return true;
|
|
61
|
+
} finally{
|
|
62
|
+
if (client) client.release();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured logger wrapper around `payload.logger` (pino). Emits every
|
|
3
|
+
* line as `(object, msg)` so transports / Grafana can index fields like
|
|
4
|
+
* `event`, `batchId`, `err.name`, `err.cause.name`. The plugin previously
|
|
5
|
+
* used the `(msg)` form everywhere, throwing structure away.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
*
|
|
9
|
+
* const log = createScopedLogger(payload, {
|
|
10
|
+
* component: 'bulk.worker', batchId, unitId,
|
|
11
|
+
* });
|
|
12
|
+
* log.event('error', 'bulk.worker.unit.failed', { err, durationMs });
|
|
13
|
+
*
|
|
14
|
+
* Levels:
|
|
15
|
+
* - error: terminal failure, batch → failed, audit-write failed
|
|
16
|
+
* - warn: transient retry, fallback / degradation, unknown classifier
|
|
17
|
+
* - info: lifecycle (unit start/end, batch transition, provider resolved)
|
|
18
|
+
* - debug: per-retry state, per-unit heartbeat
|
|
19
|
+
*/
|
|
20
|
+
import type { Payload } from 'payload';
|
|
21
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
22
|
+
export interface LogContext {
|
|
23
|
+
module?: string;
|
|
24
|
+
component?: string;
|
|
25
|
+
batchId?: string | number;
|
|
26
|
+
unitId?: string | number;
|
|
27
|
+
jobId?: string | number;
|
|
28
|
+
collection?: string;
|
|
29
|
+
documentId?: string | number;
|
|
30
|
+
locale?: string;
|
|
31
|
+
provider?: string;
|
|
32
|
+
model?: string;
|
|
33
|
+
[key: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
export interface LogEventFields {
|
|
36
|
+
err?: unknown;
|
|
37
|
+
durationMs?: number;
|
|
38
|
+
attempt?: number;
|
|
39
|
+
maxAttempts?: number;
|
|
40
|
+
failureCode?: string;
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
}
|
|
43
|
+
export interface ScopedLogger {
|
|
44
|
+
event(level: LogLevel, eventName: string, fields?: LogEventFields): void;
|
|
45
|
+
child(extraContext: LogContext): ScopedLogger;
|
|
46
|
+
}
|
|
47
|
+
interface PinoLikeLogger {
|
|
48
|
+
debug?: (...args: unknown[]) => void;
|
|
49
|
+
info?: (...args: unknown[]) => void;
|
|
50
|
+
warn?: (...args: unknown[]) => void;
|
|
51
|
+
error?: (...args: unknown[]) => void;
|
|
52
|
+
}
|
|
53
|
+
type PayloadLike = Pick<Payload, 'logger'> | {
|
|
54
|
+
logger?: PinoLikeLogger;
|
|
55
|
+
};
|
|
56
|
+
export declare function createScopedLogger(payload: PayloadLike, context?: LogContext): ScopedLogger;
|
|
57
|
+
export interface SerializedError {
|
|
58
|
+
name: string;
|
|
59
|
+
message: string;
|
|
60
|
+
stack?: string;
|
|
61
|
+
code?: string | number;
|
|
62
|
+
status?: number;
|
|
63
|
+
body?: unknown;
|
|
64
|
+
cause?: SerializedError;
|
|
65
|
+
[key: string]: unknown;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Recursively serialise an error-like value. Captures `name`, `message`,
|
|
69
|
+
* `stack`, `cause`, plus any extra enumerable own properties (HTTP-client
|
|
70
|
+
* errors typically expose `status`, `body`, `code`). Mirrors pino's
|
|
71
|
+
* default `err` serializer so Grafana queries on `err.cause.status` work.
|
|
72
|
+
*/
|
|
73
|
+
export declare function serializeErr(err: unknown): SerializedError | undefined;
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured logger wrapper around `payload.logger` (pino). Emits every
|
|
3
|
+
* line as `(object, msg)` so transports / Grafana can index fields like
|
|
4
|
+
* `event`, `batchId`, `err.name`, `err.cause.name`. The plugin previously
|
|
5
|
+
* used the `(msg)` form everywhere, throwing structure away.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
*
|
|
9
|
+
* const log = createScopedLogger(payload, {
|
|
10
|
+
* component: 'bulk.worker', batchId, unitId,
|
|
11
|
+
* });
|
|
12
|
+
* log.event('error', 'bulk.worker.unit.failed', { err, durationMs });
|
|
13
|
+
*
|
|
14
|
+
* Levels:
|
|
15
|
+
* - error: terminal failure, batch → failed, audit-write failed
|
|
16
|
+
* - warn: transient retry, fallback / degradation, unknown classifier
|
|
17
|
+
* - info: lifecycle (unit start/end, batch transition, provider resolved)
|
|
18
|
+
* - debug: per-retry state, per-unit heartbeat
|
|
19
|
+
*/ export function createScopedLogger(payload, context = {}) {
|
|
20
|
+
const baseContext = {
|
|
21
|
+
module: 'ai-translate',
|
|
22
|
+
...context
|
|
23
|
+
};
|
|
24
|
+
function emit(level, eventName, fields) {
|
|
25
|
+
const logger = payload.logger;
|
|
26
|
+
if (!logger) return;
|
|
27
|
+
const fn = logger[level];
|
|
28
|
+
if (typeof fn !== 'function') return;
|
|
29
|
+
const merged = {
|
|
30
|
+
event: eventName,
|
|
31
|
+
...baseContext
|
|
32
|
+
};
|
|
33
|
+
if (fields) {
|
|
34
|
+
for (const [k, v] of Object.entries(fields)){
|
|
35
|
+
if (k === 'err') {
|
|
36
|
+
const serialized = serializeErr(v);
|
|
37
|
+
if (serialized) merged.err = serialized;
|
|
38
|
+
} else if (v !== undefined) {
|
|
39
|
+
merged[k] = v;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
fn.call(logger, merged, eventName);
|
|
45
|
+
} catch {
|
|
46
|
+
// Logger itself failed; nothing recoverable.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
event (level, eventName, fields) {
|
|
51
|
+
emit(level, eventName, fields);
|
|
52
|
+
},
|
|
53
|
+
child (extraContext) {
|
|
54
|
+
return createScopedLogger(payload, {
|
|
55
|
+
...baseContext,
|
|
56
|
+
...extraContext
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Recursively serialise an error-like value. Captures `name`, `message`,
|
|
63
|
+
* `stack`, `cause`, plus any extra enumerable own properties (HTTP-client
|
|
64
|
+
* errors typically expose `status`, `body`, `code`). Mirrors pino's
|
|
65
|
+
* default `err` serializer so Grafana queries on `err.cause.status` work.
|
|
66
|
+
*/ export function serializeErr(err) {
|
|
67
|
+
if (err == null) return undefined;
|
|
68
|
+
if (err instanceof Error) {
|
|
69
|
+
const out = {
|
|
70
|
+
name: err.name,
|
|
71
|
+
message: err.message
|
|
72
|
+
};
|
|
73
|
+
if (err.stack) out.stack = err.stack;
|
|
74
|
+
for (const key of Object.keys(err)){
|
|
75
|
+
if (key === 'name' || key === 'message' || key === 'stack' || key === 'cause') {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
out[key] = err[key];
|
|
79
|
+
}
|
|
80
|
+
const cause = err.cause;
|
|
81
|
+
if (cause != null) {
|
|
82
|
+
out.cause = serializeErr(cause);
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
if (typeof err === 'object') {
|
|
87
|
+
return {
|
|
88
|
+
name: 'NonError',
|
|
89
|
+
message: 'Non-Error thrown',
|
|
90
|
+
...err
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
name: 'NonError',
|
|
95
|
+
message: String(err)
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { Payload, PayloadRequest } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* SHA-1 of a stable JSON serialization of a value. Used to fingerprint
|
|
4
|
+
* field values for the `preserveManualEdits` feature so we can detect
|
|
5
|
+
* when a target locale row has drifted from the last plugin-written
|
|
6
|
+
* shape.
|
|
7
|
+
*
|
|
8
|
+
* Stable serialization: object keys sorted, undefined dropped. Same
|
|
9
|
+
* value → same hash across processes / machines.
|
|
10
|
+
*/
|
|
11
|
+
export declare function hashFieldValue(value: unknown): string;
|
|
12
|
+
/**
|
|
13
|
+
* Fetch every (fieldPath → lastWrittenHash) row for one
|
|
14
|
+
* (collection, documentId, locale) tuple. Used by the writer before
|
|
15
|
+
* issuing the locale-update so it can filter out fields that have
|
|
16
|
+
* drifted from the stored hash.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadFieldHashes(payload: Payload, metaCollection: string, collection: string, documentId: string | number, locale: string): Promise<Map<string, string>>;
|
|
19
|
+
/**
|
|
20
|
+
* Inspect the current target doc and decide which top-level fields in
|
|
21
|
+
* `writeData` would overwrite a manually edited value. Returns the set
|
|
22
|
+
* of field names to DROP from `writeData` before issuing the update.
|
|
23
|
+
*
|
|
24
|
+
* Detection: for each top-level field path that has a stored hash, if
|
|
25
|
+
* the current target value's hash differs from the stored one, the
|
|
26
|
+
* field has been edited since the plugin's last write — skip it.
|
|
27
|
+
*/
|
|
28
|
+
export declare function pickManualEditedFields(writeData: Record<string, unknown>, targetDoc: Record<string, unknown> | null | undefined, storedHashes: Map<string, string>): {
|
|
29
|
+
fieldsToDrop: string[];
|
|
30
|
+
skipReasons: Map<string, string>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Upsert per-field hashes after a successful locale write. One row per
|
|
34
|
+
* (collection, documentId, locale, fieldPath). Best-effort — failures
|
|
35
|
+
* here do NOT roll back the translation, they just mean the next write
|
|
36
|
+
* can't detect divergence for that field.
|
|
37
|
+
*
|
|
38
|
+
* `sourceValues` (BUG-21): map of `fieldPath → source value at time of
|
|
39
|
+
* write`. When provided, the upsert also stores `lastSourceHash` so the
|
|
40
|
+
* next translate can compare current-source-hash to stored-source-hash
|
|
41
|
+
* and skip the LLM call entirely if both source AND target are
|
|
42
|
+
* unchanged. Omitted when source values aren't available (rare).
|
|
43
|
+
*/
|
|
44
|
+
export declare function recordFieldHashes(payload: Payload, metaCollection: string, collection: string, documentId: string | number, locale: string, writeData: Record<string, unknown>, sourceValues?: Record<string, unknown>,
|
|
45
|
+
/**
|
|
46
|
+
* Optional request context to thread into the inner find/update/create
|
|
47
|
+
* calls. When the caller wraps the locale-write in a fresh transaction,
|
|
48
|
+
* passing the same `req` here keeps the bookkeeping rows on the same
|
|
49
|
+
* connection lifecycle. Omit for ad-hoc / out-of-band invocations.
|
|
50
|
+
*/
|
|
51
|
+
req?: PayloadRequest): Promise<{
|
|
52
|
+
written: number;
|
|
53
|
+
failed: number;
|
|
54
|
+
}>;
|
|
55
|
+
/**
|
|
56
|
+
* Fetch every `(fieldPath → { writtenHash, sourceHash })` row for one
|
|
57
|
+
* (collection, documentId, locale) tuple. Variant of `loadFieldHashes`
|
|
58
|
+
* that also returns the BUG-21 source hash so the LLM-skip check can
|
|
59
|
+
* compare current source content to what was translated last time.
|
|
60
|
+
*
|
|
61
|
+
* Returns an empty map on read failure (caller treats as "no prior data,
|
|
62
|
+
* proceed with full translate"). Rows pre-BUG-21 have `sourceHash:
|
|
63
|
+
* undefined` — those are treated as "can't safely skip; run translate."
|
|
64
|
+
*/
|
|
65
|
+
export declare function loadFieldHashesWithSource(payload: Payload, metaCollection: string, collection: string, documentId: string | number, locale: string): Promise<Map<string, {
|
|
66
|
+
writtenHash: string;
|
|
67
|
+
sourceHash?: string;
|
|
68
|
+
}>>;
|
|
69
|
+
import type { FieldLocaleResult, TranslationUnit } from '../types.js';
|
|
70
|
+
/**
|
|
71
|
+
* Result of a hash-skip preflight.
|
|
72
|
+
* - `remainingUnits`: units the caller should still send to the LLM.
|
|
73
|
+
* - `hashSkipped`: synthetic FieldLocaleResults to add to the success
|
|
74
|
+
* bucket so the per-field event stream and counters reflect that
|
|
75
|
+
* these fields were processed (just not via the LLM).
|
|
76
|
+
*/
|
|
77
|
+
export type HashSkipResult = {
|
|
78
|
+
remainingUnits: TranslationUnit[];
|
|
79
|
+
hashSkipped: FieldLocaleResult[];
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Read stored source+target hashes for `(surfaceSlug, documentId,
|
|
83
|
+
* targetLocale)`, compare against current source + target, and remove
|
|
84
|
+
* units whose top-level field is unchanged on both sides. Returns the
|
|
85
|
+
* remaining units the caller still needs to translate plus synthetic
|
|
86
|
+
* `status: 'success'` results for the skipped units (characterCount = 0,
|
|
87
|
+
* durationMs = 0 — these are the "fieldsHashSkipped" rows in usage).
|
|
88
|
+
*
|
|
89
|
+
* Surface-agnostic: pass a `loadTargetDoc` closure so callers can supply
|
|
90
|
+
* `findByIdNoFallback(payload, collection, docId, locale)` for collections
|
|
91
|
+
* or `findGlobalNoFallback(payload, globalSlug, locale)` for globals.
|
|
92
|
+
*
|
|
93
|
+
* Wrapped in try/catch internally — best-effort optimization, never throws.
|
|
94
|
+
*/
|
|
95
|
+
export declare function applyHashSkip(args: {
|
|
96
|
+
payload: Payload;
|
|
97
|
+
metaCollection: string;
|
|
98
|
+
surfaceSlug: string;
|
|
99
|
+
documentId: string | number;
|
|
100
|
+
targetLocale: string;
|
|
101
|
+
units: TranslationUnit[];
|
|
102
|
+
sourceDoc: Record<string, unknown>;
|
|
103
|
+
loadTargetDoc: () => Promise<Record<string, unknown> | null>;
|
|
104
|
+
}): Promise<HashSkipResult>;
|
|
105
|
+
/**
|
|
106
|
+
* Persist per-field source+target hashes after a successful locale
|
|
107
|
+
* write. Surface-agnostic — `surfaceSlug` is the collection slug for
|
|
108
|
+
* collection writes, or the global slug for global writes (matching the
|
|
109
|
+
* existing `bulk-translate` persist contract).
|
|
110
|
+
*
|
|
111
|
+
* Builds the `sourceValues` map from `sourceDoc` for exactly the fields
|
|
112
|
+
* in `writeData`, then defers to `recordFieldHashes`. Wraps the call so
|
|
113
|
+
* upstream errors are logged via the payload logger but never re-thrown
|
|
114
|
+
* — translation succeeded already, bookkeeping failure shouldn't surface
|
|
115
|
+
* to the editor.
|
|
116
|
+
*/
|
|
117
|
+
export declare function recordHashesAfterWrite(args: {
|
|
118
|
+
payload: Payload;
|
|
119
|
+
metaCollection: string;
|
|
120
|
+
surfaceSlug: string;
|
|
121
|
+
documentId: string | number;
|
|
122
|
+
targetLocale: string;
|
|
123
|
+
writeData: Record<string, unknown>;
|
|
124
|
+
sourceDoc: Record<string, unknown>;
|
|
125
|
+
}): Promise<{
|
|
126
|
+
written: number;
|
|
127
|
+
failed: number;
|
|
128
|
+
}>;
|