@focus-reactive/payload-plugin-translator 0.8.2 → 0.9.1

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 (148) hide show
  1. package/README.md +37 -0
  2. package/dist/auto-translate-config.d.ts +40 -0
  3. package/dist/auto-translate-config.js +46 -0
  4. package/dist/client/entities/translation/index.d.ts +1 -0
  5. package/dist/client/entities/translation/index.js +1 -0
  6. package/dist/client/entities/translation/model/autoTranslateSummary.d.ts +15 -0
  7. package/dist/client/entities/translation/model/autoTranslateSummary.js +22 -0
  8. package/dist/client/entities/translation/ui/AutoTranslateMarker/AutoTranslateMarker.d.ts +14 -0
  9. package/dist/client/entities/translation/ui/AutoTranslateMarker/AutoTranslateMarker.js +34 -0
  10. package/dist/client/entities/translation/ui/AutoTranslateMarker/index.d.ts +1 -0
  11. package/dist/client/entities/translation/ui/AutoTranslateMarker/index.js +3 -0
  12. package/dist/client/entities/translation/ui/AutoTranslateMarker/styles.module.scss +30 -0
  13. package/dist/client/shared/lib/assets/icons/AutoTranslateIcon.d.ts +3 -0
  14. package/dist/client/shared/lib/assets/icons/AutoTranslateIcon.js +14 -0
  15. package/dist/client/widgets/bulk-translation-dashboard/ui/BulkTranslationDashboard.d.ts +3 -1
  16. package/dist/client/widgets/bulk-translation-dashboard/ui/BulkTranslationDashboard.js +14 -5
  17. package/dist/client/widgets/bulk-translation-dashboard/ui/BulkTranslationDashboard.server.js +4 -1
  18. package/dist/client/widgets/bulk-translation-dashboard/ui/styles.module.scss +9 -0
  19. package/dist/client/widgets/translate-document/ui/TranslateDocument.d.ts +3 -1
  20. package/dist/client/widgets/translate-document/ui/TranslateDocument.js +14 -5
  21. package/dist/client/widgets/translate-document/ui/TranslateDocument.server.js +4 -1
  22. package/dist/client/widgets/translate-document/ui/styles.module.scss +9 -0
  23. package/dist/core/domain/auto-translate/getAutoTranslateConfig.d.ts +14 -0
  24. package/dist/core/domain/auto-translate/getAutoTranslateConfig.js +29 -0
  25. package/dist/core/domain/auto-translate/hasSourceContentChanged.d.ts +21 -0
  26. package/dist/core/domain/auto-translate/hasSourceContentChanged.js +25 -0
  27. package/dist/core/domain/auto-translate/index.d.ts +4 -0
  28. package/dist/core/domain/auto-translate/index.js +5 -0
  29. package/dist/core/domain/auto-translate/types.d.ts +32 -0
  30. package/dist/core/domain/auto-translate/types.js +7 -0
  31. package/dist/core/{content-projection → domain/content-projection}/computeSourceFingerprint.d.ts +1 -1
  32. package/dist/core/{content-projection → domain/content-projection}/contentProjector.d.ts +1 -1
  33. package/dist/core/{content-projection → domain/content-projection}/contentProjector.js +2 -2
  34. package/dist/core/{content-projection → domain/content-projection}/translatableLeaf.d.ts +1 -1
  35. package/dist/core/{content-projection → domain/content-projection}/translatableLeaf.js +2 -2
  36. package/dist/core/index.d.ts +10 -10
  37. package/dist/core/index.js +5 -5
  38. package/dist/core/translation-pipeline/TranslationPipeline.d.ts +1 -1
  39. package/dist/core/translation-pipeline/stages/data-reconciler/DataReconciler.d.ts +1 -1
  40. package/dist/core/translation-pipeline/stages/data-reconciler/DataReconciler.js +3 -3
  41. package/dist/core/translation-pipeline/stages/field-collector/FieldChunkCollector.d.ts +1 -1
  42. package/dist/core/translation-pipeline/stages/field-collector/FieldChunkCollector.js +3 -3
  43. package/dist/core/translation-pipeline/stages/text-expander/RichTextExpander.js +1 -1
  44. package/dist/core/translation-pipeline/stages/translation/Translation.stage.d.ts +1 -1
  45. package/dist/core/translation-pipeline/strategies/Overwrite.strategy.js +1 -1
  46. package/dist/core/translation-pipeline/strategies/SkipExisting.strategy.js +3 -3
  47. package/dist/core/translation-pipeline/translateContent.d.ts +2 -2
  48. package/dist/core/translation-pipeline/types/FieldChunk.d.ts +1 -1
  49. package/dist/core/translation-pipeline/types/Pipeline.d.ts +1 -1
  50. package/dist/core/translation-pipeline/types/PipelineContext.d.ts +1 -1
  51. package/dist/core/translation-pipeline/types/TextChunk.d.ts +1 -1
  52. package/dist/field-config.d.ts +1 -1
  53. package/dist/field-config.js +1 -1
  54. package/dist/index.d.ts +2 -0
  55. package/dist/index.js +4 -0
  56. package/dist/plugin.d.ts +1 -1
  57. package/dist/plugin.js +5 -0
  58. package/dist/server/features/translate-document/handler.d.ts +1 -1
  59. package/dist/server/features/translate-document/handler.js +9 -1
  60. package/dist/server/features/translate-document/wireTranslateRunner.d.ts +1 -1
  61. package/dist/server/features/translate-field/model.d.ts +1 -1
  62. package/dist/server/features/translate-field/resolveFieldSubtree.js +1 -1
  63. package/dist/server/modules/auto-translate/AutoTranslate.policy.d.ts +79 -0
  64. package/dist/server/modules/auto-translate/AutoTranslate.policy.js +77 -0
  65. package/dist/server/modules/auto-translate/AutoTranslate.shapes.d.ts +22 -0
  66. package/dist/server/modules/auto-translate/AutoTranslate.shapes.js +3 -0
  67. package/dist/server/modules/auto-translate/AutoTranslate.wiring.d.ts +21 -0
  68. package/dist/server/modules/auto-translate/AutoTranslate.wiring.js +68 -0
  69. package/dist/server/modules/auto-translate/AutoTranslateEnqueue.hook.d.ts +39 -0
  70. package/dist/server/modules/auto-translate/AutoTranslateEnqueue.hook.js +94 -0
  71. package/dist/server/modules/auto-translate/index.d.ts +4 -0
  72. package/dist/server/modules/auto-translate/index.js +7 -0
  73. package/dist/server/modules/index.d.ts +1 -1
  74. package/dist/server/modules/index.js +1 -1
  75. package/dist/server/modules/provenance/Provenance.service.d.ts +1 -1
  76. package/dist/server/modules/provenance/Provenance.service.js +2 -2
  77. package/dist/server/modules/provenance/Provenance.store.d.ts +1 -1
  78. package/dist/server/modules/provenance/index.js +1 -1
  79. package/dist/server/modules/task-runner/payload-jobs-runner/PayloadJobsTaskRunner.js +4 -0
  80. package/dist/server/modules/task-runner/types.d.ts +7 -0
  81. package/dist/server/modules/translation-levels/PluginConfigBuilder.d.ts +1 -1
  82. package/dist/server/modules/translation-levels/types.d.ts +1 -1
  83. package/dist/server/shared/index.d.ts +5 -5
  84. package/dist/server/shared/index.js +3 -3
  85. package/dist/server/shared/utils/filterLocalizedFields.js +2 -2
  86. package/dist/server/shared/utils/getByPath.js +1 -1
  87. package/dist/server/shared/utils/index.d.ts +2 -2
  88. package/dist/server/shared/utils/index.js +2 -2
  89. package/dist/server/shared/utils/setByPath.js +1 -1
  90. package/dist/translation-providers/index.d.ts +1 -1
  91. package/dist/translation-providers/openai/OpenAITranslation.provider.d.ts +1 -1
  92. package/dist/translation-providers/openai/OpenAITranslation.provider.js +1 -1
  93. package/dist/types/AutoTranslateContext.d.ts +10 -0
  94. package/dist/types/AutoTranslateContext.js +11 -0
  95. package/package.json +1 -1
  96. /package/dist/core/{content-projection → domain/content-projection}/computeSourceFingerprint.js +0 -0
  97. /package/dist/core/{content-projection → domain/content-projection}/fingerprinter.d.ts +0 -0
  98. /package/dist/core/{content-projection → domain/content-projection}/fingerprinter.js +0 -0
  99. /package/dist/core/{content-projection → domain/content-projection}/idPath.d.ts +0 -0
  100. /package/dist/core/{content-projection → domain/content-projection}/idPath.js +0 -0
  101. /package/dist/core/{field-config → domain/field-config}/getFieldConfig.d.ts +0 -0
  102. /package/dist/core/{field-config → domain/field-config}/getFieldConfig.js +0 -0
  103. /package/dist/core/{field-config → domain/field-config}/index.d.ts +0 -0
  104. /package/dist/core/{field-config → domain/field-config}/index.js +0 -0
  105. /package/dist/core/{field-config → domain/field-config}/types.d.ts +0 -0
  106. /package/dist/core/{field-config → domain/field-config}/types.js +0 -0
  107. /package/dist/core/{provenance → domain/provenance}/ProvenanceStore.interface.d.ts +0 -0
  108. /package/dist/core/{provenance → domain/provenance}/ProvenanceStore.interface.js +0 -0
  109. /package/dist/core/{provenance → domain/provenance}/index.d.ts +0 -0
  110. /package/dist/core/{provenance → domain/provenance}/index.js +0 -0
  111. /package/dist/core/{provenance → domain/provenance}/staleness.d.ts +0 -0
  112. /package/dist/core/{provenance → domain/provenance}/staleness.js +0 -0
  113. /package/dist/core/{translation-providers → domain/translation-providers}/TranslationProvider.interface.d.ts +0 -0
  114. /package/dist/core/{translation-providers → domain/translation-providers}/TranslationProvider.interface.js +0 -0
  115. /package/dist/core/{translation-providers → domain/translation-providers}/index.d.ts +0 -0
  116. /package/dist/core/{translation-providers → domain/translation-providers}/index.js +0 -0
  117. /package/dist/core/{field-traversal → kernel/field-traversal}/findFieldByPath.d.ts +0 -0
  118. /package/dist/core/{field-traversal → kernel/field-traversal}/findFieldByPath.js +0 -0
  119. /package/dist/core/{field-traversal → kernel/field-traversal}/guards.d.ts +0 -0
  120. /package/dist/core/{field-traversal → kernel/field-traversal}/guards.js +0 -0
  121. /package/dist/core/{field-traversal → kernel/field-traversal}/index.d.ts +0 -0
  122. /package/dist/core/{field-traversal → kernel/field-traversal}/index.js +0 -0
  123. /package/dist/core/{field-traversal → kernel/field-traversal}/kernel.d.ts +0 -0
  124. /package/dist/core/{field-traversal → kernel/field-traversal}/kernel.js +0 -0
  125. /package/dist/core/{field-traversal → kernel/field-traversal}/predicates.d.ts +0 -0
  126. /package/dist/core/{field-traversal → kernel/field-traversal}/predicates.js +0 -0
  127. /package/dist/core/{field-traversal → kernel/field-traversal}/types.d.ts +0 -0
  128. /package/dist/core/{field-traversal → kernel/field-traversal}/types.js +0 -0
  129. /package/dist/core/{field-traversal → kernel/field-traversal}/walkFields.d.ts +0 -0
  130. /package/dist/core/{field-traversal → kernel/field-traversal}/walkFields.js +0 -0
  131. /package/dist/core/{lexical → kernel/lexical}/collectTextNodes.d.ts +0 -0
  132. /package/dist/core/{lexical → kernel/lexical}/collectTextNodes.js +0 -0
  133. /package/dist/core/{lexical → kernel/lexical}/guards.d.ts +0 -0
  134. /package/dist/core/{lexical → kernel/lexical}/guards.js +0 -0
  135. /package/dist/core/{lexical → kernel/lexical}/index.d.ts +0 -0
  136. /package/dist/core/{lexical → kernel/lexical}/index.js +0 -0
  137. /package/dist/core/{lexical → kernel/lexical}/isEmptyRichText.d.ts +0 -0
  138. /package/dist/core/{lexical → kernel/lexical}/isEmptyRichText.js +0 -0
  139. /package/dist/core/{lexical → kernel/lexical}/traverseLexicalTree.d.ts +0 -0
  140. /package/dist/core/{lexical → kernel/lexical}/traverseLexicalTree.js +0 -0
  141. /package/dist/core/{lexical → kernel/lexical}/types.d.ts +0 -0
  142. /package/dist/core/{lexical → kernel/lexical}/types.js +0 -0
  143. /package/dist/core/{utils → kernel/utils}/index.d.ts +0 -0
  144. /package/dist/core/{utils → kernel/utils}/index.js +0 -0
  145. /package/dist/core/{utils → kernel/utils}/isEmpty.d.ts +0 -0
  146. /package/dist/core/{utils → kernel/utils}/isEmpty.js +0 -0
  147. /package/dist/core/{utils → kernel/utils}/isObject.d.ts +0 -0
  148. /package/dist/core/{utils → kernel/utils}/isObject.js +0 -0
