@blinkk/root-cms 2.5.16 → 3.0.1-alpha.1

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 CHANGED
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  getServerVersion
3
- } from "./chunk-JGLMVZE4.js";
3
+ } from "./chunk-BBOESYH7.js";
4
4
  import {
5
5
  getCollectionSchema,
6
6
  getProjectSchemas
7
- } from "./chunk-HDIPAT3C.js";
7
+ } from "./chunk-R4LKO3EZ.js";
8
8
  import "./chunk-MLKGABMK.js";
9
9
 
10
10
  // core/app.tsx
11
11
  import crypto from "crypto";
12
12
  import path from "path";
13
- import { render as renderToString } from "preact-render-to-string";
13
+ import { renderJsxToString } from "@blinkk/root/jsx";
14
14
  import { jsx, jsxs } from "preact/jsx-runtime";
15
15
  var DEFAULT_FAVICON_URL = "https://lh3.googleusercontent.com/ijK50TfQlV_yJw3i-CMlnD6osH4PboZBILZrJcWhoNMEmoyCD5e1bAxXbaOPe5w4gG_Scf37EXrmZ6p8sP2lue5fLZ419m5JyLMs=e385-w256";
16
16
  function App(props) {
@@ -139,7 +139,7 @@ async function renderApp(req, res, options) {
139
139
  };
140
140
  const projectName = cmsConfig.name || cmsConfig.id || "";
141
141
  const title = getCmsTitle(projectName, cmsConfig.minimalBranding);
142
- const mainHtml = renderToString(
142
+ const mainHtml = renderJsxToString(
143
143
  /* @__PURE__ */ jsx(App, { title, ctx, favicon: cmsConfig.favicon })
144
144
  );
145
145
  const nonce = generateNonce();
@@ -234,10 +234,8 @@ async function renderSignIn(req, res, options) {
234
234
  ctx.warning = "Dev warning: Server may be misconfigured. See logs for more information.";
235
235
  }
236
236
  }
237
- const siteName = ctx.name;
238
- const title = siteName ? `Sign in \u2013 ${siteName}` : "Sign in";
239
- const mainHtml = renderToString(
240
- /* @__PURE__ */ jsx(SignIn, { title, ctx, favicon: options.cmsConfig.favicon })
237
+ const mainHtml = renderJsxToString(
238
+ /* @__PURE__ */ jsx(SignIn, { title: "Sign in", ctx, favicon: options.cmsConfig.favicon })
241
239
  );
242
240
  const nonce = generateNonce();
243
241
  const html = `<!doctype html>
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "@blinkk/root-cms",
4
- version: "2.5.16",
4
+ version: "3.0.1-alpha.1",
5
5
  author: "s@blinkk.com",
6
6
  license: "MIT",
7
7
  engines: {
@@ -12,9 +12,6 @@ var package_default = {
12
12
  url: "git+https://github.com/blinkk/rootjs.git",
13
13
  directory: "packages/root-cms"
14
14
  },
15
- publishConfig: {
16
- provenance: true
17
- },
18
15
  files: [
19
16
  "dist/**/*"
20
17
  ],
@@ -88,10 +85,12 @@ var package_default = {
88
85
  "date-fns-tz": "3.2.0",
89
86
  diff: "8.0.2",
90
87
  "dts-dom": "3.7.0",
88
+ "@mantine/spotlight": "4.2.12",
91
89
  "fnv-plus": "1.3.1",
92
90
  genkit: "1.26.0",
93
91
  jsonwebtoken: "9.0.2",
94
92
  kleur: "4.1.5",
93
+ minisearch: "7.2.0",
95
94
  sirv: "2.0.3",
96
95
  "tiny-glob": "0.2.9"
97
96
  },
@@ -125,7 +124,6 @@ var package_default = {
125
124
  "@mantine/hooks": "4.2.12",
126
125
  "@mantine/modals": "4.2.12",
127
126
  "@mantine/notifications": "4.2.12",
128
- "@mantine/spotlight": "4.2.12",
129
127
  "@preact/compat": "18.3.1",
130
128
  "@tabler/icons-preact": "3.35.0",
131
129
  "@testing-library/preact": "3.2.4",
@@ -138,8 +136,8 @@ var package_default = {
138
136
  "@types/google.accounts": "0.0.14",
139
137
  "@types/jsonwebtoken": "9.0.1",
140
138
  "@types/node": "24.3.1",
141
- "@vitest/browser": "4.0.10",
142
- "@vitest/browser-playwright": "4.0.10",
139
+ "@vitest/browser": "4.1.2",
140
+ "@vitest/browser-playwright": "4.1.2",
143
141
  concurrently: "7.6.0",
144
142
  esbuild: "0.25.9",
145
143
  firebase: "12.2.1",
@@ -157,23 +155,21 @@ var package_default = {
157
155
  playwright: "1.56.1",
158
156
  preact: "10.27.1",
159
157
  "preact-iso": "2.11.1",
160
- "preact-render-to-string": "6.6.1",
158
+ "preact-render-to-string": "6.6.7",
161
159
  react: "npm:@preact/compat@18.3.1",
162
160
  "react-dom": "npm:@preact/compat@18.3.1",
163
161
  "react-easy-crop": "5.5.6",
164
162
  "react-json-view-compare": "2.0.2",
165
163
  tsup: "8.5.0",
166
164
  typescript: "5.9.2",
167
- vite: "7.1.4",
168
- vitest: "4.0.10",
165
+ vite: "8.0.3",
166
+ vitest: "4.1.2",
169
167
  yjs: "13.6.27"
170
168
  },
171
169
  peerDependencies: {
172
- "@blinkk/root": "2.5.16",
170
+ "@blinkk/root": "3.0.1-alpha.1",
173
171
  "firebase-admin": ">=11",
174
- "firebase-functions": ">=4",
175
- preact: ">=10",
176
- "preact-render-to-string": ">=5"
172
+ "firebase-functions": ">=4"
177
173
  },
178
174
  peerDependenciesMeta: {
179
175
  "firebase-functions": {
@@ -1,13 +1,11 @@
1
1
  // core/project.ts
2
- var SCHEMA_MODULES = import.meta.glob(
3
- [
4
- "/**/*.schema.ts",
5
- "!/appengine/**/*.schema.ts",
6
- "!/functions/**/*.schema.ts",
7
- "!/gae/**/*.schema.ts"
8
- ],
9
- { eager: true }
10
- );
2
+ var _SCHEMA_MODULES = {};
3
+ try {
4
+ const mod = await import("virtual:root/schemas");
5
+ _SCHEMA_MODULES = mod.SCHEMA_MODULES || {};
6
+ } catch {
7
+ }
8
+ var SCHEMA_MODULES = _SCHEMA_MODULES;
11
9
  function getProjectSchemas() {
12
10
  const schemas = {};
13
11
  for (const fileId in SCHEMA_MODULES) {
@@ -57,10 +55,10 @@ function testValidCollectionId(id) {
57
55
  function isSchemaPattern(value) {
58
56
  return typeof value === "object" && value !== null && "_schemaPattern" in value && value._schemaPattern === true;
59
57
  }
60
- function buildSchemaNameMap(schemaModules = SCHEMA_MODULES) {
58
+ function buildSchemaNameMap() {
61
59
  const nameMap = {};
62
- for (const fileId in schemaModules) {
63
- const module = schemaModules[fileId];
60
+ for (const fileId in SCHEMA_MODULES) {
61
+ const module = SCHEMA_MODULES[fileId];
64
62
  if (module.default && module.default.name) {
65
63
  nameMap[module.default.name] = module.default;
66
64
  }
@@ -71,16 +69,16 @@ function globToRegex(pattern) {
71
69
  const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{DOUBLE_STAR}}").replace(/\*/g, "[^/]*").replace(/\{\{DOUBLE_STAR\}\}/g, ".*");
72
70
  return new RegExp(`^${regexStr}$`);
73
71
  }
74
- function resolveSchemaPattern(pattern, schemaModules = SCHEMA_MODULES) {
72
+ function resolveSchemaPattern(pattern) {
75
73
  const regex = globToRegex(pattern.pattern);
76
74
  const excludeSet = new Set(pattern.exclude || []);
77
75
  const names = [];
78
76
  const schemas = {};
79
- for (const fileId in schemaModules) {
77
+ for (const fileId in SCHEMA_MODULES) {
80
78
  if (!regex.test(fileId)) {
81
79
  continue;
82
80
  }
83
- const module = schemaModules[fileId];
81
+ const module = SCHEMA_MODULES[fileId];
84
82
  if (!module.default || !module.default.name) {
85
83
  continue;
86
84
  }
@@ -88,25 +86,28 @@ function resolveSchemaPattern(pattern, schemaModules = SCHEMA_MODULES) {
88
86
  if (excludeSet.has(schemaName)) {
89
87
  continue;
90
88
  }
91
- const schemaObj = structuredClone(module.default);
89
+ let schemaObj = module.default;
92
90
  if (pattern.omitFields && pattern.omitFields.length > 0) {
93
91
  const omitSet = new Set(pattern.omitFields);
94
- schemaObj.fields = schemaObj.fields.filter(
95
- (f) => !omitSet.has(f.id || "")
96
- );
92
+ schemaObj = {
93
+ ...schemaObj,
94
+ fields: schemaObj.fields.filter(
95
+ (f) => !omitSet.has(f.id || "")
96
+ )
97
+ };
97
98
  }
98
99
  names.push(schemaName);
99
100
  schemas[schemaName] = schemaObj;
100
101
  }
101
102
  return { names, schemas };
102
103
  }
103
- function convertOneOfTypes(collection, schemaModules = SCHEMA_MODULES) {
104
+ function convertOneOfTypes(collection) {
104
105
  const clone = structuredClone(collection);
105
106
  const types = clone.types || {};
106
- const schemaNameMap = buildSchemaNameMap(schemaModules);
107
+ const schemaNameMap = buildSchemaNameMap();
107
108
  function handleOneOfField(field) {
108
109
  if (isSchemaPattern(field.types)) {
109
- const resolved = resolveSchemaPattern(field.types, schemaModules);
110
+ const resolved = resolveSchemaPattern(field.types);
110
111
  for (const [name, schemaObj] of Object.entries(resolved.schemas)) {
111
112
  if (!types[name]) {
112
113
  types[name] = schemaObj;
@@ -123,7 +124,7 @@ function convertOneOfTypes(collection, schemaModules = SCHEMA_MODULES) {
123
124
  if (typeof sub === "string") {
124
125
  names.push(sub);
125
126
  if (!types[sub] && schemaNameMap[sub]) {
126
- const resolvedSchema = structuredClone(schemaNameMap[sub]);
127
+ const resolvedSchema = schemaNameMap[sub];
127
128
  types[sub] = resolvedSchema;
128
129
  if (resolvedSchema.fields) {
129
130
  walk(resolvedSchema);
@@ -165,6 +166,5 @@ export {
165
166
  SCHEMA_MODULES,
166
167
  getProjectSchemas,
167
168
  resolveOneOfPatterns,
168
- getCollectionSchema,
169
- convertOneOfTypes
169
+ getCollectionSchema
170
170
  };
@@ -1501,41 +1501,10 @@ ${errorMessages}`);
1501
1501
  const docRef = this.db.doc(dbPath);
1502
1502
  const doc = await docRef.get();
1503
1503
  if (doc.exists) {
1504
- const dataSource = doc.data();
1505
- if (dataSource.archivedAt) {
1506
- console.warn(
1507
- `warning: data source "${dataSourceId}" is archived` + (dataSource.archivedBy ? ` (archived by ${dataSource.archivedBy})` : "")
1508
- );
1509
- }
1510
- return dataSource;
1504
+ return doc.data();
1511
1505
  }
1512
1506
  return null;
1513
1507
  }
