@blinkk/root-cms 2.0.9 → 2.1.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
@@ -7,7 +7,7 @@ import { render as renderToString } from "preact-render-to-string";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "@blinkk/root-cms",
10
- version: "2.0.9",
10
+ version: "2.1.1",
11
11
  author: "s@blinkk.com",
12
12
  license: "MIT",
13
13
  engines: {
@@ -154,7 +154,7 @@ var package_default = {
154
154
  yjs: "13.6.27"
155
155
  },
156
156
  peerDependencies: {
157
- "@blinkk/root": "2.0.9",
157
+ "@blinkk/root": "2.1.1",
158
158
  "firebase-admin": ">=11",
159
159
  "firebase-functions": ">=4",
160
160
  preact: ">=10",
@@ -168,16 +168,18 @@ var package_default = {
168
168
  };
169
169
 
170
170
  // core/project.ts
171
- var IGNORED_SCHEMA_FOLDERS = ["/appengine/", "/functions/", "/gae/"];
172
- var SCHEMA_MODULES = import.meta.glob("/**/*.schema.ts", {
173
- eager: true
174
- });
175
- function getProjectSchemas() {
171
+ var SCHEMA_MODULES = import.meta.glob(
172
+ [
173
+ "/**/*.schema.ts",
174
+ "!/appengine/**/*.schema.ts",
175
+ "!/functions/**/*.schema.ts",
176
+ "!/gae/**/*.schema.ts"
177
+ ],
178
+ { eager: true }
179
+ );
180
+ async function getProjectSchemas() {
176
181
  const schemas = {};
177
182
  for (const fileId in SCHEMA_MODULES) {
178
- if (IGNORED_SCHEMA_FOLDERS.some((prefix) => fileId.startsWith(prefix))) {
179
- continue;
180
- }
181
183
  const schemaModule = SCHEMA_MODULES[fileId];
182
184
  if (schemaModule.default) {
183
185
  schemas[fileId] = schemaModule.default;
@@ -185,6 +187,62 @@ function getProjectSchemas() {
185
187
  }
186
188
  return schemas;
187
189
  }
190
+ async function getCollectionSchema(collectionId) {
191
+ if (!testValidCollectionId(collectionId)) {
192
+ throw new Error(`invalid collection id: ${collectionId}`);
193
+ }
194
+ const fileId = `/collections/${collectionId}.schema.ts`;
195
+ const module = SCHEMA_MODULES[fileId];
196
+ if (!module.default) {
197
+ console.warn(`collection schema not exported in: ${fileId}`);
198
+ return null;
199
+ }
200
+ const collection = module.default;
201
+ collection.id = collectionId;
202
+ return convertOneOfTypes(collection);
203
+ }
204
+ function testValidCollectionId(id) {
205
+ return /^[A-Za-z0-9_-]+$/.test(id);
206
+ }
207
+ function convertOneOfTypes(collection) {
208
+ const clone = structuredClone(collection);
209
+ const types = clone.types || {};
210
+ function handleOneOfField(field) {
211
+ const names = [];
212
+ (field.types || []).forEach((sub) => {
213
+ if (typeof sub === "string") {
214
+ names.push(sub);
215
+ return;
216
+ }
217
+ if (sub.name) {
218
+ names.push(sub.name);
219
+ if (!types[sub.name]) {
220
+ types[sub.name] = sub;
221
+ if (sub.fields) {
222
+ walk(sub);
223
+ }
224
+ }
225
+ }
226
+ });
227
+ field.types = names;
228
+ }
229
+ function handleField(field) {
230
+ if (field.type === "oneof") {
231
+ handleOneOfField(field);
232
+ } else if (field.type === "object") {
233
+ walk(field);
234
+ } else if (field.type === "array" && field.of) {
235
+ handleField(field.of);
236
+ }
237
+ }
238
+ function walk(schema) {
239
+ const fields = schema?.fields || [];
240
+ fields.forEach(handleField);
241
+ }
242
+ walk(clone);
243
+ clone.types = types;
244
+ return clone;
245
+ }
188
246
 
189
247
  // core/app.tsx
190
248
  import { jsx, jsxs } from "preact/jsx-runtime";
@@ -259,9 +317,11 @@ function App(props) {
259
317
  }
260
318
  async function renderApp(req, res, options) {
261
319
  const collections = {};
262
- Object.entries(getCollections()).forEach(([collectionId, collection]) => {
263
- collections[collectionId] = serializeCollection(collection);
264
- });
320
+ Object.entries(await getCollections()).forEach(
321
+ ([collectionId, collection]) => {
322
+ collections[collectionId] = serializeCollection(collection);
323
+ }
324
+ );
265
325
  const rootConfig = options.rootConfig || {};
266
326
  const cmsConfig = options.cmsConfig || {};
267
327
  let gci = cmsConfig.gci;
@@ -324,7 +384,8 @@ function serializeCollection(collection) {
324
384
  preview: collection.preview,
325
385
  slugRegex: collection.slugRegex,
326
386
  autolock: collection.autolock,
327
- autolockReason: collection.autolockReason
387
+ autolockReason: collection.autolockReason,
388
+ sortOptions: collection.sortOptions
328
389
  };
329
390
  }
330
391
  function SignIn(props) {
@@ -407,18 +468,21 @@ ${mainHtml}`;
407
468
  res.status(403);
408
469
  res.send(html);
409
470
  }
410
- function getCollections() {
471
+ async function getCollections() {
411
472
  const collections = {};
412
- const schemas = getProjectSchemas();
473
+ const schemas = await getProjectSchemas();
413
474
  Object.entries(schemas).forEach(([fileId, schema]) => {
414
475
  if (fileId.startsWith("/collections/")) {
415
- const collectionId = parseCollectionId(fileId);
416
- collections[collectionId] = { ...schema, id: collectionId };
476
+ const collectionId = toCollectionId(fileId);
477
+ collections[collectionId] = { id: collectionId, ...schema };
417
478
  }
418
479
  });
419
480
  return collections;
420
481
  }
421
- function parseCollectionId(fileId) {
482
+ async function getCollection(collectionId) {
483
+ return getCollectionSchema(collectionId);
484
+ }
485
+ function toCollectionId(fileId) {
422
486
  return path.basename(fileId).split(".")[0];
423
487
  }
424
488
  function generateNonce() {
@@ -461,6 +525,7 @@ function cachebust(req, url) {
461
525
  return `${url}?c=${value}`;
462
526
  }
463
527
  export {
528
+ getCollection,
464
529
  getCollections,
465
530
  renderApp,
466
531
  renderSignIn
package/dist/cli.js CHANGED
@@ -17,7 +17,7 @@ async function generateTypes() {
17
17
  rootConfig,
18
18
  modulePath
19
19
  );
20
- const schemas = project.getProjectSchemas();
20
+ const schemas = await project.getProjectSchemas();
21
21
  const outputPath = path.resolve(rootDir, "root-cms.d.ts");
22
22
  await generateSchemaDts(outputPath, schemas);
23
23
  console.log("saved root-cms.d.ts!");
@@ -231,12 +231,19 @@ function fieldType(field, options) {
231
231
  if (field.types && Array.isArray(field.types)) {
232
232
  const unionTypes = [];
233
233
  field.types.forEach((schema) => {
234
- if (!schema.name) {
234
+ let typeName;
235
+ if (typeof schema === "string") {
236
+ typeName = schema;
235
237
  return;
238
+ } else {
239
+ typeName = schema.name;
236
240
  }
237
- const cleanName = alphanumeric(schema.name);
241
+ if (!typeName) {
242
+ return;
243
+ }
244
+ const cleanName = alphanumeric(typeName);
238
245
  const oneOfTypeId = `${cleanName}Fields`;
239
- if (!options.oneOfTypes[oneOfTypeId]) {
246
+ if (typeof schema === "object" && !options.oneOfTypes[oneOfTypeId]) {
240
247
  const oneOfTypeInterface = dom.create.interface(
241
248
  oneOfTypeId,
242
249
  dom.DeclarationFlags.Export
@@ -252,7 +259,7 @@ function fieldType(field, options) {
252
259
  }
253
260
  const oneOfOption = dom.create.namedTypeReference("RootCMSOneOfOption");
254
261
  oneOfOption.typeArguments = [
255
- dom.type.stringLiteral(schema.name),
262
+ dom.type.stringLiteral(typeName),
256
263
  dom.create.namedTypeReference(oneOfTypeId)
257
264
  ];
258
265
  unionTypes.push(oneOfOption);
@@ -267,6 +274,10 @@ function fieldType(field, options) {
267
274
  const referenceType = dom.create.namedTypeReference("RootCMSReference");
268
275
  return referenceType;
269
276
  }
277
+ if (field.type === "references") {
278
+ const referenceType = dom.create.namedTypeReference("RootCMSReference");
279
+ return dom.type.array(referenceType);
280
+ }
270
281
  if (field.type === "richtext") {
271
282
  const richtextType = dom.create.namedTypeReference("RootCMSRichText");
272
283
  return richtextType;
package/dist/core.d.ts CHANGED
@@ -2,9 +2,8 @@ import { L as LoadTranslationsOptions, T as TranslationsMap, a as LocaleTranslat
2
2
  export { A as Action, p as ArrayObject, z as BatchRequest, B as BatchRequestOptions, w as BatchRequestQuery, x as BatchRequestQueryOptions, C as BatchResponse, c as DataSource, d as DataSourceData, e as DataSourceMode, D as Doc, b as DocMode, h as GetCountOptions, G as GetDocOptions, H as HttpMethod, j as ListActionsOptions, g as ListDocsOptions, E as Locale, N as MultiLocaleTranslationsMap, R as Release, k as RootCMSClient, f as SaveDraftOptions, S as SetDocOptions, O as SingleLocaleTranslationsMap, F as SourceString, I as TranslatedString, i as Translation, y as TranslationsDoc, J as TranslationsDocMode, M as TranslationsLocaleDocEntry, K as TranslationsLocaleDocHashMap, P as TranslationsManager, U as UserRole, Q as buildTranslationsDbPath, V as buildTranslationsLocaleDocDbPath, m as getCmsPlugin, l as isRichTextData, q as marshalArray, n as marshalData, o as normalizeData, v as parseDocId, t as toArrayObject, s as translationsForLocale, r as unmarshalArray, u as unmarshalData } from './client-Vr32ZRmC.js';
3
3
  import { RootConfig } from '@blinkk/root';
4
4
  import { Query } from 'firebase-admin/firestore';
5
- export { s as schema } from './schema-KpQGOA1Z.js';
5
+ export { s as schema } from './schema-UCmjggdA.js';
6
6
  import 'firebase-admin/app';
7
- import 'preact';
8
7
 
9
8
  /** @deprecated Use client.ts instead. */
10
9
 
package/dist/core.js CHANGED
@@ -1700,51 +1700,55 @@ __export(schema_exports, {
1700
1700
  object: () => object,
1701
1701
  oneOf: () => oneOf,
1702
1702
  reference: () => reference,
1703
+ references: () => references,
1703
1704
  richtext: () => richtext,
1704
1705
  select: () => select,
1705
1706
  string: () => string
1706
1707
  });
1707
1708
  function string(field) {
1708
- return { ...field, type: "string" };
1709
+ return { type: "string", ...field };
1709
1710
  }
1710
1711
  function number(field) {
1711
- return { ...field, type: "number" };
1712
+ return { type: "number", ...field };
1712
1713
  }
1713
1714
  function date(field) {
1714
- return { ...field, type: "date" };
1715
+ return { type: "date", ...field };
1715
1716
  }
1716
1717
  function datetime(field) {
1717
- return { ...field, type: "datetime" };
1718
+ return { type: "datetime", ...field };
1718
1719
  }
1719
1720
  function boolean(field) {
1720
- return { ...field, type: "boolean" };
1721
+ return { type: "boolean", ...field };
1721
1722
  }
1722
1723
  function select(field) {
1723
- return { ...field, type: "select" };
1724
+ return { type: "select", ...field };
1724
1725
  }
1725
1726
  function multiselect(field) {
1726
- return { ...field, type: "multiselect" };
1727
+ return { type: "multiselect", ...field };
1727
1728
  }
1728
1729
  function image(field) {
1729
- return { ...field, type: "image" };
1730
+ return { type: "image", ...field };
1730
1731
  }
1731
1732
  function file(field) {
1732
- return { ...field, type: "file" };
1733
+ return { type: "file", ...field };
1733
1734
  }
1734
1735
  function object(field) {
1735
- return { ...field, type: "object" };
1736
+ return { type: "object", ...field };
1736
1737
  }
1737
1738
  function array(field) {
1738
- return { ...field, type: "array" };
1739
+ return { type: "array", ...field };
1739
1740
  }
1740
1741
  function oneOf(field) {
1741
- return { ...field, type: "oneof" };
1742
+ return { type: "oneof", ...field };
1742
1743
  }
1743
1744
  function richtext(field) {
1744
- return { ...field, type: "richtext" };
1745
+ return { type: "richtext", ...field };
1745
1746
  }
1746
1747
  function reference(field) {
1747
- return { ...field, type: "reference" };
1748
+ return { type: "reference", ...field };
1749
+ }
1750
+ function references(field) {
1751
+ return { ...field, type: "references" };
1748
1752
  }
1749
1753
  function defineSchema(schema) {
1750
1754
  return schema;
package/dist/plugin.js CHANGED
@@ -51,21 +51,21 @@ __export(generate_types_exports, {
51
51
  generateSchemaDts: () => generateSchemaDts,
52
52
  generateTypes: () => generateTypes
53
53
  });
54
- import { promises as fs2 } from "node:fs";
55
- import path3 from "node:path";
54
+ import { promises as fs3 } from "node:fs";
55
+ import path4 from "node:path";
56
56
  import { fileURLToPath } from "node:url";
57
57
  import { loadRootConfig, viteSsrLoadModule } from "@blinkk/root/node";
58
58
  import * as dom from "dts-dom";
59
59
  async function generateTypes() {
60
60
  const rootDir = process.cwd();
61
61
  const rootConfig = await loadRootConfig(rootDir, { command: "root-cms" });
62
- const modulePath = path3.resolve(__dirname, "./project.js");
62
+ const modulePath = path4.resolve(__dirname, "./project.js");
63
63
  const project = await viteSsrLoadModule(
64
64
  rootConfig,
65
65
  modulePath
66
66
  );
67
- const schemas = project.getProjectSchemas();
68
- const outputPath = path3.resolve(rootDir, "root-cms.d.ts");
67
+ const schemas = await project.getProjectSchemas();
68
+ const outputPath = path4.resolve(rootDir, "root-cms.d.ts");
69
69
  await generateSchemaDts(outputPath, schemas);
70
70
  console.log("saved root-cms.d.ts!");
71
71
  }
@@ -76,7 +76,7 @@ async function generateSchemaDts(outputPath, schemas) {
76
76
  dtsFormatter.addSchemaFile(fileId, schema);
77
77
  }
78
78
  const output = dtsFormatter.toString();
79
- await fs2.writeFile(outputPath, output, "utf-8");
79
+ await fs3.writeFile(outputPath, output, "utf-8");
80
80
  }
81
81
  function fieldProperty(field, options) {
82
82
  const prop = dom.create.property(
@@ -129,12 +129,19 @@ function fieldType(field, options) {
129
129
  if (field.types && Array.isArray(field.types)) {
130
130
  const unionTypes = [];
131
131
  field.types.forEach((schema) => {
132
- if (!schema.name) {
132
+ let typeName;
133
+ if (typeof schema === "string") {
134
+ typeName = schema;
135
+ return;
136
+ } else {
137
+ typeName = schema.name;
138
+ }
139
+ if (!typeName) {
133
140
  return;
134
141
  }
135
- const cleanName = alphanumeric(schema.name);
142
+ const cleanName = alphanumeric(typeName);
136
143
  const oneOfTypeId = `${cleanName}Fields`;
137
- if (!options.oneOfTypes[oneOfTypeId]) {
144
+ if (typeof schema === "object" && !options.oneOfTypes[oneOfTypeId]) {
138
145
  const oneOfTypeInterface = dom.create.interface(
139
146
  oneOfTypeId,
140
147
  dom.DeclarationFlags.Export
@@ -150,7 +157,7 @@ function fieldType(field, options) {
150
157
  }
151
158
  const oneOfOption = dom.create.namedTypeReference("RootCMSOneOfOption");
152
159
  oneOfOption.typeArguments = [
153
- dom.type.stringLiteral(schema.name),
160
+ dom.type.stringLiteral(typeName),
154
161
  dom.create.namedTypeReference(oneOfTypeId)
155
162
  ];
156
163
  unionTypes.push(oneOfOption);
@@ -165,6 +172,10 @@ function fieldType(field, options) {
165
172
  const referenceType = dom.create.namedTypeReference("RootCMSReference");
166
173
  return referenceType;
167
174
  }
175
+ if (field.type === "references") {
176
+ const referenceType = dom.create.namedTypeReference("RootCMSReference");
177
+ return dom.type.array(referenceType);
178
+ }
168
179
  if (field.type === "richtext") {
169
180
  const richtextType = dom.create.namedTypeReference("RootCMSRichText");
170
181
  return richtextType;
@@ -190,7 +201,7 @@ var __dirname, TEMPLATE, DtsFormatter;
190
201
  var init_generate_types = __esm({
191
202
  "cli/generate-types.ts"() {
192
203
  "use strict";
193
- __dirname = path3.dirname(fileURLToPath(import.meta.url));
204
+ __dirname = path4.dirname(fileURLToPath(import.meta.url));
194
205
  TEMPLATE = `/* eslint-disable */
195
206
  /** Root.js CMS types. This file is autogenerated. */
196
207
 
@@ -264,7 +275,7 @@ export interface RootCMSDoc<Fields extends {}> {
264
275
  /** Adds the types for a `.schema.ts` file to the `.d.ts` file. */
265
276
  addSchemaFile(fileId, schema) {
266
277
  const jsdoc = `Generated from \`${fileId}\`.`;
267
- const typeId = alphanumeric(path3.parse(fileId).name.split(".")[0]);
278
+ const typeId = alphanumeric(path4.parse(fileId).name.split(".")[0]);
268
279
  const oneOfTypes = {};
269
280
  const fieldsTypeId = `${typeId}Fields`;
270
281
  const fieldsType = dom.create.interface(
@@ -344,9 +355,10 @@ export interface RootCMSDoc<Fields extends {}> {
344
355
  });
345
356
 
346
357
  // core/plugin.ts
347
- import { promises as fs3 } from "node:fs";
348
- import path4 from "node:path";
358
+ import { promises as fs4 } from "node:fs";
359
+ import path5 from "node:path";
349
360
  import { fileURLToPath as fileURLToPath2 } from "node:url";
361
+ import { viteSsrLoadModule as viteSsrLoadModule2 } from "@blinkk/root/node";
350
362
  import bodyParser from "body-parser";
351
363
  import {
352
364
  applicationDefault,
@@ -359,6 +371,8 @@ import * as jsonwebtoken from "jsonwebtoken";
359
371
  import sirv from "sirv";
360
372
 
361
373
  // core/api.ts
374
+ import { promises as fs2 } from "node:fs";
375
+ import path3 from "node:path";
362
376
  import { multipartMiddleware } from "@blinkk/root/middleware";
363
377
 
364
378
  // core/ai.ts
@@ -2192,21 +2206,48 @@ function csvToArray(csvString) {
2192
2206
  }
2193
2207
 
2194
2208
  // core/api.ts
2209
+ function testValidCollectionId(id) {
2210
+ return /^[A-Za-z0-9_-]+$/.test(id);
2211
+ }
2195
2212
  function api(server, options) {
2213
+ async function getCollectionSchema(req, collectionId) {
2214
+ if (req.viteServer) {
2215
+ const app = await options.getRenderer(req);
2216
+ return await app.getCollection(collectionId);
2217
+ }
2218
+ try {
2219
+ const schemaPath = path3.join(
2220
+ req.rootConfig.rootDir,
2221
+ "dist",
2222
+ "collections",
2223
+ `${collectionId}.schema.json`
2224
+ );
2225
+ const contents = await fs2.readFile(schemaPath, "utf8");
2226
+ return JSON.parse(contents);
2227
+ } catch (err) {
2228
+ if (err && err.code === "ENOENT") {
2229
+ return null;
2230
+ }
2231
+ throw err;
2232
+ }
2233
+ }
2196
2234
  server.use("/cms/api/collection.get", async (req, res) => {
2197
2235
  if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
2198
2236
  res.status(400).json({ success: false, error: "BAD_REQUEST" });
2199
2237
  return;
2200
2238
  }
2201
2239
  const reqBody = req.body || {};
2202
- if (!reqBody.collectionId) {
2240
+ const collectionId = String(reqBody.collectionId || "");
2241
+ if (!collectionId) {
2203
2242
  res.status(400).json({ success: false, error: "MISSING_COLLECTION_ID" });
2204
2243
  return;
2205
2244
  }
2245
+ if (!testValidCollectionId(collectionId)) {
2246
+ res.status(400).json({ success: false, error: "INVALID_COLLECTION_ID" });
2247
+ return;
2248
+ }
2206
2249
  try {
2207
- const app = await options.getRenderer(req);
2208
- const collections = app.getCollections();
2209
- const collection = collections[reqBody.collectionId];
2250
+ const collection = await getCollectionSchema(req, collectionId);
2210
2251
  if (!collection) {
2211
2252
  res.status(404).json({ success: false, error: "NOT_FOUND" });
2212
2253
  return;
@@ -2373,7 +2414,31 @@ function api(server, options) {
2373
2414
  }
2374
2415
 
2375
2416
  // core/plugin.ts
2376
- var __dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
2417
+ var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
2418
+ async function writeCollectionSchemasToJson(rootConfig) {
2419
+ const modulePath = path5.resolve(__dirname2, "./project.js");
2420
+ const project = await viteSsrLoadModule2(
2421
+ rootConfig,
2422
+ modulePath
2423
+ );
2424
+ const outDir = path5.join(rootConfig.rootDir, "dist", "collections");
2425
+ await fs4.mkdir(outDir, { recursive: true });
2426
+ const fileIds = Object.keys(project.SCHEMA_MODULES);
2427
+ for (const fileId of fileIds) {
2428
+ if (!fileId.startsWith("/collections/")) {
2429
+ continue;
2430
+ }
2431
+ const collectionId = path5.basename(fileId).split(".")[0];
2432
+ const schema = await project.getCollectionSchema(collectionId);
2433
+ if (!schema) {
2434
+ console.warn(`collection is missing a default export: ${fileId}`);
2435
+ continue;
2436
+ }
2437
+ const jsonPath = path5.join(outDir, `${collectionId}.schema.json`);
2438
+ const data = JSON.stringify(schema, null, 2);
2439
+ await fs4.writeFile(jsonPath, data, "utf-8");
2440
+ }
2441
+ }
2377
2442
  var SESSION_COOKIE_AUTH = "root-cms-auth";
2378
2443
  var NONCONF_STATIC_PATHS = [
2379
2444
  "/cms/static/signin.css",
@@ -2574,12 +2639,23 @@ function cmsPlugin(options) {
2574
2639
  const databaseId = firebaseConfig.databaseId || "(default)";
2575
2640
  return getFirestore(app, databaseId);
2576
2641
  },
2642
+ hooks: {
2643
+ /**
2644
+ * Saves collection schema files from `collections/*.schema.ts` to
2645
+ * `dist/collections/*.schema.json` for prod builds.
2646
+ */
2647
+ preBuild: async (rootConfig) => {
2648
+ await writeCollectionSchemasToJson(rootConfig);
2649
+ }
2650
+ },
2577
2651
  /**
2578
2652
  * A map of ssr files to include when running `root build`.
2579
2653
  */
2580
- ssrInput: () => ({
2581
- cms: path4.resolve(__dirname2, "./app.js")
2582
- }),
2654
+ ssrInput: () => {
2655
+ return {
2656
+ cms: path5.resolve(__dirname2, "./app.js")
2657
+ };
2658
+ },
2583
2659
  /**
2584
2660
  * Attaches CMS-specific middleware to the Root.js server.
2585
2661
  */
@@ -2587,13 +2663,13 @@ function cmsPlugin(options) {
2587
2663
  server.use(bodyParser.json());
2588
2664
  async function getRenderer(req) {
2589
2665
  if (serverOptions.type === "dev") {
2590
- const appFilePath = path4.resolve(__dirname2, "./app.js");
2666
+ const appFilePath = path5.resolve(__dirname2, "./app.js");
2591
2667
  const app3 = await req.viteServer.ssrLoadModule(
2592
2668
  appFilePath
2593
2669
  );
2594
2670
  return app3;
2595
2671
  }
2596
- const appImportPath = path4.resolve(
2672
+ const appImportPath = path5.resolve(
2597
2673
  req.rootConfig.rootDir,
2598
2674
  "dist/server/cms.js"
2599
2675
  );
@@ -2665,7 +2741,7 @@ function cmsPlugin(options) {
2665
2741
  }
2666
2742
  next();
2667
2743
  });
2668
- const staticDir = path4.resolve(__dirname2, "ui");
2744
+ const staticDir = path5.resolve(__dirname2, "ui");
2669
2745
  server.use("/cms/static", sirv(staticDir, { dev: false }));
2670
2746
  api(server, { getRenderer });
2671
2747
  server.use("/cms", async (req, res) => {
@@ -2699,7 +2775,7 @@ function cmsPlugin(options) {
2699
2775
  return plugin;
2700
2776
  }
2701
2777
  function fileExists(filepath) {
2702
- return fs3.access(filepath).then(() => true).catch(() => false);
2778
+ return fs4.access(filepath).then(() => true).catch(() => false);
2703
2779
  }
2704
2780
  export {
2705
2781
  cmsPlugin
package/dist/project.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { S as Schema } from './schema-KpQGOA1Z.js';
2
- import 'preact';
1
+ import { S as Schema, C as Collection } from './schema-UCmjggdA.js';
3
2
 
4
3
  /**
5
4
  * Loads various files or configurations from the project.
@@ -11,6 +10,17 @@ import 'preact';
11
10
  interface SchemaModule {
12
11
  default: Schema;
13
12
  }
14
- declare function getProjectSchemas(): Record<string, Schema>;
13
+ declare const SCHEMA_MODULES: Record<string, SchemaModule>;
14
+ /**
15
+ * Returns a map of all `schema.ts` files defined in the project as
16
+ * fileId => schema. This is used by `generate-types.ts` to build the
17
+ * `root-cms.d.ts` file.
18
+ */
19
+ declare function getProjectSchemas(): Promise<Record<string, Schema>>;
20
+ /**
21
+ * Returns a collection's schema definition as defined in
22
+ * `/collections/<id>.schema.ts`.
23
+ */
24
+ declare function getCollectionSchema(collectionId: string): Promise<Collection | null>;
15
25
 
16
- export { type SchemaModule, getProjectSchemas };
26
+ export { SCHEMA_MODULES, type SchemaModule, getCollectionSchema, getProjectSchemas };
package/dist/project.js CHANGED
@@ -1,14 +1,16 @@
1
1
  // core/project.ts
2
- var IGNORED_SCHEMA_FOLDERS = ["/appengine/", "/functions/", "/gae/"];
3
- var SCHEMA_MODULES = import.meta.glob("/**/*.schema.ts", {
4
- eager: true
5
- });
6
- function getProjectSchemas() {
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
+ );
11
+ async function getProjectSchemas() {
7
12
  const schemas = {};
8
13
  for (const fileId in SCHEMA_MODULES) {
9
- if (IGNORED_SCHEMA_FOLDERS.some((prefix) => fileId.startsWith(prefix))) {
10
- continue;
11
- }
12
14
  const schemaModule = SCHEMA_MODULES[fileId];
13
15
  if (schemaModule.default) {
14
16
  schemas[fileId] = schemaModule.default;
@@ -16,6 +18,64 @@ function getProjectSchemas() {
16
18
  }
17
19
  return schemas;
18
20
  }
21
+ async function getCollectionSchema(collectionId) {
22
+ if (!testValidCollectionId(collectionId)) {
23
+ throw new Error(`invalid collection id: ${collectionId}`);
24
+ }
25
+ const fileId = `/collections/${collectionId}.schema.ts`;
26
+ const module = SCHEMA_MODULES[fileId];
27
+ if (!module.default) {
28
+ console.warn(`collection schema not exported in: ${fileId}`);
29
+ return null;
30
+ }
31
+ const collection = module.default;
32
+ collection.id = collectionId;
33
+ return convertOneOfTypes(collection);
34
+ }
35
+ function testValidCollectionId(id) {
36
+ return /^[A-Za-z0-9_-]+$/.test(id);
37
+ }
38
+ function convertOneOfTypes(collection) {
39
+ const clone = structuredClone(collection);
40
+ const types = clone.types || {};
41
+ function handleOneOfField(field) {
42
+ const names = [];
43
+ (field.types || []).forEach((sub) => {
44
+ if (typeof sub === "string") {
45
+ names.push(sub);
46
+ return;
47
+ }
48
+ if (sub.name) {
49
+ names.push(sub.name);
50
+ if (!types[sub.name]) {
51
+ types[sub.name] = sub;
52
+ if (sub.fields) {
53
+ walk(sub);
54
+ }
55
+ }
56
+ }
57
+ });
58
+ field.types = names;
59
+ }
60
+ function handleField(field) {
61
+ if (field.type === "oneof") {
62
+ handleOneOfField(field);
63
+ } else if (field.type === "object") {
64
+ walk(field);
65
+ } else if (field.type === "array" && field.of) {
66
+ handleField(field.of);
67
+ }
68
+ }
69
+ function walk(schema) {
70
+ const fields = schema?.fields || [];
71
+ fields.forEach(handleField);
72
+ }
73
+ walk(clone);
74
+ clone.types = types;
75
+ return clone;
76
+ }
19
77
  export {
78
+ SCHEMA_MODULES,
79
+ getCollectionSchema,
20
80
  getProjectSchemas
21
81
  };