@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/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/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 ??
|
|
417
|
+
createdAt: draftSys.createdAt ?? Timestamp2.now(),
|
|
67
418
|
createdBy: draftSys.createdBy ?? modifiedBy,
|
|
68
|
-
modifiedAt:
|
|
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 ??
|
|
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:
|
|
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":
|
|
196
|
-
"sys.scheduledBy":
|
|
546
|
+
"sys.scheduledAt": FieldValue2.delete(),
|
|
547
|
+
"sys.scheduledBy": FieldValue2.delete(),
|
|
197
548
|
"sys.firstPublishedAt": firstPublishedAt,
|
|
198
549
|
"sys.firstPublishedBy": firstPublishedBy,
|
|
199
|
-
"sys.publishedAt":
|
|
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:
|
|
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":
|
|
282
|
-
"sys.scheduledBy":
|
|
632
|
+
"sys.scheduledAt": FieldValue2.delete(),
|
|
633
|
+
"sys.scheduledBy": FieldValue2.delete(),
|
|
283
634
|
"sys.firstPublishedAt": firstPublishedAt,
|
|
284
635
|
"sys.firstPublishedBy": firstPublishedBy,
|
|
285
|
-
"sys.publishedAt":
|
|
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", "<=",
|
|
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:
|
|
666
|
+
publishedAt: Timestamp2.now(),
|
|
316
667
|
publishedBy,
|
|
317
|
-
scheduledAt:
|
|
318
|
-
scheduledBy:
|
|
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 =
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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":
|
|
902
|
-
"sys.scheduledBy":
|
|
1276
|
+
"sys.scheduledAt": FieldValue3.delete(),
|
|
1277
|
+
"sys.scheduledBy": FieldValue3.delete(),
|
|
903
1278
|
"sys.firstPublishedAt": firstPublishedAt,
|
|
904
1279
|
"sys.firstPublishedBy": firstPublishedBy,
|
|
905
|
-
"sys.publishedAt":
|
|
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,
|