@blinkk/root-cms 3.1.5 → 3.1.7

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.
Files changed (120) hide show
  1. package/dist/app.js +1 -1
  2. package/dist/{chunk-MUXSUGFF.js → chunk-NIBAHWGP.js} +2 -2
  3. package/dist/{chunk-ETHWZSNM.js → chunk-PLSNGCYU.js} +196 -22
  4. package/dist/{chunk-6CTCV2PN.js → chunk-Z2CTQVYR.js} +1 -1
  5. package/dist/cli.js +1 -1
  6. package/dist/client.js +1 -1
  7. package/dist/core/client.d.ts +30 -2
  8. package/dist/core/cron-schedule.d.ts +63 -0
  9. package/dist/core/plugin.d.ts +6 -1
  10. package/dist/core.js +1 -1
  11. package/dist/functions.js +2 -2
  12. package/dist/plugin.js +7 -5
  13. package/dist/richtext.js +1 -1
  14. package/dist/shared/embed-protocol.d.ts +19 -0
  15. package/dist/ui/AIPage-XPE7F5AF.js +1 -0
  16. package/dist/ui/AssetsPage-C3ZEENBO.js +1 -0
  17. package/dist/ui/CollectionPage-VQ4WJHJB.js +1 -0
  18. package/dist/ui/ComparePage-VKOXKID4.js +1 -0
  19. package/dist/ui/{DataPage-RFNGSDDN.js → DataPage-JXZ2Z3DG.js} +1 -1
  20. package/dist/ui/{DataSourcePage-U46CDIH2.js → DataSourcePage-X3NSUOTP.js} +1 -1
  21. package/dist/ui/{DocTranslationsPage-BFCM2PE3.js → DocTranslationsPage-AJGT52XQ.js} +1 -1
  22. package/dist/ui/DocumentPage-ZFD2KE3A.js +1 -0
  23. package/dist/ui/{EditDataSourcePage-NKKIF6AD.js → EditDataSourcePage-EMK3F6HD.js} +1 -1
  24. package/dist/ui/EditReleasePage-3IAERDSH.js +1 -0
  25. package/dist/ui/EmbeddedAIPage-2W5F7QFL.js +1 -0
  26. package/dist/ui/{EmbeddedDocumentPage-2CNQJTW5.js → EmbeddedDocumentPage-BESFW2YZ.js} +1 -1
  27. package/dist/ui/{ImageEditorDialog-YDKOGVJV.js → ImageEditorDialog-JDXMU2FC.js} +1 -1
  28. package/dist/ui/LogsPage-KCD4XZ2N.js +1 -0
  29. package/dist/ui/NewDataSourcePage-EZDU7HGA.js +1 -0
  30. package/dist/ui/NewReleasePage-FFTRUIP4.js +1 -0
  31. package/dist/ui/{NotFoundPage-JRYFUENW.js → NotFoundPage-JGG2NTUE.js} +1 -1
  32. package/dist/ui/{ProjectPage-2WIMWCSX.js → ProjectPage-HT5JKR33.js} +1 -1
  33. package/dist/ui/ReleasePage-OYWLGV4D.js +1 -0
  34. package/dist/ui/{ReleasesPage-AW6PICTQ.js → ReleasesPage-XEII7FBC.js} +1 -1
  35. package/dist/ui/{SettingsPage-KFLNZ356.js → SettingsPage-7GIBTPB5.js} +1 -1
  36. package/dist/ui/SidebarToolsPage-GQYTZDAC.js +1 -0
  37. package/dist/ui/{TaskPage-GZGPKIL5.js → TaskPage-SNDDYDZY.js} +1 -1
  38. package/dist/ui/TasksPage-CEVWOY67.js +1 -0
  39. package/dist/ui/{TranslationsArbPage-K272JEAA.js → TranslationsArbPage-ADRLLAIW.js} +1 -1
  40. package/dist/ui/{TranslationsEditPage-XWXPNKRQ.js → TranslationsEditPage-PSKA7K44.js} +1 -1
  41. package/dist/ui/{TranslationsPage-7KUHVTLK.js → TranslationsPage-NGCLDSK7.js} +2 -2
  42. package/dist/ui/{chunk-75NMQ3LW.js → chunk-3YBRRPAN.js} +1 -1
  43. package/dist/ui/{chunk-QLIIVG5Q.js → chunk-5F5XJPKD.js} +1 -1
  44. package/dist/ui/{chunk-7UHZS56B.js → chunk-6ZVMJXLE.js} +53 -53
  45. package/dist/ui/chunk-7OU4ERTS.js +87 -0
  46. package/dist/ui/chunk-A2JZYRE3.js +1 -0
  47. package/dist/ui/chunk-B74M7NEV.js +1 -0
  48. package/dist/ui/chunk-BLHXP3SR.js +3 -0
  49. package/dist/ui/{chunk-UJX25CXD.js → chunk-BOIY4J2R.js} +1 -1
  50. package/dist/ui/chunk-CEWU5VSJ.js +1 -0
  51. package/dist/ui/{chunk-34W6H4YG.js → chunk-CXMVFNX3.js} +1 -1
  52. package/dist/ui/{chunk-F7RQT4XN.js → chunk-EET5AS64.js} +1 -1
  53. package/dist/ui/{chunk-WASID5UU.js → chunk-GJ7ES2LA.js} +1 -1
  54. package/dist/ui/{chunk-2RRM4HEQ.js → chunk-H5JL376P.js} +1 -1
  55. package/dist/ui/{chunk-H37RXS4R.js → chunk-HKCOKHA2.js} +1 -1
  56. package/dist/ui/{chunk-5KSGCGYY.js → chunk-HUYP5U4K.js} +1 -1
  57. package/dist/ui/{chunk-NCZD5IWS.js → chunk-HWON6MV4.js} +1 -1
  58. package/dist/ui/{chunk-HXSB6YB2.js → chunk-I3H465Z6.js} +1 -1
  59. package/dist/ui/{chunk-535JU7FU.js → chunk-ILFWJKSS.js} +1 -1
  60. package/dist/ui/chunk-K7IG7PO2.js +1 -0
  61. package/dist/ui/chunk-LKTN7YVM.js +31 -0
  62. package/dist/ui/{chunk-PLYSJ7W5.js → chunk-LS2JH5QF.js} +1 -1
  63. package/dist/ui/{chunk-QFDU6FXP.js → chunk-MALK5YEV.js} +1 -1
  64. package/dist/ui/{chunk-YP6ZH3MT.js → chunk-MFRPYPCF.js} +1 -1
  65. package/dist/ui/chunk-MYPLMHFW.js +1 -0
  66. package/dist/ui/{chunk-RB23LLKL.js → chunk-OKNBFTYR.js} +1 -1
  67. package/dist/ui/{chunk-OWBT6YUM.js → chunk-P2RVKNG4.js} +1 -1
  68. package/dist/ui/{chunk-JBLSUM6W.js → chunk-P57AFOMR.js} +1 -1
  69. package/dist/ui/{chunk-HV3JJ5UT.js → chunk-PLTI4NG7.js} +1 -1
  70. package/dist/ui/chunk-PY4JH5SN.js +1 -0
  71. package/dist/ui/{chunk-44ZU5WMI.js → chunk-SBAY52SC.js} +1 -1
  72. package/dist/ui/{chunk-Y5EOMMNI.js → chunk-TCKR2BL4.js} +1 -1
  73. package/dist/ui/{chunk-WC7KMPEQ.js → chunk-TCRDGNM4.js} +1 -1
  74. package/dist/ui/chunk-TCWN5YRW.js +1 -0
  75. package/dist/ui/{chunk-DWV5J2W7.js → chunk-TZWCCNSA.js} +1 -1
  76. package/dist/ui/{chunk-2PTLQRTU.js → chunk-U5OXKKBN.js} +1 -1
  77. package/dist/ui/{chunk-XUBZH4ON.js → chunk-VZGDXWRK.js} +1 -1
  78. package/dist/ui/{chunk-IGZDFHVM.js → chunk-X3VDQDHP.js} +1 -1
  79. package/dist/ui/chunk-XBYMUPZF.js +19 -0
  80. package/dist/ui/{chunk-QRAFYIAW.js → chunk-XYXL5HSZ.js} +1 -1
  81. package/dist/ui/chunk-Y7RYSRP7.js +7 -0
  82. package/dist/ui/{chunk-WRA24P66.js.LEGAL.txt → chunk-Y7RYSRP7.js.LEGAL.txt} +1 -0
  83. package/dist/ui/chunk-YIKHJONY.js +1 -0
  84. package/dist/ui/chunk-Z2CRNO3I.js +1 -0
  85. package/dist/ui/{chunk-UCYWQEAS.js → chunk-ZD3ORGA6.js} +1 -1
  86. package/dist/ui/{chunk-YBHFGQ3Z.js → chunk-ZQDUWURP.js} +1 -1
  87. package/dist/ui/{chunk-PIJ3E47R.js → chunk-ZYSR5UIW.js} +1 -1
  88. package/dist/ui/ui.css +1 -1
  89. package/dist/ui/ui.js +1 -1
  90. package/package.json +3 -3
  91. package/dist/ui/AIPage-25WICIOT.js +0 -1
  92. package/dist/ui/AssetsPage-WKTM2J2G.js +0 -1
  93. package/dist/ui/CollectionPage-5ZOVG7IG.js +0 -1
  94. package/dist/ui/ComparePage-DDIFYYTK.js +0 -1
  95. package/dist/ui/DocumentPage-OTBXM55S.js +0 -1
  96. package/dist/ui/EditReleasePage-TKNOUT7A.js +0 -1
  97. package/dist/ui/EmbeddedAIPage-4AX7VO3T.js +0 -1
  98. package/dist/ui/LogsPage-SCECIAZH.js +0 -1
  99. package/dist/ui/NewDataSourcePage-FAOPK2RZ.js +0 -1
  100. package/dist/ui/NewReleasePage-HD6T6UV2.js +0 -1
  101. package/dist/ui/ReleasePage-GUMEMLF6.js +0 -1
  102. package/dist/ui/SidebarToolsPage-3R7BQ2F7.js +0 -1
  103. package/dist/ui/TasksPage-G3GBTV3L.js +0 -1
  104. package/dist/ui/chunk-2FQMT5AB.js +0 -3
  105. package/dist/ui/chunk-FVWSVOOY.js +0 -1
  106. package/dist/ui/chunk-GCSN3YEC.js +0 -31
  107. package/dist/ui/chunk-N5RJIILP.js +0 -1
  108. package/dist/ui/chunk-NXWYS4HG.js +0 -19
  109. package/dist/ui/chunk-OWYV43BT.js +0 -1
  110. package/dist/ui/chunk-QWB6MAPJ.js +0 -7
  111. package/dist/ui/chunk-RN4XVVWD.js +0 -1
  112. package/dist/ui/chunk-T5BJNOWE.js +0 -1
  113. package/dist/ui/chunk-TNW2GNUR.js +0 -1
  114. package/dist/ui/chunk-UPN3EMUS.js +0 -1
  115. package/dist/ui/chunk-VPZHG7JL.js +0 -81
  116. package/dist/ui/chunk-WRA24P66.js +0 -7
  117. package/dist/ui/chunk-Z3ZJJWFT.js +0 -1
  118. /package/dist/ui/{ImageEditorDialog-YDKOGVJV.js.LEGAL.txt → ImageEditorDialog-JDXMU2FC.js.LEGAL.txt} +0 -0
  119. /package/dist/ui/{chunk-7UHZS56B.js.LEGAL.txt → chunk-6ZVMJXLE.js.LEGAL.txt} +0 -0
  120. /package/dist/ui/{chunk-VPZHG7JL.js.LEGAL.txt → chunk-7OU4ERTS.js.LEGAL.txt} +0 -0
