@blinkk/root-cms 1.4.4 → 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/client.js CHANGED
@@ -1,9 +1,360 @@
1
1
  // core/client.ts
2
2
  import crypto from "node:crypto";
3
+ import {
4
+ FieldValue as FieldValue2,
5
+ Timestamp as Timestamp2
6
+ } from "firebase-admin/firestore";
7
+
8
+ // core/translations-manager.ts
3
9
  import {
4
10
  FieldValue,
5
11
  Timestamp
6
12
  } from "firebase-admin/firestore";
13
+
14
+ // shared/strings.ts
15
+ import fnv from "fnv-plus";
16
+ function normalizeStr(str) {
17
+ const lines = str.trim().split("\n").map((line) => removeTrailingWhitespace(line));
18
+ return lines.join("\n");
19
+ }
20
+ function removeTrailingWhitespace(str) {
21
+ return str.trimEnd().replace(/ $/, "");
22
+ }
23
+ function hashStr(str) {
24
+ if (!str || typeof str !== "string") {
25
+ throw new Error("input string is invalid");
26
+ }
27
+ return fnv.fast1a52hex(normalizeStr(str));
28
+ }
29
+
30
+ // core/translations-manager.ts
31
+ var TRANSLATIONS_DB_PATH_FORMAT = "/Projects/{project}/TranslationsManager/{mode}/Translations";
32
+ var TRANSLATIONS_LOCALE_DOC_DB_PATH_FORMAT = `${TRANSLATIONS_DB_PATH_FORMAT}/{id}:{locale}`;
33
+ var TranslationsManager = class {
34
+ constructor(cmsClient) {
35
+ this.cmsClient = cmsClient;
36
+ }
37
+ /**
38
+ * Saves draft translations for a translations doc id.
39
+ *
40
+ * Example:
41
+ * ```
42
+ * const strings = {
43
+ * 'one': {es: 'uno', fr: 'un'},
44
+ * 'two': {es: 'dos', fr: 'deux'},
45
+ * };
46
+ * await tm.saveTranslations('Pages/index', strings);
47
+ * ```
48
+ */
49
+ async saveTranslations(id, strings, options) {
50
+ const mode = "draft";
51
+ const localesSet = /* @__PURE__ */ new Set();
52
+ Object.values(strings).forEach((entry) => {
53
+ Object.keys(entry).forEach((locale) => {
54
+ if (locale !== "source") {
55
+ localesSet.add(locale);
56
+ }
57
+ });
58
+ });
59
+ const batch = this.cmsClient.db.batch();
60
+ const locales = Array.from(localesSet);
61
+ locales.forEach((locale) => {
62
+ const updates = {
63
+ id,
64
+ locale,
65
+ sys: {
66
+ modifiedAt: Timestamp.now(),
67
+ modifiedBy: (options == null ? void 0 : options.modifiedBy) || "root-cms-client"
68
+ },
69
+ strings: {}
70
+ };
71
+ if ((options == null ? void 0 : options.tags) && options.tags.length > 0) {
72
+ updates.tags = FieldValue.arrayUnion(...options.tags);
73
+ }
74
+ let numUpdates = 0;
75
+ const hashMap = this.toLocaleDocHashMap(strings, locale);
76
+ Object.entries(hashMap).forEach(([hash, translations]) => {
77
+ Object.entries(translations).forEach(([locale2, translation]) => {
78
+ if (translation) {
79
+ updates.strings[hash] ??= {};
80
+ updates.strings[hash][locale2] = translation;
81
+ numUpdates += 1;
82
+ }
83
+ });
84
+ });
85
+ if (numUpdates > 0) {
86
+ const localeDocPath = buildTranslationsLocaleDocDbPath({
87
+ project: this.cmsClient.projectId,
88
+ mode,
89
+ id,
90
+ locale
91
+ });
92
+ const localeDocRef = this.cmsClient.db.doc(localeDocPath);
93
+ batch.set(localeDocRef, updates, { merge: true });
94
+ }
95
+ });
96
+ await batch.commit();
97
+ }
98
+ /**
99
+ * Publishes a translations doc.
100
+ */
101
+ async publishTranslations(id, options) {
102
+ const db = this.cmsClient.db;
103
+ const project = this.cmsClient.projectId;
104
+ const draftPath = buildTranslationsDbPath({ project, mode: "draft" });
105
+ const query = db.collection(draftPath).where("id", "==", id);
106
+ const res = await query.get();
107
+ if (res.size === 0) {
108
+ console.warn(`no translations to publish for ${id}`);
109
+ return;
110
+ }
111
+ const batch = (options == null ? void 0 : options.batch) || db.batch();
112
+ res.docs.forEach((doc) => {
113
+ const translationsLocaleDoc = doc.data();
114
+ const sys = {
115
+ ...translationsLocaleDoc.sys,
116
+ publishedAt: Timestamp.now(),
117
+ publishedBy: (options == null ? void 0 : options.publishedBy) || "root-cms-client"
118
+ };
119
+ batch.update(doc.ref, { sys });
120
+ const publishedDocPath = buildTranslationsLocaleDocDbPath({
121
+ project,
122
+ mode: "published",
123
+ id: translationsLocaleDoc.id,
124
+ locale: translationsLocaleDoc.locale
125
+ });
126
+ const publishedDocRef = db.doc(publishedDocPath);
127
+ batch.set(publishedDocRef, { ...translationsLocaleDoc, sys });
128
+ });
129
+ const shouldCommitBatch = !(options == null ? void 0 : options.batch);
130
+ if (shouldCommitBatch) {
131
+ await batch.commit();
132
+ }
133
+ }
134
+ /**
135
+ * Fetches translations from one or more translations docs in the translations
136
+ * manager.
137
+ *
138
+ * Example:
139
+ * ```
140
+ * await tm.loadTranslations();
141
+ * // =>
142
+ * // {
143
+ * // "one": {"es": "uno", "fr": "un"},
144
+ * // "two": {"es": "dos", "fr": "deux"}
145
+ * // }
146
+ * ```
147
+ *
148
+ * To load a specific set of translations docs by id:
149
+ * ```
150
+ * const translationsToLoad = ['Global/strings', 'Global/header', 'Global/footer', 'Pages/index'];
151
+ * await tm.loadTranslations({ids: translationsToLoad});
152
+ * // =>
153
+ * // {
154
+ * // "one": {"es": "uno", "fr": "un"},
155
+ * // "two": {"es": "dos", "fr": "deux"}
156
+ * // }
157
+ * ```
158
+ *
159
+ * To load a subset of locales (more performant):
160
+ * ```
161
+ * await tm.loadTranslations({locales: ['es']});
162
+ * // =>
163
+ * // {
164
+ * // "one": {"es": "uno"},
165
+ * // "two": {"es": "dos"}
166
+ * // }
167
+ * ```
168
+ */
169
+ async loadTranslations(options) {
170
+ const mode = (options == null ? void 0 : options.mode) || "published";
171
+ const dbPath = buildTranslationsDbPath({
172
+ project: this.cmsClient.projectId,
173
+ mode
174
+ });
175
+ let query = this.cmsClient.db.collection(dbPath);
176
+ if ((options == null ? void 0 : options.ids) && options.ids.length > 0) {
177
+ query = query.where("id", "in", options.ids);
178
+ }
179
+ if ((options == null ? void 0 : options.tags) && options.tags.length > 0) {
180
+ query = query.where("tags", "array-contains", options.tags);
181
+ }
182
+ if ((options == null ? void 0 : options.locales) && options.locales.length > 0) {
183
+ query = query.where("locale", "in", options.locales);
184
+ }
185
+ const results = await query.get();
186
+ const strings = {};
187
+ results.forEach((result) => {
188
+ const localeDoc = result.data();
189
+ Object.values(localeDoc.strings || {}).forEach((item) => {
190
+ strings[item.source] ??= { source: item.source };
191
+ strings[item.source][localeDoc.locale] = item.translation;
192
+ });
193
+ });
194
+ return strings;
195
+ }
196
+ /**
197
+ * Fetches translations for a given locale, with optional fallbacks.
198
+ * The return value is a map of source string to translated string.
199
+ *
200
+ * Example:
201
+ * ```
202
+ * await translationsDoc.loadTranslationsForLocale('es');
203
+ * // =>
204
+ * // {
205
+ * // "one": "uno",
206
+ * // "two": "dos",
207
+ * // }
208
+ * ```
209
+ */
210
+ async loadTranslationsForLocale(locale, options) {
211
+ const localeSet = /* @__PURE__ */ new Set([
212
+ locale,
213
+ ...(options == null ? void 0 : options.fallbackLocales) || []
214
+ ]);
215
+ const fallbackLocales = Array.from(localeSet);
216
+ const multiLocaleStrings = await this.loadTranslations({
217
+ mode: options == null ? void 0 : options.mode,
218
+ locales: fallbackLocales
219
+ });
220
+ return this.toSingleLocaleMap(multiLocaleStrings, fallbackLocales);
221
+ }
222
+ /**
223
+ * Converts a multi-locale translations map to a flat single-locale map,
224
+ * with optional support for fallback locales.
225
+ *
226
+ * ```
227
+ * const multiLocaleStrings = {
228
+ * 'one': {es: 'uno', fr: 'un'},
229
+ * 'two': {es: 'dos', fr: 'deux'}
230
+ * };
231
+ * translationsDoc.toSingleLocaleMap(multiLocaleStrings, ['es']);
232
+ * // =>
233
+ * // {
234
+ * // "one": "uno",
235
+ * // "two": "dos",
236
+ * // }
237
+ * ```
238
+ */
239
+ toSingleLocaleMap(multiLocaleStrings, fallbackLocales) {
240
+ const singleLocaleStrings = {};
241
+ Object.entries(multiLocaleStrings).forEach(([source, translations]) => {
242
+ let translation = source;
243
+ for (const locale of fallbackLocales) {
244
+ if (translations[locale]) {
245
+ translation = translations[locale];
246
+ break;
247
+ }
248
+ }
249
+ singleLocaleStrings[source] = translation;
250
+ });
251
+ return singleLocaleStrings;
252
+ }
253
+ /**
254
+ * Converts a multi-locale translations map to a single-locale hashed version,
255
+ * used for storage in in the DB.
256
+ *
257
+ * ```
258
+ * const multiLocaleStrings = {
259
+ * 'one': {es: 'uno', fr: 'un'},
260
+ * 'two': {es: 'dos', fr: 'deux'}
261
+ * };
262
+ * translationsDoc.toLocaleDocHashMap(multiLocaleStrings, 'es');
263
+ * // =>
264
+ * // {
265
+ * // "<hash1>": {"source": "one", "translation": "uno"},
266
+ * // "<hash2>": {"source": "two", "translation": "dos"},
267
+ * // }
268
+ * ```
269
+ *
270
+ * One reason for using hashes is because the DB has limits on the number of
271
+ * chars that can be used as the "key" in a object map.
272
+ */
273
+ toLocaleDocHashMap(multiLocaleStrings, locale) {
274
+ const hashMap = {};
275
+ Object.entries(multiLocaleStrings).forEach(([source, translations]) => {
276
+ const translation = translations[locale];
277
+ if (translation) {
278
+ const hash = hashStr(source);
279
+ hashMap[hash] = { source, translation };
280
+ }
281
+ });
282
+ return hashMap;
283
+ }
284
+ /**
285
+ * Import translations from the v1 system to the TranslationsManager.
286
+ */
287
+ async importTranslationsFromV1() {
288
+ var _a;
289
+ const projectId = this.cmsClient.projectId;
290
+ const db = this.cmsClient.db;
291
+ const dbPath = `Projects/${projectId}/Translations`;
292
+ const query = db.collection(dbPath);
293
+ const querySnapshot = await query.get();
294
+ if (querySnapshot.size === 0) {
295
+ return;
296
+ }
297
+ console.log(
298
+ "[root cms] importing v1 Translations to v2 TranslationsManager"
299
+ );
300
+ const translationsDocs = {};
301
+ querySnapshot.forEach((doc) => {
302
+ const translation = doc.data();
303
+ const source = this.cmsClient.normalizeString(translation.source);
304
+ delete translation.source;
305
+ const tags = translation.tags || [];
306
+ delete translation.tags;
307
+ for (const tag of tags) {
308
+ if (tag.includes("/")) {
309
+ const translationsId = tag;
310
+ translationsDocs[translationsId] ??= {
311
+ id: translationsId,
312
+ tags,
313
+ strings: {}
314
+ };
315
+ translationsDocs[translationsId].strings[source] = translation;
316
+ }
317
+ }
318
+ });
319
+ if (Object.keys(translationsDocs).length === 0) {
320
+ console.log("[root cms] no v1 translations to save");
321
+ return;
322
+ }
323
+ for (const docId in translationsDocs) {
324
+ const [collection, slug] = docId.split("/");
325
+ if (collection && slug) {
326
+ const doc = await this.cmsClient.getDoc(collection, slug, {
327
+ mode: "draft"
328
+ });
329
+ const linkedSheet = (_a = doc == null ? void 0 : doc.sys) == null ? void 0 : _a.l10nSheet;
330
+ if (linkedSheet) {
331
+ translationsDocs[docId].sys.linkedSheet = linkedSheet;
332
+ }
333
+ }
334
+ }
335
+ Object.entries(translationsDocs).forEach(([translationsId, data]) => {
336
+ const len = Object.keys(data.strings).length;
337
+ console.log(`[root cms] saving ${len} string(s) to ${translationsId}...`);
338
+ this.saveTranslations(translationsId, data.strings, {
339
+ tags: data.tags || [translationsId]
340
+ });
341
+ });
342
+ }
343
+ };
344
+ function buildTranslationsDbPath(options) {
345
+ return TRANSLATIONS_DB_PATH_FORMAT.replace(
346
+ "{project}",
347
+ options.project
348
+ ).replace("{mode}", options.mode);
349
+ }
350
+ function buildTranslationsLocaleDocDbPath(options) {
351
+ return TRANSLATIONS_LOCALE_DOC_DB_PATH_FORMAT.replace(
352
+ "{project}",
353
+ options.project
354
+ ).replace("{mode}", options.mode).replace("{id}", options.id.replaceAll("/", "--")).replace("{locale}", options.locale);
355
+ }
356
+
357
+ // core/client.ts
7
358
  var RootCMSClient = class {
8
359
  constructor(rootConfig) {
9
360
  this.rootConfig = rootConfig;
@@ -57,9 +408,9 @@ var RootCMSClient = class {
57
408
  slug,
58
409
  sys: {
59
410
  ...draftSys,
60
- createdAt: draftSys.createdAt ?? Timestamp.now(),
411
+ createdAt: draftSys.createdAt ?? Timestamp2.now(),
61
412
  createdBy: draftSys.createdBy ?? modifiedBy,
62
- modifiedAt: Timestamp.now(),
413
+ modifiedAt: Timestamp2.now(),
63
414
  modifiedBy,
64
415
  locales: (options == null ? void 0 : options.locales) ?? draftSys.locales ?? ["en"]
65
416
  },
@@ -167,7 +518,7 @@ var RootCMSClient = class {
167
518
  const publishedRef = this.db.doc(
168
519
  `${projectCollectionsPath}/${collection}/Published/${slug}`
169
520
  );
170
- const firstPublishedAt = sys.firstPublishedAt ?? FieldValue.serverTimestamp();
521
+ const firstPublishedAt = sys.firstPublishedAt ?? FieldValue2.serverTimestamp();
171
522
  const firstPublishedBy = sys.firstPublishedBy ?? publishedBy;
172
523
  batch.set(publishedRef, {
173
524
  id,
@@ -178,7 +529,7 @@ var RootCMSClient = class {
178
529
  ...sys,
179
530
  firstPublishedAt,
180
531
  firstPublishedBy,
181
- publishedAt: FieldValue.serverTimestamp(),
532
+ publishedAt: FieldValue2.serverTimestamp(),
182
533
  publishedBy
183
534
  }
184
535
  });
@@ -186,11 +537,11 @@ var RootCMSClient = class {
186
537
  batch.delete(scheduledRef);
187
538
  batchCount += 1;
188
539
  batch.update(draftRef, {
189
- "sys.scheduledAt": FieldValue.delete(),
190
- "sys.scheduledBy": FieldValue.delete(),
540
+ "sys.scheduledAt": FieldValue2.delete(),
541
+ "sys.scheduledBy": FieldValue2.delete(),
191
542
  "sys.firstPublishedAt": firstPublishedAt,
192
543
  "sys.firstPublishedBy": firstPublishedBy,
193
- "sys.publishedAt": FieldValue.serverTimestamp(),
544
+ "sys.publishedAt": FieldValue2.serverTimestamp(),
194
545
  "sys.publishedBy": publishedBy
195
546
  });
196
547
  batchCount += 1;
@@ -264,7 +615,7 @@ var RootCMSClient = class {
264
615
  ...sys,
265
616
  firstPublishedAt,
266
617
  firstPublishedBy,
267
- publishedAt: FieldValue.serverTimestamp(),
618
+ publishedAt: FieldValue2.serverTimestamp(),
268
619
  publishedBy: scheduledBy || ""
269
620
  }
270
621
  });
@@ -272,11 +623,11 @@ var RootCMSClient = class {
272
623
  batch.delete(scheduledRef);
273
624
  batchCount += 1;
274
625
  batch.update(draftRef, {
275
- "sys.scheduledAt": FieldValue.delete(),
276
- "sys.scheduledBy": FieldValue.delete(),
626
+ "sys.scheduledAt": FieldValue2.delete(),
627
+ "sys.scheduledBy": FieldValue2.delete(),
277
628
  "sys.firstPublishedAt": firstPublishedAt,
278
629
  "sys.firstPublishedBy": firstPublishedBy,
279
- "sys.publishedAt": FieldValue.serverTimestamp(),
630
+ "sys.publishedAt": FieldValue2.serverTimestamp(),
280
631
  "sys.publishedBy": scheduledBy || "root-cms-client"
281
632
  });
282
633
  batchCount += 1;
@@ -299,17 +650,17 @@ var RootCMSClient = class {
299
650
  async publishScheduledReleases() {
300
651
  const releasesPath = `Projects/${this.projectId}/Releases`;
301
652
  const now = Math.ceil((/* @__PURE__ */ new Date()).getTime());
302
- const query = this.db.collection(releasesPath).where("scheduledAt", "<=", Timestamp.fromMillis(now));
653
+ const query = this.db.collection(releasesPath).where("scheduledAt", "<=", Timestamp2.fromMillis(now));
303
654
  const querySnapshot = await query.get();
304
655
  for (const snapshot of querySnapshot.docs) {
305
656
  const release = snapshot.data();
306
657
  const batch = this.db.batch();
307
658
  const publishedBy = release.scheduledBy || "root-cms-client";
308
659
  batch.update(snapshot.ref, {
309
- publishedAt: Timestamp.now(),
660
+ publishedAt: Timestamp2.now(),
310
661
  publishedBy,
311
- scheduledAt: FieldValue.delete(),
312
- scheduledBy: FieldValue.delete()
662
+ scheduledAt: FieldValue2.delete(),
663
+ scheduledBy: FieldValue2.delete()
313
664
  });
314
665
  await this.publishDocs(release.docIds || [], { publishedBy, batch });
315
666
  }
@@ -321,7 +672,7 @@ var RootCMSClient = class {
321
672
  var _a;
322
673
  if ((_a = doc.sys) == null ? void 0 : _a.publishingLocked) {
323
674
  if (doc.sys.publishingLocked.until) {
324
- const now = Timestamp.now().toMillis();
675
+ const now = Timestamp2.now().toMillis();
325
676
  const until = doc.sys.publishingLocked.until.toMillis();
326
677
  return now < until;
327
678
  }
@@ -329,6 +680,30 @@ var RootCMSClient = class {
329
680
  }
330
681
  return false;
331
682
  }
683
+ /**
684
+ * Returns a `TranslationsManager` object for managing translations.
685
+ *
686
+ * To get translations:
687
+ * ```
688
+ * await tm.loadTranslations({
689
+ * ids: ['Global/strings', 'Pages/index'],
690
+ * locales: ['es'],
691
+ * });
692
+ * ```
693
+ *
694
+ * NOTE: The `TranslationsManager` is a v2 feature that will eventually
695
+ * replace the v1 translations system.
696
+ */
697
+ getTranslationsManager() {
698
+ var _a;
699
+ const cmsPluginOptions = this.cmsPlugin.getConfig();
700
+ if ((_a = cmsPluginOptions.experiments) == null ? void 0 : _a.v2TranslationsManager) {
701
+ throw new Error(
702
+ "`v2TranslationsManager` is not enabled. update root.config.ts and add: `{experiments: {v2TranslationsManager: true}}`"
703
+ );
704
+ }
705
+ return new TranslationsManager(this);
706
+ }
332
707
  /**
333
708
  * Loads translations saved in the translations collection, optionally
334
709
  * filtered by tag.
@@ -374,7 +749,7 @@ var RootCMSClient = class {
374
749
  source: this.normalizeString(source)
375
750
  };
376
751
  if (tags) {
377
- data.tags = FieldValue.arrayUnion(...tags);
752
+ data.tags = FieldValue2.arrayUnion(...tags);
378
753
  }
379
754
  batch.set(translationRef, data, { merge: true });
380
755
  batchCount += 1;
@@ -445,7 +820,7 @@ var RootCMSClient = class {
445
820
  const syncedBy = (options == null ? void 0 : options.syncedBy) || "root-cms-client";
446
821
  const updatedDataSource = {
447
822
  ...dataSource,
448
- syncedAt: Timestamp.now(),
823
+ syncedAt: Timestamp2.now(),
449
824
  syncedBy
450
825
  };
451
826
  const batch = this.db.batch();
@@ -454,7 +829,7 @@ var RootCMSClient = class {
454
829
  data
455
830
  });
456
831
  batch.update(dataSourceDocRef, {
457
- syncedAt: Timestamp.now(),
832
+ syncedAt: Timestamp2.now(),
458
833
  syncedBy
459
834
  });
460
835
  await batch.commit();
@@ -479,7 +854,7 @@ var RootCMSClient = class {
479
854
  const publishedBy = (options == null ? void 0 : options.publishedBy) || "root-cms-client";
480
855
  const updatedDataSource = {
481
856
  ...dataSource,
482
- publishedAt: Timestamp.now(),
857
+ publishedAt: Timestamp2.now(),
483
858
  publishedBy
484
859
  };
485
860
  const batch = this.db.batch();
@@ -491,7 +866,7 @@ var RootCMSClient = class {
491
866
  dataSource: updatedDataSource
492
867
  });
493
868
  batch.update(dataSourceDocRef, {
494
- publishedAt: Timestamp.now(),
869
+ publishedAt: Timestamp2.now(),
495
870
  publishedBy
496
871
  });
497
872
  await batch.commit();
@@ -598,7 +973,7 @@ var RootCMSClient = class {
598
973
  }
599
974
  const data = {
600
975
  action,
601
- timestamp: Timestamp.now(),
976
+ timestamp: Timestamp2.now(),
602
977
  by: (options == null ? void 0 : options.by) || "system",
603
978
  metadata: (options == null ? void 0 : options.metadata) || {}
604
979
  };
package/dist/core.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { L as LoadTranslationsOptions, T as TranslationsMap, a as LocaleTranslations } from './client-98Le1XxF.js';
2
- export { A as Action, p as ArrayObject, c as DataSource, d as DataSourceData, e as DataSourceMode, D as Doc, b as DocMode, h as GetCountOptions, G as GetDocOptions, H as HttpMethod, j as ListActionsOptions, g as ListDocsOptions, R as Release, k as RootCMSClient, f as SaveDraftOptions, S as SetDocOptions, i as Translation, U as UserRole, m as getCmsPlugin, l as isRichTextData, q as marshalArray, n as marshalData, o as normalizeData, v as parseDocId, t as toArrayObject, s as translationsForLocale, r as unmarshalArray, u as unmarshalData } from './client-98Le1XxF.js';
1
+ import { L as LoadTranslationsOptions, T as TranslationsMap, a as LocaleTranslations } from './client-c_QAurxr.js';
2
+ export { A as Action, p as ArrayObject, c as DataSource, d as DataSourceData, e as DataSourceMode, D as Doc, b as DocMode, h as GetCountOptions, G as GetDocOptions, H as HttpMethod, j as ListActionsOptions, g as ListDocsOptions, w as Locale, M as MultiLocaleTranslationsMap, R as Release, k as RootCMSClient, f as SaveDraftOptions, S as SetDocOptions, E as SingleLocaleTranslationsMap, x as SourceString, y as TranslatedString, i as Translation, z as TranslationsDocMode, C as TranslationsLocaleDocEntry, B as TranslationsLocaleDocHashMap, F as TranslationsManager, U as UserRole, I as buildTranslationsDbPath, J as buildTranslationsLocaleDocDbPath, m as getCmsPlugin, l as isRichTextData, q as marshalArray, n as marshalData, o as normalizeData, v as parseDocId, t as toArrayObject, s as translationsForLocale, r as unmarshalArray, u as unmarshalData } from './client-c_QAurxr.js';
3
3
  import { RootConfig } from '@blinkk/root';
4
4
  import { Query } from 'firebase-admin/firestore';
5
- export { s as schema } from './schema--Xq_zQlv.js';
5
+ export { s as schema } from './schema-hlZc-G1f.js';
6
6
  import 'firebase-admin/app';
7
7
  import 'preact';
8
8