@@ -0,0 +1,39 @@
1
+ import type { CollectionAfterChangeHook } from "payload";
2
+ import type { CollectionSchemaMap } from "../../../types/CollectionSchemaMap";
3
+ import type { TaskRunnerFactory } from "../task-runner";
4
+ import type { AutoTranslatePolicyResolver, NormalizedAutoTranslatePolicy } from "./AutoTranslate.policy";
5
+ import type { AutoTranslateManagedConfig } from "./AutoTranslate.shapes";
6
+ type AutoTranslateHookDeps = {
7
+ resolvePolicy: AutoTranslatePolicyResolver;
8
+ schemaMap: CollectionSchemaMap;
9
+ taskRunnerFactory: TaskRunnerFactory;
10
+ };
11
+ /**
12
+ * Build the `afterChange` hook that auto-enqueues translations when a document's source-locale content
13
+ * changes. Thin orchestration only — every decision lives in `AutoTranslate.policy.ts` or the core
14
+ * drift predicate. Best-effort by contract: any failure is logged and swallowed, never failing the
15
+ * editor's save.
16
+ *
17
+ * Order (cheap guards first): (1) skip the translator's own writes via the `req.context` flag;
18
+ * (2) resolve the policy — off ⇒ skip; (3) resolve the source locale (per-collection override else
19
+ * `localization.defaultLocale`) — unresolved ⇒ log + skip; (4) skip non-source-locale writes (the
20
+ * pipeline's target writes never match); (5) publish-gate (D8); (6) drift-gate (D3); then enqueue one
21
+ * job per target locale.
22
+ */
23
+ export declare function makeAutoTranslateHook(deps: AutoTranslateHookDeps): CollectionAfterChangeHook;
24
+ /**
25
+ * Attach the auto-translate hook to every enabled collection on `config`, appending to any
26
+ * consumer-supplied `afterChange` array. Idempotent: a collection that already carries the marked hook
27
+ * is skipped, so a repeated `init()` never stacks duplicates.
28
+ */
29
+ export declare function injectAutoTranslateHook(config: AutoTranslateManagedConfig, enabledSlugs: Set<string>, hook: CollectionAfterChangeHook): void;
30
+ /**
31
+ * Propagate each enabled collection's resolved policy onto the REGISTERED collection's `custom` bag, so
32
+ * the admin UI can read the opt-in back via `getAutoTranslateConfig`. This is required because
33
+ * `withAutoTranslate` stamps `custom` on the object passed to the plugin's `collections` param, which
34
+ * can be a DIFFERENT object than the one registered in `buildConfig.collections` (the reader would
35
+ * otherwise see no config and the indicator would disagree with the behaviour). Idempotent + additive:
36
+ * re-stamping the same value is a no-op and the behaviour wiring still reads from the plugin param.
37
+ */
38
+ export declare function propagateAutoTranslateCustom(config: AutoTranslateManagedConfig, enabledSlugs: Set<string>, policies: Map<string, NormalizedAutoTranslatePolicy>): void;
39
+ export {};
@@ -0,0 +1,94 @@
1
+ import { hasSourceContentChanged } from "../../../core/domain/auto-translate";
2
+ import { AUTO_TRANSLATE_CUSTOM_KEY } from "../../../core/domain/auto-translate";
3
+ import { AUTO_TRANSLATE_SKIP_CONTEXT_KEY } from "../../../types/AutoTranslateContext";
4
+ import { buildAutoTranslateTasks, passesPublishGate } from "./AutoTranslate.policy";
5
+ /**
6
+ * Build the `afterChange` hook that auto-enqueues translations when a document's source-locale content
7
+ * changes. Thin orchestration only — every decision lives in `AutoTranslate.policy.ts` or the core
8
+ * drift predicate. Best-effort by contract: any failure is logged and swallowed, never failing the
9
+ * editor's save.
10
+ *
11
+ * Order (cheap guards first): (1) skip the translator's own writes via the `req.context` flag;
12
+ * (2) resolve the policy — off ⇒ skip; (3) resolve the source locale (per-collection override else
13
+ * `localization.defaultLocale`) — unresolved ⇒ log + skip; (4) skip non-source-locale writes (the
14
+ * pipeline's target writes never match); (5) publish-gate (D8); (6) drift-gate (D3); then enqueue one
15
+ * job per target locale.
16
+ */ export function makeAutoTranslateHook(deps) {
17
+ const { resolvePolicy, schemaMap, taskRunnerFactory } = deps;
18
+ const hook = async ({ doc, previousDoc, req, collection })=>{
19
+ try {
20
+ if (req.context?.[AUTO_TRANSLATE_SKIP_CONTEXT_KEY]) return doc;
21
+ const policy = resolvePolicy(collection.slug, doc);
22
+ if (!policy) return doc;
23
+ const localization = req.payload.config.localization;
24
+ const sourceLocale = policy.sourceLocale ?? (localization ? localization.defaultLocale : undefined);
25
+ if (!sourceLocale) {
26
+ req.payload.logger.warn({
27
+ collection: collection.slug,
28
+ documentId: String(doc.id),
29
+ msg: "translator: auto-translate skipped — no source locale resolvable (set localization.defaultLocale or a per-collection sourceLocale)"
30
+ });
31
+ return doc;
32
+ }
33
+ if (req.locale !== sourceLocale) return doc;
34
+ const hasDrafts = Boolean(collection.versions && collection.versions.drafts);
35
+ if (!passesPublishGate(doc, hasDrafts)) return doc;
36
+ const schema = schemaMap.get(collection.slug);
37
+ if (schema && !hasSourceContentChanged(previousDoc, doc, schema)) return doc;
38
+ const tasks = buildAutoTranslateTasks({
39
+ policy,
40
+ collectionSlug: collection.slug,
41
+ documentId: String(doc.id),
42
+ sourceLocale,
43
+ doc,
44
+ hasDrafts,
45
+ now: Date.now()
46
+ });
47
+ if (tasks.length === 0) return doc;
48
+ await taskRunnerFactory.create(req.payload).enqueue(tasks);
49
+ } catch (error) {
50
+ req.payload.logger.error({
51
+ err: error,
52
+ collection: collection.slug,
53
+ documentId: String(doc.id),
54
+ msg: "translator: auto-translate hook failed"
55
+ });
56
+ }
57
+ return doc;
58
+ };
59
+ hook.__translatorAutoTranslate = true;
60
+ return hook;
61
+ }
62
+ /**
63
+ * Attach the auto-translate hook to every enabled collection on `config`, appending to any
64
+ * consumer-supplied `afterChange` array. Idempotent: a collection that already carries the marked hook
65
+ * is skipped, so a repeated `init()` never stacks duplicates.
66
+ */ export function injectAutoTranslateHook(config, enabledSlugs, hook) {
67
+ for (const collection of config.collections ?? []){
68
+ if (!enabledSlugs.has(collection.slug)) continue;
69
+ collection.hooks ??= {};
70
+ collection.hooks.afterChange ??= [];
71
+ const alreadyInjected = collection.hooks.afterChange.some((existing)=>existing.__translatorAutoTranslate === true);
72
+ if (!alreadyInjected) collection.hooks.afterChange.push(hook);
73
+ }
74
+ }
75
+ /**
76
+ * Propagate each enabled collection's resolved policy onto the REGISTERED collection's `custom` bag, so
77
+ * the admin UI can read the opt-in back via `getAutoTranslateConfig`. This is required because
78
+ * `withAutoTranslate` stamps `custom` on the object passed to the plugin's `collections` param, which
79
+ * can be a DIFFERENT object than the one registered in `buildConfig.collections` (the reader would
80
+ * otherwise see no config and the indicator would disagree with the behaviour). Idempotent + additive:
81
+ * re-stamping the same value is a no-op and the behaviour wiring still reads from the plugin param.
82
+ */ export function propagateAutoTranslateCustom(config, enabledSlugs, policies) {
83
+ for (const collection of config.collections ?? []){
84
+ if (!enabledSlugs.has(collection.slug)) continue;
85
+ const policy = policies.get(collection.slug);
86
+ if (!policy) continue;
87
+ collection.custom = {
88
+ ...collection.custom ?? {},
89
+ [AUTO_TRANSLATE_CUSTOM_KEY]: policy
90
+ };
91
+ }
92
+ }
93
+
94
+ //# sourceMappingURL=AutoTranslateEnqueue.hook.js.map
@@ -0,0 +1,4 @@
1
+ export { configureAutoTranslate } from "./AutoTranslate.wiring";
2
+ export type { AutoTranslateModule } from "./AutoTranslate.wiring";
3
+ export { makeAutoTranslateHook, injectAutoTranslateHook } from "./AutoTranslateEnqueue.hook";
4
+ export type { AutoTranslatePolicyResolver, NormalizedAutoTranslatePolicy, } from "./AutoTranslate.policy";
@@ -0,0 +1,7 @@
1
+ // Auto-translate adapter (Payload-backed). The payload-free drift predicate + config reader live in
2
+ // the core (src/core/domain/auto-translate, src/core/domain/content-projection); this module is the config-time
3
+ // wiring + the afterChange hook that enqueues translations on a source-locale change (#51).
4
+ export { configureAutoTranslate } from "./AutoTranslate.wiring";
5
+ export { makeAutoTranslateHook, injectAutoTranslateHook } from "./AutoTranslateEnqueue.hook";
6
+
7
+ //# sourceMappingURL=index.js.map
@@ -1,2 +1,2 @@
1
- export * from "../../core/translation-providers";
1
+ export * from "../../core/domain/translation-providers";
2
2
  export * from "../../core/translation-pipeline";
