@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/app.js +8 -3
- package/dist/cli.js +15 -0
- package/dist/{client-98Le1XxF.d.ts → client-c_QAurxr.d.ts} +209 -2
- package/dist/client.d.ts +1 -1
- package/dist/client.js +397 -22
- package/dist/core.d.ts +3 -3
- package/dist/core.js +405 -27
- package/dist/functions.js +401 -26
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.js +401 -26
- package/dist/project.d.ts +1 -1
- package/dist/{schema--Xq_zQlv.d.ts → schema-hlZc-G1f.d.ts} +11 -0
- package/dist/ui/ui.js +44 -44
- package/package.json +5 -3
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 ??
|
|
416
|
+
createdAt: draftSys.createdAt ?? Timestamp2.now(),
|
|
66
417
|
createdBy: draftSys.createdBy ?? modifiedBy,
|
|
67
|
-
modifiedAt:
|
|
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 ??
|
|
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:
|
|
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":
|
|
195
|
-
"sys.scheduledBy":
|
|
545
|
+
"sys.scheduledAt": FieldValue2.delete(),
|
|
546
|
+
"sys.scheduledBy": FieldValue2.delete(),
|
|
196
547
|
"sys.firstPublishedAt": firstPublishedAt,
|
|
197
548
|
"sys.firstPublishedBy": firstPublishedBy,
|
|
198
|
-
"sys.publishedAt":
|
|
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:
|
|
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":
|
|
281
|
-
"sys.scheduledBy":
|
|
631
|
+
"sys.scheduledAt": FieldValue2.delete(),
|
|
632
|
+
"sys.scheduledBy": FieldValue2.delete(),
|
|
282
633
|
"sys.firstPublishedAt": firstPublishedAt,
|
|
283
634
|
"sys.firstPublishedBy": firstPublishedBy,
|
|
284
|
-
"sys.publishedAt":
|
|
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", "<=",
|
|
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:
|
|
665
|
+
publishedAt: Timestamp2.now(),
|
|
315
666
|
publishedBy,
|
|
316
|
-
scheduledAt:
|
|
317
|
-
scheduledBy:
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 {
|
|
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';
|