1514
- /**
1515
- * Archives a data source. Archived data sources cannot be synced or published.
1516
- */
1517
- async archiveDataSource(dataSourceId, options) {
1518
- const dbPath = `Projects/${this.projectId}/DataSources/${dataSourceId}`;
1519
- const docRef = this.db.doc(dbPath);
1520
- const archivedBy = options?.archivedBy || "root-cms-client";
1521
- await docRef.update({
1522
- archivedAt: Timestamp2.now(),
1523
- archivedBy
1524
- });
1525
- console.log(`archived data source: ${dataSourceId}`);
1526
- }
1527
- /**
1528
- * Unarchives a data source.
1529
- */
1530
- async unarchiveDataSource(dataSourceId) {
1531
- const dbPath = `Projects/${this.projectId}/DataSources/${dataSourceId}`;
1532
- const docRef = this.db.doc(dbPath);
1533
- await docRef.update({
1534
- archivedAt: FieldValue2.delete(),
1535
- archivedBy: FieldValue2.delete()
1536
- });
1537
- console.log(`unarchived data source: ${dataSourceId}`);
1538
- }
1539
1508
  /**
1540
1509
  * Syncs a data source to draft state.
1541
1510
  */
@@ -1544,9 +1513,6 @@ ${errorMessages}`);
1544
1513
  if (!dataSource) {
1545
1514
  throw new Error(`data source not found: ${dataSourceId}`);
1546
1515
  }
1547
- if (dataSource.archivedAt) {
1548
- throw new Error(`data source is archived: ${dataSourceId}`);
1549
- }
1550
1516
  const result = await this.fetchData(dataSource);
1551
1517
  const dataSourceDocRef = this.db.doc(
1552
1518
  `Projects/${this.projectId}/DataSources/${dataSourceId}`
@@ -1579,9 +1545,6 @@ ${errorMessages}`);
1579
1545
  if (!dataSource) {
1580
1546
  throw new Error(`data source not found: ${dataSourceId}`);
1581
1547
  }
