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