@@ -1,4 +1,4 @@
1
- export * from "../../core/translation-providers";
1
+ export * from "../../core/domain/translation-providers";
2
2
  export * from "../../core/translation-pipeline";
3
3
 
4
4
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
1
  import type { CollectionSlug, Payload } from "payload";
2
- import type { ProvenanceKey, ProvenanceStore } from "../../../core/provenance";
2
+ import type { ProvenanceKey, ProvenanceStore } from "../../../core/domain/provenance";
3
3
  import type { CollectionSchemaMap } from "../../../types/CollectionSchemaMap";
4
4
  /** Per-locale staleness for one document (snake_case, matching the other translation endpoints). */
5
5
  export type StalenessLocale = {
@@ -1,5 +1,5 @@
1
- import { computeSourceFingerprint } from "../../../core/content-projection/computeSourceFingerprint";
2
- import { isRecordStale } from "../../../core/provenance";
1
+ import { computeSourceFingerprint } from "../../../core/domain/content-projection/computeSourceFingerprint";
2
+ import { isRecordStale } from "../../../core/domain/provenance";
3
3
  import { fetchSourceDocument } from "../../shared/payload/sourceDocument";
4
4
  /**
5
5
  * The single owner of provenance fingerprint policy — how the source is hashed on write, re-hashed on
@@ -1,5 +1,5 @@
1
1
  import type { Payload } from "payload";
2
- import type { ProvenanceKey, ProvenanceStore, TranslationProvenanceRecord } from "../../../core/provenance";
2
+ import type { ProvenanceKey, ProvenanceStore, TranslationProvenanceRecord } from "../../../core/domain/provenance";
3
3
  /** Builds a provenance store bound to a Payload instance; absent when provenance is disabled. */
4
4
  export type ProvenanceStoreFactory = (payload: Payload) => ProvenanceStore;
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  // Provenance adapter (Payload-backed). The framework-agnostic port + record types live in the core
2
- // (src/core/provenance); this module is the plugin-side implementation + its config-time wiring.
2
+ // (src/core/domain/provenance); this module is the plugin-side implementation + its config-time wiring.
3
3
  export { DEFAULT_PROVENANCE_SLUG, isProvenanceCollection, makeProvenanceCollection } from "./Provenance.collection";
4
4
  export { PayloadProvenanceStore } from "./Provenance.store";
5
5
  export { injectProvenanceCleanup, makeProvenanceCleanupHook } from "./ProvenanceCleanup.hook";
@@ -31,6 +31,10 @@ const documentLocaleKey = (collectionId, targetLng)=>`${String(collectionId)}:${
31
31
  await Promise.all(tasks.map((task)=>this.payload.jobs.queue({
32
32
  task: this.config.taskName,
33
33
  queue: this.config.queueName,
34
+ // Debounce: when set, Payload holds the job until this instant. A superseding enqueue for
35
+ // the same (document, targetLng) cancels the pending delayed job first (see enqueue above),
36
+ // so rapid source edits coalesce to the final one. Undefined for the manual path.
37
+ waitUntil: task.waitUntil,
34
38
  input: {
35
39
  // Flat text reference (ID-agnostic). Stored as a string — no
36
40
  // relationship type validation against the collection's ID type,
@@ -23,6 +23,13 @@ export type TaskInput = {
23
23
  targetLng: string;
24
24
  strategy: "overwrite" | "skip_existing";
25
25
  publishOnTranslation: boolean;
26
+ /**
27
+ * Optional scheduled-run time (debounce). When set, the job runs no earlier than this instant;
28
+ * omitted/`undefined` = run as soon as the runner picks it up (every existing caller). Honored by
29
+ * the Payload Jobs runner via `payload.jobs.queue({ waitUntil })`; the sync (dev) runner ignores it
30
+ * and runs immediately.
31
+ */
32
+ waitUntil?: Date;
26
33
  };
27
34
  /**
28
35
  * Normalized task representation
@@ -3,7 +3,7 @@ import type { AccessGuard } from "../../../types/AccessGuard";
3
3
  import type { RawPayloadComponentExport } from "../../../types/PayloadComponentExport";
4
4
  import type { CollectionSchemaMap } from "../../../types/CollectionSchemaMap";
5
5
  import type { ConfigModifier } from "../../../types/ConfigModifier";
6
- import type { TranslationProvider } from "../../../core/translation-providers";
6
+ import type { TranslationProvider } from "../../../core/domain/translation-providers";
7
7
  import type { TaskRunnerFactory } from "../task-runner";
8
8
  import type { ProvenanceServiceFactory } from "../provenance";
9
9
  import type { CollectionAdminSlot, LevelContext, TranslationContext } from "./types";
@@ -2,7 +2,7 @@ import type { CollectionConfig, Endpoint } from "payload";
2
2
  import type { AccessGuard } from "../../../types/AccessGuard";
3
3
  import type { RawPayloadComponentExport } from "../../../types/PayloadComponentExport";
4
4
  import type { CollectionSchemaMap } from "../../../types/CollectionSchemaMap";
5
- import type { TranslationProvider } from "../../../core/translation-providers";
5
+ import type { TranslationProvider } from "../../../core/domain/translation-providers";
6
6
  import type { TaskRunnerFactory } from "../task-runner";
7
7
  import type { ProvenanceServiceFactory } from "../provenance";
8
8
  export type CollectionAdminSlot = "beforeDocumentControls" | "beforeListTable";
@@ -4,9 +4,9 @@ export type { AccessGuard, AccessGuardRequest, Handler } from "./access";
4
4
  export { isEmpty, isObject, normalizePath, pipe, getByPath, setByPath, filterLocalizedFields, } from "./utils";
5
5
  export type { TranslatableField } from "./guards";
6
6
  export { isTranslatableField, isLocalizedField, isRelationshipField } from "./guards";
7
- export { isTabsField, isBlockItem, hasFields } from "../../core/field-traversal";
8
- export { isFieldExcludedFromTranslation, getTranslateKitFieldConfig, } from "../../core/field-config";
9
- export type { TranslateKitFieldConfig } from "../../core/field-config";
10
- export { isSerializedLexicalRoot, isEmptyRichText, traverseLexicalTree, collectSerializedLexicalTextNodes, } from "../../core/lexical";
11
- export type { SerializedTextNodeRef } from "../../core/lexical";
7
+ export { isTabsField, isBlockItem, hasFields } from "../../core/kernel/field-traversal";
8
+ export { isFieldExcludedFromTranslation, getTranslateKitFieldConfig, } from "../../core/domain/field-config";
9
+ export type { TranslateKitFieldConfig } from "../../core/domain/field-config";
10
+ export { isSerializedLexicalRoot, isEmptyRichText, traverseLexicalTree, collectSerializedLexicalTextNodes, } from "../../core/kernel/lexical";
11
+ export type { SerializedTextNodeRef } from "../../core/kernel/lexical";
12
12
  export { JobIdSchema } from "./validation";
@@ -5,11 +5,11 @@ export { AnyAccessGuard } from "./access";
5
5
  // General utilities
6
6
  export { isEmpty, isObject, normalizePath, pipe, getByPath, setByPath, filterLocalizedFields } from "./utils";
7
7
  export { isTranslatableField, isLocalizedField, isRelationshipField } from "./guards";
8
- export { isTabsField, isBlockItem, hasFields } from "../../core/field-traversal";
8
+ export { isTabsField, isBlockItem, hasFields } from "../../core/kernel/field-traversal";
9
9
  // Field config
10
- export { isFieldExcludedFromTranslation, getTranslateKitFieldConfig } from "../../core/field-config";
10
+ export { isFieldExcludedFromTranslation, getTranslateKitFieldConfig } from "../../core/domain/field-config";
11
11
  // Lexical utilities
12
- export { isSerializedLexicalRoot, isEmptyRichText, traverseLexicalTree, collectSerializedLexicalTextNodes } from "../../core/lexical";
12
+ export { isSerializedLexicalRoot, isEmptyRichText, traverseLexicalTree, collectSerializedLexicalTextNodes } from "../../core/kernel/lexical";
13
13
  // Validation primitives
14
14
  export { JobIdSchema } from "./validation";
15
15
 
@@ -1,6 +1,6 @@
1
- import { resolveBlockFields, walkFields } from "../../../core/field-traversal";
1
+ import { resolveBlockFields, walkFields } from "../../../core/kernel/field-traversal";
2
2
  import { isLocalizedField, isTranslatableField } from "../guards";
3
- import { isObject } from "../../../core/utils/isObject";
3
+ import { isObject } from "../../../core/kernel/utils/isObject";
4
4
  /**
5
5
  * Filter walker: keeps only translatable, localized leaves, rebuilding the surrounding
6
6
  * object/array/blocks structure and dropping any container left empty. `id` (array/block
@@ -1,4 +1,4 @@
1
- import { isObject } from "../../../core/utils/isObject";
1
+ import { isObject } from "../../../core/kernel/utils/isObject";
2
2
  /**
3
3
  * Gets a value from an object by dot-notation path.
4
4
  *
@@ -1,5 +1,5 @@
1
- export { isEmpty } from "../../../core/utils/isEmpty";
2
- export { isObject } from "../../../core/utils/isObject";
1
+ export { isEmpty } from "../../../core/kernel/utils/isEmpty";
2
+ export { isObject } from "../../../core/kernel/utils/isObject";
3
3
  export { normalizePath } from "./normalizePath";
4
4
  export { getByPath } from "./getByPath";
5
5
  export { setByPath } from "./setByPath";
@@ -1,5 +1,5 @@
1
- export { isEmpty } from "../../../core/utils/isEmpty";
2
- export { isObject } from "../../../core/utils/isObject";
1
+ export { isEmpty } from "../../../core/kernel/utils/isEmpty";
2
+ export { isObject } from "../../../core/kernel/utils/isObject";
3
3
  export { normalizePath } from "./normalizePath";
4
4
  export { getByPath } from "./getByPath";
5
5
  export { setByPath } from "./setByPath";
@@ -1,4 +1,4 @@
1
- import { isObject } from "../../../core/utils/isObject";
1
+ import { isObject } from "../../../core/kernel/utils/isObject";
2
2
  /**
3
3
  * Sets a value in an object by dot-notation path.
4
4
  * Creates intermediate objects/arrays as needed.
@@ -1,2 +1,2 @@
1
- export type { TranslationProvider, TranslationInput, TranslationOutput, } from "../core/translation-providers";
1
+ export type { TranslationProvider, TranslationInput, TranslationOutput, } from "../core/domain/translation-providers";
2
2
  export * from "./openai";
@@ -1,4 +1,4 @@
1
- import type { TranslationProvider, TranslationInput, TranslationOutput } from "../../core/translation-providers/TranslationProvider.interface";
1
+ import type { TranslationProvider, TranslationInput, TranslationOutput } from "../../core/domain/translation-providers/TranslationProvider.interface";
2
2
  import type { ChatModel } from "openai/resources/index.mjs";
3
3
  /**
4
4
  * Function to transform text in dry run mode.
@@ -1,5 +1,5 @@
1
1
  import OpenAI from "openai";
2
- import { isObject } from "../../core/utils/isObject";
2
+ import { isObject } from "../../core/kernel/utils/isObject";
3
3
  /** @deprecated Use `createOpenAIProvider` function instead */ export class OpenAITranslationProvider {
4
4
  openAiClient;
5
5
  config;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The `req.context` flag the translator sets on its OWN document writes so the auto-translate
3
+ * `afterChange` hook skips them — the loop guard's second barrier (#51 D5). A single exported constant
4
+ * shared by the setter (`TranslateDocumentHandler.saveTranslatedDocument`) and the reader (the
5
+ * auto-translate hook), so the set-side and honor-side keys can never diverge.
6
+ *
7
+ * Lives in `types/` (a leaf contract) so both `server/features/translate-document` and
8
+ * `server/modules/auto-translate` import it without creating a cross-module edge.
9
+ */
10
+ export declare const AUTO_TRANSLATE_SKIP_CONTEXT_KEY = "translatorSkipAutoTranslate";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The `req.context` flag the translator sets on its OWN document writes so the auto-translate
3
+ * `afterChange` hook skips them — the loop guard's second barrier (#51 D5). A single exported constant
4
+ * shared by the setter (`TranslateDocumentHandler.saveTranslatedDocument`) and the reader (the
5
+ * auto-translate hook), so the set-side and honor-side keys can never diverge.
6
+ *
7
+ * Lives in `types/` (a leaf contract) so both `server/features/translate-document` and
8
+ * `server/modules/auto-translate` import it without creating a cross-module edge.
9
+ */ export const AUTO_TRANSLATE_SKIP_CONTEXT_KEY = "translatorSkipAutoTranslate";
10
+
11
+ //# sourceMappingURL=AutoTranslateContext.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@focus-reactive/payload-plugin-translator",
3
- "version": "0.8.2",
3
+ "version": "0.9.1",
4
4
  "description": "Translation plugin for Payload CMS 3.x. Automatically translate your localized content using any translation provider.",
5
5
  "type": "module",
6
6
  "license": "MIT",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes