@focus-reactive/payload-plugin-translator 0.4.0 → 0.5.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/README.md +44 -29
- package/dist/index.d.ts +16 -14
- package/dist/index.js +9 -7
- package/dist/plugin.d.ts +15 -4
- package/dist/plugin.js +34 -57
- package/dist/server/features/cancel/handler.d.ts +2 -2
- package/dist/server/features/cancel/handler.js +3 -1
- package/dist/server/features/cancel/route.d.ts +4 -4
- package/dist/server/features/cancel/route.js +4 -4
- package/dist/server/features/cancel-by-collection/handler.d.ts +3 -3
- package/dist/server/features/cancel-by-collection/handler.js +1 -1
- package/dist/server/features/cancel-by-collection/route.d.ts +5 -5
- package/dist/server/features/cancel-by-collection/route.js +4 -4
- package/dist/server/features/createTranslationRoutes.d.ts +23 -0
- package/dist/server/features/createTranslationRoutes.js +27 -0
- package/dist/server/features/enqueue-translation/handler.d.ts +3 -3
- package/dist/server/features/enqueue-translation/handler.js +1 -1
- package/dist/server/features/enqueue-translation/route.d.ts +5 -5
- package/dist/server/features/enqueue-translation/route.js +4 -4
- package/dist/server/features/get-collection-status/handler.d.ts +3 -3
- package/dist/server/features/get-collection-status/handler.js +1 -1
- package/dist/server/features/get-collection-status/route.d.ts +5 -5
- package/dist/server/features/get-collection-status/route.js +4 -4
- package/dist/server/features/get-document-status/handler.d.ts +3 -3
- package/dist/server/features/get-document-status/handler.js +1 -1
- package/dist/server/features/get-document-status/route.d.ts +5 -5
- package/dist/server/features/get-document-status/route.js +4 -4
- package/dist/server/features/index.d.ts +7 -6
- package/dist/server/features/index.js +7 -6
- package/dist/server/features/run-translation/handler.d.ts +2 -2
- package/dist/server/features/run-translation/route.d.ts +4 -4
- package/dist/server/features/run-translation/route.js +4 -4
- package/dist/server/features/translate-document/handler.js +7 -11
- package/dist/server/modules/task-runner/TaskRunnerProvider.interface.d.ts +18 -1
- package/dist/server/modules/task-runner/index.d.ts +6 -6
- package/dist/server/modules/task-runner/index.js +2 -2
- package/dist/server/modules/task-runner/payload-jobs-runner/PayloadJobsRunnerProvider.d.ts +16 -1
- package/dist/server/modules/task-runner/payload-jobs-runner/PayloadJobsRunnerProvider.js +16 -1
- package/dist/server/modules/task-runner/sync-runner/SyncRunnerProvider.d.ts +18 -10
- package/dist/server/modules/task-runner/sync-runner/SyncRunnerProvider.js +24 -14
- package/dist/server/modules/translation-levels/PluginConfigBuilder.d.ts +46 -0
- package/dist/server/modules/translation-levels/PluginConfigBuilder.js +108 -0
- package/dist/server/modules/translation-levels/collectionLevel.d.ts +15 -0
- package/dist/server/modules/translation-levels/collectionLevel.js +24 -0
- package/dist/server/modules/translation-levels/documentLevel.d.ts +19 -0
- package/dist/server/modules/translation-levels/documentLevel.js +28 -0
- package/dist/server/modules/translation-levels/index.d.ts +3 -0
- package/dist/server/modules/translation-levels/index.js +4 -0
- package/dist/server/modules/translation-levels/types.d.ts +42 -0
- package/dist/server/modules/translation-levels/types.js +10 -0
- package/dist/server/modules/translation-levels/useDocTranslationApi.d.ts +8 -0
- package/dist/server/modules/translation-levels/useDocTranslationApi.js +18 -0
- package/dist/server/modules/translation-pipeline/index.d.ts +5 -3
- package/dist/server/modules/translation-pipeline/index.js +3 -2
- package/dist/server/modules/translation-pipeline/translateContent.d.ts +35 -0
- package/dist/server/modules/translation-pipeline/translateContent.js +30 -0
- package/dist/server/modules/translation-providers/OpenAITranslation.provider.d.ts +4 -3
- package/dist/server/modules/translation-providers/OpenAITranslation.provider.js +15 -15
- package/dist/types/AccessGuard.d.ts +24 -1
- package/dist/types/AccessGuard.js +12 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ Translation plugin for Payload CMS 3.x. Automatically translate your localized c
|
|
|
12
12
|
- **Pluggable providers** — use OpenAI or create custom translation providers
|
|
13
13
|
- **Field exclusion** — exclude specific fields from translation via `withFieldTranslation`
|
|
14
14
|
- **Translation strategies** — choose between overwrite all or skip existing translations
|
|
15
|
+
- **Configurable surfaces** — enable the per-document popup and/or the bulk-collection dashboard via the `levels` option _(since v0.5.0)_
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
|
|
@@ -33,11 +34,7 @@ yarn add @focus-reactive/payload-plugin-translator
|
|
|
33
34
|
|
|
34
35
|
```typescript
|
|
35
36
|
import { buildConfig } from "payload";
|
|
36
|
-
import {
|
|
37
|
-
translatorPlugin,
|
|
38
|
-
createOpenAIProvider,
|
|
39
|
-
createPayloadJobsRunner,
|
|
40
|
-
} from "@focus-reactive/payload-plugin-translator";
|
|
37
|
+
import { translatorPlugin, createOpenAIProvider, createPayloadJobsRunner } from "@focus-reactive/payload-plugin-translator";
|
|
41
38
|
import { Posts } from "./collections/Posts";
|
|
42
39
|
import { Pages } from "./collections/Pages";
|
|
43
40
|
|
|
@@ -65,13 +62,14 @@ export default buildConfig({
|
|
|
65
62
|
|
|
66
63
|
Configuration for `translatorPlugin()`.
|
|
67
64
|
|
|
68
|
-
| Property | Type | Required | Default
|
|
69
|
-
| --------------------- | --------------------- | -------- |
|
|
70
|
-
| `collections` | `CollectionConfig[]` | Yes | —
|
|
71
|
-
| `translationProvider` | `TranslationProvider` | Yes | —
|
|
72
|
-
| `runner` | `TaskRunnerProvider` | Yes | —
|
|
73
|
-
| `access` | `AccessGuard` | No | `undefined`
|
|
74
|
-
| `basePath` | `string` | No | `'/translate'`
|
|
65
|
+
| Property | Type | Required | Default | Description |
|
|
66
|
+
| --------------------- | --------------------- | -------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
67
|
+
| `collections` | `CollectionConfig[]` | Yes | — | Original collection configs to enable translation for. Must be the same objects passed to `buildConfig`, not slugs. |
|
|
68
|
+
| `translationProvider` | `TranslationProvider` | Yes | — | Translation provider instance (e.g., `createOpenAIProvider(...)`) |
|
|
69
|
+
| `runner` | `TaskRunnerProvider` | Yes | — | Task runner provider for background processing (e.g., `createPayloadJobsRunner()`) |
|
|
70
|
+
| `access` | `AccessGuard` | No | `undefined` | Access guard (`{ check }`) for the translation endpoints; omit to leave them open |
|
|
71
|
+
| `basePath` | `string` | No | `'/translate'` | Base path for all API endpoints |
|
|
72
|
+
| `levels` | `TranslationLevel[]` | No | `[documentLevel(), collectionLevel()]` | Which translation surfaces to enable. See [Translation Levels](#translation-levels) |
|
|
75
73
|
|
|
76
74
|
```typescript
|
|
77
75
|
translatorPlugin({
|
|
@@ -80,11 +78,37 @@ translatorPlugin({
|
|
|
80
78
|
apiKey: process.env.OPENAI_API_KEY,
|
|
81
79
|
}),
|
|
82
80
|
runner: createPayloadJobsRunner(),
|
|
83
|
-
access:
|
|
81
|
+
access: { check: ({ req }) => req.user?.role === "admin" },
|
|
84
82
|
basePath: "/translate",
|
|
85
83
|
});
|
|
86
84
|
```
|
|
87
85
|
|
|
86
|
+
### Translation Levels
|
|
87
|
+
|
|
88
|
+
_Since v0.5.0._
|
|
89
|
+
|
|
90
|
+
The `levels` option controls which translation surfaces the plugin exposes. Each entry is a factory you import and list:
|
|
91
|
+
|
|
92
|
+
- `documentLevel()` — a **Translate** popup on the document edit view (translate one document).
|
|
93
|
+
- `collectionLevel()` — a **bulk dashboard** on the collection list view (translate many at once).
|
|
94
|
+
|
|
95
|
+
Both run through the configured `runner` (async Payload Jobs by default, or synchronous via `createSyncRunner()`) and share the same translation REST API.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { translatorPlugin, documentLevel, collectionLevel, createOpenAIProvider, createPayloadJobsRunner } from "@focus-reactive/payload-plugin-translator";
|
|
99
|
+
|
|
100
|
+
translatorPlugin({
|
|
101
|
+
collections: [Posts],
|
|
102
|
+
translationProvider: createOpenAIProvider({
|
|
103
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
104
|
+
}),
|
|
105
|
+
runner: createPayloadJobsRunner(),
|
|
106
|
+
levels: [collectionLevel()], // bulk dashboard only — no per-document popup
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Omit `levels` for the default `[documentLevel(), collectionLevel()]`, which is identical to the previous behaviour — so adopting this option is non-breaking. A synchronous `fieldLevel()` (in-place field translation) is planned for a later release.
|
|
111
|
+
|
|
88
112
|
### OpenAIProviderConfig
|
|
89
113
|
|
|
90
114
|
Configuration for `createOpenAIProvider()`.
|
|
@@ -100,8 +124,7 @@ Configuration for `createOpenAIProvider()`.
|
|
|
100
124
|
createOpenAIProvider({
|
|
101
125
|
apiKey: process.env.OPENAI_API_KEY,
|
|
102
126
|
model: "gpt-4o-mini",
|
|
103
|
-
systemPrompt: ({ sourceLang, targetLang, defaultPrompt }) =>
|
|
104
|
-
`${defaultPrompt}\nUse formal language. Keep brand names unchanged.`,
|
|
127
|
+
systemPrompt: ({ sourceLang, targetLang, defaultPrompt }) => `${defaultPrompt}\nUse formal language. Keep brand names unchanged.`,
|
|
105
128
|
dryRun: false,
|
|
106
129
|
});
|
|
107
130
|
```
|
|
@@ -165,10 +188,7 @@ Configuration for `withFieldTranslation()` helper or `field.custom.translateKit`
|
|
|
165
188
|
```typescript
|
|
166
189
|
import { withFieldTranslation } from "@focus-reactive/payload-plugin-translator";
|
|
167
190
|
|
|
168
|
-
withFieldTranslation(
|
|
169
|
-
{ name: "sku", type: "text", localized: true },
|
|
170
|
-
{ exclude: true },
|
|
171
|
-
);
|
|
191
|
+
withFieldTranslation({ name: "sku", type: "text", localized: true }, { exclude: true });
|
|
172
192
|
```
|
|
173
193
|
|
|
174
194
|
## Translation Strategies
|
|
@@ -310,20 +330,12 @@ type TranslationOutput = Record<TranslationIndex, string>;
|
|
|
310
330
|
#### Example Implementation
|
|
311
331
|
|
|
312
332
|
```typescript
|
|
313
|
-
import type {
|
|
314
|
-
TranslationProvider,
|
|
315
|
-
TranslationInput,
|
|
316
|
-
TranslationOutput,
|
|
317
|
-
} from "@focus-reactive/payload-plugin-translator";
|
|
333
|
+
import type { TranslationProvider, TranslationInput, TranslationOutput } from "@focus-reactive/payload-plugin-translator";
|
|
318
334
|
|
|
319
335
|
class DeepLProvider implements TranslationProvider {
|
|
320
336
|
constructor(private apiKey: string) {}
|
|
321
337
|
|
|
322
|
-
async translate(
|
|
323
|
-
content: TranslationInput,
|
|
324
|
-
sourceLng: string,
|
|
325
|
-
targetLng: string,
|
|
326
|
-
): Promise<TranslationOutput | null> {
|
|
338
|
+
async translate(content: TranslationInput, sourceLng: string, targetLng: string): Promise<TranslationOutput | null> {
|
|
327
339
|
try {
|
|
328
340
|
// content example: { "0": "Hello", "1": "World" }
|
|
329
341
|
const texts = Object.values(content);
|
|
@@ -401,6 +413,9 @@ import type {
|
|
|
401
413
|
|
|
402
414
|
// Field config
|
|
403
415
|
FieldTranslationConfig,
|
|
416
|
+
|
|
417
|
+
// Translation levels
|
|
418
|
+
TranslationLevel,
|
|
404
419
|
} from "@focus-reactive/payload-plugin-translator";
|
|
405
420
|
```
|
|
406
421
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
export { translatorPlugin } from
|
|
2
|
-
export type { TranslatorPluginConfig } from
|
|
3
|
-
export type { AccessGuard, AccessGuardRequest } from
|
|
4
|
-
export { createOpenAIProvider } from
|
|
5
|
-
export type { TranslationProvider, TranslationInput, TranslationOutput, OpenAIProviderConfig, DryRunConfig
|
|
6
|
-
export { createPayloadJobsRunner, createSyncRunner } from
|
|
7
|
-
export type { TaskRunnerProvider, PayloadJobsRunnerOptions } from
|
|
8
|
-
export {
|
|
9
|
-
export type {
|
|
10
|
-
export {
|
|
11
|
-
export type {
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
14
|
-
export
|
|
1
|
+
export { translatorPlugin } from "./plugin";
|
|
2
|
+
export type { TranslatorPluginConfig } from "./plugin";
|
|
3
|
+
export type { AccessGuard, AccessGuardRequest } from "./types/AccessGuard";
|
|
4
|
+
export { createOpenAIProvider } from "./server/modules/translation-providers";
|
|
5
|
+
export type { TranslationProvider, TranslationInput, TranslationOutput, OpenAIProviderConfig, DryRunConfig } from "./server/modules/translation-providers";
|
|
6
|
+
export { createPayloadJobsRunner, createSyncRunner } from "./server/modules/task-runner";
|
|
7
|
+
export type { TaskRunnerProvider, PayloadJobsRunnerOptions } from "./server/modules/task-runner";
|
|
8
|
+
export { documentLevel, collectionLevel } from "./server/modules/translation-levels";
|
|
9
|
+
export type { TranslationLevel } from "./server/modules/translation-levels";
|
|
10
|
+
export { withFieldTranslation } from "./field-config";
|
|
11
|
+
export type { FieldTranslationConfig } from "./field-config";
|
|
12
|
+
export { createTranslatePlugin, TranslateCollectionPlugin } from "./plugin";
|
|
13
|
+
export type { TranslateCollectionPluginConfig } from "./plugin";
|
|
14
|
+
export { OpenAITranslationProvider } from "./server/modules/translation-providers";
|
|
15
|
+
export { translateKitField } from "./field-config";
|
|
16
|
+
export type { TranslateKitFieldConfig } from "./field-config";
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// Main plugin
|
|
2
|
-
export { translatorPlugin } from
|
|
2
|
+
export { translatorPlugin } from "./plugin";
|
|
3
3
|
// Translation providers
|
|
4
|
-
export { createOpenAIProvider } from
|
|
4
|
+
export { createOpenAIProvider } from "./server/modules/translation-providers";
|
|
5
5
|
// Task runners
|
|
6
|
-
export { createPayloadJobsRunner, createSyncRunner } from
|
|
6
|
+
export { createPayloadJobsRunner, createSyncRunner } from "./server/modules/task-runner";
|
|
7
|
+
// Translation levels
|
|
8
|
+
export { documentLevel, collectionLevel } from "./server/modules/translation-levels";
|
|
7
9
|
// Field config
|
|
8
|
-
export { withFieldTranslation } from
|
|
10
|
+
export { withFieldTranslation } from "./field-config";
|
|
9
11
|
// Deprecated exports (for backwards compatibility)
|
|
10
|
-
export { createTranslatePlugin, TranslateCollectionPlugin } from
|
|
11
|
-
export { OpenAITranslationProvider } from
|
|
12
|
-
export { translateKitField } from
|
|
12
|
+
export { createTranslatePlugin, TranslateCollectionPlugin } from "./plugin";
|
|
13
|
+
export { OpenAITranslationProvider } from "./server/modules/translation-providers";
|
|
14
|
+
export { translateKitField } from "./field-config";
|
|
13
15
|
|
|
14
16
|
//# sourceMappingURL=index.js.map
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { CollectionConfig, Config } from
|
|
2
|
-
import type { AccessGuard } from
|
|
3
|
-
import type { TranslationProvider } from
|
|
4
|
-
import type { TaskRunnerProvider } from
|
|
1
|
+
import type { CollectionConfig, Config } from "payload";
|
|
2
|
+
import type { AccessGuard } from "./types/AccessGuard";
|
|
3
|
+
import type { TranslationProvider } from "./server/modules/translation-providers";
|
|
4
|
+
import type { TaskRunnerProvider } from "./server/modules/task-runner";
|
|
5
|
+
import type { TranslationLevel } from "./server/modules/translation-levels";
|
|
5
6
|
export type TranslatorPluginConfig = {
|
|
6
7
|
/**
|
|
7
8
|
* Original collection configs (same objects passed to buildConfig).
|
|
@@ -32,6 +33,16 @@ export type TranslatorPluginConfig = {
|
|
|
32
33
|
* @default '/translate'
|
|
33
34
|
*/
|
|
34
35
|
basePath?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Which translation surfaces to enable, as level factories — `documentLevel()`,
|
|
38
|
+
* `collectionLevel()` (and, from a later phase, `fieldLevel()`). Omit for the
|
|
39
|
+
* default `[documentLevel(), collectionLevel()]`, which is exactly today's behaviour.
|
|
40
|
+
* Each level should appear at most once: endpoints are deduplicated, but admin
|
|
41
|
+
* components are not, so a duplicated level renders a duplicated control.
|
|
42
|
+
* @default [documentLevel(), collectionLevel()]
|
|
43
|
+
* @since 0.5.0
|
|
44
|
+
*/
|
|
45
|
+
levels?: TranslationLevel[];
|
|
35
46
|
};
|
|
36
47
|
/** @deprecated Use `TranslatorPluginConfig` instead */
|
|
37
48
|
export type TranslateCollectionPluginConfig = TranslatorPluginConfig;
|
package/dist/plugin.js
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import { CacheProviderExport } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { createRunRoute } from './server/features/run-translation';
|
|
7
|
-
import { createCancelRoute } from './server/features/cancel';
|
|
8
|
-
import { createCancelByCollectionRoute } from './server/features/cancel-by-collection';
|
|
9
|
-
import { createGetDocumentStatusRoute } from './server/features/get-document-status';
|
|
10
|
-
import { createGetCollectionStatusRoute } from './server/features/get-collection-status';
|
|
11
|
-
import { normalizePath, pipe } from './server/shared';
|
|
1
|
+
import { CacheProviderExport } from "./client/app/cache/CacheProvider.export";
|
|
2
|
+
import { TranslateDocumentHandler } from "./server/features/translate-document";
|
|
3
|
+
import { documentLevel, collectionLevel } from "./server/modules/translation-levels";
|
|
4
|
+
import { PluginConfigBuilder } from "./server/modules/translation-levels/PluginConfigBuilder";
|
|
5
|
+
import { normalizePath } from "./server/shared";
|
|
12
6
|
/** @deprecated Use `translatorPlugin` function instead */ export class TranslateCollectionPlugin {
|
|
13
7
|
pluginConfig;
|
|
14
8
|
constructor(pluginConfig){
|
|
@@ -16,7 +10,7 @@ import { normalizePath, pipe } from './server/shared';
|
|
|
16
10
|
}
|
|
17
11
|
init() {
|
|
18
12
|
return async (config)=>{
|
|
19
|
-
const { access, translationProvider, basePath: rawBasePath =
|
|
13
|
+
const { access, translationProvider, runner, collections, levels, basePath: rawBasePath = "/translate" } = this.pluginConfig;
|
|
20
14
|
// Build schema map from deep-cloned collections
|
|
21
15
|
// Deep clone is required because Payload mutates the original collection objects,
|
|
22
16
|
// removing `localized: true` from nested fields during sanitization.
|
|
@@ -25,14 +19,14 @@ import { normalizePath, pipe } from './server/shared';
|
|
|
25
19
|
// TODO: Consider introducing a FieldLike interface with only the properties
|
|
26
20
|
// used by the pipeline (name, type, localized, fields, blocks, tabs, custom)
|
|
27
21
|
// to make the contract explicit and avoid reliance on JSON round-trip.
|
|
28
|
-
const schemaMap = new Map(
|
|
22
|
+
const schemaMap = new Map(collections.map((col)=>[
|
|
29
23
|
col.slug,
|
|
30
24
|
JSON.parse(JSON.stringify(col.fields))
|
|
31
25
|
]));
|
|
32
26
|
const collectionSlugs = new Set(schemaMap.keys());
|
|
33
27
|
const basePath = normalizePath(rawBasePath);
|
|
34
28
|
const translateHandler = new TranslateDocumentHandler(translationProvider, schemaMap);
|
|
35
|
-
const
|
|
29
|
+
const runnerContext = {
|
|
36
30
|
handler: async (payload, input)=>{
|
|
37
31
|
await translateHandler.handle(payload, {
|
|
38
32
|
collection: input.collection,
|
|
@@ -44,50 +38,33 @@ import { normalizePath, pipe } from './server/shared';
|
|
|
44
38
|
});
|
|
45
39
|
},
|
|
46
40
|
collections: Array.from(collectionSlugs)
|
|
41
|
+
};
|
|
42
|
+
const runnerConfigModifier = runner.configure(runnerContext);
|
|
43
|
+
// Bind the context once so routes receive a self-sufficient factory: the
|
|
44
|
+
// runner needs no mutable per-instance handler state and create() has no
|
|
45
|
+
// "configure() must run first" ordering coupling (translator plan, 0c).
|
|
46
|
+
const taskRunnerFactory = {
|
|
47
|
+
create: (payload)=>runner.create(payload, runnerContext.handler)
|
|
48
|
+
};
|
|
49
|
+
const activeLevels = levels ?? [
|
|
50
|
+
documentLevel(),
|
|
51
|
+
collectionLevel()
|
|
52
|
+
];
|
|
53
|
+
const builder = new PluginConfigBuilder({
|
|
54
|
+
collections,
|
|
55
|
+
basePath,
|
|
56
|
+
access,
|
|
57
|
+
taskRunnerFactory
|
|
47
58
|
});
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
(
|
|
57
|
-
cfg.collections?.forEach((collection)=>{
|
|
58
|
-
if (!collectionSlugs.has(collection.slug)) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
if (!collection.admin) collection.admin = {};
|
|
62
|
-
if (!collection.admin.components) collection.admin.components = {};
|
|
63
|
-
if (!collection.admin.components.edit) collection.admin.components.edit = {};
|
|
64
|
-
if (!collection.admin.components.edit.beforeDocumentControls) {
|
|
65
|
-
collection.admin.components.edit.beforeDocumentControls = [];
|
|
66
|
-
}
|
|
67
|
-
if (!collection.admin.components.beforeListTable) {
|
|
68
|
-
collection.admin.components.beforeListTable = [];
|
|
69
|
-
}
|
|
70
|
-
collection.admin.components.edit.beforeDocumentControls.push(new TranslateDocumentExport(collection, access));
|
|
71
|
-
collection.admin.components.beforeListTable.push(new BulkDocumentTranslationDashboard(access));
|
|
72
|
-
});
|
|
73
|
-
return cfg;
|
|
74
|
-
}, // 3. Runner configures jobs/tasks/autorun
|
|
75
|
-
runnerConfigModifier, // 4. Add global endpoints
|
|
76
|
-
(cfg)=>{
|
|
77
|
-
if (!cfg.endpoints) cfg.endpoints = [];
|
|
78
|
-
const collectionConfig = {
|
|
79
|
-
availableCollections: collectionSlugs
|
|
80
|
-
};
|
|
81
|
-
cfg.endpoints.push(...[
|
|
82
|
-
createEnqueueRoute(this.pluginConfig.runner, collectionConfig, access, basePath),
|
|
83
|
-
createRunRoute(this.pluginConfig.runner, access, basePath),
|
|
84
|
-
createCancelRoute(this.pluginConfig.runner, access, basePath),
|
|
85
|
-
createCancelByCollectionRoute(collectionConfig, this.pluginConfig.runner, access, basePath),
|
|
86
|
-
createGetDocumentStatusRoute(collectionConfig, this.pluginConfig.runner, access, basePath),
|
|
87
|
-
createGetCollectionStatusRoute(collectionConfig, this.pluginConfig.runner, access, basePath)
|
|
88
|
-
]);
|
|
89
|
-
return cfg;
|
|
90
|
-
})(config);
|
|
59
|
+
for (const level of activeLevels)level.extend(builder);
|
|
60
|
+
// Plugin-level contributions, routed through the same single config-writer:
|
|
61
|
+
// - the runner's jobs/autorun/onInit modifier (the builder applies it first,
|
|
62
|
+
// so a modifier returning a fresh config object doesn't drop later writes),
|
|
63
|
+
// - the always-on client cache provider.
|
|
64
|
+
builder.addConfigModifier(runnerConfigModifier);
|
|
65
|
+
builder.addAdminProvider(new CacheProviderExport(basePath));
|
|
66
|
+
// The single place the Payload config is mutated.
|
|
67
|
+
return builder.applyTo(config);
|
|
91
68
|
};
|
|
92
69
|
}
|
|
93
70
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { PayloadRequest } from "payload";
|
|
2
|
-
import type {
|
|
2
|
+
import type { TaskRunnerFactory } from "../../modules/task-runner";
|
|
3
3
|
/**
|
|
4
4
|
* Cancels and deletes translation tasks by IDs
|
|
5
5
|
*/
|
|
6
6
|
export declare class CancelHandler {
|
|
7
7
|
private readonly taskRunnerFactory;
|
|
8
|
-
constructor(taskRunnerFactory:
|
|
8
|
+
constructor(taskRunnerFactory: TaskRunnerFactory);
|
|
9
9
|
handle(req: PayloadRequest): Promise<Response>;
|
|
10
10
|
}
|
|
@@ -9,7 +9,9 @@ import { CancelInputSchema } from "./model";
|
|
|
9
9
|
}
|
|
10
10
|
async handle(req) {
|
|
11
11
|
const validationResult = CancelInputSchema.safeParse(await req.json?.());
|
|
12
|
-
if (validationResult.error)
|
|
12
|
+
if (validationResult.error) {
|
|
13
|
+
return ServerResponse.validationError(validationResult.error.issues);
|
|
14
|
+
}
|
|
13
15
|
const { ids } = validationResult.data;
|
|
14
16
|
const runner = this.taskRunnerFactory.create(req.payload);
|
|
15
17
|
await runner.cancel(ids);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Endpoint } from
|
|
2
|
-
import type { AccessGuard } from
|
|
3
|
-
import type {
|
|
1
|
+
import type { Endpoint } from "payload";
|
|
2
|
+
import type { AccessGuard } from "../../shared";
|
|
3
|
+
import type { TaskRunnerFactory } from "../../modules/task-runner";
|
|
4
4
|
/**
|
|
5
5
|
* Creates the batch cancel endpoint
|
|
6
6
|
*/
|
|
7
|
-
export declare function createCancelRoute(taskRunnerFactory:
|
|
7
|
+
export declare function createCancelRoute(taskRunnerFactory: TaskRunnerFactory, access?: AccessGuard, basePath?: string): Endpoint;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { withErrorHandler, withAccessCheck } from
|
|
2
|
-
import { CancelHandler } from
|
|
1
|
+
import { withErrorHandler, withAccessCheck } from "../../shared";
|
|
2
|
+
import { CancelHandler } from "./handler";
|
|
3
3
|
/**
|
|
4
4
|
* Creates the batch cancel endpoint
|
|
5
|
-
*/ export function createCancelRoute(taskRunnerFactory, access, basePath =
|
|
5
|
+
*/ export function createCancelRoute(taskRunnerFactory, access, basePath = "/translate") {
|
|
6
6
|
const handler = new CancelHandler(taskRunnerFactory);
|
|
7
7
|
return {
|
|
8
8
|
path: `${basePath}/cancel`,
|
|
9
|
-
method:
|
|
9
|
+
method: "delete",
|
|
10
10
|
handler: withAccessCheck(withErrorHandler(handler.handle.bind(handler)), access)
|
|
11
11
|
};
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { PayloadRequest } from "payload";
|
|
2
|
-
import type {
|
|
3
|
-
import type { CancelConfig } from
|
|
2
|
+
import type { TaskRunnerFactory } from "../../modules/task-runner";
|
|
3
|
+
import type { CancelConfig } from "./model";
|
|
4
4
|
/**
|
|
5
5
|
* Cancels all pending translation tasks for a collection
|
|
6
6
|
*/
|
|
7
7
|
export declare class CancelByCollectionHandler {
|
|
8
8
|
private readonly config;
|
|
9
9
|
private readonly taskRunnerFactory;
|
|
10
|
-
constructor(config: CancelConfig, taskRunnerFactory:
|
|
10
|
+
constructor(config: CancelConfig, taskRunnerFactory: TaskRunnerFactory);
|
|
11
11
|
handle(req: PayloadRequest): Promise<Response>;
|
|
12
12
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ServerResponse } from "../../shared";
|
|
2
2
|
import { isCollectionAvailable } from "../_lib/collection-utils";
|
|
3
|
-
import { CancelByCollectionInputSchema } from
|
|
3
|
+
import { CancelByCollectionInputSchema } from "./model";
|
|
4
4
|
/**
|
|
5
5
|
* Cancels all pending translation tasks for a collection
|
|
6
6
|
*/ export class CancelByCollectionHandler {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Endpoint } from
|
|
2
|
-
import type { AccessGuard } from
|
|
3
|
-
import type {
|
|
4
|
-
import type { CancelConfig } from
|
|
1
|
+
import type { Endpoint } from "payload";
|
|
2
|
+
import type { AccessGuard } from "../../shared";
|
|
3
|
+
import type { TaskRunnerFactory } from "../../modules/task-runner";
|
|
4
|
+
import type { CancelConfig } from "./model";
|
|
5
5
|
/**
|
|
6
6
|
* Creates the cancel by collection endpoint
|
|
7
7
|
*/
|
|
8
|
-
export declare function createCancelByCollectionRoute(config: CancelConfig, taskRunnerFactory:
|
|
8
|
+
export declare function createCancelByCollectionRoute(config: CancelConfig, taskRunnerFactory: TaskRunnerFactory, access?: AccessGuard, basePath?: string): Endpoint;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { withErrorHandler, withAccessCheck } from
|
|
2
|
-
import { CancelByCollectionHandler } from
|
|
1
|
+
import { withErrorHandler, withAccessCheck } from "../../shared";
|
|
2
|
+
import { CancelByCollectionHandler } from "./handler";
|
|
3
3
|
/**
|
|
4
4
|
* Creates the cancel by collection endpoint
|
|
5
|
-
*/ export function createCancelByCollectionRoute(config, taskRunnerFactory, access, basePath =
|
|
5
|
+
*/ export function createCancelByCollectionRoute(config, taskRunnerFactory, access, basePath = "/translate") {
|
|
6
6
|
const handler = new CancelByCollectionHandler(config, taskRunnerFactory);
|
|
7
7
|
return {
|
|
8
8
|
path: `${basePath}/cancel-by-collection/:collection_slug`,
|
|
9
|
-
method:
|
|
9
|
+
method: "delete",
|
|
10
10
|
handler: withAccessCheck(withErrorHandler(handler.handle.bind(handler)), access)
|
|
11
11
|
};
|
|
12
12
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CollectionSlug, Endpoint } from "payload";
|
|
2
|
+
import type { AccessGuard } from "../shared";
|
|
3
|
+
import type { TaskRunnerFactory } from "../modules/task-runner";
|
|
4
|
+
export type TranslationRoutesDeps = {
|
|
5
|
+
taskRunnerFactory: TaskRunnerFactory;
|
|
6
|
+
/** Collections the plugin manages — gates which slugs the routes accept. */
|
|
7
|
+
collectionConfig: {
|
|
8
|
+
availableCollections: Set<CollectionSlug>;
|
|
9
|
+
};
|
|
10
|
+
access?: AccessGuard;
|
|
11
|
+
basePath?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The translator's HTTP surface as one shared bundle: the 6 job-API endpoints
|
|
15
|
+
* (enqueue / run / cancel / cancel-by-collection / document-status /
|
|
16
|
+
* collection-status), all wired to the same runner factory and access guard.
|
|
17
|
+
*
|
|
18
|
+
* Extracted from the plugin's `init()` so registration is a single unit. The
|
|
19
|
+
* document and collection levels each contribute this bundle via the level
|
|
20
|
+
* context; the plugin deduplicates by method + path, so it registers exactly
|
|
21
|
+
* once. Behaviour-preserving: same endpoints, same order, same wiring.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createTranslationRoutes({ taskRunnerFactory, collectionConfig, access, basePath }: TranslationRoutesDeps): Endpoint[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createEnqueueRoute } from "./enqueue-translation";
|
|
2
|
+
import { createRunRoute } from "./run-translation";
|
|
3
|
+
import { createCancelRoute } from "./cancel";
|
|
4
|
+
import { createCancelByCollectionRoute } from "./cancel-by-collection";
|
|
5
|
+
import { createGetDocumentStatusRoute } from "./get-document-status";
|
|
6
|
+
import { createGetCollectionStatusRoute } from "./get-collection-status";
|
|
7
|
+
/**
|
|
8
|
+
* The translator's HTTP surface as one shared bundle: the 6 job-API endpoints
|
|
9
|
+
* (enqueue / run / cancel / cancel-by-collection / document-status /
|
|
10
|
+
* collection-status), all wired to the same runner factory and access guard.
|
|
11
|
+
*
|
|
12
|
+
* Extracted from the plugin's `init()` so registration is a single unit. The
|
|
13
|
+
* document and collection levels each contribute this bundle via the level
|
|
14
|
+
* context; the plugin deduplicates by method + path, so it registers exactly
|
|
15
|
+
* once. Behaviour-preserving: same endpoints, same order, same wiring.
|
|
16
|
+
*/ export function createTranslationRoutes({ taskRunnerFactory, collectionConfig, access, basePath }) {
|
|
17
|
+
return [
|
|
18
|
+
createEnqueueRoute(taskRunnerFactory, collectionConfig, access, basePath),
|
|
19
|
+
createRunRoute(taskRunnerFactory, access, basePath),
|
|
20
|
+
createCancelRoute(taskRunnerFactory, access, basePath),
|
|
21
|
+
createCancelByCollectionRoute(collectionConfig, taskRunnerFactory, access, basePath),
|
|
22
|
+
createGetDocumentStatusRoute(collectionConfig, taskRunnerFactory, access, basePath),
|
|
23
|
+
createGetCollectionStatusRoute(collectionConfig, taskRunnerFactory, access, basePath)
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=createTranslationRoutes.js.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { PayloadRequest } from "payload";
|
|
2
|
-
import type {
|
|
3
|
-
import type { EnqueueConfig } from
|
|
2
|
+
import type { TaskRunnerFactory } from "../../modules/task-runner";
|
|
3
|
+
import type { EnqueueConfig } from "./model";
|
|
4
4
|
/**
|
|
5
5
|
* Enqueues translation tasks for documents
|
|
6
6
|
*/
|
|
7
7
|
export declare class EnqueueTranslationHandler {
|
|
8
8
|
private readonly config;
|
|
9
9
|
private readonly taskRunnerFactory;
|
|
10
|
-
constructor(config: EnqueueConfig, taskRunnerFactory:
|
|
10
|
+
constructor(config: EnqueueConfig, taskRunnerFactory: TaskRunnerFactory);
|
|
11
11
|
handle(req: PayloadRequest): Promise<Response>;
|
|
12
12
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ServerResponse } from "../../shared";
|
|
2
2
|
import { isCollectionAvailable, getAllCollectionIds } from "../_lib/collection-utils";
|
|
3
|
-
import { EnqueueInputSchema } from
|
|
3
|
+
import { EnqueueInputSchema } from "./model";
|
|
4
4
|
/**
|
|
5
5
|
* Enqueues translation tasks for documents
|
|
6
6
|
*/ export class EnqueueTranslationHandler {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Endpoint } from
|
|
2
|
-
import type { AccessGuard } from
|
|
3
|
-
import type {
|
|
4
|
-
import type { EnqueueConfig } from
|
|
1
|
+
import type { Endpoint } from "payload";
|
|
2
|
+
import type { AccessGuard } from "../../shared";
|
|
3
|
+
import type { TaskRunnerFactory } from "../../modules/task-runner";
|
|
4
|
+
import type { EnqueueConfig } from "./model";
|
|
5
5
|
/**
|
|
6
6
|
* Creates the enqueue translation endpoint
|
|
7
7
|
*/
|
|
8
|
-
export declare function createEnqueueRoute(taskRunnerFactory:
|
|
8
|
+
export declare function createEnqueueRoute(taskRunnerFactory: TaskRunnerFactory, config: EnqueueConfig, access?: AccessGuard, basePath?: string): Endpoint;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { withErrorHandler, withAccessCheck } from
|
|
2
|
-
import { EnqueueTranslationHandler } from
|
|
1
|
+
import { withErrorHandler, withAccessCheck } from "../../shared";
|
|
2
|
+
import { EnqueueTranslationHandler } from "./handler";
|
|
3
3
|
/**
|
|
4
4
|
* Creates the enqueue translation endpoint
|
|
5
|
-
*/ export function createEnqueueRoute(taskRunnerFactory, config, access, basePath =
|
|
5
|
+
*/ export function createEnqueueRoute(taskRunnerFactory, config, access, basePath = "/translate") {
|
|
6
6
|
const handler = new EnqueueTranslationHandler(config, taskRunnerFactory);
|
|
7
7
|
return {
|
|
8
8
|
path: `${basePath}/enqueue`,
|
|
9
|
-
method:
|
|
9
|
+
method: "post",
|
|
10
10
|
handler: withAccessCheck(withErrorHandler(handler.handle.bind(handler)), access)
|
|
11
11
|
};
|
|
12
12
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { PayloadRequest } from "payload";
|
|
2
|
-
import type {
|
|
3
|
-
import type { GetCollectionStatusConfig } from
|
|
2
|
+
import type { TaskRunnerFactory } 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
|
*/
|
|
7
7
|
export declare class GetCollectionStatusHandler {
|
|
8
8
|
private readonly config;
|
|
9
9
|
private readonly taskRunnerFactory;
|
|
10
|
-
constructor(config: GetCollectionStatusConfig, taskRunnerFactory:
|
|
10
|
+
constructor(config: GetCollectionStatusConfig, taskRunnerFactory: TaskRunnerFactory);
|
|
11
11
|
handle(req: PayloadRequest): Promise<Response>;
|
|
12
12
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ServerResponse } from "../../shared";
|
|
2
2
|
import { isCollectionAvailable } from "../_lib/collection-utils";
|
|
3
|
-
import { GetCollectionStatusInputSchema } from
|
|
3
|
+
import { GetCollectionStatusInputSchema } from "./model";
|
|
4
4
|
/**
|
|
5
5
|
* Gets translation status for all documents in a collection
|
|
6
6
|
*/ export class GetCollectionStatusHandler {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Endpoint } from
|
|
2
|
-
import type { AccessGuard } from
|
|
3
|
-
import type {
|
|
4
|
-
import type { GetCollectionStatusConfig } from
|
|
1
|
+
import type { Endpoint } from "payload";
|
|
2
|
+
import type { AccessGuard } from "../../shared";
|
|
3
|
+
import type { TaskRunnerFactory } from "../../modules/task-runner";
|
|
4
|
+
import type { GetCollectionStatusConfig } from "./model";
|
|
5
5
|
/**
|
|
6
6
|
* Creates the get collection status endpoint
|
|
7
7
|
*/
|
|
8
|
-
export declare function createGetCollectionStatusRoute(config: GetCollectionStatusConfig, taskRunnerFactory:
|
|
8
|
+
export declare function createGetCollectionStatusRoute(config: GetCollectionStatusConfig, taskRunnerFactory: TaskRunnerFactory, access?: AccessGuard, basePath?: string): Endpoint;
|