1582
- if (dataSource.archivedAt) {
1583
- throw new Error(`data source is archived: ${dataSourceId}`);
1584
- }
1585
1548
  const dataSourceDocRef = this.db.doc(
1586
1549
  `Projects/${this.projectId}/DataSources/${dataSourceId}`
1587
1550
  );
@@ -1615,40 +1578,6 @@ ${errorMessages}`);
1615
1578
  console.log(`published data ${dataSourceId}`);
1616
1579
  console.log(`published by: ${publishedBy}`);
1617
1580
  }
1618
- /**
1619
- * Unpublishes a data source. Removes the `publishedAt`/`publishedBy`
1620
- * metadata from the DataSource doc and deletes the `Data/published` doc.
1621
- */
1622
- async unpublishDataSource(dataSourceId) {
1623
- const dataSource = await this.getDataSource(dataSourceId);
1624
- if (!dataSource) {
1625
- throw new Error(`data source not found: ${dataSourceId}`);
1626
- }
1627
- const dataSourceDocRef = this.db.doc(
1628
- `Projects/${this.projectId}/DataSources/${dataSourceId}`
1629
- );
1630
- const dataDocRefDraft = this.db.doc(
1631
- `Projects/${this.projectId}/DataSources/${dataSourceId}/Data/draft`
1632
- );
1633
- const dataDocRefPublished = this.db.doc(
1634
- `Projects/${this.projectId}/DataSources/${dataSourceId}/Data/published`
1635
- );
1636
- const batch = this.db.batch();
1637
- batch.update(dataSourceDocRef, {
1638
- publishedAt: FieldValue2.delete(),
1639
- publishedBy: FieldValue2.delete()
1640
- });
1641
- const draftSnapshot = await dataDocRefDraft.get();
1642
- if (draftSnapshot.exists) {
1643
- batch.update(dataDocRefDraft, {
1644
- "dataSource.publishedAt": FieldValue2.delete(),
1645
- "dataSource.publishedBy": FieldValue2.delete()
1646
- });
1647
- }
1648
- batch.delete(dataDocRefPublished);
1649
- await batch.commit();
1650
- console.log(`unpublished data source: ${dataSourceId}`);
1651
- }
1652
1581
  async publishDataSources(dataSourceIds, options) {
1653
1582
  const publishedBy = options?.publishedBy || "root-cms-client";
1654
1583
  const batch = options?.batch || this.db.batch();
@@ -1657,9 +1586,6 @@ ${errorMessages}`);
1657
1586
  if (!dataSource) {
1658
1587
  throw new Error(`data source not found: ${id}`);
1659
1588
  }