package/dist/app.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getServerVersion,
3
3
  serializeAiConfig
4
- } from "./chunk-MUXSUGFF.js";
4
+ } from "./chunk-NIBAHWGP.js";
5
5
  import {
6
6
  getCollectionSchema,
7
7
  getProjectSchemas
@@ -552,7 +552,7 @@ async function generateImage(rootConfig, options) {
552
552
  // package.json
553
553
  var package_default = {
554
554
  name: "@blinkk/root-cms",
555
- version: "3.1.5",
555
+ version: "3.1.7",
556
556
  author: "s@blinkk.com",
557
557
  license: "MIT",
558
558
  engines: {
@@ -727,7 +727,7 @@ var package_default = {
727
727
  yjs: "13.6.27"
728
728
  },
729
729
  peerDependencies: {
730
- "@blinkk/root": "3.1.5",
730
+ "@blinkk/root": "3.1.7",
731
731
  "firebase-admin": ">=11",
732
732
  "firebase-functions": ">=4"
733
733
  },
@@ -9,6 +9,155 @@ import {
9
9
  Timestamp as Timestamp2
10
10
  } from "firebase-admin/firestore";
11
11
 
12
+ // core/cron-schedule.ts
13
+ var ONE_MINUTE_MS = 60 * 1e3;
14
+ var MAX_LOOKBACK_MS = 366 * 24 * 60 * 60 * 1e3;
15
+ var FIELD_RANGES = [
16
+ { min: 0, max: 59 },
17
+ // minute
18
+ { min: 0, max: 23 },
19
+ // hour
20
+ { min: 1, max: 31 },
21
+ // day of month
22
+ { min: 1, max: 12 },
23
+ // month
24
+ { min: 0, max: 7 }
25
+ // day of week (0 and 7 both mean Sunday)
26
+ ];
27
+ function parseField(field, range) {
28
+ const values = /* @__PURE__ */ new Set();
29
+ const parts = field.split(",");
30
+ for (const part of parts) {
31
+ const trimmed = part.trim();
32
+ if (!trimmed) {
33
+ throw new Error(`invalid cron field: "${field}"`);
34
+ }
35
+ let rangePart = trimmed;
36
+ let step = 1;
37
+ if (trimmed.includes("/")) {
38
+ const [rangeStr, stepStr] = trimmed.split("/");
39
+ rangePart = rangeStr;
40
+ step = Number(stepStr);
41
+ if (!Number.isInteger(step) || step <= 0) {
42
+ throw new Error(`invalid cron step: "${trimmed}"`);
43
+ }
44
+ }
45
+ let start;
46
+ let end;
47
+ if (rangePart === "*") {
48
+ start = range.min;
49
+ end = range.max;
50
+ } else if (rangePart.includes("-")) {
51
+ const [startStr, endStr] = rangePart.split("-");
52
+ start = Number(startStr);
53
+ end = Number(endStr);
54
+ } else {
55
+ start = Number(rangePart);
56
+ end = start;
57
+ }
58
+ if (!Number.isInteger(start) || !Number.isInteger(end) || start < range.min || end > range.max || start > end) {
59
+ throw new Error(`invalid cron field: "${field}"`);
60
+ }
61
+ for (let value = start; value <= end; value += step) {
62
+ values.add(value);
63
+ }
64
+ }
65
+ return values;
66
+ }
67
+ function parseCronExpression(expression) {
68
+ const fields = expression.trim().split(/\s+/);
69
+ if (fields.length !== 5) {
70
+ throw new Error(
71
+ `invalid cron expression: "${expression}" (expected 5 fields)`
72
+ );
73
+ }
74
+ const [minute, hour, dayOfMonth, month, dayOfWeek] = fields;
75
+ const parsedDayOfWeek = parseField(dayOfWeek, FIELD_RANGES[4]);
76
+ if (parsedDayOfWeek.has(7)) {
77
+ parsedDayOfWeek.delete(7);
78
+ parsedDayOfWeek.add(0);
79
+ }
80
+ return {
81
+ minute: parseField(minute, FIELD_RANGES[0]),
82
+ hour: parseField(hour, FIELD_RANGES[1]),
83
+ dayOfMonth: parseField(dayOfMonth, FIELD_RANGES[2]),
84
+ month: parseField(month, FIELD_RANGES[3]),
85
+ dayOfWeek: parsedDayOfWeek,
86
+ dayOfMonthRestricted: dayOfMonth.trim() !== "*",
87
+ dayOfWeekRestricted: dayOfWeek.trim() !== "*"
88
+ };
89
+ }
90
+ function getZonedFields(date, timeZone) {
91
+ const dtf = new Intl.DateTimeFormat("en-US", {
92
+ timeZone,
93
+ hour12: false,
94
+ year: "numeric",
95
+ month: "2-digit",
96
+ day: "2-digit",
97
+ hour: "2-digit",
98
+ minute: "2-digit"
99
+ });
100
+ const parts = {};
101
+ for (const part of dtf.formatToParts(date)) {
102
+ parts[part.type] = part.value;
103
+ }
104
+ const year = Number(parts.year);
105
+ const month = Number(parts.month);
106
+ const day = Number(parts.day);
107
+ let hour = Number(parts.hour);
108
+ if (hour === 24) {
109
+ hour = 0;
110
+ }
111
+ const minute = Number(parts.minute);
112
+ const dayOfWeek = new Date(Date.UTC(year, month - 1, day)).getUTCDay();
113
+ return { minute, hour, dayOfMonth: day, month, dayOfWeek };
114
+ }
115
+ function matchesCron(parsed, fields) {
116
+ if (!parsed.minute.has(fields.minute)) {
117
+ return false;
118
+ }
119
+ if (!parsed.hour.has(fields.hour)) {
120
+ return false;
121
+ }
122
+ if (!parsed.month.has(fields.month)) {
123
+ return false;
124
+ }
125
+ const domMatch = parsed.dayOfMonth.has(fields.dayOfMonth);
126
+ const dowMatch = parsed.dayOfWeek.has(fields.dayOfWeek);
127
+ if (parsed.dayOfMonthRestricted && parsed.dayOfWeekRestricted) {
128
+ return domMatch || dowMatch;
129
+ }
130
+ if (parsed.dayOfMonthRestricted) {
131
+ return domMatch;
132
+ }
133
+ if (parsed.dayOfWeekRestricted) {
134
+ return dowMatch;
135
+ }
136
+ return true;
137
+ }
138
+ function isCronDue(options) {
139
+ const { expression, now } = options;
140
+ const timezone = options.timezone || "UTC";
141
+ const parsed = parseCronExpression(expression);
142
+ let lastSyncMs = options.lastSyncMs;
143
+ if (lastSyncMs > now) {
144
+ lastSyncMs = now;
145
+ }
146
+ const nowMinute = Math.floor(now / ONE_MINUTE_MS) * ONE_MINUTE_MS;
147
+ let lowerBound;
148
+ if (lastSyncMs > 0) {
149
+ lowerBound = Math.max(lastSyncMs, now - MAX_LOOKBACK_MS);
150
+ } else {
151
+ lowerBound = nowMinute - ONE_MINUTE_MS;
152
+ }
153
+ for (let t = nowMinute; t > lowerBound; t -= ONE_MINUTE_MS) {
154
+ if (matchesCron(parsed, getZonedFields(new Date(t), timezone))) {
155
+ return true;
156
+ }
157
+ }
158
+ return false;
159
+ }
160
+
12
161
  // core/translations-manager.ts
13
162
  import {
14
163
  FieldValue,
@@ -1302,32 +1451,35 @@ ${errorMessages}`);
1302
1451
  for (const snapshot of querySnapshot.docs) {
1303
1452
  const dataSource = snapshot.data();
1304
1453
  const cron = dataSource.cron;
1305
- if (!cron || !cron.enabled || typeof cron.interval !== "number" || !cron.unit) {
1306
- continue;
1307
- }
1308
- const interval = Number(cron.interval);
1309
- if (!Number.isFinite(interval) || interval <= 0 || !Number.isInteger(interval)) {
1454
+ if (!cron || !cron.enabled) {
1310
1455
  continue;
1311
1456
  }
1312
- let intervalMs = interval;
1313
- switch (cron.unit) {
1314
- case "minutes":
1315
- intervalMs *= 60 * 1e3;
1316
- break;
1317
- case "hours":
1318
- intervalMs *= 60 * 60 * 1e3;
1319
- break;
1320
- case "days":
1321
- intervalMs *= 24 * 60 * 60 * 1e3;
1322
- break;
1323
- default:
1457
+ const lastSyncMs = dataSource.syncedAt ? dataSource.syncedAt.toMillis() : 0;
1458
+ const schedule = cron.schedule || "interval";
1459
+ let due;
1460
+ if (schedule === "interval") {
1461
+ due = isIntervalDue(cron, lastSyncMs, now);
1462
+ } else {
1463
+ const expression = (cron.expression || "").trim();
1464
+ if (!expression) {
1324
1465
  continue;
1466
+ }
1467
+ try {
1468
+ due = isCronDue({
1469
+ expression,
1470
+ timezone: cron.timezone,
1471
+ lastSyncMs,
1472
+ now
1473
+ });
1474
+ } catch (err) {
1475
+ console.error(
1476
+ `cron: invalid cron expression for data source ${dataSource.id}: "${expression}":`,
1477
+ String(err)
1478
+ );
1479
+ continue;
1480
+ }
1325
1481
  }
1326
- let lastSyncMs = dataSource.syncedAt ? dataSource.syncedAt.toMillis() : 0;
1327
- if (lastSyncMs > now) {
1328
- lastSyncMs = now;
1329
- }
1330
- if (now - lastSyncMs < intervalMs) {
1482
+ if (!due) {
1331
1483
  continue;
1332
1484
  }
1333
1485
  try {
@@ -2151,6 +2303,28 @@ function unmarshalArray(arrObject) {
2151
2303
  function isObject(data) {
2152
2304
  return typeof data === "object" && !Array.isArray(data) && data !== null;
2153
2305
  }
2306
+ function isIntervalDue(cron, lastSyncMs, now) {
2307
+ const interval = Number(cron.interval);
2308
+ if (!cron.unit || !Number.isFinite(interval) || interval <= 0 || !Number.isInteger(interval)) {
2309
+ return false;
2310
+ }
2311
+ let intervalMs = interval;
2312
+ switch (cron.unit) {
2313
+ case "minutes":
2314
+ intervalMs *= 60 * 1e3;
2315
+ break;
2316
+ case "hours":
2317
+ intervalMs *= 60 * 60 * 1e3;
2318
+ break;
2319
+ case "days":
2320
+ intervalMs *= 24 * 60 * 60 * 1e3;
2321
+ break;
2322
+ default:
2323
+ return false;
2324
+ }
2325
+ const normalizedLastSyncMs = lastSyncMs > now ? now : lastSyncMs;
2326
+ return now - normalizedLastSyncMs >= intervalMs;
2327
+ }
2154
2328
  function parseSpreadsheetUrl(url) {
2155
2329
  if (!url.startsWith("https://docs.google.com/spreadsheets/d/")) {
2156
2330
  return null;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  RootCMSClient,
3
3
  getCmsPlugin
4
- } from "./chunk-ETHWZSNM.js";
4
+ } from "./chunk-PLSNGCYU.js";
5
5
 
6
6
  // core/cron.ts
7
7
  import { Timestamp as Timestamp3 } from "firebase-admin/firestore";
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  RootCMSClient,
6
6
  getCmsPlugin,
7
7
  unmarshalData
8
- } from "./chunk-ETHWZSNM.js";
8
+ } from "./chunk-PLSNGCYU.js";
9
9
  import "./chunk-J72JK4KR.js";
10
10
  import "./chunk-MLKGABMK.js";
11
11
 
package/dist/client.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  translationsForLocale,
13
13
  unmarshalArray,
14
14
  unmarshalData
15
- } from "./chunk-ETHWZSNM.js";
15
+ } from "./chunk-PLSNGCYU.js";
16
16
  import "./chunk-J72JK4KR.js";
17
17
  import "./chunk-MLKGABMK.js";
18
18
  export {
@@ -39,10 +39,38 @@ export type DocMode = 'draft' | 'published';
39
39
  export type UserRole = 'ADMIN' | 'EDITOR' | 'CONTRIBUTOR' | 'VIEWER';
40
40
  export type HttpMethod = 'GET' | 'POST';
41
41
  export type CronUnit = 'minutes' | 'hours' | 'days';
42
+ /**
43
+ * Data source sync schedule type.
44
+ *
45
+ * - `interval`: sync every N minutes/hours/days (uses `interval` + `unit`).
46
+ * - `daily` / `weekly` / `custom`: sync on a specific cron schedule (uses
47
+ * `expression` + `timezone`). `daily` and `weekly` are UI presets that are
48
+ * stored as standard cron expressions; `custom` allows an arbitrary
49
+ * expression.
50
+ */
51
+ export type CronScheduleType = 'interval' | 'daily' | 'weekly' | 'custom';
42
52
  export interface DataSourceCron {
43
53
  enabled: boolean;
44
- interval: number;
45
- unit: CronUnit;
54
+ /**
55
+ * Scheduling mode. Defaults to `interval` when unset (for backwards
56
+ * compatibility with data sources created before specific schedules were
57
+ * supported).
58
+ */
59
+ schedule?: CronScheduleType;
60
+ /** Interval value, used when `schedule` is `interval`. */
61
+ interval?: number;
62
+ /** Interval unit, used when `schedule` is `interval`. */
63
+ unit?: CronUnit;
64
+ /**
65
+ * Standard 5-field cron expression, used when `schedule` is `daily`,
66
+ * `weekly`, or `custom`, e.g. `0 19 * * *` for "every day at 7pm".
67
+ */
68
+ expression?: string;
69
+ /**
70
+ * IANA timezone used to evaluate `expression`, e.g. `America/New_York`.
71
+ * Defaults to UTC when unset.
72
+ */
73
+ timezone?: string;
46
74
  autoPublish?: boolean;
47
75
  }
48
76
  export interface DataSource {
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Utilities for evaluating standard cron expressions for data source
3
+ * scheduling.
4
+ *
5
+ * Supports the standard 5-field cron format:
6
+ *
7
+ * ┌───────────── minute (0 - 59)
8
+ * │ ┌───────────── hour (0 - 23)
9
+ * │ │ ┌───────────── day of month (1 - 31)
10
+ * │ │ │ ┌───────────── month (1 - 12)
11
+ * │ │ │ │ ┌───────────── day of week (0 - 6, where 0 and 7 are Sunday)
12
+ * │ │ │ │ │
13
+ * * * * * *
14
+ *
15
+ * Each field supports `*`, single values, ranges (`a-b`), lists (`a,b,c`),
16
+ * steps (`* /n`, `a-b/n`), and combinations thereof. Expressions are evaluated
17
+ * against a specific IANA timezone so that schedules like "every day at 7pm"
18
+ * fire at the expected wall-clock time.
19
+ */
20
+ interface ParsedCron {
21
+ minute: Set<number>;
22
+ hour: Set<number>;
23
+ dayOfMonth: Set<number>;
24
+ month: Set<number>;
25
+ dayOfWeek: Set<number>;
26
+ /** Whether the day-of-month field was restricted (not `*`). */
27
+ dayOfMonthRestricted: boolean;
28
+ /** Whether the day-of-week field was restricted (not `*`). */
29
+ dayOfWeekRestricted: boolean;
30
+ }
31
+ /**
32
+ * Parses a standard 5-field cron expression. Throws if the expression is
33
+ * malformed.
34
+ */
35
+ export declare function parseCronExpression(expression: string): ParsedCron;
36
+ /**
37
+ * Returns true if the given cron expression is syntactically valid.
38
+ */
39
+ export declare function isValidCronExpression(expression: string): boolean;
40
+ export interface IsCronDueOptions {
41
+ /** Standard 5-field cron expression, e.g. "0 19 * * *". */
42
+ expression: string;
43
+ /** IANA timezone used to evaluate the expression. Defaults to "UTC". */
44
+ timezone?: string;
45
+ /** Timestamp (ms) of the last successful sync, or 0 if never synced. */
46
+ lastSyncMs: number;
47
+ /** Current time (ms). */
48
+ now: number;
49
+ }
50
+ /**
51
+ * Determines whether a cron-scheduled data source is due for sync.
52
+ *
53
+ * Uses catch-up semantics: the source is due if there is any scheduled minute
54
+ * in the window `(lastSyncMs, now]` that matches the cron expression. This
55
+ * ensures a missed cron tick (e.g. the scheduler was briefly down at the
56
+ * scheduled minute) still triggers a sync on a later tick.
57
+ *
58
+ * When the source has never synced (`lastSyncMs <= 0`), only the current minute
59
+ * is considered so that enabling a schedule does not trigger an unexpected
60
+ * immediate backfill — the source will first sync at its next scheduled time.
61
+ */
62
+ export declare function isCronDue(options: IsCronDueOptions): boolean;
63
+ export {};
@@ -3,7 +3,7 @@ import { App } from 'firebase-admin/app';
3
3
  import { Firestore } from 'firebase-admin/firestore';
4
4
  import { type AiConfig } from './ai.js';
5
5
  import { type CMSCheck } from './checks.js';
6
- import { Action } from './client.js';
6
+ import { Action, UserRole } from './client.js';
7
7
  import { type CMSNotificationService } from './services-notifications.js';
8
8
  import { type CMSTranslationService } from './translations.js';
9
9
  export type { AiConfig, AiModelConfig, AiProvider } from './ai.js';
@@ -19,6 +19,11 @@ export type { CMSTranslationService, RootLocale, TranslationExportResult, Transl
19
19
  export type CMSBuiltInSidebarTool = 'home' | 'content' | 'releases' | 'data' | 'assets' | 'translations' | 'ai' | 'settings';
20
20
  export interface CMSUser {
21
21
  email: string;
22
+ /**
23
+ * The user's role within the project's ACL, or `null` if unassigned. This is
24
+ * populated on `req.user` for every authenticated request.
25
+ */
26
+ role?: UserRole | null;
22
27
  }
23
28
  /**
24
29
  * @deprecated The `experiments.ai` flag is now used only as a sidebar toggle.
package/dist/core.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  translationsForLocale,
16
16
  unmarshalArray,
17
17
  unmarshalData
18
- } from "./chunk-ETHWZSNM.js";
18
+ } from "./chunk-PLSNGCYU.js";
19
19
  import {
20
20
  normalizeSlug
21
21
  } from "./chunk-J72JK4KR.js";
package/dist/functions.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  runCronJobs
3
- } from "./chunk-6CTCV2PN.js";
4
- import "./chunk-ETHWZSNM.js";
3
+ } from "./chunk-Z2CTQVYR.js";
4
+ import "./chunk-PLSNGCYU.js";
5
5
  import "./chunk-J72JK4KR.js";
6
6
  import "./chunk-MLKGABMK.js";
7
7
 
package/dist/plugin.js CHANGED
@@ -12,16 +12,16 @@ import {
12
12
  serializeAiConfig,
13
13
  summarizeDiff,
14
14
  translateString
15
- } from "./chunk-MUXSUGFF.js";
15
+ } from "./chunk-NIBAHWGP.js";
16
16
  import {
17
17
  SearchIndexService,
18
18
  runCronJobs
19
- } from "./chunk-6CTCV2PN.js";
19
+ } from "./chunk-Z2CTQVYR.js";
20
20
  import {
21
21
  RootCMSClient,
22
22
  parseDocId,
23
23
  unmarshalData
24
- } from "./chunk-ETHWZSNM.js";
24
+ } from "./chunk-PLSNGCYU.js";
25
25
  import "./chunk-J72JK4KR.js";
26
26
  import "./chunk-MLKGABMK.js";
27
27
 
@@ -1389,7 +1389,8 @@ function cmsPlugin(options) {
1389
1389
  console.log("session failed: user is not in the firestore acl list");
1390
1390
  return null;
1391
1391
  }
1392
- return { email: jwt.email };
1392
+ const role = await cmsClient.getUserRole(jwt.email).catch(() => null);
1393
+ return { email: jwt.email, role };
1393
1394
  }
1394
1395
  try {
1395
1396
  const jwt = await auth.verifySessionCookie(sessionCookie, true);
@@ -1416,7 +1417,8 @@ function cmsPlugin(options) {
1416
1417
  console.log("session failed: user is not in the firestore acl list");
1417
1418
  return null;
1418
1419
  }
1419
- return { email: jwt.email };
1420
+ const role = await cmsClient.getUserRole(jwt.email).catch(() => null);
1421
+ return { email: jwt.email, role };
1420
1422
  } catch (err) {
1421
1423
  console.error("failed to verify jwt token");
1422
1424
  console.error(err);
package/dist/richtext.js CHANGED
@@ -63,7 +63,7 @@ var ALLOWED_ATTRIBUTES = {
63
63
  img: ["src", "alt", "title", "width", "height"]
64
64
  };
65
65
  var COMMON_OPTIONS = {
66
- allowedSchemes: ["http", "https", "mailto"],
66
+ allowedSchemes: ["http", "https", "mailto", "tel", "sms"],
67
67
  allowedSchemesByTag: {
68
68
  img: ["http", "https", "data"]
69
69
  },
@@ -44,6 +44,23 @@ export interface RootEmbedMessage {
44
44
  error?: string;
45
45
  };
46
46
  }
47
+ /**
48
+ * Posted from an iframed sidebar tool to the parent CMS window to report the
49
+ * tool's current location. The CMS mirrors this into its own address bar so
50
+ * the tool's sub-path, query params, and hash survive a refresh and can be
51
+ * deep-linked/shared.
52
+ *
53
+ * Same-origin tools are synced automatically (the CMS reads
54
+ * `contentWindow.location` directly), so this message only needs to be posted
55
+ * by cross-origin tools, which the browser prevents the CMS from reading.
56
+ */
57
+ export interface RootToolLocationMessage {
58
+ rootTool: {
59
+ type: 'locationchange';
60
+ /** The tool's current URL, absolute or relative to the tool's origin. */
61
+ url: string;
62
+ };
63
+ }
47
64
  /** Requests that the doc editor scroll to (focus) a specific field. */
48
65
  export interface ScrollToDeeplinkMessage {
49
66
  scrollToDeeplink: {
@@ -66,6 +83,8 @@ export interface HighlightNodeMessage {
66
83
  }
67
84
  /** Returns whether a postMessage payload is a {@link RootEmbedMessage}. */
68
85
  export declare function isRootEmbedMessage(data: unknown): data is RootEmbedMessage;
86
+ /** Returns whether a postMessage payload is a {@link RootToolLocationMessage}. */
87
+ export declare function isRootToolLocationMessage(data: unknown): data is RootToolLocationMessage;
69
88
  /** Returns whether a postMessage payload is a {@link ScrollToDeeplinkMessage}. */
70
89
  export declare function isScrollToDeeplinkMessage(data: unknown): data is ScrollToDeeplinkMessage;
71
90
  /** Returns whether a postMessage payload is a {@link HighlightNodeMessage}. */
@@ -0,0 +1 @@
1
+ import{a}from"./chunk-XBYMUPZF.js";import"./chunk-LKTN7YVM.js";import"./chunk-AAR7DBEN.js";import"./chunk-7OU4ERTS.js";import"./chunk-YDO2EDVY.js";import"./chunk-5MV6XVGA.js";import{a as i}from"./chunk-5F5XJPKD.js";import{c as o}from"./chunk-P2RVKNG4.js";import"./chunk-AAHM6RYJ.js";import"./chunk-ISUGBMXC.js";import"./chunk-DZYYWCHX.js";import"./chunk-XOWWPE7X.js";import"./chunk-GJ7ES2LA.js";import{fd as t}from"./chunk-Y7RYSRP7.js";import"./chunk-KUCVLANF.js";import"./chunk-7SK47HHZ.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";function n(r){return i("AI"),t(o,{children:t(a,{variant:"page",initialChatId:r.chatId})})}export{n as AIPage};
@@ -0,0 +1 @@
1
+ import{a as p}from"./chunk-OKNBFTYR.js";import{a as d}from"./chunk-P57AFOMR.js";import"./chunk-H5JL376P.js";import{a as n}from"./chunk-I3H465Z6.js";import"./chunk-LS2JH5QF.js";import"./chunk-HKCOKHA2.js";import"./chunk-W5DOO6ID.js";import"./chunk-ZQDUWURP.js";import"./chunk-U5OXKKBN.js";import"./chunk-ZYSR5UIW.js";import{a as i}from"./chunk-HWON6MV4.js";import{a as g}from"./chunk-5F5XJPKD.js";import{c as m}from"./chunk-P2RVKNG4.js";import"./chunk-AAHM6RYJ.js";import"./chunk-ISUGBMXC.js";import"./chunk-DZYYWCHX.js";import"./chunk-XOWWPE7X.js";import{d as o}from"./chunk-GJ7ES2LA.js";import{a as t,c as r,fd as e,j as h}from"./chunk-Y7RYSRP7.js";import"./chunk-KUCVLANF.js";import"./chunk-7SK47HHZ.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";h();function k(){g("Asset Library");let[l,u]=p("folder",""),{query:c,route:f}=o(),[a]=t(c.asset||"");return r(()=>{if(a){let s=new URL(window.location.href);s.searchParams.delete("asset"),f(s.pathname+s.search,!0)}},[]),e(m,{children:e("div",{className:"AssetsPage","data-testid":"assets-page",children:[e("div",{className:"AssetsPage__header",children:[e(n,{size:"h1",children:"Asset Library"}),e(i,{as:"p",children:"The asset library can be used to store and organize files used throughout the project. Replacing a file in the asset library automatically updates all draft docs that use it (requires re-publishing)."})]}),e(d,{className:"AssetsPage__browser",mode:"manage",folder:l,onFolderChange:u,initialAssetId:a})]})})}export{k as AssetsPage};
@@ -0,0 +1 @@
1
+ import{a as no}from"./chunk-SBAY52SC.js";import{a as ao}from"./chunk-TCWN5YRW.js";import{f as B}from"./chunk-A2JZYRE3.js";import{a as to,f as lo}from"./chunk-PY4JH5SN.js";import"./chunk-PLTI4NG7.js";import{b as G,d as J,e as Q,m as S,o as A}from"./chunk-CEWU5VSJ.js";import"./chunk-B74M7NEV.js";import"./chunk-YIKHJONY.js";import{a as y}from"./chunk-MALK5YEV.js";import"./chunk-JDW4UORS.js";import{a as z}from"./chunk-BOIY4J2R.js";import"./chunk-Z2CRNO3I.js";import"./chunk-XYXL5HSZ.js";import"./chunk-ATTW36EM.js";import"./chunk-7OU4ERTS.js";import"./chunk-YDO2EDVY.js";import{b as h}from"./chunk-5MV6XVGA.js";import"./chunk-LS2JH5QF.js";import"./chunk-HKCOKHA2.js";import{b as eo}from"./chunk-W5DOO6ID.js";import{a as oo}from"./chunk-ZQDUWURP.js";import"./chunk-U5OXKKBN.js";import"./chunk-ZYSR5UIW.js";import"./chunk-HWON6MV4.js";import{a as co}from"./chunk-5F5XJPKD.js";import{c as io}from"./chunk-P2RVKNG4.js";import"./chunk-AAHM6RYJ.js";import{N as Y}from"./chunk-ISUGBMXC.js";import"./chunk-DZYYWCHX.js";import{B as Z,z as V}from"./chunk-XOWWPE7X.js";import{d as X,e as w}from"./chunk-GJ7ES2LA.js";import{$a as W,Ca as $,Fa as j,Sa as F,Ta as H,V as k,W as E,_a as K,a as M,bb as q,c as U,da as D,eb as I,fd as o,j as fo,qa as P,za as R}from"./chunk-Y7RYSRP7.js";import{d as N}from"./chunk-KUCVLANF.js";import"./chunk-7SK47HHZ.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";fo();var O=[{value:"comfortable",label:"Comfortable",Icon:F},{value:"compact",label:"Compact",Icon:H}];function L(e){let{route:c}=X(),l=window.__ROOT_CTX.rootConfig.projectId;co(e.collection?e.collection:"Content"),U(()=>{let n=window.__ROOT_CTX.collections,a=`root-cms::${l}::last_collection`;if(e.collection)window.localStorage.setItem(a,e.collection);else{let s=window.localStorage.getItem(a);if(s&&n[s]){c(`/cms/content/${s}`);return}if(window.__ROOT_CTX.collections.Pages){c("/cms/content/Pages");return}let d=Object.keys(window.__ROOT_CTX.collections)||[];if(d.length>0){let u=d[0];c(`/cms/content/${u}`);return}console.warn("no collections")}},[e.collection,l]);let i=window.__ROOT_CTX.collections;return o(io,{children:o("div",{className:"CollectionPage",children:o("div",{className:"CollectionPage__layout",children:[o("div",{className:"CollectionPage__side",children:o(no,{collections:i,activeCollectionId:e.collection,projectId:l})}),o("div",{className:"CollectionPage__main",children:e.collection&&o(L.Collection,{collection:e.collection},e.collection)})]})})})}L.Collection=e=>{let{roles:c}=oo(),l=window.firebase.user.email||"",i=eo(c,l),[n,a]=y(`root::CollectionPage:${e.collection}:orderBy`,"modifiedAt"),[s,d]=y(`root::CollectionPage:${e.collection}:showArchived`,!1),[u,v]=M(!1),r=window.__ROOT_CTX.collections[e.collection];if(!r)return route("/cms/content"),o(N,{});let t=!!r.viewOptions?.compact,[_,g]=y("root::CollectionPage:density","comfortable"),m=t?"compact":_,f=m==="compact",C=[{value:"slug",label:"A-Z"},{value:"slugDesc",label:"Z-A"},{value:"title",label:"Title (A-Z)"},{value:"titleDesc",label:"Title (Z-A)"},{value:"newest",label:"Newest"},{value:"oldest",label:"Oldest"},{value:"modifiedAt",label:"Last modified"},{value:"modifiedAtAsc",label:"Least recently modified"},...r.sortOptions?.map(p=>({value:p.id,label:p.label}))||[]],[b,ro,T]=to(e.collection,{orderBy:n,includeArchived:s}),go=b&&T.length===0,mo=!b&&T.length===0;return o(N,{children:[o(lo,{collection:e.collection,opened:u,onClose:()=>v(!1)}),o("div",{className:"CollectionPage__collection",children:o("div",{className:"CollectionPage__collection__docsTab",children:[o("div",{className:"CollectionPage__collection__docsTab__header",children:[o("div",{className:"CollectionPage__collection__docsTab__header__title",children:r.name||e.collection}),o("div",{className:"CollectionPage__collection__docsTab__controls",children:[o("div",{className:"CollectionPage__collection__docsTab__controls__showArchived",children:o($,{size:"sm",color:"dark",label:"Show archived:",checked:s,onChange:p=>d(!!p.currentTarget.checked)})}),o("div",{className:"CollectionPage__collection__docsTab__controls__sort",children:[o("div",{className:"CollectionPage__collection__docsTab__controls__sort__label",children:"Sort:"}),o(R,{size:"xs",value:n,onChange:p=>a(p||"modifiedAt"),data:C})]}),o(po,{density:m,locked:t,onChange:g}),o("div",{className:"CollectionPage__collection__docsTab__controls__newDoc",children:o(z,{label:"You don't have access to create new documents",condition:!i,children:o(D,{color:"dark",size:"xs",leftIcon:o(I,{size:16}),onClick:()=>v(!0),disabled:!i,children:"New"})})})]})]}),o(ao,{className:"CollectionPage__collection__docsTab__content",children:go?o("div",{className:"CollectionPage__collection__docsTab__content__loading",children:o(k,{color:"gray",size:"xl"})}):mo?o("div",{class:"CollectionPage__collection__docsEmpty",children:[o("div",{class:"CollectionPage__collection__docsEmpty__title",children:"Collection is empty."}),o("div",{class:"CollectionPage__collection__docsEmpty__button",children:o(z,{label:"You don't have access to create new documents",condition:!i,children:o(D,{color:"dark",size:"xs",leftIcon:o(I,{size:16}),onClick:()=>v(!0),disabled:!i,children:"New"})})})]}):o("div",{className:"CollectionPage__collection__docsTab__content__list",children:[b&&o("div",{className:"CollectionPage__collection__docsTab__content__reloading",children:o(k,{color:"gray",size:"md"})}),o("div",{className:w("CollectionPage__collection__docsTab__content__items",b&&"CollectionPage__collection__docsTab__content__items--loading"),children:o(L.DocsList,{collection:e.collection,docs:T,compact:f,orderBy:n,onSort:a,reloadDocs:()=>ro()})})]})})]})})]})};function po(e){let l=(O.find(n=>n.value===e.density)||O[0]).Icon,i=o(E,{variant:"default",color:"gray",size:30,disabled:e.locked,"aria-label":"Change view density",children:o(l,{size:16})});return o("div",{className:"CollectionPage__collection__docsTab__controls__density",children:[o("div",{className:"CollectionPage__collection__docsTab__controls__density__label",children:"View:"}),e.locked?o(j,{label:"This collection is locked to the compact view",transition:"pop",withArrow:!0,children:i}):o(P,{position:"bottom",placement:"end",control:i,children:[o(P.Label,{children:"View density"}),O.map(n=>{let a=n.Icon,s=n.value===e.density;return o(P.Item,{icon:o(a,{size:18}),rightSection:s?o(K,{size:14,stroke:2.5}):void 0,onClick:()=>e.onChange(n.value),children:n.label},n.value)})]})]})}var so={slug:{asc:"slug",desc:"slugDesc",defaultDir:"asc"},title:{asc:"title",desc:"titleDesc",defaultDir:"asc"},created:{asc:"oldest",desc:"newest",defaultDir:"desc"},modified:{asc:"modifiedAtAsc",desc:"modifiedAt",defaultDir:"desc"}};function uo(e,c){let l=so[e];return!l||!c?null:c===l.asc?"asc":c===l.desc?"desc":null}function x(e){let c=so[e.column],l=uo(e.column,e.orderBy),i=l!==null;function n(){if(!e.onSort)return;let a;l==="asc"?a="desc":l==="desc"?a="asc":a=c.defaultDir,e.onSort(c[a])}return o("button",{type:"button",className:w("CollectionPage__collection__docsList__header__cell","CollectionPage__collection__docsList__header__cell--sortable",i&&"CollectionPage__collection__docsList__header__cell--active"),onClick:n,"aria-label":`Sort by ${e.label}`,children:[o("span",{children:e.label}),i&&(l==="asc"?o(q,{size:12,stroke:2.5}):o(W,{size:12,stroke:2.5}))]})}function vo(e,c){let l=e?.sys||{},i=0;return(!l.publishedAt||!l.modifiedAt||l.modifiedAt>l.publishedAt)&&i++,l.publishedAt&&i++,l.scheduledAt&&i++,Y(e)&&i++,l.archivedAt&&i++,c>0&&i++,i}L.DocsList=e=>{let c=e.collection,l=window.__ROOT_CTX.collections[e.collection];if(!l)throw new Error(`could not find collection: ${c}`);let i=!!l.url,n=!!e.compact,a=e.docs||[],{getReleasesForDoc:s}=G(),d=n?a.reduce((t,_)=>Math.max(t,vo(_,s(_.id).length)),1):1,u=d<=1?80:d===2?150:215;function v(t){return i?Q({collectionId:c,slug:t}):""}function r(t){(t.action==="archive"||t.action==="copy"||t.action==="unarchive"||t.action==="delete"||t.action==="unpublish"||t.action==="locked"||t.action==="unlocked")&&e.reloadDocs()}return n?o("div",{className:w("CollectionPage__collection__docsList","CollectionPage__collection__docsList--compact"),style:{"--docs-status-width":`${u}px`},children:[o("div",{className:"CollectionPage__collection__docsList__header",children:[o("div",{className:"CollectionPage__collection__docsList__header__image"}),o(x,{column:"slug",label:"Slug",orderBy:e.orderBy,onSort:e.onSort}),o(x,{column:"title",label:"Title",orderBy:e.orderBy,onSort:e.onSort}),o("div",{className:"CollectionPage__collection__docsList__header__cell",children:"Status"}),o(x,{column:"created",label:"Created",orderBy:e.orderBy,onSort:e.onSort}),o(x,{column:"modified",label:"Modified",orderBy:e.orderBy,onSort:e.onSort}),o("div",{className:"CollectionPage__collection__docsList__header__controls"})]}),a.map(t=>{let _=`/cms/content/${c}/${t.slug}`,g=t.fields||{},m=h(g,l.preview?.title||"meta.title"),f=h(g,l.preview?.image||"meta.image")||l.preview?.defaultImage;return o("div",{className:"CollectionPage__collection__docsList__doc",children:[o("a",{className:"CollectionPage__collection__docsList__doc__image",href:_,children:o(A,{file:f,width:40,height:30,withPlaceholder:!f?.src})}),o("a",{className:"CollectionPage__collection__docsList__doc__docId",href:_,children:t.slug}),o("a",{className:"CollectionPage__collection__docsList__doc__title",href:_,children:m||"[UNTITLED]"}),o("div",{className:"CollectionPage__collection__docsList__doc__badges",children:o(S,{doc:t})}),o("div",{className:"CollectionPage__collection__docsList__doc__timestamp",children:_o(t?.sys?.createdAt,t?.sys?.createdBy)}),o("div",{className:"CollectionPage__collection__docsList__doc__timestamp",children:_o(t?.sys?.modifiedAt,t?.sys?.modifiedBy)}),o("div",{className:"CollectionPage__collection__docsList__doc__controls",children:o(B,{docId:t.id,data:t,onAction:r})})]},t.id)})]}):o("div",{className:"CollectionPage__collection__docsList",children:a.map(t=>{let _=`/cms/content/${c}/${t.slug}`,g=v(t.slug),m=t.fields||{},f=h(m,l.preview?.title||"meta.title"),C=h(m,l.preview?.image||"meta.image")||l.preview?.defaultImage;return o("div",{className:"CollectionPage__collection__docsList__doc",children:[o("div",{className:"CollectionPage__collection__docsList__doc__image",children:o("a",{href:_,children:o(A,{file:C,width:120,height:90,withPlaceholder:!C?.src})})}),o("a",{className:"CollectionPage__collection__docsList__doc__content",href:_,children:[o("div",{className:"CollectionPage__collection__docsList__doc__content__header",children:[o("div",{className:"CollectionPage__collection__docsList__doc__content__header__docId",children:t.id}),o(S,{doc:t})]}),o("div",{className:"CollectionPage__collection__docsList__doc__content__title",children:f||"[UNTITLED]"}),i&&g&&o("div",{className:"CollectionPage__collection__docsList__doc__content__url",children:g})]}),o("div",{className:"CollectionPage__collection__docsList__doc__controls",children:o(B,{docId:t.id,data:t,onAction:r})})]},t.id)})})};function _o(e,c){return!e||typeof e.toMillis!="function"?o("span",{className:"CollectionPage__collection__docsList__doc__timestamp__value",children:"\u2014"}):o(J,{message:Z(e),user:c,children:o("span",{className:"CollectionPage__collection__docsList__doc__timestamp__value",children:V(e.toMillis())})})}export{L as CollectionPage};
@@ -0,0 +1 @@
1
+ import{b as s}from"./chunk-YIKHJONY.js";import"./chunk-7OU4ERTS.js";import"./chunk-5MV6XVGA.js";import"./chunk-HWON6MV4.js";import{a as m}from"./chunk-5F5XJPKD.js";import{c as d}from"./chunk-P2RVKNG4.js";import"./chunk-AAHM6RYJ.js";import"./chunk-ISUGBMXC.js";import"./chunk-DZYYWCHX.js";import"./chunk-XOWWPE7X.js";import{d as t}from"./chunk-GJ7ES2LA.js";import{fd as a,g as i,j as p}from"./chunk-Y7RYSRP7.js";import"./chunk-KUCVLANF.js";import"./chunk-7SK47HHZ.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";p();function h(){let{query:r}=t();m("Compare");let o=i(()=>n(r.left||""),[r.left]),e=i(()=>n(r.right||""),[r.right]);return a(d,{children:a("div",{className:"ComparePage",children:o&&e?a(s,{className:"ComparePage__diff",left:o,right:e}):a("div",{className:"ComparePage__error",children:"Invalid params, double check query params."})})})}function n(r){let[o,e]=String(r).split("@");return o&&e?{docId:o,versionId:e}:null}export{h as ComparePage};
@@ -1 +1 @@
1
- import{a as m}from"./chunk-5KSGCGYY.js";import"./chunk-535JU7FU.js";import{a as y}from"./chunk-HXSB6YB2.js";import{a as k}from"./chunk-OWYV43BT.js";import{a as x}from"./chunk-QFDU6FXP.js";import{a as w}from"./chunk-UJX25CXD.js";import{b as v}from"./chunk-RN4XVVWD.js";import"./chunk-ATTW36EM.js";import{b as P}from"./chunk-W5DOO6ID.js";import{a as T}from"./chunk-YBHFGQ3Z.js";import"./chunk-PIJ3E47R.js";import{a as p}from"./chunk-NCZD5IWS.js";import{a as B}from"./chunk-QLIIVG5Q.js";import{c as A}from"./chunk-OWBT6YUM.js";import"./chunk-AAHM6RYJ.js";import{z as u}from"./chunk-XOWWPE7X.js";import"./chunk-WASID5UU.js";import{Ca as b,Da as D,Fa as i,Ga as e,Qc as S,V as g,W as f,a as d,ba as s,c as h,da as _,g as c,j as G}from"./chunk-WRA24P66.js";import"./chunk-KUCVLANF.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";G();var z={transition:"pop"};function O(a){let o=a.dataSource;return N(o.archivedAt)?e(i,{...z,label:`Archived ${L(o.archivedAt)} by ${o.archivedBy}`,children:e(s,{size:"xs",color:"gray",variant:"filled",children:"Archived"})}):N(o.publishedAt)?e(i,{...z,label:`Published ${L(o.publishedAt)} by ${o.publishedBy}`,children:e(s,{size:"xs",variant:"gradient",gradient:{from:"teal",to:"lime",deg:105},children:"Published"})}):e(s,{size:"xs",variant:"gradient",gradient:{from:"indigo",to:"cyan"},children:"Unpublished"})}function N(a){return!!(a&&a.toMillis)}function L(a){return a?.toMillis?u(a.toMillis()):u(new Date().getTime())}function E(){B("Data Sources");let{roles:a}=T(),o=window.firebase.user.email||"",r=P(a,o);return e(A,{children:e("div",{className:"DataPage",children:[e("div",{className:"DataPage__header",children:[e(y,{size:"h1",children:"Data Sources"}),e(p,{as:"p",children:"Add data sources to sync data from external services, like Google Sheets."}),e("div",{className:"DataPage__header__buttons",children:e(w,{label:"You don't have access to create new data sources",condition:!r,children:e(_,{component:"a",color:"blue",size:"xs",href:"/cms/data/new",disabled:!r,style:r?void 0:{pointerEvents:"none"},children:"New data source"})})})]}),e(E.DataSourcesTable,{})]})})}E.DataSourcesTable=()=>{let[a,o]=d(!0),[r,I]=d([]),[l,$]=x("root::DataPage:showArchived",!1),C=t=>!!t&&t.startsWith("https://docs.google.com/spreadsheets/");async function M(){let t=await v();I(t),o(!1)}let H=c(()=>r.some(t=>!!t.archivedAt),[r]),n=c(()=>r.filter(t=>{let U=!!t.archivedAt;return l?!0:!U}),[r,l]);return h(()=>{M()},[]),e("div",{className:"DataPage__DataSourcesTable",children:[!a&&H&&e("div",{className:"DataPage__DataSourcesTable__filters",children:e(b,{size:"sm",color:"dark",label:"Show archived",checked:l,onChange:t=>$(!!t.currentTarget.checked)})}),e(k,{children:[a&&e(g,{color:"gray",size:"xl"}),!a&&n.length>0&&e(D,{verticalSpacing:"xs",striped:!0,highlightOnHover:!0,fontSize:"xs",children:[e("thead",{children:e("tr",{children:[e("th",{children:"id"}),e("th",{children:"description"}),e("th",{children:"type"}),e("th",{children:"url"}),e("th",{children:"status"}),e("th",{children:"last synced"}),e("th",{children:"last published"})]})}),e("tbody",{children:n.map(t=>e("tr",{children:[e("td",{children:e("a",{href:`/cms/data/${t.id}`,children:t.id})}),e("td",{children:t.description||""}),e("td",{children:t.type}),e("td",{children:C(t.url)?e("div",{className:"DataPage__DataSourcesTable__url",children:[e(i,{label:"Open spreadsheet",children:e(f,{component:"a",href:t.url,target:"_blank",rel:"noreferrer",variant:"filled",color:"green",size:"sm","aria-label":"Open spreadsheet",children:e(S,{size:16,stroke:"2.25"})})}),e("a",{className:"DataPage__DataSourcesTable__url__text",href:t.url,target:"_blank",rel:"noreferrer",children:t.url})]}):t.url||""}),e("td",{children:e(O,{dataSource:t})}),e("td",{children:e(m,{dataSource:t,action:"sync"})}),e("td",{children:e(m,{dataSource:t,action:"publish"})})]},t.id))})]}),!a&&n.length===0&&e(p,{as:"p",children:"No data sources found for this filter."})]})]})};export{E as DataPage};
1
+ import{a as m}from"./chunk-HUYP5U4K.js";import"./chunk-ILFWJKSS.js";import{a as y}from"./chunk-I3H465Z6.js";import{a as k}from"./chunk-TCWN5YRW.js";import{a as x}from"./chunk-MALK5YEV.js";import{a as w}from"./chunk-BOIY4J2R.js";import{b as v}from"./chunk-Z2CRNO3I.js";import"./chunk-ATTW36EM.js";import{b as P}from"./chunk-W5DOO6ID.js";import{a as T}from"./chunk-ZQDUWURP.js";import"./chunk-ZYSR5UIW.js";import{a as p}from"./chunk-HWON6MV4.js";import{a as B}from"./chunk-5F5XJPKD.js";import{c as A}from"./chunk-P2RVKNG4.js";import"./chunk-AAHM6RYJ.js";import{z as u}from"./chunk-XOWWPE7X.js";import"./chunk-GJ7ES2LA.js";import{Ca as b,Da as D,Fa as i,Qc as S,V as g,W as f,a as d,ba as s,c as h,da as _,fd as e,g as c,j as G}from"./chunk-Y7RYSRP7.js";import"./chunk-KUCVLANF.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";G();var z={transition:"pop"};function O(a){let o=a.dataSource;return N(o.archivedAt)?e(i,{...z,label:`Archived ${L(o.archivedAt)} by ${o.archivedBy}`,children:e(s,{size:"xs",color:"gray",variant:"filled",children:"Archived"})}):N(o.publishedAt)?e(i,{...z,label:`Published ${L(o.publishedAt)} by ${o.publishedBy}`,children:e(s,{size:"xs",variant:"gradient",gradient:{from:"teal",to:"lime",deg:105},children:"Published"})}):e(s,{size:"xs",variant:"gradient",gradient:{from:"indigo",to:"cyan"},children:"Unpublished"})}function N(a){return!!(a&&a.toMillis)}function L(a){return a?.toMillis?u(a.toMillis()):u(new Date().getTime())}function E(){B("Data Sources");let{roles:a}=T(),o=window.firebase.user.email||"",r=P(a,o);return e(A,{children:e("div",{className:"DataPage",children:[e("div",{className:"DataPage__header",children:[e(y,{size:"h1",children:"Data Sources"}),e(p,{as:"p",children:"Add data sources to sync data from external services, like Google Sheets."}),e("div",{className:"DataPage__header__buttons",children:e(w,{label:"You don't have access to create new data sources",condition:!r,children:e(_,{component:"a",color:"blue",size:"xs",href:"/cms/data/new",disabled:!r,style:r?void 0:{pointerEvents:"none"},children:"New data source"})})})]}),e(E.DataSourcesTable,{})]})})}E.DataSourcesTable=()=>{let[a,o]=d(!0),[r,I]=d([]),[l,$]=x("root::DataPage:showArchived",!1),C=t=>!!t&&t.startsWith("https://docs.google.com/spreadsheets/");async function M(){let t=await v();I(t),o(!1)}let H=c(()=>r.some(t=>!!t.archivedAt),[r]),n=c(()=>r.filter(t=>{let U=!!t.archivedAt;return l?!0:!U}),[r,l]);return h(()=>{M()},[]),e("div",{className:"DataPage__DataSourcesTable",children:[!a&&H&&e("div",{className:"DataPage__DataSourcesTable__filters",children:e(b,{size:"sm",color:"dark",label:"Show archived",checked:l,onChange:t=>$(!!t.currentTarget.checked)})}),e(k,{children:[a&&e(g,{color:"gray",size:"xl"}),!a&&n.length>0&&e(D,{verticalSpacing:"xs",striped:!0,highlightOnHover:!0,fontSize:"xs",children:[e("thead",{children:e("tr",{children:[e("th",{children:"id"}),e("th",{children:"description"}),e("th",{children:"type"}),e("th",{children:"url"}),e("th",{children:"status"}),e("th",{children:"last synced"}),e("th",{children:"last published"})]})}),e("tbody",{children:n.map(t=>e("tr",{children:[e("td",{children:e("a",{href:`/cms/data/${t.id}`,children:t.id})}),e("td",{children:t.description||""}),e("td",{children:t.type}),e("td",{children:C(t.url)?e("div",{className:"DataPage__DataSourcesTable__url",children:[e(i,{label:"Open spreadsheet",children:e(f,{component:"a",href:t.url,target:"_blank",rel:"noreferrer",variant:"filled",color:"green",size:"sm","aria-label":"Open spreadsheet",children:e(S,{size:16,stroke:"2.25"})})}),e("a",{className:"DataPage__DataSourcesTable__url__text",href:t.url,target:"_blank",rel:"noreferrer",children:t.url})]}):t.url||""}),e("td",{children:e(O,{dataSource:t})}),e("td",{children:e(m,{dataSource:t,action:"sync"})}),e("td",{children:e(m,{dataSource:t,action:"publish"})})]},t.id))})]}),!a&&n.length===0&&e(p,{as:"p",children:"No data sources found for this filter."})]})]})};export{E as DataPage};
@@ -1 +1 @@
1
- import{a as f}from"./chunk-5KSGCGYY.js";import"./chunk-535JU7FU.js";import{a as g}from"./chunk-HXSB6YB2.js";import{a as h}from"./chunk-OWYV43BT.js";import"./chunk-UJX25CXD.js";import{c as T,d as N}from"./chunk-RN4XVVWD.js";import"./chunk-ATTW36EM.js";import"./chunk-W5DOO6ID.js";import"./chunk-YBHFGQ3Z.js";import"./chunk-PIJ3E47R.js";import{a as A}from"./chunk-NCZD5IWS.js";import{a as H}from"./chunk-QLIIVG5Q.js";import{c as W}from"./chunk-OWBT6YUM.js";import"./chunk-AAHM6RYJ.js";import"./chunk-XOWWPE7X.js";import"./chunk-WASID5UU.js";import{Da as D,Fa as p,Ga as a,Jc as k,Qc as z,V as y,W as m,a as _,c as b,ca as v,da as P,j as B,na as x,oa as w}from"./chunk-WRA24P66.js";import"./chunk-KUCVLANF.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";B();function r(t){H(`Data Source: ${t.id}`);let[o,l]=_(!0),[e,u]=_(null),[n,c]=_(null),i=t.id;async function d(){let s=await T(i);if(u(s),s){let I=await N(i,{mode:"draft"});c(I)}l(!1)}b(()=>{d()},[]);function S(s){s==="sync"&&d()}return a(W,{children:a("div",{className:"DataSourcePage",children:[a("div",{className:"DataSourcePage__header",children:[a(v,{className:"DataSourcePage__header__breadcrumbs",children:[a("a",{href:"/cms/data",children:"Data"}),a("div",{children:i})]}),a("div",{className:"DataSourcePage__header__titleWrap",children:[a(g,{size:"h1",children:["Data Source: ",i]}),a("div",{className:"DataSourcePage__header__controls",children:a(p,{label:"Configure",children:a(m,{component:"a",href:`/cms/data/${t.id}/edit`,children:a(k,{size:24,stroke:"1.5"})})})})]}),e?.description&&a(A,{as:"p",children:e.description})]}),e&&a(r.SyncStatus,{dataSource:e,onAction:S}),o?a(y,{color:"gray",size:"xl"}):e?a(r.DataSection,{dataSource:e,data:n}):a("div",{className:"DataSourcePage__notFound",children:"Not Found"})]})})}r.SyncStatus=t=>{let o=t.dataSource;return a("div",{className:"DataSourcePage__SyncStatus",children:[a(g,{size:"h2",children:"Status"}),a(h,{className:"DataSourcePage__SyncStatus__content",children:a(D,{verticalSpacing:"xs",striped:!0,highlightOnHover:!0,fontSize:"xs",children:[a("thead",{children:a("tr",{children:[a("th",{children:"last synced"}),a("th",{children:"last published"})]})}),a("tbody",{children:a("tr",{children:[a("td",{children:a(f,{dataSource:o,action:"sync",onAction:()=>{t.onAction&&t.onAction("sync")}})}),a("td",{children:a(f,{dataSource:o,action:"publish"})})]})})]})})]})};r.DataSection=t=>{let{data:o,headers:l}=t.data||{},e=t.dataSource;if(!o)return null;let u=e.dataFormat||"map";if(e.type==="gsheet"){let n=l,c=[];if(u==="array")c=o;else if(u==="map"){let i=o;if(!n){let d=new Set;i.forEach(S=>{for(let s in S)s&&d.add(s)}),n=Array.from(d)}i.forEach(d=>{c.push(n.map(S=>d[S]||""))})}return a(r.DataSectionWrap,{dataSource:e,children:a(r.DataTable,{headers:n,rows:c})})}return e.type==="http"?a(r.DataSectionWrap,{dataSource:e,children:a(r.DefaultDataDisplay,{data:o})}):(console.log(`unsupported data type: ${e.type}`),null)};r.DataSectionWrap=t=>a("div",{className:"DataSourcePage__DataSection",children:[a("div",{className:"DataSourcePage__DataSection__header",children:[a(g,{size:"h2",children:"Data"}),a("div",{className:"DataSourcePage__DataSection__header__actions",children:[t.dataSource.url?.startsWith("https://docs.google.com/spreadsheets/")&&a(p,{label:"Open spreadsheet",children:a(m,{component:"a",href:t.dataSource.url,target:"_blank",variant:"filled",color:"green",children:a(z,{size:16,stroke:"2.25"})})}),t.dataSource.previewUrl&&a(P,{component:"a",variant:"default",size:"xs",href:t.dataSource.previewUrl,target:"_blank",children:"Preview"})]})]}),a(h,{className:"DataSourcePage__DataSection__content",children:t.children})]});r.DataTable=t=>{let o=t.headers||[],l=t.rows||[];return a("div",{className:"DataSourcePage__DataTable",children:a(D,{className:"DataSourcePage__DataTable__table",verticalSpacing:"xs",striped:!0,highlightOnHover:!0,fontSize:"xs",children:[o.length>0&&a("thead",{children:a("tr",{children:o.map(e=>a("th",{children:e},e))})}),a("tbody",{children:l.map((e,u)=>a("tr",{children:e.map((n,c)=>a("td",{children:n},c))},u))})]})})};r.DefaultDataDisplay=t=>{let o=t.data;return typeof o=="object"?a(w,{value:JSON.stringify(o,null,2),minRows:20,maxRows:100}):a(x,{value:String(o),minRows:12,maxRows:100})};export{r as DataSourcePage};
1
+ import{a as f}from"./chunk-HUYP5U4K.js";import"./chunk-ILFWJKSS.js";import{a as g}from"./chunk-I3H465Z6.js";import{a as h}from"./chunk-TCWN5YRW.js";import"./chunk-BOIY4J2R.js";import{c as T,d as N}from"./chunk-Z2CRNO3I.js";import"./chunk-ATTW36EM.js";import"./chunk-W5DOO6ID.js";import"./chunk-ZQDUWURP.js";import"./chunk-ZYSR5UIW.js";import{a as A}from"./chunk-HWON6MV4.js";import{a as H}from"./chunk-5F5XJPKD.js";import{c as W}from"./chunk-P2RVKNG4.js";import"./chunk-AAHM6RYJ.js";import"./chunk-XOWWPE7X.js";import"./chunk-GJ7ES2LA.js";import{Da as D,Fa as p,Jc as k,Qc as z,V as y,W as m,a as _,c as b,ca as v,da as P,fd as a,j as B,na as x,oa as w}from"./chunk-Y7RYSRP7.js";import"./chunk-KUCVLANF.js";import"./chunk-DTMZLUNY.js";import"./chunk-PVAE643M.js";B();function r(t){H(`Data Source: ${t.id}`);let[o,l]=_(!0),[e,u]=_(null),[n,c]=_(null),i=t.id;async function d(){let s=await T(i);if(u(s),s){let I=await N(i,{mode:"draft"});c(I)}l(!1)}b(()=>{d()},[]);function S(s){s==="sync"&&d()}return a(W,{children:a("div",{className:"DataSourcePage",children:[a("div",{className:"DataSourcePage__header",children:[a(v,{className:"DataSourcePage__header__breadcrumbs",children:[a("a",{href:"/cms/data",children:"Data"}),a("div",{children:i})]}),a("div",{className:"DataSourcePage__header__titleWrap",children:[a(g,{size:"h1",children:["Data Source: ",i]}),a("div",{className:"DataSourcePage__header__controls",children:a(p,{label:"Configure",children:a(m,{component:"a",href:`/cms/data/${t.id}/edit`,children:a(k,{size:24,stroke:"1.5"})})})})]}),e?.description&&a(A,{as:"p",children:e.description})]}),e&&a(r.SyncStatus,{dataSource:e,onAction:S}),o?a(y,{color:"gray",size:"xl"}):e?a(r.DataSection,{dataSource:e,data:n}):a("div",{className:"DataSourcePage__notFound",children:"Not Found"})]})})}r.SyncStatus=t=>{let o=t.dataSource;return a("div",{className:"DataSourcePage__SyncStatus",children:[a(g,{size:"h2",children:"Status"}),a(h,{className:"DataSourcePage__SyncStatus__content",children:a(D,{verticalSpacing:"xs",striped:!0,highlightOnHover:!0,fontSize:"xs",children:[a("thead",{children:a("tr",{children:[a("th",{children:"last synced"}),a("th",{children:"last published"})]})}),a("tbody",{children:a("tr",{children:[a("td",{children:a(f,{dataSource:o,action:"sync",onAction:()=>{t.onAction&&t.onAction("sync")}})}),a("td",{children:a(f,{dataSource:o,action:"publish"})})]})})]})})]})};r.DataSection=t=>{let{data:o,headers:l}=t.data||{},e=t.dataSource;if(!o)return null;let u=e.dataFormat||"map";if(e.type==="gsheet"){let n=l,c=[];if(u==="array")c=o;else if(u==="map"){let i=o;if(!n){let d=new Set;i.forEach(S=>{for(let s in S)s&&d.add(s)}),n=Array.from(d)}i.forEach(d=>{c.push(n.map(S=>d[S]||""))})}return a(r.DataSectionWrap,{dataSource:e,children:a(r.DataTable,{headers:n,rows:c})})}return e.type==="http"?a(r.DataSectionWrap,{dataSource:e,children:a(r.DefaultDataDisplay,{data:o})}):(console.log(`unsupported data type: ${e.type}`),null)};r.DataSectionWrap=t=>a("div",{className:"DataSourcePage__DataSection",children:[a("div",{className:"DataSourcePage__DataSection__header",children:[a(g,{size:"h2",children:"Data"}),a("div",{className:"DataSourcePage__DataSection__header__actions",children:[t.dataSource.url?.startsWith("https://docs.google.com/spreadsheets/")&&a(p,{label:"Open spreadsheet",children:a(m,{component:"a",href:t.dataSource.url,target:"_blank",variant:"filled",color:"green",children:a(z,{size:16,stroke:"2.25"})})}),t.dataSource.previewUrl&&a(P,{component:"a",variant:"default",size:"xs",href:t.dataSource.previewUrl,target:"_blank",children:"Preview"})]})]}),a(h,{className:"DataSourcePage__DataSection__content",children:t.children})]});r.DataTable=t=>{let o=t.headers||[],l=t.rows||[];return a("div",{className:"DataSourcePage__DataTable",children:a(D,{className:"DataSourcePage__DataTable__table",verticalSpacing:"xs",striped:!0,highlightOnHover:!0,fontSize:"xs",children:[o.length>0&&a("thead",{children:a("tr",{children:o.map(e=>a("th",{children:e},e))})}),a("tbody",{children:l.map((e,u)=>a("tr",{children:e.map((n,c)=>a("td",{children:n},c))},u))})]})})};r.DefaultDataDisplay=t=>{let o=t.data;return typeof o=="object"?a(w,{value:JSON.stringify(o,null,2),minRows:20,maxRows:100}):a(x,{value:String(o),minRows:12,maxRows:100})};export{r as DataSourcePage};