@nitrogenbuilder/connector-payload 1.1.0 → 1.2.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/collections/NitrogenComponentUsage.js +3 -21
- package/dist/collections/NitrogenTemplates.js +8 -0
- package/dist/components/NitrogenComponentInventoryClient.js +1 -1
- package/dist/endpoints/all.js +3 -0
- package/dist/endpoints/batch.js +9 -0
- package/dist/endpoints/collection-endpoints.js +119 -3
- package/dist/endpoints/helpers.js +2 -1
- package/dist/endpoints/menu.js +2 -0
- package/dist/endpoints/sitemap.js +47 -7
- package/dist/endpoints/templates.js +116 -0
- package/dist/endpoints/translation-status.d.ts +11 -0
- package/dist/endpoints/translation-status.js +105 -0
- package/dist/frontend/NitrogenPageClient.d.ts +6 -1
- package/dist/frontend/NitrogenPageClient.js +6 -2
- package/dist/frontend/NitrogenWrapper.d.ts +8 -1
- package/dist/frontend/NitrogenWrapper.js +25 -3
- package/dist/index.d.ts +22 -0
- package/dist/index.js +69 -2
- package/dist/inventory/indexing.d.ts +2 -5
- package/dist/inventory/indexing.js +129 -73
- package/dist/localization.d.ts +121 -0
- package/dist/localization.js +563 -0
- package/dist/types.d.ts +3 -5
- package/package.json +1 -1
|
@@ -10,50 +10,41 @@ function moduleNameFromNode(node) {
|
|
|
10
10
|
}
|
|
11
11
|
return null;
|
|
12
12
|
}
|
|
13
|
-
function collectUsageFromTree(modules, pathPrefix, results
|
|
13
|
+
function collectUsageFromTree(modules, pathPrefix, results) {
|
|
14
14
|
if (!Array.isArray(modules))
|
|
15
15
|
return;
|
|
16
16
|
modules.forEach((maybeNode, index) => {
|
|
17
17
|
if (!maybeNode || typeof maybeNode !== 'object')
|
|
18
18
|
return;
|
|
19
19
|
const node = maybeNode;
|
|
20
|
-
const modulePath = `${pathPrefix}[${index}]`;
|
|
21
20
|
const componentName = moduleNameFromNode(node);
|
|
21
|
+
// The tree path is no longer stored, but it is still the identity of last
|
|
22
|
+
// resort for a node that carries no id of its own. Such a node reverts to
|
|
23
|
+
// the old positional behaviour and churns when the tree is reordered.
|
|
24
|
+
const modulePath = `${pathPrefix}[${index}]`;
|
|
22
25
|
const moduleId = typeof node.id === 'string' || typeof node.id === 'number'
|
|
23
26
|
? String(node.id)
|
|
24
27
|
: modulePath;
|
|
25
|
-
const currentContext = componentName
|
|
26
|
-
? {
|
|
27
|
-
componentName,
|
|
28
|
-
moduleId,
|
|
29
|
-
modulePath,
|
|
30
|
-
}
|
|
31
|
-
: parentContext;
|
|
32
28
|
if (componentName) {
|
|
33
29
|
results.push({
|
|
34
30
|
componentName,
|
|
35
|
-
depth,
|
|
36
31
|
moduleId,
|
|
37
|
-
modulePath,
|
|
38
|
-
parentComponentName: parentContext?.componentName,
|
|
39
|
-
parentModuleId: parentContext?.moduleId,
|
|
40
|
-
parentModulePath: parentContext?.modulePath,
|
|
41
32
|
});
|
|
42
33
|
}
|
|
43
34
|
const props = node.props;
|
|
44
35
|
if (props && typeof props === 'object') {
|
|
45
36
|
const children = props.children;
|
|
46
37
|
if (Array.isArray(children)) {
|
|
47
|
-
collectUsageFromTree(children, `${modulePath}.props.children`, results
|
|
38
|
+
collectUsageFromTree(children, `${modulePath}.props.children`, results);
|
|
48
39
|
}
|
|
49
40
|
else if (children && typeof children === 'object') {
|
|
50
41
|
for (const [slotName, slotChildren] of Object.entries(children)) {
|
|
51
|
-
collectUsageFromTree(slotChildren, `${modulePath}.props.children.${slotName}`, results
|
|
42
|
+
collectUsageFromTree(slotChildren, `${modulePath}.props.children.${slotName}`, results);
|
|
52
43
|
}
|
|
53
44
|
}
|
|
54
45
|
}
|
|
55
46
|
if (Array.isArray(node.children)) {
|
|
56
|
-
collectUsageFromTree(node.children, `${modulePath}.children`, results
|
|
47
|
+
collectUsageFromTree(node.children, `${modulePath}.children`, results);
|
|
57
48
|
}
|
|
58
49
|
});
|
|
59
50
|
}
|
|
@@ -71,6 +62,7 @@ export async function findAllDocs(payload, collection, options, req) {
|
|
|
71
62
|
collection: collection,
|
|
72
63
|
depth: 0,
|
|
73
64
|
limit: options?.limit ?? 200,
|
|
65
|
+
locale: options?.locale,
|
|
74
66
|
page,
|
|
75
67
|
overrideAccess: true,
|
|
76
68
|
pagination: true,
|
|
@@ -84,62 +76,77 @@ export async function findAllDocs(payload, collection, options, req) {
|
|
|
84
76
|
}
|
|
85
77
|
return docs;
|
|
86
78
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Delete usage rows in one bulk statement rather than paginating the ids and
|
|
81
|
+
* issuing a `payload.delete` per row. Usage is rebuilt wholesale on every save,
|
|
82
|
+
* so a document with N modules previously cost N deletes (plus the finds to
|
|
83
|
+
* discover them) on the critical path of the save transaction.
|
|
84
|
+
*/
|
|
85
|
+
async function deleteUsageDocsWhere(payload, where, req) {
|
|
86
|
+
await payload.delete({
|
|
87
|
+
collection: NITROGEN_COMPONENT_USAGE_COLLECTION,
|
|
88
|
+
overrideAccess: true,
|
|
89
|
+
req,
|
|
90
|
+
where,
|
|
91
|
+
});
|
|
96
92
|
}
|
|
97
|
-
|
|
98
|
-
return
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
where: {
|
|
104
|
-
and: [
|
|
105
|
-
{
|
|
106
|
-
sourceCollection: {
|
|
107
|
-
equals: sourceCollection,
|
|
108
|
-
},
|
|
93
|
+
function usageDocsForSourceWhere(sourceCollection, sourceDocumentId) {
|
|
94
|
+
return {
|
|
95
|
+
and: [
|
|
96
|
+
{
|
|
97
|
+
sourceCollection: {
|
|
98
|
+
equals: sourceCollection,
|
|
109
99
|
},
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
sourceDocumentId: {
|
|
103
|
+
equals: sourceDocumentId,
|
|
114
104
|
},
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function usageKeyFor(sourceCollection, sourceDocumentId, record) {
|
|
110
|
+
return `${sourceCollection}:${sourceDocumentId}:${record.moduleId}:${record.componentName}`;
|
|
111
|
+
}
|
|
112
|
+
function usageSourceFields(doc) {
|
|
113
|
+
return {
|
|
114
|
+
sourceTitle: doc.title || '',
|
|
115
|
+
sourceSlug: doc.slug || '',
|
|
116
|
+
sourceStatus: doc.status || doc._status || '',
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* `componentName`, `moduleId`, `sourceCollection` and `sourceDocumentId` are all
|
|
121
|
+
* encoded in the usage key, so a key match already implies they agree; the
|
|
122
|
+
* source fields are reconciled separately by a single bulk update. That leaves
|
|
123
|
+
* only `sourceType`, which is checked defensively so a row written with the
|
|
124
|
+
* wrong value can still heal.
|
|
125
|
+
*/
|
|
126
|
+
function usageRowIsCurrent(row, sourceType) {
|
|
127
|
+
return row.sourceType === sourceType;
|
|
128
|
+
}
|
|
129
|
+
function usageRowData(sourceCollection, sourceType, sourceDocumentId, sourceFields, record) {
|
|
130
|
+
return {
|
|
131
|
+
usageKey: usageKeyFor(sourceCollection, sourceDocumentId, record),
|
|
132
|
+
componentName: record.componentName,
|
|
133
|
+
sourceCollection,
|
|
134
|
+
sourceType,
|
|
135
|
+
sourceDocumentId,
|
|
136
|
+
...sourceFields,
|
|
137
|
+
moduleId: record.moduleId,
|
|
138
|
+
};
|
|
118
139
|
}
|
|
119
140
|
async function createUsageDocs(payload, sourceCollection, sourceType, doc, req) {
|
|
120
141
|
const sourceDocumentId = String(doc.id);
|
|
142
|
+
const sourceFields = usageSourceFields(doc);
|
|
121
143
|
const usageRecords = extractComponentUsageRecords(doc.nitrogenData);
|
|
122
144
|
for (const record of usageRecords) {
|
|
123
145
|
await payload.create({
|
|
124
146
|
collection: NITROGEN_COMPONENT_USAGE_COLLECTION,
|
|
125
147
|
overrideAccess: true,
|
|
126
148
|
req,
|
|
127
|
-
data:
|
|
128
|
-
usageKey: `${sourceCollection}:${sourceDocumentId}:${record.moduleId}:${record.modulePath}:${record.componentName}`,
|
|
129
|
-
componentName: record.componentName,
|
|
130
|
-
depth: record.depth,
|
|
131
|
-
sourceCollection,
|
|
132
|
-
sourceType,
|
|
133
|
-
sourceDocumentId,
|
|
134
|
-
sourceTitle: doc.title || '',
|
|
135
|
-
sourceSlug: doc.slug || '',
|
|
136
|
-
sourceStatus: doc.status || doc._status || '',
|
|
137
|
-
moduleId: record.moduleId,
|
|
138
|
-
modulePath: record.modulePath,
|
|
139
|
-
parentComponentName: record.parentComponentName || '',
|
|
140
|
-
parentModuleId: record.parentModuleId || '',
|
|
141
|
-
parentModulePath: record.parentModulePath || '',
|
|
142
|
-
},
|
|
149
|
+
data: usageRowData(sourceCollection, sourceType, sourceDocumentId, sourceFields, record),
|
|
143
150
|
});
|
|
144
151
|
}
|
|
145
152
|
return usageRecords.length;
|
|
@@ -193,23 +200,72 @@ export async function syncComponentCatalog(payload, manifestInput, req) {
|
|
|
193
200
|
}
|
|
194
201
|
export async function reindexDocumentUsage(payload, sourceCollection, doc, sourceType = 'document', req) {
|
|
195
202
|
const sourceDocumentId = String(doc.id);
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
203
|
+
const sourceWhere = usageDocsForSourceWhere(sourceCollection, sourceDocumentId);
|
|
204
|
+
const sourceFields = usageSourceFields(doc);
|
|
205
|
+
const usageRecords = extractComponentUsageRecords(doc.nitrogenData);
|
|
206
|
+
// Usage rows describe tree *structure* only — nothing derived from module
|
|
207
|
+
// props or content. Editing copy therefore leaves every row identical, so
|
|
208
|
+
// diffing turns the common autosave case into zero writes instead of a full
|
|
209
|
+
// delete-and-recreate of every row on the save's critical path.
|
|
210
|
+
const existingRows = await findAllDocs(payload, NITROGEN_COMPONENT_USAGE_COLLECTION, { limit: 200, where: sourceWhere }, req);
|
|
211
|
+
const desiredByKey = new Map();
|
|
212
|
+
for (const record of usageRecords) {
|
|
213
|
+
desiredByKey.set(usageKeyFor(sourceCollection, sourceDocumentId, record), record);
|
|
214
|
+
}
|
|
215
|
+
const existingByKey = new Map();
|
|
216
|
+
const staleKeys = [];
|
|
217
|
+
for (const row of existingRows) {
|
|
218
|
+
if (desiredByKey.has(row.usageKey))
|
|
219
|
+
existingByKey.set(row.usageKey, row);
|
|
220
|
+
else
|
|
221
|
+
staleKeys.push(row.usageKey);
|
|
222
|
+
}
|
|
223
|
+
if (staleKeys.length > 0) {
|
|
224
|
+
await deleteUsageDocsWhere(payload, { usageKey: { in: staleKeys } }, req);
|
|
225
|
+
}
|
|
226
|
+
// Title/slug/status are identical across every row of the document, so when
|
|
227
|
+
// they change one bulk update fixes the whole set rather than N row updates.
|
|
228
|
+
const sourceFieldsStale = existingRows.some((row) => (row.sourceTitle || '') !== sourceFields.sourceTitle ||
|
|
229
|
+
(row.sourceSlug || '') !== sourceFields.sourceSlug ||
|
|
230
|
+
(row.sourceStatus || '') !== sourceFields.sourceStatus);
|
|
231
|
+
if (sourceFieldsStale && existingByKey.size > 0) {
|
|
232
|
+
await payload.update({
|
|
233
|
+
collection: NITROGEN_COMPONENT_USAGE_COLLECTION,
|
|
234
|
+
overrideAccess: true,
|
|
235
|
+
req,
|
|
236
|
+
where: sourceWhere,
|
|
237
|
+
data: sourceFields,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
for (const [usageKey, record] of desiredByKey) {
|
|
241
|
+
const row = existingByKey.get(usageKey);
|
|
242
|
+
const data = usageRowData(sourceCollection, sourceType, sourceDocumentId, sourceFields, record);
|
|
243
|
+
if (!row) {
|
|
244
|
+
await payload.create({
|
|
245
|
+
collection: NITROGEN_COMPONENT_USAGE_COLLECTION,
|
|
246
|
+
overrideAccess: true,
|
|
247
|
+
req,
|
|
248
|
+
data,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
else if (!usageRowIsCurrent(row, sourceType)) {
|
|
252
|
+
await payload.update({
|
|
253
|
+
collection: NITROGEN_COMPONENT_USAGE_COLLECTION,
|
|
254
|
+
id: row.id,
|
|
255
|
+
overrideAccess: true,
|
|
256
|
+
req,
|
|
257
|
+
data,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return usageRecords.length;
|
|
199
262
|
}
|
|
200
263
|
export async function deleteDocumentUsage(payload, sourceCollection, sourceDocumentId, req) {
|
|
201
|
-
|
|
202
|
-
await deleteUsageDocs(payload, docsForSource, req);
|
|
264
|
+
await deleteUsageDocsWhere(payload, usageDocsForSourceWhere(sourceCollection, String(sourceDocumentId)), req);
|
|
203
265
|
}
|
|
204
266
|
export async function reindexAllComponentInventory(payload, collections, manifestInput, req) {
|
|
205
267
|
const entries = await syncComponentCatalog(payload, manifestInput, req);
|
|
206
|
-
|
|
207
|
-
limit: 200,
|
|
208
|
-
select: {
|
|
209
|
-
id: true,
|
|
210
|
-
},
|
|
211
|
-
}, req);
|
|
212
|
-
await deleteUsageDocs(payload, usageDocs, req);
|
|
268
|
+
await deleteUsageDocsWhere(payload, { id: { exists: true } }, req);
|
|
213
269
|
let documentCount = 0;
|
|
214
270
|
let usageCount = 0;
|
|
215
271
|
for (const collection of collections) {
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Localization support — locale threading for read endpoints, dynamic-data
|
|
3
|
+
* translation metadata, and per-language translation status for builder
|
|
4
|
+
* content.
|
|
5
|
+
*
|
|
6
|
+
* Two independent layers cooperate here:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Payload-native localization** (field data). When the host project's
|
|
9
|
+
* Payload config has `localization` configured (either directly or injected
|
|
10
|
+
* from the plugin's `localization` option), eligible text fields are
|
|
11
|
+
* localized and read endpoints thread `?lang=` through to
|
|
12
|
+
* `payload.find`/`findByID` as `locale` + `fallbackLocale`.
|
|
13
|
+
* 2. **Nitrogen settings localization** (builder content). Site languages live
|
|
14
|
+
* in the `nitrogen-settings` global under `nitrogenConfig.localization`.
|
|
15
|
+
* Translatable prop values inside `nitrogenData` are stored language-keyed
|
|
16
|
+
* with the language as the OUTER key (`{ en: ..., es: ... }`); legacy bare
|
|
17
|
+
* values read as the default language.
|
|
18
|
+
*
|
|
19
|
+
* Everything is gated: with no localization configured anywhere, every helper
|
|
20
|
+
* returns null/empty and callers behave exactly as before.
|
|
21
|
+
*/
|
|
22
|
+
import type { Field, Payload, PayloadRequest, SanitizedCollectionConfig } from 'payload';
|
|
23
|
+
import type { ComponentManifestProp, DynamicDataMeta, LanguageStatus, LocalizationSettings } from '@nitrogenbuilder/types';
|
|
24
|
+
import type { NitrogenSettingsGlobal } from './types.js';
|
|
25
|
+
export interface PayloadLocaleConfig {
|
|
26
|
+
locales: string[];
|
|
27
|
+
defaultLocale: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Reads the sanitized Payload localization config at runtime. Returns null
|
|
31
|
+
* when the project has no Payload-level localization configured.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getPayloadLocalization(payload: Payload): PayloadLocaleConfig | null;
|
|
34
|
+
/** Reads `?lang=` (alias `?locale=`) off a request URL. */
|
|
35
|
+
export declare function getRequestLanguage(req: PayloadRequest): string | null;
|
|
36
|
+
export interface LocaleQueryOptions {
|
|
37
|
+
locale?: string;
|
|
38
|
+
fallbackLocale?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Turns a requested language into `locale`/`fallbackLocale` options for
|
|
42
|
+
* `payload.find`/`findByID`. Returns `{}` (default behavior) when no language
|
|
43
|
+
* was requested, the project has no Payload localization, or the language is
|
|
44
|
+
* not a configured locale.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveLocaleOptions(payload: Payload, lang?: string | null): LocaleQueryOptions;
|
|
47
|
+
export declare function getCollectionConfig(payload: Payload, collectionSlug: string): SanitizedCollectionConfig | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Dot-paths of localized text-typed fields of a collection config. Walks
|
|
50
|
+
* groups, rows, collapsibles, and tabs; named groups/tabs contribute a path
|
|
51
|
+
* segment.
|
|
52
|
+
*/
|
|
53
|
+
export declare function getLocalizedTextFieldPaths(fields: Field[] | undefined, prefix?: string): string[];
|
|
54
|
+
/**
|
|
55
|
+
* The site language config from the `nitrogen-settings` global. Null when
|
|
56
|
+
* localization is absent or disabled — callers must treat null as
|
|
57
|
+
* "no localization behavior at all".
|
|
58
|
+
*/
|
|
59
|
+
export declare function getLocalizationSettings(settings: NitrogenSettingsGlobal): LocalizationSettings | null;
|
|
60
|
+
/**
|
|
61
|
+
* Fetches a doc with every locale's value for localized fields (one
|
|
62
|
+
* `locale: 'all'` query). Returns null when the project has no Payload
|
|
63
|
+
* localization configured or the doc can't be read — advisory callers keep
|
|
64
|
+
* their default behavior in that case.
|
|
65
|
+
*/
|
|
66
|
+
export declare function fetchLocaleAllDoc(payload: Payload, collectionSlug: string, docId: string | number): Promise<Record<string, unknown> | null>;
|
|
67
|
+
/**
|
|
68
|
+
* The default-locale title from a `locale: 'all'` doc, for editor-facing
|
|
69
|
+
* single-doc responses. The editor edits default-language field data only
|
|
70
|
+
* (the CMS admin owns field translations), so the `title` it displays and
|
|
71
|
+
* PATCHes back on save must never be a locale-resolved one. Returns null when
|
|
72
|
+
* there is nothing to override (no localization, title not localized, or no
|
|
73
|
+
* stored title) — callers then keep the response title as-is.
|
|
74
|
+
*/
|
|
75
|
+
export declare function getDefaultLocaleTitle(payload: Payload, localeAllDoc: Record<string, unknown> | null): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* Builds the connector-provided metadata the editor uses to badge
|
|
78
|
+
* untranslated CMS fields. Eligible keys are the localized text-typed fields
|
|
79
|
+
* of the collection config; translated keys per language are the fields whose
|
|
80
|
+
* locale-specific value exists (checked via one `locale: 'all'` fetch, or a
|
|
81
|
+
* caller-preloaded `localeAllDoc` to avoid a duplicate query).
|
|
82
|
+
*
|
|
83
|
+
* Returns null when the project has no Payload localization configured, so
|
|
84
|
+
* callers can attach `dynamic_data_meta` conditionally with zero change to
|
|
85
|
+
* the default response shape.
|
|
86
|
+
*/
|
|
87
|
+
export declare function buildDynamicDataMeta(payload: Payload, collectionSlug: string, docId?: string | number, localeAllDoc?: Record<string, unknown> | null): Promise<DynamicDataMeta | null>;
|
|
88
|
+
type CatalogPropDef = ComponentManifestProp & {
|
|
89
|
+
translatable?: boolean;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* componentName → group key → propKey → prop definition. Module prop values
|
|
93
|
+
* are stored group-keyed (`props[groupKey][propKey]`, mirroring the renderer's
|
|
94
|
+
* `spreadProps`), so the group level is preserved for lookups during the walk.
|
|
95
|
+
*/
|
|
96
|
+
export type TranslatablePropDefs = Map<string, Record<string, Record<string, CatalogPropDef>>>;
|
|
97
|
+
/**
|
|
98
|
+
* Loads component prop definitions from the nitrogen-component-catalog
|
|
99
|
+
* collection so translatable props can be identified precisely. Returns an
|
|
100
|
+
* empty map when the catalog is unavailable — the walk then falls back to the
|
|
101
|
+
* lang-map heuristic.
|
|
102
|
+
*/
|
|
103
|
+
export declare function loadTranslatablePropDefs(payload: Payload): Promise<TranslatablePropDefs>;
|
|
104
|
+
export interface ComputeTranslationStatusArgs {
|
|
105
|
+
payload: Payload;
|
|
106
|
+
collectionSlug: string;
|
|
107
|
+
docId: string | number;
|
|
108
|
+
nitrogenData: unknown;
|
|
109
|
+
localization: LocalizationSettings;
|
|
110
|
+
/** Preloaded catalog defs (avoids a re-fetch when batching). */
|
|
111
|
+
propDefs?: TranslatablePropDefs;
|
|
112
|
+
/** Preloaded `locale: 'all'` doc (avoids a re-fetch when batching). */
|
|
113
|
+
localeAllDoc?: Record<string, unknown> | null;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Computes per-language translation status for one page/template. Returns a
|
|
117
|
+
* `{ [langCode]: LanguageStatus }` map covering every configured non-default
|
|
118
|
+
* language, or null when there is nothing to compute.
|
|
119
|
+
*/
|
|
120
|
+
export declare function computeDocTranslationStatus(args: ComputeTranslationStatusArgs): Promise<Record<string, LanguageStatus> | null>;
|
|
121
|
+
export {};
|