@justanarthur/payload-plugin-translator 3.3.4 → 3.4.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.
- package/dist/index.js +3 -3
- package/dist/jobs.js +2 -2
- package/dist/rsc.js +2 -2
- package/dist/shared/{chunk-g3rrecgk.js → chunk-2vkxapbb.js} +38 -5
- package/dist/shared/{chunk-8hm2r1wg.js → chunk-9ky9w8k3.js} +40 -28
- package/dist/shared/{chunk-8hqdxnxy.js → chunk-m5jdmn2w.js} +43 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,15 +8,15 @@ import {
|
|
|
8
8
|
isTranslateMode,
|
|
9
9
|
createTranslateTask2,
|
|
10
10
|
createTranslateWorkflow2
|
|
11
|
-
} from "./shared/chunk-
|
|
12
|
-
import"./shared/chunk-
|
|
11
|
+
} from "./shared/chunk-2vkxapbb.js";
|
|
12
|
+
import"./shared/chunk-m5jdmn2w.js";
|
|
13
13
|
import {
|
|
14
14
|
readLocales,
|
|
15
15
|
parseEntityKey,
|
|
16
16
|
hasTranslateWorkflow,
|
|
17
17
|
queueBulkTranslation,
|
|
18
18
|
runQueuedTranslations
|
|
19
|
-
} from "./shared/chunk-
|
|
19
|
+
} from "./shared/chunk-9ky9w8k3.js";
|
|
20
20
|
import {
|
|
21
21
|
TRANSLATION_STATUS_SLUG2,
|
|
22
22
|
REVIEW_VIEW_PATH
|
package/dist/jobs.js
CHANGED
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
createAutoTranslateGlobalHook2,
|
|
4
4
|
createTranslateTask2,
|
|
5
5
|
createTranslateWorkflow2
|
|
6
|
-
} from "./shared/chunk-
|
|
6
|
+
} from "./shared/chunk-2vkxapbb.js";
|
|
7
7
|
import {
|
|
8
8
|
TRANSLATE_TASK_SLUG2,
|
|
9
9
|
TRANSLATE_WORKFLOW_SLUG2
|
|
10
|
-
} from "./shared/chunk-
|
|
10
|
+
} from "./shared/chunk-m5jdmn2w.js";
|
|
11
11
|
|
|
12
12
|
// src/exports/jobs.ts
|
|
13
13
|
var jobs = {
|
package/dist/rsc.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import"./shared/chunk-
|
|
1
|
+
import"./shared/chunk-m5jdmn2w.js";
|
|
2
2
|
import {
|
|
3
3
|
readLocales,
|
|
4
4
|
loadCollectionReview,
|
|
5
5
|
loadGlobalsReview,
|
|
6
6
|
loadEntityLocaleReview,
|
|
7
7
|
loadJobsProgress
|
|
8
|
-
} from "./shared/chunk-
|
|
8
|
+
} from "./shared/chunk-9ky9w8k3.js";
|
|
9
9
|
import {
|
|
10
10
|
REVIEW_VIEW_PATH
|
|
11
11
|
} from "./shared/chunk-31qgv1zk.js";
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadWrongLanguageCheck,
|
|
3
|
-
samePlaceholders,
|
|
4
3
|
plainText,
|
|
4
|
+
buildCrossLocaleTexts,
|
|
5
|
+
crossLocaleMatch,
|
|
5
6
|
traverseFields,
|
|
6
|
-
TRANSLATE_WORKFLOW_SLUG2,
|
|
7
7
|
collectTranslatableFields,
|
|
8
|
+
samePlaceholders,
|
|
9
|
+
TRANSLATE_WORKFLOW_SLUG2,
|
|
8
10
|
entityKey,
|
|
9
11
|
sourceHash
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-m5jdmn2w.js";
|
|
11
13
|
import {
|
|
12
14
|
TRANSLATION_STATUS_SLUG2
|
|
13
15
|
} from "./chunk-31qgv1zk.js";
|
|
@@ -102,6 +104,35 @@ var preview = (value) => {
|
|
|
102
104
|
return JSON.stringify(flat.length > 60 ? `${flat.slice(0, 57)}…` : flat);
|
|
103
105
|
};
|
|
104
106
|
var localeCodes = (req) => (req.payload.config.localization ? req.payload.config.localization.locales : []).map((each) => typeof each === "string" ? each : each.code);
|
|
107
|
+
var buildCrossLocaleTextsFor = async ({
|
|
108
|
+
config,
|
|
109
|
+
dataFrom,
|
|
110
|
+
req,
|
|
111
|
+
args
|
|
112
|
+
}) => {
|
|
113
|
+
const others = localeCodes(req).filter((each) => each !== args.locale && each !== args.localeFrom);
|
|
114
|
+
const fieldsPerLocale = [];
|
|
115
|
+
for (const locale of others) {
|
|
116
|
+
const { doc } = await findEntityWithConfig({
|
|
117
|
+
collectionSlug: args.collectionSlug,
|
|
118
|
+
globalSlug: args.globalSlug,
|
|
119
|
+
id: args.id,
|
|
120
|
+
locale,
|
|
121
|
+
overrideAccess: args.overrideAccess,
|
|
122
|
+
req
|
|
123
|
+
});
|
|
124
|
+
fieldsPerLocale.push({
|
|
125
|
+
locale,
|
|
126
|
+
fields: collectTranslatableFields({
|
|
127
|
+
config,
|
|
128
|
+
dataFrom,
|
|
129
|
+
dataTarget: doc,
|
|
130
|
+
options: req.payload.config.custom?.translator?._options
|
|
131
|
+
})
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return buildCrossLocaleTexts(fieldsPerLocale);
|
|
135
|
+
};
|
|
105
136
|
var translateOperation2 = async (args) => {
|
|
106
137
|
const req = "req" in args ? args.req : {
|
|
107
138
|
payload: args.payload
|
|
@@ -136,8 +167,10 @@ var translateOperation2 = async (args) => {
|
|
|
136
167
|
});
|
|
137
168
|
translatedData = doc;
|
|
138
169
|
}
|
|
139
|
-
const
|
|
140
|
-
const
|
|
170
|
+
const detectionOn = args.retranslateIdentical && req.payload.config.custom?.translator?.languageDetection !== false;
|
|
171
|
+
const wrongLanguageCheck = detectionOn ? await loadWrongLanguageCheck(localeCodes(req)) : null;
|
|
172
|
+
const crossLocale = detectionOn ? await buildCrossLocaleTextsFor({ config, dataFrom, req, args }) : undefined;
|
|
173
|
+
const isWrongLanguage = wrongLanguageCheck || crossLocale ? (target, path) => Boolean(wrongLanguageCheck?.(plainText(target), args.locale)) || Boolean(crossLocale && path && crossLocaleMatch(crossLocale, path, args.locale, plainText(target))) : undefined;
|
|
141
174
|
traverseFields({
|
|
142
175
|
dataFrom,
|
|
143
176
|
emptyOnly: args.emptyOnly,
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadWrongLanguageCheck,
|
|
3
|
-
samePlaceholders,
|
|
4
3
|
plainText,
|
|
4
|
+
buildCrossLocaleTexts,
|
|
5
|
+
crossLocaleMatch,
|
|
6
|
+
isOpaqueText,
|
|
5
7
|
looksUntranslated,
|
|
6
|
-
TRANSLATE_WORKFLOW_SLUG2,
|
|
7
8
|
collectTranslatableFields,
|
|
9
|
+
samePlaceholders,
|
|
10
|
+
TRANSLATE_WORKFLOW_SLUG2,
|
|
8
11
|
entityKey,
|
|
9
12
|
sourceHash
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-m5jdmn2w.js";
|
|
11
14
|
import {
|
|
12
15
|
TRANSLATION_STATUS_SLUG2
|
|
13
16
|
} from "./chunk-31qgv1zk.js";
|
|
@@ -23,15 +26,15 @@ var classify = (field, sourceText, targetText) => {
|
|
|
23
26
|
return "identical";
|
|
24
27
|
return "ok";
|
|
25
28
|
};
|
|
26
|
-
var computeStatus = (fields,
|
|
29
|
+
var computeStatus = (fields, options) => {
|
|
27
30
|
const statuses = [];
|
|
28
31
|
for (const field of fields) {
|
|
29
32
|
const sourceText = plainText(field.source);
|
|
30
33
|
if (!normalize(sourceText))
|
|
31
34
|
continue;
|
|
32
|
-
const targetText = plainText(field.target);
|
|
35
|
+
const targetText = normalize(plainText(field.target));
|
|
33
36
|
const state = classify(field, sourceText, targetText);
|
|
34
|
-
const detectedLanguage = state === "ok" && field.type !== "slug" &&
|
|
37
|
+
const detectedLanguage = state === "ok" && field.type !== "slug" && !isOpaqueText(targetText) ? options?.check?.(targetText, options.locale) ?? (options?.crossLocale ? crossLocaleMatch(options.crossLocale, field.path, options.locale, targetText) : undefined) : undefined;
|
|
35
38
|
statuses.push({
|
|
36
39
|
...field,
|
|
37
40
|
sourceText,
|
|
@@ -80,19 +83,27 @@ var loadStatusRows = async (req, keys) => {
|
|
|
80
83
|
return docs;
|
|
81
84
|
};
|
|
82
85
|
var loadLanguageCheck = (req) => pluginOptions(req)?.languageDetection === false ? Promise.resolve(null) : loadWrongLanguageCheck(readLocales(req).targetLocales.concat(readLocales(req).defaultLocale));
|
|
83
|
-
var
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
86
|
+
var reviewEntity = (config, source, targetByLocale, targetLocales, hash, rowByLocale, req, check) => {
|
|
87
|
+
const options = pluginOptions(req)?._options;
|
|
88
|
+
const fieldsPerLocale = targetLocales.map((locale) => ({
|
|
89
|
+
locale,
|
|
90
|
+
fields: collectTranslatableFields({
|
|
91
|
+
config,
|
|
92
|
+
dataFrom: source,
|
|
93
|
+
dataTarget: targetByLocale[locale] ?? {},
|
|
94
|
+
options
|
|
95
|
+
})
|
|
96
|
+
}));
|
|
97
|
+
const crossLocale = buildCrossLocaleTexts(fieldsPerLocale);
|
|
98
|
+
return Object.fromEntries(fieldsPerLocale.map(({ locale, fields }) => {
|
|
99
|
+
const row = rowByLocale(locale);
|
|
100
|
+
const { fields: _fields, ...review } = {
|
|
101
|
+
...computeStatus(fields, { locale, check, crossLocale }),
|
|
102
|
+
stale: Boolean(row?.sourceHash) && row?.sourceHash !== hash,
|
|
103
|
+
reviewed: Boolean(row?.reviewedHash) && row?.reviewedHash === hash
|
|
104
|
+
};
|
|
105
|
+
return [locale, review];
|
|
106
|
+
}));
|
|
96
107
|
};
|
|
97
108
|
var labelOf = (config, doc) => {
|
|
98
109
|
const title = doc[config.admin?.useAsTitle ?? "id"];
|
|
@@ -143,10 +154,7 @@ var loadCollectionReview = async ({ req, collectionSlug, page = 1, limit = 25 })
|
|
|
143
154
|
label: labelOf(config, source),
|
|
144
155
|
collectionSlug,
|
|
145
156
|
id: source.id,
|
|
146
|
-
locales: Object.fromEntries(targetLocales.map((locale) =>
|
|
147
|
-
const { fields: _fields, ...review } = reviewLocale(config, source, targets[locale].get(String(source.id)), hash, rows.find((row) => row.entity === key && row.locale === locale), req, locale, check);
|
|
148
|
-
return [locale, review];
|
|
149
|
-
}))
|
|
157
|
+
locales: reviewEntity(config, source, Object.fromEntries(targetLocales.map((locale) => [locale, targets[locale].get(String(source.id))])), targetLocales, hash, (locale) => rows.find((row) => row.entity === key && row.locale === locale), req, check)
|
|
150
158
|
};
|
|
151
159
|
});
|
|
152
160
|
return {
|
|
@@ -177,11 +185,11 @@ var loadGlobalsReview = async ({ req, globalSlugs }) => {
|
|
|
177
185
|
const source = await read(defaultLocale);
|
|
178
186
|
const hash = hashOf(config, source, req);
|
|
179
187
|
const key = entityKey({ globalSlug });
|
|
180
|
-
const
|
|
188
|
+
const targetByLocale = {};
|
|
181
189
|
for (const locale of targetLocales) {
|
|
182
|
-
|
|
183
|
-
locales[locale] = review;
|
|
190
|
+
targetByLocale[locale] = await read(locale);
|
|
184
191
|
}
|
|
192
|
+
const locales = reviewEntity(config, source, targetByLocale, targetLocales, hash, (locale) => rows.find((row) => row.entity === key && row.locale === locale), req, check);
|
|
185
193
|
entities.push({ key, label: globalLabel(config), globalSlug, locales });
|
|
186
194
|
}
|
|
187
195
|
return { entities, locales: targetLocales };
|
|
@@ -199,10 +207,14 @@ var loadEntityLocaleReview = async ({ req, entity, locale }) => {
|
|
|
199
207
|
throw new Error(`unknown entity ${entity}`);
|
|
200
208
|
const read = (readLocale) => globalSlug ? req.payload.findGlobal({ slug: globalSlug, locale: readLocale, fallbackLocale: false, depth: 0, overrideAccess: false, req }) : req.payload.findByID({ collection: collectionSlug, id, locale: readLocale, fallbackLocale: false, depth: 0, overrideAccess: false, req });
|
|
201
209
|
const source = await read(defaultLocale);
|
|
202
|
-
const target = await read(locale);
|
|
203
210
|
const rows = await loadStatusRows(req, [entity]);
|
|
204
211
|
const row = rows.find((each) => each.locale === locale);
|
|
205
|
-
const
|
|
212
|
+
const { targetLocales } = readLocales(req);
|
|
213
|
+
const targetByLocale = {};
|
|
214
|
+
for (const each of targetLocales) {
|
|
215
|
+
targetByLocale[each] = await read(each);
|
|
216
|
+
}
|
|
217
|
+
const review = reviewEntity(config, source, targetByLocale, targetLocales, hashOf(config, source, req), (each) => rows.find((row2) => row2.locale === each), req, await loadLanguageCheck(req))[locale];
|
|
206
218
|
return {
|
|
207
219
|
...review,
|
|
208
220
|
entity,
|
|
@@ -20,9 +20,10 @@ var looksLikeList = (text) => {
|
|
|
20
20
|
const lengths = lines.map((line) => line.length).sort((a, b) => a - b);
|
|
21
21
|
return lengths[Math.floor(lengths.length / 2)] < MIN_LENGTH;
|
|
22
22
|
};
|
|
23
|
-
var
|
|
23
|
+
var eldModule;
|
|
24
24
|
var loadWrongLanguageCheck = (locales) => {
|
|
25
|
-
|
|
25
|
+
eldModule ??= import("eld/small");
|
|
26
|
+
return eldModule.then(({ eld }) => {
|
|
26
27
|
const languages = [...new Set(locales.map(base))];
|
|
27
28
|
eld.setLanguageSubset(languages);
|
|
28
29
|
return (text, expectedLocale) => {
|
|
@@ -41,13 +42,8 @@ var loadWrongLanguageCheck = (locales) => {
|
|
|
41
42
|
return lead >= margin ? result.language : null;
|
|
42
43
|
};
|
|
43
44
|
}).catch(() => null);
|
|
44
|
-
return detector;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
// src/utils/placeholders.ts
|
|
48
|
-
var placeholdersOf = (value) => (value.match(/\{\s*[\w.]+\s*(?:,[^{}]*)?\}/g) ?? []).map((each) => each.replace(/\s+/g, "")).sort();
|
|
49
|
-
var samePlaceholders = (source, translated) => placeholdersOf(source).join("|") === placeholdersOf(translated).join("|");
|
|
50
|
-
|
|
51
47
|
// src/utils/plainText.ts
|
|
52
48
|
var plainText = (value) => {
|
|
53
49
|
if (typeof value === "string")
|
|
@@ -67,6 +63,34 @@ var plainText = (value) => {
|
|
|
67
63
|
return "";
|
|
68
64
|
};
|
|
69
65
|
|
|
66
|
+
// src/utils/crossLocale.ts
|
|
67
|
+
var normalize = (text) => text.replace(/\s+/g, " ").trim();
|
|
68
|
+
var buildCrossLocaleTexts = (fieldsPerLocale) => {
|
|
69
|
+
const texts = new Map;
|
|
70
|
+
for (const { locale, fields } of fieldsPerLocale) {
|
|
71
|
+
for (const field of fields) {
|
|
72
|
+
const value = normalize(plainText(field.target));
|
|
73
|
+
if (!value || value === normalize(plainText(field.source)))
|
|
74
|
+
continue;
|
|
75
|
+
const byLocale = texts.get(field.path) ?? new Map;
|
|
76
|
+
byLocale.set(locale, value);
|
|
77
|
+
texts.set(field.path, byLocale);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return texts;
|
|
81
|
+
};
|
|
82
|
+
var crossLocaleMatch = (texts, path, locale, text) => {
|
|
83
|
+
const value = normalize(text);
|
|
84
|
+
if (!value)
|
|
85
|
+
return;
|
|
86
|
+
for (const [other, otherValue] of texts.get(path) ?? []) {
|
|
87
|
+
if (other === locale || otherValue !== value || CLOSE_PAIRS.has(`${locale}|${other}`))
|
|
88
|
+
continue;
|
|
89
|
+
return other;
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
};
|
|
93
|
+
|
|
70
94
|
// src/translate/traverseFields.ts
|
|
71
95
|
import ObjectID from "bson-objectid";
|
|
72
96
|
import { tabHasName } from "payload/shared";
|
|
@@ -112,10 +136,10 @@ var isOpaqueText = (value) => {
|
|
|
112
136
|
};
|
|
113
137
|
|
|
114
138
|
// src/utils/looksUntranslated.ts
|
|
115
|
-
var
|
|
139
|
+
var normalize2 = (text) => text.replace(/\s+/g, " ").trim();
|
|
116
140
|
var looksUntranslated = (source, target) => {
|
|
117
|
-
const sourceText =
|
|
118
|
-
return sourceText.length > 3 && /\s/.test(sourceText) && !isOpaqueText(sourceText) && sourceText ===
|
|
141
|
+
const sourceText = normalize2(plainText(source));
|
|
142
|
+
return sourceText.length > 3 && /\s/.test(sourceText) && !isOpaqueText(sourceText) && sourceText === normalize2(plainText(target));
|
|
119
143
|
};
|
|
120
144
|
|
|
121
145
|
// src/utils/sanitizeSlug.ts
|
|
@@ -182,7 +206,7 @@ var traverseFields = (args) => {
|
|
|
182
206
|
const { additionalTraverseRichText } = _options ?? {};
|
|
183
207
|
const siblingDataFrom = args.siblingDataFrom ?? dataFrom;
|
|
184
208
|
const siblingDataTranslated = args.siblingDataTranslated ?? translatedData;
|
|
185
|
-
const keepsTarget = (source, target) => Boolean(emptyOnly) && hasText(target) && !(retranslateIdentical && (looksUntranslated(source, target) || Boolean(isWrongLanguage?.(target))));
|
|
209
|
+
const keepsTarget = (source, target, path) => Boolean(emptyOnly) && hasText(target) && !(retranslateIdentical && (looksUntranslated(source, target) || Boolean(isWrongLanguage?.(target, path))));
|
|
186
210
|
const recurse = (overrides) => traverseFields({ ...args, siblingDataFrom, siblingDataTranslated, ...overrides });
|
|
187
211
|
for (const field of fields) {
|
|
188
212
|
switch (field.type) {
|
|
@@ -307,7 +331,7 @@ var traverseFields = (args) => {
|
|
|
307
331
|
onField?.({ path: keyPath, type: "json", source: value, target: previous });
|
|
308
332
|
if (!value.trim())
|
|
309
333
|
continue;
|
|
310
|
-
if (keepsTarget(value, previous))
|
|
334
|
+
if (keepsTarget(value, previous, keyPath))
|
|
311
335
|
continue;
|
|
312
336
|
target[key] = value;
|
|
313
337
|
if (isOpaqueText(value))
|
|
@@ -347,7 +371,7 @@ var traverseFields = (args) => {
|
|
|
347
371
|
if (typeof value !== "string" || !value.trim())
|
|
348
372
|
break;
|
|
349
373
|
const hasCurrent = typeof current === "string" && current.trim().length > 0;
|
|
350
|
-
if (isSlug ? hasCurrent : keepsTarget(value, current))
|
|
374
|
+
if (isSlug ? hasCurrent : keepsTarget(value, current, fieldPath))
|
|
351
375
|
break;
|
|
352
376
|
if (isOpaqueText(value)) {
|
|
353
377
|
siblingDataTranslated[field.name] = value;
|
|
@@ -389,7 +413,7 @@ var traverseFields = (args) => {
|
|
|
389
413
|
}
|
|
390
414
|
break;
|
|
391
415
|
}
|
|
392
|
-
if (keepsTarget(richTextDataFrom, current))
|
|
416
|
+
if (keepsTarget(richTextDataFrom, current, richTextPath))
|
|
393
417
|
break;
|
|
394
418
|
const isSlate = Array.isArray(richTextDataFrom);
|
|
395
419
|
const isLexical = typeof richTextDataFrom === "object" && "root" in richTextDataFrom;
|
|
@@ -435,6 +459,10 @@ var collectTranslatableFields = ({ config, dataFrom, dataTarget, options }) => {
|
|
|
435
459
|
return fields;
|
|
436
460
|
};
|
|
437
461
|
|
|
462
|
+
// src/utils/placeholders.ts
|
|
463
|
+
var placeholdersOf = (value) => (value.match(/\{\s*[\w.]+\s*(?:,[^{}]*)?\}/g) ?? []).map((each) => each.replace(/\s+/g, "")).sort();
|
|
464
|
+
var samePlaceholders = (source, translated) => placeholdersOf(source).join("|") === placeholdersOf(translated).join("|");
|
|
465
|
+
|
|
438
466
|
// src/review/entityKey.ts
|
|
439
467
|
var entityKey = ({ collectionSlug, globalSlug, id }) => globalSlug ? `global:${globalSlug}` : `${collectionSlug}:${id}`;
|
|
440
468
|
|
|
@@ -442,4 +470,4 @@ var entityKey = ({ collectionSlug, globalSlug, id }) => globalSlug ? `global:${g
|
|
|
442
470
|
import { createHash } from "node:crypto";
|
|
443
471
|
var sourceHash = (fields) => createHash("sha1").update(JSON.stringify(fields.map(({ path, source }) => [path, source]))).digest("hex");
|
|
444
472
|
|
|
445
|
-
export { loadWrongLanguageCheck,
|
|
473
|
+
export { loadWrongLanguageCheck, plainText, buildCrossLocaleTexts, crossLocaleMatch, isOpaqueText, looksUntranslated, traverseFields, collectTranslatableFields, samePlaceholders, TRANSLATE_TASK_SLUG2, TRANSLATE_WORKFLOW_SLUG2, entityKey, sourceHash };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justanarthur/payload-plugin-translator",
|
|
3
3
|
"description": "Translator plugin for Payload CMS — automatic localization via Google, OpenAI, LibreTranslate, or custom resolvers.",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.4.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"files": [
|