@blinkk/root-cms 1.3.27 → 1.4.2

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: "1.3.27",
10
+ version: "1.4.2",
11
11
  author: "s@blinkk.com",
12
12
  license: "MIT",
13
13
  engines: {
@@ -138,7 +138,7 @@ var package_default = {
138
138
  vitest: "0.34.6"
139
139
  },
140
140
  peerDependencies: {
141
- "@blinkk/root": "1.3.27",
141
+ "@blinkk/root": "1.4.2",
142
142
  "firebase-admin": ">=11",
143
143
  "firebase-functions": ">=4",
144
144
  preact: ">=10",
@@ -151,6 +151,25 @@ var package_default = {
151
151
  }
152
152
  };
153
153
 
154
+ // core/project.ts
155
+ var IGNORED_SCHEMA_FOLDERS = ["/appengine/", "/functions/", "/gae/"];
156
+ var SCHEMA_MODULES = import.meta.glob("/**/*.schema.ts", {
157
+ eager: true
158
+ });
159
+ function getProjectSchemas() {
160
+ const schemas = {};
161
+ for (const fileId in SCHEMA_MODULES) {
162
+ if (IGNORED_SCHEMA_FOLDERS.some((prefix) => fileId.startsWith(prefix))) {
163
+ continue;
164
+ }
165
+ const schemaModule = SCHEMA_MODULES[fileId];
166
+ if (schemaModule.default) {
167
+ schemas[fileId] = schemaModule.default;
168
+ }
169
+ }
170
+ return schemas;
171
+ }
172
+
154
173
  // core/app.tsx
155
174
  import { jsx, jsxs } from "preact/jsx-runtime";
156
175
  var __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -223,15 +242,9 @@ function App(props) {
223
242
  }
224
243
  async function renderApp(req, res, options) {
225
244
  var _a;
226
- const collectionModules = import.meta.glob("/collections/*.schema.ts", {
227
- eager: true
228
- });
229
245
  const collections = {};
230
- Object.keys(collectionModules).forEach((moduleId) => {
231
- const collectionId = path.parse(moduleId).base.split(".")[0];
232
- const module = collectionModules[moduleId];
233
- const collection = module.default;
234
- collections[collectionId] = collection;
246
+ Object.entries(getCollections()).forEach(([collectionId, collection]) => {
247
+ collections[collectionId] = serializeCollection(collection);
235
248
  });
236
249
  const rootConfig = options.rootConfig || {};
237
250
  const cmsConfig = options.cmsConfig || {};
@@ -279,6 +292,17 @@ ${mainHtml}`;
279
292
  setSecurityHeaders(options, req, res, nonce);
280
293
  res.send(html);
281
294
  }
295
+ function serializeCollection(collection) {
296
+ return {
297
+ id: collection.id,
298
+ name: collection.name ?? collection.id,
299
+ description: collection.description,
300
+ domain: collection.domain,
301
+ url: collection.url,
302
+ previewUrl: collection.previewUrl,
303
+ preview: collection.preview
304
+ };
305
+ }
282
306
  function SignIn(props) {
283
307
  return /* @__PURE__ */ jsxs("html", { children: [
284
308
  /* @__PURE__ */ jsxs("head", { children: [
@@ -357,6 +381,20 @@ ${mainHtml}`;
357
381
  res.status(403);
358
382
  res.send(html);
359
383
  }
384
+ function getCollections() {
385
+ const collections = {};
386
+ const schemas = getProjectSchemas();
387
+ Object.entries(schemas).forEach(([fileId, schema]) => {
388
+ if (fileId.startsWith("/collections/")) {
389
+ const collectionId = parseCollectionId(fileId);
390
+ collections[collectionId] = { ...schema, id: collectionId };
391
+ }
392
+ });
393
+ return collections;
394
+ }
395
+ function parseCollectionId(fileId) {
396
+ return path.basename(fileId).split(".")[0];
397
+ }
360
398
  function generateNonce() {
361
399
  return crypto.randomBytes(16).toString("base64");
362
400
  }
@@ -397,6 +435,7 @@ function cachebust(req, url) {
397
435
  return `${url}?c=${value}`;
398
436
  }
399
437
  export {
438
+ getCollections,
400
439
  renderApp,
401
440
  renderSignIn
402
441
  };
package/dist/cli.js CHANGED
@@ -22,7 +22,8 @@ async function generateTypes() {
22
22
  await generateSchemaDts(outputPath, schemas);
23
23
  console.log("saved root-cms.d.ts!");
24
24
  }
25
- var TEMPLATE = `/** Root.js CMS types. This file is autogenerated. */
25
+ var TEMPLATE = `/* eslint-disable */
26
+ /** Root.js CMS types. This file is autogenerated. */
26
27
 
27
28
  export interface RootCMSFile {
28
29
  src: string;
@@ -35,6 +36,8 @@ export type RootCMSImage = RootCMSFile;
35
36
 
36
37
  export type RootCMSOneOf<T = any> = T;
37
38
 
39
+ export type RootCMSOneOfOption<T, Base> = Base & {_type: T};
40
+
38
41
  export interface RootCMSRichTextBlock {
39
42
  type: string;
40
43
  data: any;
@@ -75,13 +78,106 @@ export interface RootCMSDoc<Fields extends {}> {
75
78
  /** User-entered field values from the CMS. */
76
79
  fields?: Fields;
77
80
  }`;
81
+ var DtsFormatter = class {
82
+ constructor() {
83
+ /**
84
+ * Field types defined in either `.schema.ts` files or used by `oneOf()`
85
+ * fields.
86
+ */
87
+ this.fieldsTypes = {};
88
+ /**
89
+ * For `collections/*.schema.ts` files, output a corresponding "RootCMSDoc"
90
+ * type. For example, `collections/BlogPosts.schema.ts` would output a type
91
+ * called `BlogPostsDoc`.
92
+ */
93
+ this.docTypes = {};
94
+ }
95
+ /** Adds the types for a `.schema.ts` file to the `.d.ts` file. */
96
+ addSchemaFile(fileId, schema) {
97
+ const jsdoc = `Generated from \`${fileId}\`.`;
98
+ const typeId = alphanumeric(path.parse(fileId).name.split(".")[0]);
99
+ const oneOfTypes = {};
100
+ const fieldsTypeId = `${typeId}Fields`;
101
+ const fieldsType = dom.create.interface(
102
+ fieldsTypeId,
103
+ dom.DeclarationFlags.Export
104
+ );
105
+ fieldsType.jsDocComment = jsdoc;
106
+ for (const field of schema.fields) {
107
+ if (!field.id) {
108
+ continue;
109
+ }
110
+ fieldsType.members.push(fieldProperty(field, { typeId, oneOfTypes }));
111
+ }
112
+ for (const oneOfTypeId in oneOfTypes) {
113
+ if (!this.fieldsTypes[fieldsTypeId]) {
114
+ this.fieldsTypes[oneOfTypeId] = oneOfTypes[oneOfTypeId];
115
+ }
116
+ }
117
+ this.fieldsTypes[fieldsTypeId] = fieldsType;
118
+ if (fileId.startsWith("/collections/")) {
119
+ const docTypeId = `${typeId}Doc`;
120
+ const baseType = dom.create.namedTypeReference("RootCMSDoc");
121
+ baseType.typeArguments.push(dom.create.namedTypeReference(fieldsTypeId));
122
+ const docType = dom.create.alias(
123
+ docTypeId,
124
+ baseType,
125
+ dom.DeclarationFlags.Export
126
+ );
127
+ docType.jsDocComment = jsdoc;
128
+ this.docTypes[docTypeId] = docType;
129
+ }
130
+ }
131
+ /** Generates the `.d.ts` output as a string. */
132
+ toString() {
133
+ const results = [TEMPLATE];
134
+ const sortedFieldsTypes = Object.keys(this.fieldsTypes).sort();
135
+ for (const fieldsTypeId of sortedFieldsTypes) {
136
+ const fieldsType = this.fieldsTypes[fieldsTypeId];
137
+ results.push(this.typeToString(fieldsType));
138
+ const docTypeId = this.fieldsTypeToDocType(fieldsTypeId);
139
+ const docType = this.docTypes[docTypeId];
140
+ if (docType) {
141
+ results.push(this.typeToString(docType));
142
+ }
143
+ }
144
+ const output = results.join("\n\n").replaceAll("/** ", "/** ").replaceAll(" */", " */").replace(/\r\n|\r|\n/g, "\n") + "\n";
145
+ return output;
146
+ }
147
+ /**
148
+ * Converts "<Name>Fields" to "<Name>Doc".
149
+ */
150
+ fieldsTypeToDocType(fieldsTypeId) {
151
+ if (!fieldsTypeId.endsWith("Fields")) {
152
+ throw new Error(`"${fieldsTypeId}" should be suffixed with "Fields"`);
153
+ }
154
+ const name = fieldsTypeId.slice(0, -6);
155
+ return `${name}Doc`;
156
+ }
157
+ typeToString(type2) {
158
+ return this.reformatOutput(dom.emit(type2, { singleLineJsDocComments: true }));
159
+ }
160
+ /**
161
+ * Formats the output to Google style conventions, e.g. 2-space indents and
162
+ * single quote strings.
163
+ */
164
+ reformatOutput(input) {
165
+ const lines = input.trim().split("\n");
166
+ const results = [];
167
+ for (const line of lines) {
168
+ const convertedLine = line.replace(/ {4}/g, " ").replaceAll('"', "'");
169
+ results.push(convertedLine);
170
+ }
171
+ return results.join("\n");
172
+ }
173
+ };
78
174
  async function generateSchemaDts(outputPath, schemas) {
79
- const results = [TEMPLATE];
175
+ const dtsFormatter = new DtsFormatter();
80
176
  for (const fileId in schemas) {
81
177
  const schema = schemas[fileId];
82
- results.push(renderSchema(fileId, schema));
178
+ dtsFormatter.addSchemaFile(fileId, schema);
83
179
  }
84
- const output = results.join("\n\n").replaceAll("/** ", "/** ").replaceAll(" */", " */").replace(/\r\n|\r|\n/g, "\n") + "\n";
180
+ const output = dtsFormatter.toString();
85
181
  await fs.writeFile(outputPath, output, "utf-8");
86
182
  }
87
183
  function fieldProperty(field, options) {
@@ -139,26 +235,27 @@ function fieldType(field, options) {
139
235
  return;
140
236
  }
141
237
  const cleanName = alphanumeric(schema.name);
142
- const oneOfTypeId = `${options.typeId}_${cleanName}`;
238
+ const oneOfTypeId = `${cleanName}Fields`;
143
239
  if (!options.oneOfTypes[oneOfTypeId]) {
144
240
  const oneOfTypeInterface = dom.create.interface(
145
241
  oneOfTypeId,
146
242
  dom.DeclarationFlags.Export
147
243
  );
148
- oneOfTypeInterface.members.push(
149
- dom.create.property(
150
- "_type",
151
- dom.type.stringLiteral(schema.name),
152
- dom.DeclarationFlags.None
153
- )
154
- );
244
+ if (schema.description) {
245
+ oneOfTypeInterface.jsDocComment = schema.description;
246
+ }
155
247
  const oneOfTypeFields = schema.fields || [];
156
248
  oneOfTypeFields.forEach((f) => {
157
249
  oneOfTypeInterface.members.push(fieldProperty(f, options));
158
250
  });
159
251
  options.oneOfTypes[oneOfTypeId] = oneOfTypeInterface;
160
252
  }
161
- unionTypes.push(dom.create.namedTypeReference(oneOfTypeId));
253
+ const oneOfOption = dom.create.namedTypeReference("RootCMSOneOfOption");
254
+ oneOfOption.typeArguments = [
255
+ dom.type.stringLiteral(schema.name),
256
+ dom.create.namedTypeReference(oneOfTypeId)
257
+ ];
258
+ unionTypes.push(oneOfOption);
162
259
  });
163
260
  if (unionTypes.length > 0) {
164
261
  oneOf.typeArguments = [dom.create.union(unionTypes)];
@@ -188,55 +285,6 @@ function fieldType(field, options) {
188
285
  }
189
286
  return dom.type.unknown;
190
287
  }
191
- function renderSchema(fileId, schema) {
192
- const jsdoc = `Generated from \`${fileId}\`.`;
193
- const typeId = alphanumeric(path.parse(fileId).name.split(".")[0]);
194
- const oneOfTypes = {};
195
- const fieldsInterface = `${typeId}Fields`;
196
- const fieldsType = dom.create.interface(
197
- fieldsInterface,
198
- dom.DeclarationFlags.Export
199
- );
200
- fieldsType.jsDocComment = jsdoc;
201
- for (const field of schema.fields) {
202
- if (!field.id) {
203
- continue;
204
- }
205
- fieldsType.members.push(fieldProperty(field, { typeId, oneOfTypes }));
206
- }
207
- const typesOutput = [];
208
- Object.values(oneOfTypes).forEach((oneOfTypeDef) => {
209
- oneOfTypeDef.jsDocComment = jsdoc;
210
- typesOutput.push(
211
- dom.emit(oneOfTypeDef, { singleLineJsDocComments: true }).trim()
212
- );
213
- });
214
- typesOutput.push(
215
- dom.emit(fieldsType, { singleLineJsDocComments: true }).trim()
216
- );
217
- if (fileId.startsWith("/collections/")) {
218
- const baseType = dom.create.namedTypeReference("RootCMSDoc");
219
- baseType.typeArguments.push(dom.create.namedTypeReference(fieldsInterface));
220
- const docType = dom.create.alias(
221
- `${typeId}Doc`,
222
- baseType,
223
- dom.DeclarationFlags.Export
224
- );
225
- docType.jsDocComment = jsdoc;
226
- const docTypeOutput = dom.emit(docType, { singleLineJsDocComments: true }).trim();
227
- typesOutput.push(docTypeOutput);
228
- }
229
- return reformatOutput(typesOutput.join("\n\n"));
230
- }
231
- function reformatOutput(input) {
232
- const lines = input.split("\n");
233
- const results = [];
234
- for (const line of lines) {
235
- const convertedLine = line.replace(/ {4}/g, " ").replaceAll('"', "'");
236
- results.push(convertedLine);
237
- }
238
- return results.join("\n");
239
- }
240
288
  function alphanumeric(input) {
241
289
  return input.replace(/[^a-zA-Z0-9]/g, "");
242
290
  }
package/dist/core.d.ts CHANGED
@@ -2,7 +2,7 @@ import { L as LoadTranslationsOptions, T as TranslationsMap, a as LocaleTranslat
2
2
  export { A as Action, p as ArrayObject, 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, R as Release, k as RootCMSClient, f as SaveDraftOptions, S as SetDocOptions, i as Translation, U as UserRole, 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-98Le1XxF.js';
3
3
  import { RootConfig } from '@blinkk/root';
4
4
  import { Query } from 'firebase-admin/firestore';
5
- export { s as schema } from './schema-ix967Ud0.js';
5
+ export { s as schema } from './schema-QC1UuoYc.js';
6
6
  import 'firebase-admin/app';
7
7
  import 'preact';
8
8
 
package/dist/plugin.js CHANGED
@@ -33,12 +33,12 @@ async function generateTypes() {
33
33
  console.log("saved root-cms.d.ts!");
34
34
  }
35
35
  async function generateSchemaDts(outputPath, schemas) {
36
- const results = [TEMPLATE];
36
+ const dtsFormatter = new DtsFormatter();
37
37
  for (const fileId in schemas) {
38
38
  const schema = schemas[fileId];
39
- results.push(renderSchema(fileId, schema));
39
+ dtsFormatter.addSchemaFile(fileId, schema);
40
40
  }
41
- const output = results.join("\n\n").replaceAll("/** ", "/** ").replaceAll(" */", " */").replace(/\r\n|\r|\n/g, "\n") + "\n";
41
+ const output = dtsFormatter.toString();
42
42
  await fs.writeFile(outputPath, output, "utf-8");
43
43
  }
44
44
  function fieldProperty(field, options) {
@@ -96,26 +96,27 @@ function fieldType(field, options) {
96
96
  return;
97
97
  }
98
98
  const cleanName = alphanumeric(schema.name);
99
- const oneOfTypeId = `${options.typeId}_${cleanName}`;
99
+ const oneOfTypeId = `${cleanName}Fields`;
100
100
  if (!options.oneOfTypes[oneOfTypeId]) {
101
101
  const oneOfTypeInterface = dom.create.interface(
102
102
  oneOfTypeId,
103
103
  dom.DeclarationFlags.Export
104
104
  );
105
- oneOfTypeInterface.members.push(
106
- dom.create.property(
107
- "_type",
108
- dom.type.stringLiteral(schema.name),
109
- dom.DeclarationFlags.None
110
- )
111
- );
105
+ if (schema.description) {
106
+ oneOfTypeInterface.jsDocComment = schema.description;
107
+ }
112
108
  const oneOfTypeFields = schema.fields || [];
113
109
  oneOfTypeFields.forEach((f) => {
114
110
  oneOfTypeInterface.members.push(fieldProperty(f, options));
115
111
  });
116
112
  options.oneOfTypes[oneOfTypeId] = oneOfTypeInterface;
117
113
  }
118
- unionTypes.push(dom.create.namedTypeReference(oneOfTypeId));
114
+ const oneOfOption = dom.create.namedTypeReference("RootCMSOneOfOption");
115
+ oneOfOption.typeArguments = [
116
+ dom.type.stringLiteral(schema.name),
117
+ dom.create.namedTypeReference(oneOfTypeId)
118
+ ];
119
+ unionTypes.push(oneOfOption);
119
120
  });
120
121
  if (unionTypes.length > 0) {
121
122
  oneOf.typeArguments = [dom.create.union(unionTypes)];
@@ -145,64 +146,16 @@ function fieldType(field, options) {
145
146
  }
146
147
  return dom.type.unknown;
147
148
  }
148
- function renderSchema(fileId, schema) {
149
- const jsdoc = `Generated from \`${fileId}\`.`;
150
- const typeId = alphanumeric(path2.parse(fileId).name.split(".")[0]);
151
- const oneOfTypes = {};
152
- const fieldsInterface = `${typeId}Fields`;
153
- const fieldsType = dom.create.interface(
154
- fieldsInterface,
155
- dom.DeclarationFlags.Export
156
- );
157
- fieldsType.jsDocComment = jsdoc;
158
- for (const field of schema.fields) {
159
- if (!field.id) {
160
- continue;
161
- }
162
- fieldsType.members.push(fieldProperty(field, { typeId, oneOfTypes }));
163
- }
164
- const typesOutput = [];
165
- Object.values(oneOfTypes).forEach((oneOfTypeDef) => {
166
- oneOfTypeDef.jsDocComment = jsdoc;
167
- typesOutput.push(
168
- dom.emit(oneOfTypeDef, { singleLineJsDocComments: true }).trim()
169
- );
170
- });
171
- typesOutput.push(
172
- dom.emit(fieldsType, { singleLineJsDocComments: true }).trim()
173
- );
174
- if (fileId.startsWith("/collections/")) {
175
- const baseType = dom.create.namedTypeReference("RootCMSDoc");
176
- baseType.typeArguments.push(dom.create.namedTypeReference(fieldsInterface));
177
- const docType = dom.create.alias(
178
- `${typeId}Doc`,
179
- baseType,
180
- dom.DeclarationFlags.Export
181
- );
182
- docType.jsDocComment = jsdoc;
183
- const docTypeOutput = dom.emit(docType, { singleLineJsDocComments: true }).trim();
184
- typesOutput.push(docTypeOutput);
185
- }
186
- return reformatOutput(typesOutput.join("\n\n"));
187
- }
188
- function reformatOutput(input) {
189
- const lines = input.split("\n");
190
- const results = [];
191
- for (const line of lines) {
192
- const convertedLine = line.replace(/ {4}/g, " ").replaceAll('"', "'");
193
- results.push(convertedLine);
194
- }
195
- return results.join("\n");
196
- }
197
149
  function alphanumeric(input) {
198
150
  return input.replace(/[^a-zA-Z0-9]/g, "");
199
151
  }
200
- var __dirname, TEMPLATE;
152
+ var __dirname, TEMPLATE, DtsFormatter;
201
153
  var init_generate_types = __esm({
202
154
  "cli/generate-types.ts"() {
203
155
  "use strict";
204
156
  __dirname = path2.dirname(fileURLToPath(import.meta.url));
205
- TEMPLATE = `/** Root.js CMS types. This file is autogenerated. */
157
+ TEMPLATE = `/* eslint-disable */
158
+ /** Root.js CMS types. This file is autogenerated. */
206
159
 
207
160
  export interface RootCMSFile {
208
161
  src: string;
@@ -215,6 +168,8 @@ export type RootCMSImage = RootCMSFile;
215
168
 
216
169
  export type RootCMSOneOf<T = any> = T;
217
170
 
171
+ export type RootCMSOneOfOption<T, Base> = Base & {_type: T};
172
+
218
173
  export interface RootCMSRichTextBlock {
219
174
  type: string;
220
175
  data: any;
@@ -255,6 +210,99 @@ export interface RootCMSDoc<Fields extends {}> {
255
210
  /** User-entered field values from the CMS. */
256
211
  fields?: Fields;
257
212
  }`;
213
+ DtsFormatter = class {
214
+ constructor() {
215
+ /**
216
+ * Field types defined in either `.schema.ts` files or used by `oneOf()`
217
+ * fields.
218
+ */
219
+ this.fieldsTypes = {};
220
+ /**
221
+ * For `collections/*.schema.ts` files, output a corresponding "RootCMSDoc"
222
+ * type. For example, `collections/BlogPosts.schema.ts` would output a type
223
+ * called `BlogPostsDoc`.
224
+ */
225
+ this.docTypes = {};
226
+ }
227
+ /** Adds the types for a `.schema.ts` file to the `.d.ts` file. */
228
+ addSchemaFile(fileId, schema) {
229
+ const jsdoc = `Generated from \`${fileId}\`.`;
230
+ const typeId = alphanumeric(path2.parse(fileId).name.split(".")[0]);
231
+ const oneOfTypes = {};
232
+ const fieldsTypeId = `${typeId}Fields`;
233
+ const fieldsType = dom.create.interface(
234
+ fieldsTypeId,
235
+ dom.DeclarationFlags.Export
236
+ );
237
+ fieldsType.jsDocComment = jsdoc;
238
+ for (const field of schema.fields) {
239
+ if (!field.id) {
240
+ continue;
241
+ }
242
+ fieldsType.members.push(fieldProperty(field, { typeId, oneOfTypes }));
243
+ }
244
+ for (const oneOfTypeId in oneOfTypes) {
245
+ if (!this.fieldsTypes[fieldsTypeId]) {
246
+ this.fieldsTypes[oneOfTypeId] = oneOfTypes[oneOfTypeId];
247
+ }
248
+ }
249
+ this.fieldsTypes[fieldsTypeId] = fieldsType;
250
+ if (fileId.startsWith("/collections/")) {
251
+ const docTypeId = `${typeId}Doc`;
252
+ const baseType = dom.create.namedTypeReference("RootCMSDoc");
253
+ baseType.typeArguments.push(dom.create.namedTypeReference(fieldsTypeId));
254
+ const docType = dom.create.alias(
255
+ docTypeId,
256
+ baseType,
257
+ dom.DeclarationFlags.Export
258
+ );
259
+ docType.jsDocComment = jsdoc;
260
+ this.docTypes[docTypeId] = docType;
261
+ }
262
+ }
263
+ /** Generates the `.d.ts` output as a string. */
264
+ toString() {
265
+ const results = [TEMPLATE];
266
+ const sortedFieldsTypes = Object.keys(this.fieldsTypes).sort();
267
+ for (const fieldsTypeId of sortedFieldsTypes) {
268
+ const fieldsType = this.fieldsTypes[fieldsTypeId];
269
+ results.push(this.typeToString(fieldsType));
270
+ const docTypeId = this.fieldsTypeToDocType(fieldsTypeId);
271
+ const docType = this.docTypes[docTypeId];
272
+ if (docType) {
273
+ results.push(this.typeToString(docType));
274
+ }
275
+ }
276
+ const output = results.join("\n\n").replaceAll("/** ", "/** ").replaceAll(" */", " */").replace(/\r\n|\r|\n/g, "\n") + "\n";
277
+ return output;
278
+ }
279
+ /**
280
+ * Converts "<Name>Fields" to "<Name>Doc".
281
+ */
282
+ fieldsTypeToDocType(fieldsTypeId) {
283
+ if (!fieldsTypeId.endsWith("Fields")) {
284
+ throw new Error(`"${fieldsTypeId}" should be suffixed with "Fields"`);
285
+ }
286
+ const name = fieldsTypeId.slice(0, -6);
287
+ return `${name}Doc`;
288
+ }
289
+ typeToString(type2) {
290
+ return this.reformatOutput(dom.emit(type2, { singleLineJsDocComments: true }));
291
+ }
292
+ /**
293
+ * Formats the output to Google style conventions, e.g. 2-space indents and
294
+ * single quote strings.
295
+ */
296
+ reformatOutput(input) {
297
+ const lines = input.trim().split("\n");
298
+ const results = [];
299
+ for (const line of lines) {
300
+ const convertedLine = line.replace(/ {4}/g, " ").replaceAll('"', "'");
301
+ results.push(convertedLine);
302
+ }
303
+ return results.join("\n");
304
+ }
305
+ };
258
306
  }
259
307
  });
260
308
 
@@ -1298,7 +1346,31 @@ function csvToArray(csvString) {
1298
1346
  }
1299
1347
 
1300
1348
  // core/api.ts
1301
- function api(server) {
1349
+ function api(server, options) {
1350
+ server.use("/cms/api/collection.get", async (req, res) => {
1351
+ if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
1352
+ res.status(400).json({ success: false, error: "BAD_REQUEST" });
1353
+ return;
1354
+ }
1355
+ const reqBody = req.body || {};
1356
+ if (!reqBody.collectionId) {
1357
+ res.status(400).json({ success: false, error: "MISSING_COLLECTION_ID" });
1358
+ return;
1359
+ }
1360
+ try {
1361
+ const app = await options.getRenderer(req);
1362
+ const collections = app.getCollections();
1363
+ const collection = collections[reqBody.collectionId];
1364
+ if (!collection) {
1365
+ res.status(404).json({ success: false, error: "NOT_FOUND" });
1366
+ return;
1367
+ }
1368
+ res.status(200).json({ success: true, data: collection });
1369
+ } catch (err) {
1370
+ console.error(err.stack || err);
1371
+ res.status(500).json({ success: false, error: "UNKNOWN" });
1372
+ }
1373
+ });
1302
1374
  server.use("/cms/api/cron.run", async (req, res) => {
1303
1375
  try {
1304
1376
  await runCronJobs(req.rootConfig);
@@ -1752,7 +1824,7 @@ function cmsPlugin(options) {
1752
1824
  });
1753
1825
  const staticDir = path3.resolve(__dirname2, "ui");
1754
1826
  server.use("/cms/static", sirv(staticDir, { dev: false }));
1755
- api(server);
1827
+ api(server, { getRenderer });
1756
1828
  server.use("/cms", async (req, res) => {
1757
1829
  try {
1758
1830
  if (!req.user) {
package/dist/project.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { S as Schema } from './schema-ix967Ud0.js';
1
+ import { S as Schema } from './schema-QC1UuoYc.js';
2
2
  import 'preact';
3
3
 
4
4
  /**
package/dist/project.js CHANGED
@@ -1,11 +1,12 @@
1
1
  // core/project.ts
2
+ var IGNORED_SCHEMA_FOLDERS = ["/appengine/", "/functions/", "/gae/"];
2
3
  var SCHEMA_MODULES = import.meta.glob("/**/*.schema.ts", {
3
4
  eager: true
4
5
  });
5
6
  function getProjectSchemas() {
6
7
  const schemas = {};
7
8
  for (const fileId in SCHEMA_MODULES) {
8
- if (fileId.startsWith("/functions/")) {
9
+ if (IGNORED_SCHEMA_FOLDERS.some((prefix) => fileId.startsWith(prefix))) {
9
10
  continue;
10
11
  }
11
12
  const schemaModule = SCHEMA_MODULES[fileId];
@@ -164,6 +164,11 @@ declare function defineSchema(schema: Schema): Schema;
164
164
  /** Defines the schema for a collection or reusable component. */
165
165
  declare const define: typeof defineSchema;
166
166
  type Collection = Schema & {
167
+ /**
168
+ * The ID of the collection. This comes from the schema filename, e.g
169
+ * `<id>.schema.ts`.
170
+ */
171
+ id: string;
167
172
  /**
168
173
  * Domain where the collection serves from. Used for multi-domain sites,
169
174
  * defaults to the "domain" value `from root.config.ts`.