1660
- if (dataSource.archivedAt) {
1661
- throw new Error(`data source is archived: ${id}`);
1662
- }
1663
1589
  const dataSourceDocRef = this.db.doc(
1664
1590
  `Projects/${this.projectId}/DataSources/${id}`
1665
1591
  );
@@ -1825,14 +1751,7 @@ ${errorMessages}`);
1825
1751
  const docRef = this.dbDataSourceDataRef(dataSourceId, { mode });
1826
1752
  const doc = await docRef.get();
1827
1753
  if (doc.exists) {
1828
- const dataSourceData = doc.data();
1829
- if (dataSourceData.dataSource?.archivedAt) {
1830
- const archivedBy = dataSourceData.dataSource.archivedBy;
1831
- console.warn(
1832
- `warning: data source "${dataSourceId}" is archived` + (archivedBy ? ` (archived by ${archivedBy})` : "")
1833
- );
1834
- }
1835
- return dataSourceData;
1754
+ return doc.data();
1836
1755
  }
1837
1756
  return null;
1838
1757
  }
@@ -1947,6 +1866,27 @@ ${errorMessages}`);
1947
1866
  console.error(err);
1948
1867
  }
1949
1868
  }
1869
+ const notifications = cmsPluginConfig.notifications;
1870
+ if (notifications && notifications.length > 0) {
1871
+ const ctx = {
1872
+ rootConfig: this.rootConfig,
1873
+ cmsClient: this,
1874
+ user: data.by ? { email: data.by } : void 0
1875
+ };
1876
+ for (const service of notifications) {
1877
+ if (!service.onAction) {
1878
+ continue;
1879
+ }
1880
+ try {
1881
+ await service.onAction(ctx, data);
1882
+ } catch (err) {
1883
+ console.error(
1884
+ `notification service "${service.id}" onAction failed:`,
1885
+ err
1886
+ );
1887
+ }
1888
+ }
1889
+ }
1950
1890
  }
1951
1891
  /**
1952
1892
  * Creates a batch request that is capable of fetching one or more docs,