@blinkk/root-cms 1.4.3 → 1.4.5

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/core.js CHANGED
@@ -6,10 +6,361 @@ var __export = (target, all) => {
6
6
 
7
7
  // core/client.ts
8
8
  import crypto from "node:crypto";
9
+ import {
10
+ FieldValue as FieldValue2,
11
+ Timestamp as Timestamp2
12
+ } from "firebase-admin/firestore";
13
+
14
+ // core/translations-manager.ts
9
15
  import {
10
16
  FieldValue,
11
17
  Timestamp
12
18
  } from "firebase-admin/firestore";
19
+
20
+ // shared/strings.ts
21
+ import fnv from "fnv-plus";
22
+ function normalizeStr(str) {
23
+ const lines = str.trim().split("\n").map((line) => removeTrailingWhitespace(line));
24
+ return lines.join("\n");
25
+ }
26
+ function removeTrailingWhitespace(str) {
27
+ return str.trimEnd().replace(/ $/, "");
28
+ }
29
+ function hashStr(str) {
30
+ if (!str || typeof str !== "string") {
31
+ throw new Error("input string is invalid");
32
+ }
33
+ return fnv.fast1a52hex(normalizeStr(str));
34
+ }
35
+
36
+ // core/translations-manager.ts
37
+ var TRANSLATIONS_DB_PATH_FORMAT = "/Projects/{project}/TranslationsManager/{mode}/Translations";
38
+ var TRANSLATIONS_LOCALE_DOC_DB_PATH_FORMAT = `${TRANSLATIONS_DB_PATH_FORMAT}/{id}:{locale}`;
39
+ var TranslationsManager = class {
40
+ constructor(cmsClient) {
41
+ this.cmsClient = cmsClient;
42
+ }
43
+ /**
44
+ * Saves draft translations for a translations doc id.
45
+ *
46
+ * Example:
47
+ * ```
48
+ * const strings = {
49
+ * 'one': {es: 'uno', fr: 'un'},
50
+ * 'two': {es: 'dos', fr: 'deux'},
51
+ * };
52
+ * await tm.saveTranslations('Pages/index', strings);
53
+ * ```
54
+ */
55
+ async saveTranslations(id, strings, options) {
56
+ const mode = "draft";
57
+ const localesSet = /* @__PURE__ */ new Set();
58
+ Object.values(strings).forEach((entry) => {
59
+ Object.keys(entry).forEach((locale) => {
60
+ if (locale !== "source") {
61
+ localesSet.add(locale);
62
+ }
63
+ });
64
+ });
65
+ const batch = this.cmsClient.db.batch();
66
+ const locales = Array.from(localesSet);
67
+ locales.forEach((locale) => {
68
+ const updates = {
69
+ id,
70
+ locale,
71
+ sys: {
72
+ modifiedAt: Timestamp.now(),
73
+ modifiedBy: (options == null ? void 0 : options.modifiedBy) || "root-cms-client"
74
+ },
75
+ strings: {}
76
+ };
77
+ if ((options == null ? void 0 : options.tags) && options.tags.length > 0) {
78
+ updates.tags = FieldValue.arrayUnion(...options.tags);
79
+ }
80
+ let numUpdates = 0;
81
+ const hashMap = this.toLocaleDocHashMap(strings, locale);
82
+ Object.entries(hashMap).forEach(([hash, translations]) => {
83
+ Object.entries(translations).forEach(([locale2, translation]) => {
84
+ if (translation) {
85
+ updates.strings[hash] ??= {};
86
+ updates.strings[hash][locale2] = translation;
87
+ numUpdates += 1;
88
+ }
89
+ });
90
+ });
91
+ if (numUpdates > 0) {
92
+ const localeDocPath = buildTranslationsLocaleDocDbPath({
93
+ project: this.cmsClient.projectId,
94
+ mode,
95
+ id,
96
+ locale
97
+ });
98
+ const localeDocRef = this.cmsClient.db.doc(localeDocPath);
99
+ batch.set(localeDocRef, updates, { merge: true });
100
+ }
101
+ });
102
+ await batch.commit();
103
+ }
104
+ /**
105
+ * Publishes a translations doc.
106
+ */
107
+ async publishTranslations(id, options) {
108
+ const db = this.cmsClient.db;
109
+ const project = this.cmsClient.projectId;
110
+ const draftPath = buildTranslationsDbPath({ project, mode: "draft" });
111
+ const query = db.collection(draftPath).where("id", "==", id);
112
+ const res = await query.get();
113
+ if (res.size === 0) {
114
+ console.warn(`no translations to publish for ${id}`);
115
+ return;
116
+ }
117
+ const batch = (options == null ? void 0 : options.batch) || db.batch();
118
+ res.docs.forEach((doc) => {
119
+ const translationsLocaleDoc = doc.data();
120
+ const sys = {
121
+ ...translationsLocaleDoc.sys,
122
+ publishedAt: Timestamp.now(),
123
+ publishedBy: (options == null ? void 0 : options.publishedBy) || "root-cms-client"
124
+ };
125
+ batch.update(doc.ref, { sys });
126
+ const publishedDocPath = buildTranslationsLocaleDocDbPath({
127
+ project,
128
+ mode: "published",
129
+ id: translationsLocaleDoc.id,
130
+ locale: translationsLocaleDoc.locale
131
+ });
132
+ const publishedDocRef = db.doc(publishedDocPath);
133
+ batch.set(publishedDocRef, { ...translationsLocaleDoc, sys });
134
+ });
135
+ const shouldCommitBatch = !(options == null ? void 0 : options.batch);
136
+ if (shouldCommitBatch) {
137
+ await batch.commit();
138
+ }
139
+ }
140
+ /**
141
+ * Fetches translations from one or more translations docs in the translations
142
+ * manager.
143
+ *
144
+ * Example:
145
+ * ```
146
+ * await tm.loadTranslations();
147
+ * // =>
148
+ * // {
149
+ * // "one": {"es": "uno", "fr": "un"},
150
+ * // "two": {"es": "dos", "fr": "deux"}
151
+ * // }
152
+ * ```
153
+ *
154
+ * To load a specific set of translations docs by id:
155
+ * ```
156
+ * const translationsToLoad = ['Global/strings', 'Global/header', 'Global/footer', 'Pages/index'];
157
+ * await tm.loadTranslations({ids: translationsToLoad});
158
+ * // =>
159
+ * // {
160
+ * // "one": {"es": "uno", "fr": "un"},
161
+ * // "two": {"es": "dos", "fr": "deux"}
162
+ * // }
163
+ * ```
164
+ *
165
+ * To load a subset of locales (more performant):
166
+ * ```
167
+ * await tm.loadTranslations({locales: ['es']});
168
+ * // =>
169
+ * // {
170
+ * // "one": {"es": "uno"},
171
+ * // "two": {"es": "dos"}
172
+ * // }
173
+ * ```
174
+ */
175
+ async loadTranslations(options) {
176
+ const mode = (options == null ? void 0 : options.mode) || "published";
177
+ const dbPath = buildTranslationsDbPath({
178
+ project: this.cmsClient.projectId,
179
+ mode
180
+ });
181
+ let query = this.cmsClient.db.collection(dbPath);
182
+ if ((options == null ? void 0 : options.ids) && options.ids.length > 0) {
183
+ query = query.where("id", "in", options.ids);
184
+ }
185
+ if ((options == null ? void 0 : options.tags) && options.tags.length > 0) {
186
+ query = query.where("tags", "array-contains", options.tags);
187
+ }
188
+ if ((options == null ? void 0 : options.locales) && options.locales.length > 0) {
189
+ query = query.where("locale", "in", options.locales);
190
+ }
191
+ const results = await query.get();
192
+ const strings = {};
193
+ results.forEach((result) => {
194
+ const localeDoc = result.data();
195
+ Object.values(localeDoc.strings || {}).forEach((item) => {
196
+ strings[item.source] ??= { source: item.source };
197
+ strings[item.source][localeDoc.locale] = item.translation;
198
+ });
199
+ });
200
+ return strings;
201
+ }
202
+ /**
203
+ * Fetches translations for a given locale, with optional fallbacks.
204
+ * The return value is a map of source string to translated string.
205
+ *
206
+ * Example:
207
+ * ```
208
+ * await translationsDoc.loadTranslationsForLocale('es');
209
+ * // =>
210
+ * // {
211
+ * // "one": "uno",
212
+ * // "two": "dos",
213
+ * // }
214
+ * ```
215
+ */
216
+ async loadTranslationsForLocale(locale, options) {
217
+ const localeSet = /* @__PURE__ */ new Set([
218
+ locale,
219
+ ...(options == null ? void 0 : options.fallbackLocales) || []
220
+ ]);
221
+ const fallbackLocales = Array.from(localeSet);
222
+ const multiLocaleStrings = await this.loadTranslations({
223
+ mode: options == null ? void 0 : options.mode,
224
+ locales: fallbackLocales
225
+ });
226
+ return this.toSingleLocaleMap(multiLocaleStrings, fallbackLocales);
227
+ }
228
+ /**
229
+ * Converts a multi-locale translations map to a flat single-locale map,
230
+ * with optional support for fallback locales.
231
+ *
232
+ * ```
233
+ * const multiLocaleStrings = {
234
+ * 'one': {es: 'uno', fr: 'un'},
235
+ * 'two': {es: 'dos', fr: 'deux'}
236
+ * };
237
+ * translationsDoc.toSingleLocaleMap(multiLocaleStrings, ['es']);
238
+ * // =>
239
+ * // {
240
+ * // "one": "uno",
241
+ * // "two": "dos",
242
+ * // }
243
+ * ```
244
+ */
245
+ toSingleLocaleMap(multiLocaleStrings, fallbackLocales) {
246
+ const singleLocaleStrings = {};
247
+ Object.entries(multiLocaleStrings).forEach(([source, translations]) => {
248
+ let translation = source;
249
+ for (const locale of fallbackLocales) {
250
+ if (translations[locale]) {
251
+ translation = translations[locale];
252
+ break;
253
+ }
254
+ }
255
+ singleLocaleStrings[source] = translation;
256
+ });
257
+ return singleLocaleStrings;
258
+ }
259
+ /**
260
+ * Converts a multi-locale translations map to a single-locale hashed version,
261
+ * used for storage in in the DB.
262
+ *
263
+ * ```
264
+ * const multiLocaleStrings = {
265
+ * 'one': {es: 'uno', fr: 'un'},
266
+ * 'two': {es: 'dos', fr: 'deux'}
267
+ * };
268
+ * translationsDoc.toLocaleDocHashMap(multiLocaleStrings, 'es');
269
+ * // =>
270
+ * // {
271
+ * // "<hash1>": {"source": "one", "translation": "uno"},
272
+ * // "<hash2>": {"source": "two", "translation": "dos"},
273
+ * // }
274
+ * ```
275
+ *
276
+ * One reason for using hashes is because the DB has limits on the number of
277
+ * chars that can be used as the "key" in a object map.
278
+ */
279
+ toLocaleDocHashMap(multiLocaleStrings, locale) {
280
+ const hashMap = {};
281
+ Object.entries(multiLocaleStrings).forEach(([source, translations]) => {
282
+ const translation = translations[locale];
283
+ if (translation) {
284
+ const hash = hashStr(source);
285
+ hashMap[hash] = { source, translation };
286
+ }
287
+ });
288
+ return hashMap;
289
+ }
290
+ /**
291
+ * Import translations from the v1 system to the TranslationsManager.
292
+ */
293
+ async importTranslationsFromV1() {
294
+ var _a;
295
+ const projectId = this.cmsClient.projectId;
296
+ const db = this.cmsClient.db;
297
+ const dbPath = `Projects/${projectId}/Translations`;
298
+ const query = db.collection(dbPath);
299
+ const querySnapshot = await query.get();
300
+ if (querySnapshot.size === 0) {
301
+ return;
302
+ }
303
+ console.log(
304
+ "[root cms] importing v1 Translations to v2 TranslationsManager"
305
+ );
306
+ const translationsDocs = {};
307
+ querySnapshot.forEach((doc) => {
308
+ const translation = doc.data();
309
+ const source = this.cmsClient.normalizeString(translation.source);
310
+ delete translation.source;
311
+ const tags = translation.tags || [];
312
+ delete translation.tags;
313
+ for (const tag of tags) {
314
+ if (tag.includes("/")) {
315
+ const translationsId = tag;
316
+ translationsDocs[translationsId] ??= {
317
+ id: translationsId,
318
+ tags,
319
+ strings: {}
320
+ };
321
+ translationsDocs[translationsId].strings[source] = translation;
322
+ }
323
+ }
324
+ });
325
+ if (Object.keys(translationsDocs).length === 0) {
326
+ console.log("[root cms] no v1 translations to save");
327
+ return;
328
+ }
329
+ for (const docId in translationsDocs) {
330
+ const [collection2, slug] = docId.split("/");
331
+ if (collection2 && slug) {
332
+ const doc = await this.cmsClient.getDoc(collection2, slug, {
333
+ mode: "draft"
334
+ });
335
+ const linkedSheet = (_a = doc == null ? void 0 : doc.sys) == null ? void 0 : _a.l10nSheet;
336
+ if (linkedSheet) {
337
+ translationsDocs[docId].sys.linkedSheet = linkedSheet;
338
+ }
339
+ }
340
+ }
341
+ Object.entries(translationsDocs).forEach(([translationsId, data]) => {
342
+ const len = Object.keys(data.strings).length;
343
+ console.log(`[root cms] saving ${len} string(s) to ${translationsId}...`);
344
+ this.saveTranslations(translationsId, data.strings, {
345
+ tags: data.tags || [translationsId]
346
+ });
347
+ });
348
+ }
349
+ };
350
+ function buildTranslationsDbPath(options) {
351
+ return TRANSLATIONS_DB_PATH_FORMAT.replace(
352
+ "{project}",
353
+ options.project
354
+ ).replace("{mode}", options.mode);
355
+ }
356
+ function buildTranslationsLocaleDocDbPath(options) {
357
+ return TRANSLATIONS_LOCALE_DOC_DB_PATH_FORMAT.replace(
358
+ "{project}",
359
+ options.project
360
+ ).replace("{mode}", options.mode).replace("{id}", options.id.replaceAll("/", "--")).replace("{locale}", options.locale);
361
+ }
362
+
363
+ // core/client.ts
13
364
  var RootCMSClient = class {
14
365
  constructor(rootConfig) {
15
366
  this.rootConfig = rootConfig;
@@ -63,9 +414,9 @@ var RootCMSClient = class {
63
414
  slug,
64
415
  sys: {
65
416
  ...draftSys,
66
- createdAt: draftSys.createdAt ?? Timestamp.now(),
417
+ createdAt: draftSys.createdAt ?? Timestamp2.now(),
67
418
  createdBy: draftSys.createdBy ?? modifiedBy,
68
- modifiedAt: Timestamp.now(),
419
+ modifiedAt: Timestamp2.now(),
69
420
  modifiedBy,
70
421
  locales: (options == null ? void 0 : options.locales) ?? draftSys.locales ?? ["en"]
71
422
  },
@@ -173,7 +524,7 @@ var RootCMSClient = class {
173
524
  const publishedRef = this.db.doc(
174
525
  `${projectCollectionsPath}/${collection2}/Published/${slug}`
175
526
  );
176
- const firstPublishedAt = sys.firstPublishedAt ?? FieldValue.serverTimestamp();
527
+ const firstPublishedAt = sys.firstPublishedAt ?? FieldValue2.serverTimestamp();
177
528
  const firstPublishedBy = sys.firstPublishedBy ?? publishedBy;
178
529
  batch.set(publishedRef, {
179
530
  id,
@@ -184,7 +535,7 @@ var RootCMSClient = class {
184
535
  ...sys,
185
536
  firstPublishedAt,
186
537
  firstPublishedBy,
187
- publishedAt: FieldValue.serverTimestamp(),
538
+ publishedAt: FieldValue2.serverTimestamp(),
188
539
  publishedBy
189
540
  }
190
541
  });
@@ -192,11 +543,11 @@ var RootCMSClient = class {
192
543
  batch.delete(scheduledRef);
193
544
  batchCount += 1;
194
545
  batch.update(draftRef, {
195
- "sys.scheduledAt": FieldValue.delete(),
196
- "sys.scheduledBy": FieldValue.delete(),
546
+ "sys.scheduledAt": FieldValue2.delete(),
547
+ "sys.scheduledBy": FieldValue2.delete(),
197
548
  "sys.firstPublishedAt": firstPublishedAt,
198
549
  "sys.firstPublishedBy": firstPublishedBy,
199
- "sys.publishedAt": FieldValue.serverTimestamp(),
550
+ "sys.publishedAt": FieldValue2.serverTimestamp(),
200
551
  "sys.publishedBy": publishedBy
201
552
  });
202
553
  batchCount += 1;
@@ -270,7 +621,7 @@ var RootCMSClient = class {
270
621
  ...sys,
271
622
  firstPublishedAt,
272
623
  firstPublishedBy,
273
- publishedAt: FieldValue.serverTimestamp(),
624
+ publishedAt: FieldValue2.serverTimestamp(),
274
625
  publishedBy: scheduledBy || ""
275
626
  }
276
627
  });
@@ -278,11 +629,11 @@ var RootCMSClient = class {
278
629
  batch.delete(scheduledRef);
279
630
  batchCount += 1;
280
631
  batch.update(draftRef, {
281
- "sys.scheduledAt": FieldValue.delete(),
282
- "sys.scheduledBy": FieldValue.delete(),
632
+ "sys.scheduledAt": FieldValue2.delete(),
633
+ "sys.scheduledBy": FieldValue2.delete(),
283
634
  "sys.firstPublishedAt": firstPublishedAt,
284
635
  "sys.firstPublishedBy": firstPublishedBy,
285
- "sys.publishedAt": FieldValue.serverTimestamp(),
636
+ "sys.publishedAt": FieldValue2.serverTimestamp(),
286
637
  "sys.publishedBy": scheduledBy || "root-cms-client"
287
638
  });
288
639
  batchCount += 1;
@@ -305,17 +656,17 @@ var RootCMSClient = class {
305
656
  async publishScheduledReleases() {
306
657
  const releasesPath = `Projects/${this.projectId}/Releases`;
307
658
  const now = Math.ceil((/* @__PURE__ */ new Date()).getTime());
308
- const query = this.db.collection(releasesPath).where("scheduledAt", "<=", Timestamp.fromMillis(now));
659
+ const query = this.db.collection(releasesPath).where("scheduledAt", "<=", Timestamp2.fromMillis(now));
309
660
  const querySnapshot = await query.get();
310
661
  for (const snapshot of querySnapshot.docs) {
311
662
  const release = snapshot.data();
312
663
  const batch = this.db.batch();
313
664
  const publishedBy = release.scheduledBy || "root-cms-client";
314
665
  batch.update(snapshot.ref, {
315
- publishedAt: Timestamp.now(),
666
+ publishedAt: Timestamp2.now(),
316
667
  publishedBy,
317
- scheduledAt: FieldValue.delete(),
318
- scheduledBy: FieldValue.delete()
668
+ scheduledAt: FieldValue2.delete(),
669
+ scheduledBy: FieldValue2.delete()
319
670
  });
320
671
  await this.publishDocs(release.docIds || [], { publishedBy, batch });
321
672
  }
@@ -327,7 +678,7 @@ var RootCMSClient = class {
327
678
  var _a;
328
679
  if ((_a = doc.sys) == null ? void 0 : _a.publishingLocked) {
329
680
  if (doc.sys.publishingLocked.until) {
330
- const now = Timestamp.now().toMillis();
681
+ const now = Timestamp2.now().toMillis();
331
682
  const until = doc.sys.publishingLocked.until.toMillis();
332
683
  return now < until;
333
684
  }
@@ -335,6 +686,30 @@ var RootCMSClient = class {
335
686
  }
336
687
  return false;
337
688
  }
689
+ /**
690
+ * Returns a `TranslationsManager` object for managing translations.
691
+ *
692
+ * To get translations:
693
+ * ```
694
+ * await tm.loadTranslations({
695
+ * ids: ['Global/strings', 'Pages/index'],
696
+ * locales: ['es'],
697
+ * });
698
+ * ```
699
+ *
700
+ * NOTE: The `TranslationsManager` is a v2 feature that will eventually
701
+ * replace the v1 translations system.
702
+ */
703
+ getTranslationsManager() {
704
+ var _a;
705
+ const cmsPluginOptions = this.cmsPlugin.getConfig();
706
+ if ((_a = cmsPluginOptions.experiments) == null ? void 0 : _a.v2TranslationsManager) {
707
+ throw new Error(
708
+ "`v2TranslationsManager` is not enabled. update root.config.ts and add: `{experiments: {v2TranslationsManager: true}}`"
709
+ );
710
+ }
711
+ return new TranslationsManager(this);
712
+ }
338
713
  /**
339
714
  * Loads translations saved in the translations collection, optionally
340
715
  * filtered by tag.
@@ -380,7 +755,7 @@ var RootCMSClient = class {
380
755
  source: this.normalizeString(source)
381
756
  };
382
757
  if (tags) {
383
- data.tags = tags;
758
+ data.tags = FieldValue2.arrayUnion(...tags);
384
759
  }
385
760
  batch.set(translationRef, data, { merge: true });
386
761
  batchCount += 1;
@@ -451,7 +826,7 @@ var RootCMSClient = class {
451
826
  const syncedBy = (options == null ? void 0 : options.syncedBy) || "root-cms-client";
452
827
  const updatedDataSource = {
453
828
  ...dataSource,
454
- syncedAt: Timestamp.now(),
829
+ syncedAt: Timestamp2.now(),
455
830
  syncedBy
456
831
  };
457
832
  const batch = this.db.batch();
@@ -460,7 +835,7 @@ var RootCMSClient = class {
460
835
  data
461
836
  });
462
837
  batch.update(dataSourceDocRef, {
463
- syncedAt: Timestamp.now(),
838
+ syncedAt: Timestamp2.now(),
464
839
  syncedBy
465
840
  });
466
841
  await batch.commit();
@@ -485,7 +860,7 @@ var RootCMSClient = class {
485
860
  const publishedBy = (options == null ? void 0 : options.publishedBy) || "root-cms-client";
486
861
  const updatedDataSource = {
487
862
  ...dataSource,
488
- publishedAt: Timestamp.now(),
863
+ publishedAt: Timestamp2.now(),
489
864
  publishedBy
490
865
  };
491
866
  const batch = this.db.batch();
@@ -497,7 +872,7 @@ var RootCMSClient = class {
497
872
  dataSource: updatedDataSource
498
873
  });
499
874
  batch.update(dataSourceDocRef, {
500
- publishedAt: Timestamp.now(),
875
+ publishedAt: Timestamp2.now(),
501
876
  publishedBy
502
877
  });
503
878
  await batch.commit();
@@ -604,7 +979,7 @@ var RootCMSClient = class {
604
979
  }
605
980
  const data = {
606
981
  action,
607
- timestamp: Timestamp.now(),
982
+ timestamp: Timestamp2.now(),
608
983
  by: (options == null ? void 0 : options.by) || "system",
609
984
  metadata: (options == null ? void 0 : options.metadata) || {}
610
985
  };
@@ -768,7 +1143,7 @@ function parseDocId(docId) {
768
1143
  }
769
1144
 
770
1145
  // core/runtime.ts
771
- import { FieldValue as FieldValue2 } from "firebase-admin/firestore";
1146
+ import { FieldValue as FieldValue3 } from "firebase-admin/firestore";
772
1147
  async function getDoc(rootConfig, collectionId, slug, options) {
773
1148
  const cmsPlugin = getCmsPlugin(rootConfig);
774
1149
  const cmsPluginOptions = cmsPlugin.getConfig();
@@ -890,7 +1265,7 @@ async function publishScheduledDocs(rootConfig) {
890
1265
  ...sys,
891
1266
  firstPublishedAt,
892
1267
  firstPublishedBy,
893
- publishedAt: FieldValue2.serverTimestamp(),
1268
+ publishedAt: FieldValue3.serverTimestamp(),
894
1269
  publishedBy: scheduledBy || ""
895
1270
  }
896
1271
  });
@@ -898,11 +1273,11 @@ async function publishScheduledDocs(rootConfig) {
898
1273
  batch.delete(scheduledRef);
899
1274
  batchCount += 1;
900
1275
  batch.update(draftRef, {
901
- "sys.scheduledAt": FieldValue2.delete(),
902
- "sys.scheduledBy": FieldValue2.delete(),
1276
+ "sys.scheduledAt": FieldValue3.delete(),
1277
+ "sys.scheduledBy": FieldValue3.delete(),
903
1278
  "sys.firstPublishedAt": firstPublishedAt,
904
1279
  "sys.firstPublishedBy": firstPublishedBy,
905
- "sys.publishedAt": FieldValue2.serverTimestamp(),
1280
+ "sys.publishedAt": FieldValue3.serverTimestamp(),
906
1281
  "sys.publishedBy": scheduledBy || ""
907
1282
  });
908
1283
  batchCount += 1;
@@ -1012,6 +1387,9 @@ function defineCollection(collection2) {
1012
1387
  var collection = defineCollection;
1013
1388
  export {
1014
1389
  RootCMSClient,
1390
+ TranslationsManager,
1391
+ buildTranslationsDbPath,
1392
+ buildTranslationsLocaleDocDbPath,
1015
1393
  getCmsPlugin,
1016
1394
  getDoc,
1017
1395
  isRichTextData,