@focus-reactive/payload-plugin-translator 0.2.1 → 0.4.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/dist/client/features/collection-translation-form/model/schema.d.ts +2 -2
- package/dist/client/features/translate-document-form/model/schema.d.ts +3 -3
- package/dist/server/features/cancel/handler.d.ts +2 -2
- package/dist/server/features/cancel/handler.js +3 -3
- package/dist/server/features/cancel-by-collection/handler.d.ts +3 -3
- package/dist/server/features/cancel-by-collection/handler.js +5 -5
- package/dist/server/features/enqueue-translation/handler.d.ts +3 -3
- package/dist/server/features/enqueue-translation/handler.js +4 -4
- package/dist/server/features/enqueue-translation/model.d.ts +4 -4
- package/dist/server/features/get-collection-status/handler.d.ts +3 -3
- package/dist/server/features/get-collection-status/handler.js +4 -4
- package/dist/server/features/get-document-status/handler.d.ts +3 -3
- package/dist/server/features/get-document-status/handler.js +4 -4
- package/dist/server/features/get-document-status/model.d.ts +1 -1
- package/dist/server/features/run-translation/handler.d.ts +2 -2
- package/dist/server/features/run-translation/handler.js +7 -7
- package/dist/server/features/translate-document/handler.d.ts +4 -4
- package/dist/server/features/translate-document/handler.js +4 -4
- package/dist/server/features/translate-document/index.d.ts +2 -3
- package/dist/server/features/translate-document/index.js +1 -2
- package/dist/server/features/translate-document/model.d.ts +4 -31
- package/dist/server/features/translate-document/model.js +3 -27
- package/dist/server/modules/task-runner/TaskRunnerProvider.interface.d.ts +6 -4
- package/dist/server/modules/task-runner/payload-jobs-runner/PayloadJobsRunnerProvider.d.ts +4 -4
- package/dist/server/modules/task-runner/payload-jobs-runner/PayloadJobsRunnerProvider.js +72 -20
- package/dist/server/modules/task-runner/payload-jobs-runner/PayloadJobsTaskRunner.d.ts +51 -56
- package/dist/server/modules/task-runner/payload-jobs-runner/PayloadJobsTaskRunner.js +146 -91
- package/dist/server/modules/task-runner/payload-jobs-runner/normalizeJob.d.ts +2 -2
- package/dist/server/modules/task-runner/payload-jobs-runner/normalizeJob.js +14 -12
- package/dist/server/modules/task-runner/payload-jobs-runner/readCollectionRef.d.ts +22 -0
- package/dist/server/modules/task-runner/payload-jobs-runner/readCollectionRef.js +22 -0
- package/dist/server/modules/task-runner/payload-jobs-runner/types.d.ts +30 -5
- package/dist/server/modules/task-runner/sync-runner/SyncTaskRunner.d.ts +5 -5
- package/dist/server/modules/task-runner/sync-runner/SyncTaskRunner.js +7 -6
- package/dist/server/modules/task-runner/types.d.ts +15 -5
- package/package.json +1 -1
- package/dist/server/features/translate-document/task.d.ts +0 -40
- package/dist/server/features/translate-document/task.js +0 -44
|
@@ -6,16 +6,16 @@ export declare const validationSchema: z.ZodObject<{
|
|
|
6
6
|
strategy: z.ZodEnum<["overwrite", "skip_existing"]>;
|
|
7
7
|
publish_on_translation: z.ZodDefault<z.ZodBoolean>;
|
|
8
8
|
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
collection_slug: string;
|
|
9
10
|
source_lng: string;
|
|
10
11
|
target_lng: string;
|
|
11
12
|
strategy: "overwrite" | "skip_existing";
|
|
12
13
|
publish_on_translation: boolean;
|
|
13
|
-
collection_slug: string;
|
|
14
14
|
}, {
|
|
15
|
+
collection_slug: string;
|
|
15
16
|
source_lng: string;
|
|
16
17
|
target_lng: string;
|
|
17
18
|
strategy: "overwrite" | "skip_existing";
|
|
18
|
-
collection_slug: string;
|
|
19
19
|
publish_on_translation?: boolean | undefined;
|
|
20
20
|
}>;
|
|
21
21
|
export type FormValues = z.infer<typeof validationSchema>;
|
|
@@ -7,17 +7,17 @@ export declare const validationSchema: z.ZodObject<{
|
|
|
7
7
|
strategy: z.ZodDefault<z.ZodEnum<["overwrite", "skip_existing"]>>;
|
|
8
8
|
publish_on_translation: z.ZodDefault<z.ZodBoolean>;
|
|
9
9
|
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
collection_slug: string;
|
|
11
|
+
collection_id: string;
|
|
10
12
|
source_lng: string;
|
|
11
13
|
target_lng: string;
|
|
12
14
|
strategy: "overwrite" | "skip_existing";
|
|
13
15
|
publish_on_translation: boolean;
|
|
16
|
+
}, {
|
|
14
17
|
collection_slug: string;
|
|
15
18
|
collection_id: string;
|
|
16
|
-
}, {
|
|
17
19
|
source_lng: string;
|
|
18
20
|
target_lng: string;
|
|
19
|
-
collection_slug: string;
|
|
20
|
-
collection_id: string;
|
|
21
21
|
strategy?: "overwrite" | "skip_existing" | undefined;
|
|
22
22
|
publish_on_translation?: boolean | undefined;
|
|
23
23
|
}>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { PayloadRequest } from
|
|
2
|
-
import type { TaskRunnerProvider } from
|
|
1
|
+
import type { PayloadRequest } from "payload";
|
|
2
|
+
import type { TaskRunnerProvider } from "../../modules/task-runner";
|
|
3
3
|
/**
|
|
4
4
|
* Cancels and deletes translation tasks by IDs
|
|
5
5
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ServerResponse } from
|
|
2
|
-
import { CancelInputSchema } from
|
|
1
|
+
import { ServerResponse } from "../../shared";
|
|
2
|
+
import { CancelInputSchema } from "./model";
|
|
3
3
|
/**
|
|
4
4
|
* Cancels and deletes translation tasks by IDs
|
|
5
5
|
*/ export class CancelHandler {
|
|
@@ -9,7 +9,7 @@ import { CancelInputSchema } from './model';
|
|
|
9
9
|
}
|
|
10
10
|
async handle(req) {
|
|
11
11
|
const validationResult = CancelInputSchema.safeParse(await req.json?.());
|
|
12
|
-
if (validationResult.error) return ServerResponse.validationError(validationResult.error.
|
|
12
|
+
if (validationResult.error) return ServerResponse.validationError(validationResult.error.issues);
|
|
13
13
|
const { ids } = validationResult.data;
|
|
14
14
|
const runner = this.taskRunnerFactory.create(req.payload);
|
|
15
15
|
await runner.cancel(ids);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { PayloadRequest } from
|
|
2
|
-
import type { TaskRunnerProvider } from
|
|
3
|
-
import {
|
|
1
|
+
import type { PayloadRequest } from "payload";
|
|
2
|
+
import type { TaskRunnerProvider } from "../../modules/task-runner";
|
|
3
|
+
import type { CancelConfig } from './model';
|
|
4
4
|
/**
|
|
5
5
|
* Cancels all pending translation tasks for a collection
|
|
6
6
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ServerResponse } from
|
|
2
|
-
import { isCollectionAvailable } from
|
|
1
|
+
import { ServerResponse } from "../../shared";
|
|
2
|
+
import { isCollectionAvailable } from "../_lib/collection-utils";
|
|
3
3
|
import { CancelByCollectionInputSchema } from './model';
|
|
4
4
|
/**
|
|
5
5
|
* Cancels all pending translation tasks for a collection
|
|
@@ -12,13 +12,13 @@ import { CancelByCollectionInputSchema } from './model';
|
|
|
12
12
|
}
|
|
13
13
|
async handle(req) {
|
|
14
14
|
const validationResult = CancelByCollectionInputSchema.safeParse(req.routeParams);
|
|
15
|
-
if (validationResult.error) return ServerResponse.validationError(validationResult.error.
|
|
15
|
+
if (validationResult.error) return ServerResponse.validationError(validationResult.error.issues);
|
|
16
16
|
const collectionSlug = isCollectionAvailable(validationResult.data.collection_slug, this.config.availableCollections);
|
|
17
|
-
if (!collectionSlug) return ServerResponse.badRequest(
|
|
17
|
+
if (!collectionSlug) return ServerResponse.badRequest("Collection not available for translation");
|
|
18
18
|
const runner = this.taskRunnerFactory.create(req.payload);
|
|
19
19
|
const tasks = await runner.findByCollection(collectionSlug);
|
|
20
20
|
if (tasks.length === 0) return ServerResponse.noContent();
|
|
21
|
-
const pendingTaskIds = tasks.filter((task)=>task.status ===
|
|
21
|
+
const pendingTaskIds = tasks.filter((task)=>task.status === "pending").map((task)=>task.id);
|
|
22
22
|
if (pendingTaskIds.length === 0) return ServerResponse.noContent();
|
|
23
23
|
await runner.cancel(pendingTaskIds);
|
|
24
24
|
return ServerResponse.noContent();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { PayloadRequest } from
|
|
2
|
-
import type { TaskRunnerProvider } from
|
|
3
|
-
import {
|
|
1
|
+
import type { PayloadRequest } from "payload";
|
|
2
|
+
import type { TaskRunnerProvider } from "../../modules/task-runner";
|
|
3
|
+
import type { EnqueueConfig } from './model';
|
|
4
4
|
/**
|
|
5
5
|
* Enqueues translation tasks for documents
|
|
6
6
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ServerResponse } from
|
|
2
|
-
import { isCollectionAvailable, getAllCollectionIds } from
|
|
1
|
+
import { ServerResponse } from "../../shared";
|
|
2
|
+
import { isCollectionAvailable, getAllCollectionIds } from "../_lib/collection-utils";
|
|
3
3
|
import { EnqueueInputSchema } from './model';
|
|
4
4
|
/**
|
|
5
5
|
* Enqueues translation tasks for documents
|
|
@@ -12,10 +12,10 @@ import { EnqueueInputSchema } from './model';
|
|
|
12
12
|
}
|
|
13
13
|
async handle(req) {
|
|
14
14
|
const validationResult = EnqueueInputSchema.safeParse(await req.json?.());
|
|
15
|
-
if (validationResult.error) return ServerResponse.validationError(validationResult.error.
|
|
15
|
+
if (validationResult.error) return ServerResponse.validationError(validationResult.error.issues);
|
|
16
16
|
const { source_lng, target_lng, collection_slug, collection_id, select_all, strategy, publish_on_translation } = validationResult.data;
|
|
17
17
|
const collectionSlug = isCollectionAvailable(collection_slug, this.config.availableCollections);
|
|
18
|
-
if (!collectionSlug) return ServerResponse.badRequest(
|
|
18
|
+
if (!collectionSlug) return ServerResponse.badRequest("Content of this collection is not available for translation");
|
|
19
19
|
const collectionIds = select_all ? await getAllCollectionIds(req.payload, collectionSlug) : collection_id;
|
|
20
20
|
const runner = this.taskRunnerFactory.create(req.payload);
|
|
21
21
|
const tasks = collectionIds.map((id)=>({
|
|
@@ -12,18 +12,18 @@ export declare const EnqueueInputSchema: z.ZodObject<{
|
|
|
12
12
|
strategy: z.ZodDefault<z.ZodOptional<z.ZodEnum<["overwrite", "skip_existing"]>>>;
|
|
13
13
|
publish_on_translation: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
14
14
|
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
collection_slug: string;
|
|
16
|
+
collection_id: [string, ...string[]];
|
|
15
17
|
source_lng: string;
|
|
16
18
|
target_lng: string;
|
|
17
19
|
strategy: "overwrite" | "skip_existing";
|
|
18
20
|
publish_on_translation: boolean;
|
|
19
|
-
collection_slug: string;
|
|
20
|
-
collection_id: [string, ...string[]];
|
|
21
21
|
select_all?: boolean | undefined;
|
|
22
22
|
}, {
|
|
23
|
-
source_lng: string;
|
|
24
|
-
target_lng: string;
|
|
25
23
|
collection_slug: string;
|
|
26
24
|
collection_id: [string, ...string[]];
|
|
25
|
+
source_lng: string;
|
|
26
|
+
target_lng: string;
|
|
27
27
|
strategy?: "overwrite" | "skip_existing" | undefined;
|
|
28
28
|
publish_on_translation?: boolean | undefined;
|
|
29
29
|
select_all?: boolean | undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { PayloadRequest } from
|
|
2
|
-
import type { TaskRunnerProvider } from
|
|
3
|
-
import {
|
|
1
|
+
import type { PayloadRequest } from "payload";
|
|
2
|
+
import type { TaskRunnerProvider } from "../../modules/task-runner";
|
|
3
|
+
import type { GetCollectionStatusConfig } from './model';
|
|
4
4
|
/**
|
|
5
5
|
* Gets translation status for all documents in a collection
|
|
6
6
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ServerResponse } from
|
|
2
|
-
import { isCollectionAvailable } from
|
|
1
|
+
import { ServerResponse } from "../../shared";
|
|
2
|
+
import { isCollectionAvailable } from "../_lib/collection-utils";
|
|
3
3
|
import { GetCollectionStatusInputSchema } from './model';
|
|
4
4
|
/**
|
|
5
5
|
* Gets translation status for all documents in a collection
|
|
@@ -12,9 +12,9 @@ import { GetCollectionStatusInputSchema } from './model';
|
|
|
12
12
|
}
|
|
13
13
|
async handle(req) {
|
|
14
14
|
const validationResult = GetCollectionStatusInputSchema.safeParse(req.routeParams);
|
|
15
|
-
if (validationResult.error) return ServerResponse.validationError(validationResult.error.
|
|
15
|
+
if (validationResult.error) return ServerResponse.validationError(validationResult.error.issues);
|
|
16
16
|
const collectionSlug = isCollectionAvailable(validationResult.data.collection_slug, this.config.availableCollections);
|
|
17
|
-
if (!collectionSlug) return ServerResponse.badRequest(
|
|
17
|
+
if (!collectionSlug) return ServerResponse.badRequest("Collection not available for translation");
|
|
18
18
|
const runner = this.taskRunnerFactory.create(req.payload);
|
|
19
19
|
const tasks = await runner.findByCollection(collectionSlug);
|
|
20
20
|
return ServerResponse.success({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { PayloadRequest } from
|
|
2
|
-
import type { TaskRunnerProvider } from
|
|
3
|
-
import {
|
|
1
|
+
import type { PayloadRequest } from "payload";
|
|
2
|
+
import type { TaskRunnerProvider } from "../../modules/task-runner";
|
|
3
|
+
import type { GetDocumentStatusConfig } from './model';
|
|
4
4
|
/**
|
|
5
5
|
* Gets the translation status for a specific document
|
|
6
6
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ServerResponse } from
|
|
2
|
-
import { isCollectionAvailable } from
|
|
1
|
+
import { ServerResponse } from "../../shared";
|
|
2
|
+
import { isCollectionAvailable } from "../_lib/collection-utils";
|
|
3
3
|
import { GetDocumentStatusInputSchema, taskToJobStatusOutput } from './model';
|
|
4
4
|
/**
|
|
5
5
|
* Gets the translation status for a specific document
|
|
@@ -12,10 +12,10 @@ import { GetDocumentStatusInputSchema, taskToJobStatusOutput } from './model';
|
|
|
12
12
|
}
|
|
13
13
|
async handle(req) {
|
|
14
14
|
const validationResult = GetDocumentStatusInputSchema.safeParse(req.routeParams);
|
|
15
|
-
if (validationResult.error) return ServerResponse.validationError(validationResult.error.
|
|
15
|
+
if (validationResult.error) return ServerResponse.validationError(validationResult.error.issues);
|
|
16
16
|
const { collection_slug, collection_id } = validationResult.data;
|
|
17
17
|
const collectionSlug = isCollectionAvailable(collection_slug, this.config.availableCollections);
|
|
18
|
-
if (!collectionSlug) return ServerResponse.badRequest(
|
|
18
|
+
if (!collectionSlug) return ServerResponse.badRequest("Collection not available for translation");
|
|
19
19
|
const runner = this.taskRunnerFactory.create(req.payload);
|
|
20
20
|
const tasks = await runner.findByCollection(collectionSlug, [
|
|
21
21
|
collection_id
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { PayloadRequest } from
|
|
2
|
-
import type { TaskRunnerProvider } from
|
|
1
|
+
import type { PayloadRequest } from "payload";
|
|
2
|
+
import type { TaskRunnerProvider } from "../../modules/task-runner";
|
|
3
3
|
/**
|
|
4
4
|
* Runs a translation task by ID
|
|
5
5
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ServerResponse } from
|
|
2
|
-
import { RunInputSchema } from
|
|
1
|
+
import { ServerResponse } from "../../shared";
|
|
2
|
+
import { RunInputSchema } from "./model";
|
|
3
3
|
/**
|
|
4
4
|
* Runs a translation task by ID
|
|
5
5
|
*/ export class RunTranslationHandler {
|
|
@@ -9,16 +9,16 @@ import { RunInputSchema } from './model';
|
|
|
9
9
|
}
|
|
10
10
|
async handle(req) {
|
|
11
11
|
const validationResult = RunInputSchema.safeParse(req.routeParams);
|
|
12
|
-
if (validationResult.error) return ServerResponse.validationError(validationResult.error.
|
|
12
|
+
if (validationResult.error) return ServerResponse.validationError(validationResult.error.issues);
|
|
13
13
|
const { id } = validationResult.data;
|
|
14
14
|
const runner = this.taskRunnerFactory.create(req.payload);
|
|
15
15
|
const result = await runner.run(id);
|
|
16
16
|
if (!result.success) {
|
|
17
|
-
if (result.error ===
|
|
18
|
-
return ServerResponse.notFound(
|
|
17
|
+
if (result.error === "not_found" || result.error === "already_completed") {
|
|
18
|
+
return ServerResponse.notFound("Queued task not found");
|
|
19
19
|
}
|
|
20
|
-
if (result.error ===
|
|
21
|
-
return ServerResponse.tooManyRequests(
|
|
20
|
+
if (result.error === "already_running") {
|
|
21
|
+
return ServerResponse.tooManyRequests("Translation task is already in progress");
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
return ServerResponse.noContent();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Payload } from
|
|
2
|
-
import type { Handler } from
|
|
3
|
-
import type { TranslationProvider } from
|
|
4
|
-
import type { CollectionSchemaMap, TranslateDocumentInput, TranslateDocumentOutput } from
|
|
1
|
+
import type { Payload } from "payload";
|
|
2
|
+
import type { Handler } from "../../shared";
|
|
3
|
+
import type { TranslationProvider } from "../../modules/translation-providers";
|
|
4
|
+
import type { CollectionSchemaMap, TranslateDocumentInput, TranslateDocumentOutput } from "./model";
|
|
5
5
|
export type TranslateDocumentDependencies = {
|
|
6
6
|
translationProvider: TranslationProvider;
|
|
7
7
|
schemaMap: CollectionSchemaMap;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { APIError } from
|
|
2
|
-
import { TranslationPipeline } from
|
|
3
|
-
import { createTranslationStrategy } from
|
|
1
|
+
import { APIError } from "payload";
|
|
2
|
+
import { TranslationPipeline } from "../../modules/translation-pipeline";
|
|
3
|
+
import { createTranslationStrategy } from "../../modules/translation-pipeline/strategies";
|
|
4
4
|
/**
|
|
5
5
|
* Translates a single document from source language to target language
|
|
6
6
|
*/ export class TranslateDocumentHandler {
|
|
@@ -53,7 +53,7 @@ import { createTranslationStrategy } from '../../modules/translation-pipeline/st
|
|
|
53
53
|
let isAutosaveEnabled = false;
|
|
54
54
|
const versions = collectionConfig.versions;
|
|
55
55
|
if (versions && versions.drafts) {
|
|
56
|
-
translatedData[
|
|
56
|
+
translatedData["_status"] = publishOnTranslation ? "published" : "draft";
|
|
57
57
|
const drafts = versions.drafts;
|
|
58
58
|
if (!publishOnTranslation && drafts.autosave) isAutosaveEnabled = true;
|
|
59
59
|
}
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export type { CollectionSchemaMap, AutoRunConfig } from './model';
|
|
1
|
+
export { TranslateDocumentHandler } from "./handler";
|
|
2
|
+
export type { CollectionSchemaMap } from "./model";
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type { CollectionSlug, Field } from
|
|
2
|
-
import type {
|
|
1
|
+
import type { CollectionSlug, Field } from "payload";
|
|
2
|
+
import type { ID } from "../../modules/task-runner/types";
|
|
3
|
+
import type { TranslationStrategyName } from "../../modules/translation-pipeline/strategies";
|
|
3
4
|
/**
|
|
4
5
|
* Input for the translate document handler
|
|
5
6
|
*/
|
|
6
7
|
export type TranslateDocumentInput = {
|
|
7
8
|
collection: CollectionSlug;
|
|
8
|
-
collectionId:
|
|
9
|
+
collectionId: ID;
|
|
9
10
|
sourceLng: string;
|
|
10
11
|
targetLng: string;
|
|
11
12
|
strategy: TranslationStrategyName;
|
|
@@ -22,31 +23,3 @@ export type TranslateDocumentOutput = {
|
|
|
22
23
|
* Used to access original schemas before Payload sanitization.
|
|
23
24
|
*/
|
|
24
25
|
export type CollectionSchemaMap = Map<CollectionSlug, Field[]>;
|
|
25
|
-
/**
|
|
26
|
-
* Payload task configuration
|
|
27
|
-
*/
|
|
28
|
-
export type TaskConfig = {
|
|
29
|
-
collections: CollectionSlug[];
|
|
30
|
-
retries?: {
|
|
31
|
-
attempts?: number;
|
|
32
|
-
backoff?: {
|
|
33
|
-
delay?: number;
|
|
34
|
-
type: 'exponential' | 'fixed';
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* Auto-run configuration for the job queue
|
|
40
|
-
*/
|
|
41
|
-
export type AutoRunConfig = {
|
|
42
|
-
cron?: string;
|
|
43
|
-
limit?: number;
|
|
44
|
-
queue?: string;
|
|
45
|
-
};
|
|
46
|
-
/**
|
|
47
|
-
* Payload input schema for the task
|
|
48
|
-
*/
|
|
49
|
-
export declare const taskInputSchema: Field[];
|
|
50
|
-
export declare const TASK_NAME: "translate_document";
|
|
51
|
-
export declare const QUEUE_NAME: "translations";
|
|
52
|
-
export declare const DEFAULT_AUTO_RUN: AutoRunConfig;
|
|
@@ -1,30 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
type: 'relationship',
|
|
6
|
-
name: 'collection',
|
|
7
|
-
relationTo: [],
|
|
8
|
-
required: true
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
type: 'text',
|
|
12
|
-
maxLength: 256,
|
|
13
|
-
name: 'source_lng',
|
|
14
|
-
required: true
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
type: 'text',
|
|
18
|
-
maxLength: 256,
|
|
19
|
-
name: 'target_lng',
|
|
20
|
-
required: true
|
|
21
|
-
}
|
|
22
|
-
];
|
|
23
|
-
export const TASK_NAME = 'translate_document';
|
|
24
|
-
export const QUEUE_NAME = 'translations';
|
|
25
|
-
export const DEFAULT_AUTO_RUN = {
|
|
26
|
-
cron: '* * * * *',
|
|
27
|
-
limit: 50
|
|
28
|
-
};
|
|
2
|
+
* Map of collection slug to original field schema.
|
|
3
|
+
* Used to access original schemas before Payload sanitization.
|
|
4
|
+
*/ export { };
|
|
29
5
|
|
|
30
6
|
//# sourceMappingURL=model.js.map
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type { CollectionSlug, Config, Payload } from
|
|
2
|
-
import type { TaskRunner } from
|
|
3
|
-
import type {
|
|
1
|
+
import type { CollectionSlug, Config, Payload } from "payload";
|
|
2
|
+
import type { TaskRunner } from "./TaskRunner.interface";
|
|
3
|
+
import type { ID } from "./types";
|
|
4
|
+
import type { TranslationStrategyName } from "../translation-pipeline/strategies";
|
|
4
5
|
/**
|
|
5
6
|
* Input for task handler callback
|
|
6
7
|
*/
|
|
7
8
|
export type TaskHandlerInput = {
|
|
8
9
|
collection: CollectionSlug;
|
|
9
|
-
collectionId:
|
|
10
|
+
collectionId: ID;
|
|
10
11
|
sourceLng: string;
|
|
11
12
|
targetLng: string;
|
|
12
13
|
strategy: TranslationStrategyName;
|
|
@@ -40,6 +41,7 @@ export interface TaskRunnerProvider {
|
|
|
40
41
|
/**
|
|
41
42
|
* Configures the runner and returns a Payload config modifier.
|
|
42
43
|
* The modifier adds necessary tasks, jobs, queues to Payload config.
|
|
44
|
+
* Implementations may also install a `config.onInit` hook for boot-time initialization (e.g. stale-lock recovery).
|
|
43
45
|
*/
|
|
44
46
|
configure(context: TaskRunnerContext): (config: Config) => Config;
|
|
45
47
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Config, Payload } from
|
|
2
|
-
import type { TaskRunner } from
|
|
3
|
-
import type { PayloadJobsRunnerOptions } from
|
|
4
|
-
import type { TaskRunnerContext, TaskRunnerProvider } from
|
|
1
|
+
import type { Config, Payload } from "payload";
|
|
2
|
+
import type { TaskRunner } from "../TaskRunner.interface";
|
|
3
|
+
import type { PayloadJobsRunnerOptions } from "./types";
|
|
4
|
+
import type { TaskRunnerContext, TaskRunnerProvider } from "../TaskRunnerProvider.interface";
|
|
5
5
|
/**
|
|
6
6
|
* TaskRunnerProvider implementation using Payload Jobs.
|
|
7
7
|
*
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import { PayloadJobsTaskRunner } from
|
|
1
|
+
import { PayloadJobsTaskRunner } from "./PayloadJobsTaskRunner";
|
|
2
|
+
import { readCollectionRef } from "./readCollectionRef";
|
|
2
3
|
const defaultAutoRun = {
|
|
3
|
-
cron:
|
|
4
|
+
cron: "* * * * *",
|
|
4
5
|
limit: 50
|
|
5
6
|
};
|
|
7
|
+
const DEFAULT_STALE_JOB_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
6
8
|
const defaultValues = {
|
|
7
|
-
taskName:
|
|
8
|
-
queueName:
|
|
9
|
-
jobsCollection:
|
|
9
|
+
taskName: "translate_document",
|
|
10
|
+
queueName: "translations",
|
|
11
|
+
jobsCollection: "payload-jobs",
|
|
10
12
|
autoRun: defaultAutoRun,
|
|
13
|
+
staleJobTimeoutMs: DEFAULT_STALE_JOB_TIMEOUT_MS,
|
|
11
14
|
retries: {
|
|
12
15
|
attempts: 3,
|
|
13
16
|
backoff: {
|
|
14
|
-
type:
|
|
17
|
+
type: "exponential",
|
|
15
18
|
delay: 5000
|
|
16
19
|
}
|
|
17
20
|
}
|
|
@@ -27,11 +30,16 @@ const defaultValues = {
|
|
|
27
30
|
...defaultAutoRun,
|
|
28
31
|
...options.autoRun
|
|
29
32
|
} : defaultAutoRun;
|
|
33
|
+
const staleJobTimeoutMs = options?.staleJobTimeoutMs ?? defaultValues.staleJobTimeoutMs;
|
|
34
|
+
if (!Number.isFinite(staleJobTimeoutMs) || staleJobTimeoutMs <= 0) {
|
|
35
|
+
throw new Error(`[payload-plugin-translator] staleJobTimeoutMs must be a positive finite number (got ${staleJobTimeoutMs})`);
|
|
36
|
+
}
|
|
30
37
|
this.config = {
|
|
31
38
|
taskName: options?.taskName ?? defaultValues.taskName,
|
|
32
39
|
queueName: options?.queueName ?? defaultValues.queueName,
|
|
33
40
|
jobsCollection: options?.jobsCollection ?? defaultValues.jobsCollection,
|
|
34
41
|
autoRun,
|
|
42
|
+
staleJobTimeoutMs,
|
|
35
43
|
retries: options?.retries ?? defaultValues.retries
|
|
36
44
|
};
|
|
37
45
|
}
|
|
@@ -43,33 +51,57 @@ const defaultValues = {
|
|
|
43
51
|
const { handler, collections } = context;
|
|
44
52
|
return (config)=>{
|
|
45
53
|
const inputSchema = [
|
|
54
|
+
// Flat text reference (ID-agnostic). Current shape that jobs are
|
|
55
|
+
// written with — no relationship type validation against the target
|
|
56
|
+
// collection's ID type, so string IDs work for number-id collections.
|
|
46
57
|
{
|
|
47
|
-
type:
|
|
48
|
-
name:
|
|
49
|
-
|
|
58
|
+
type: "text",
|
|
59
|
+
name: "collection_slug",
|
|
60
|
+
required: true
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: "text",
|
|
64
|
+
name: "collection_id",
|
|
50
65
|
required: true
|
|
51
66
|
},
|
|
67
|
+
/**
|
|
68
|
+
* Legacy relationship reference, kept as a read-only fallback so jobs
|
|
69
|
+
* queued before the ID-agnostic migration stay readable. No longer
|
|
70
|
+
* written; demoted to `required: false` so new jobs (which omit it)
|
|
71
|
+
* pass validation. Removed in the next major.
|
|
72
|
+
* See docs/DEPRECATIONS.md#jobs-input-collection-field
|
|
73
|
+
* @deprecated
|
|
74
|
+
*/ {
|
|
75
|
+
type: "relationship",
|
|
76
|
+
name: "collection",
|
|
77
|
+
relationTo: collections,
|
|
78
|
+
required: false,
|
|
79
|
+
admin: {
|
|
80
|
+
readOnly: true,
|
|
81
|
+
description: "Deprecated. See docs/DEPRECATIONS.md#jobs-input-collection-field"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
52
84
|
{
|
|
53
|
-
type:
|
|
85
|
+
type: "text",
|
|
54
86
|
maxLength: 256,
|
|
55
|
-
name:
|
|
87
|
+
name: "source_lng",
|
|
56
88
|
required: true
|
|
57
89
|
},
|
|
58
90
|
{
|
|
59
|
-
type:
|
|
91
|
+
type: "text",
|
|
60
92
|
maxLength: 256,
|
|
61
|
-
name:
|
|
93
|
+
name: "target_lng",
|
|
62
94
|
required: true
|
|
63
95
|
},
|
|
64
96
|
{
|
|
65
|
-
type:
|
|
97
|
+
type: "text",
|
|
66
98
|
maxLength: 256,
|
|
67
|
-
name:
|
|
99
|
+
name: "strategy",
|
|
68
100
|
required: true
|
|
69
101
|
},
|
|
70
102
|
{
|
|
71
|
-
type:
|
|
72
|
-
name:
|
|
103
|
+
type: "checkbox",
|
|
104
|
+
name: "publish_on_translation",
|
|
73
105
|
defaultValue: false
|
|
74
106
|
}
|
|
75
107
|
];
|
|
@@ -78,9 +110,10 @@ const defaultValues = {
|
|
|
78
110
|
inputSchema,
|
|
79
111
|
retries,
|
|
80
112
|
handler: async (args)=>{
|
|
113
|
+
const { collectionSlug, collectionId } = readCollectionRef(args.input);
|
|
81
114
|
await handler(args.req.payload, {
|
|
82
|
-
collection:
|
|
83
|
-
collectionId
|
|
115
|
+
collection: collectionSlug,
|
|
116
|
+
collectionId,
|
|
84
117
|
sourceLng: args.input.source_lng,
|
|
85
118
|
targetLng: args.input.target_lng,
|
|
86
119
|
strategy: args.input.strategy,
|
|
@@ -104,7 +137,7 @@ const defaultValues = {
|
|
|
104
137
|
const existingAutoRun = config.jobs.autoRun;
|
|
105
138
|
if (Array.isArray(existingAutoRun)) {
|
|
106
139
|
existingAutoRun.push(autoRunConfig);
|
|
107
|
-
} else if (typeof existingAutoRun ===
|
|
140
|
+
} else if (typeof existingAutoRun === "function") {
|
|
108
141
|
config.jobs.autoRun = async (payload)=>[
|
|
109
142
|
...await existingAutoRun(payload),
|
|
110
143
|
autoRunConfig
|
|
@@ -115,6 +148,25 @@ const defaultValues = {
|
|
|
115
148
|
];
|
|
116
149
|
}
|
|
117
150
|
}
|
|
151
|
+
// Reset stale locks on boot so jobs abandoned by a killed process
|
|
152
|
+
// (deploy/crash/timeout) become eligible for the autorun picker again.
|
|
153
|
+
// The picker requires processing:false, no error, and no pending waitUntil;
|
|
154
|
+
// a mid-run casualty (no error, no waitUntil) satisfies the rest, so
|
|
155
|
+
// clearing processing is sufficient for that case. Threshold-based, so a
|
|
156
|
+
// job genuinely in flight on another live instance (fresh updatedAt) is
|
|
157
|
+
// left alone. Wrapped so a reclaim failure never blocks startup.
|
|
158
|
+
const existingOnInit = config.onInit;
|
|
159
|
+
config.onInit = async (payload)=>{
|
|
160
|
+
if (existingOnInit) await existingOnInit(payload);
|
|
161
|
+
try {
|
|
162
|
+
await new PayloadJobsTaskRunner(payload, this.config).reclaimStaleJobs();
|
|
163
|
+
} catch (err) {
|
|
164
|
+
payload.logger?.error?.({
|
|
165
|
+
err,
|
|
166
|
+
msg: "[translator] failed to reclaim stale translation jobs"
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
};
|
|
118
170
|
return config;
|
|
119
171
|
};
|
|
120
172
|
}
|