@bobbykim/manguito-cms-cli 0.8.2 → 0.9.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.
@@ -25993,6 +25993,22 @@ function buildFields(rawFields, sourceFile, ownerTableName) {
25993
25993
  }
25994
25994
  return { fields, errors };
25995
25995
  }
25996
+ function collectJunctionTables(fields) {
25997
+ const junctionTables = [];
25998
+ for (const field of fields) {
25999
+ if (field.field_type === "reference" && field.db_column?.junction) {
26000
+ const j = field.db_column.junction;
26001
+ junctionTables.push({
26002
+ table_name: j.table_name,
26003
+ left_column: j.left_column,
26004
+ right_column: j.right_column,
26005
+ right_table: j.right_table,
26006
+ order_column: j.order_column
26007
+ });
26008
+ }
26009
+ }
26010
+ return junctionTables;
26011
+ }
25996
26012
  function parseContentType(raw, sourceFile) {
25997
26013
  const result = ContentTypeRawSchema.safeParse(raw);
25998
26014
  if (!result.success) {
@@ -26018,19 +26034,6 @@ function parseContentType(raw, sourceFile) {
26018
26034
  const tableName = machineNameToTableName(v.name);
26019
26035
  const { fields, errors } = buildFields(flatRawFields, sourceFile, tableName);
26020
26036
  if (errors.length > 0) return { ok: false, errors };
26021
- const junctionTables = [];
26022
- for (const field of fields) {
26023
- if (field.field_type === "reference" && field.db_column?.junction) {
26024
- const j = field.db_column.junction;
26025
- junctionTables.push({
26026
- table_name: j.table_name,
26027
- left_column: j.left_column,
26028
- right_column: j.right_column,
26029
- right_table: j.right_table,
26030
- order_column: j.order_column
26031
- });
26032
- }
26033
- }
26034
26037
  const nameKebab = nameSegmentToKebab(getNameSegment(v.name));
26035
26038
  const api = v.only_one ? {
26036
26039
  default_base_path: v.default_base_path,
@@ -26052,7 +26055,7 @@ function parseContentType(raw, sourceFile) {
26052
26055
  system_fields: CONTENT_SYSTEM_FIELDS,
26053
26056
  fields,
26054
26057
  ui: { tabs },
26055
- db: { table_name: tableName, junction_tables: junctionTables },
26058
+ db: { table_name: tableName, junction_tables: collectJunctionTables(fields) },
26056
26059
  api
26057
26060
  };
26058
26061
  return { ok: true, schema };
@@ -26105,7 +26108,7 @@ function parseTaxonomyType(raw, sourceFile) {
26105
26108
  source_file: sourceFile,
26106
26109
  system_fields: TAXONOMY_SYSTEM_FIELDS,
26107
26110
  fields,
26108
- db: { table_name: tableName },
26111
+ db: { table_name: tableName, junction_tables: collectJunctionTables(fields) },
26109
26112
  api: {
26110
26113
  collection_path: basePath,
26111
26114
  item_path: `${basePath}/:id`
@@ -12,7 +12,7 @@ import {
12
12
  isColumnBacked,
13
13
  relationCardinality,
14
14
  resolveRelationField
15
- } from "./chunk-4LYEA2W3.js";
15
+ } from "./chunk-L36JYVXK.js";
16
16
 
17
17
  // ../../node_modules/.pnpm/@whatwg-node+fetch@0.10.13/node_modules/@whatwg-node/fetch/dist/shouldSkipPonyfill.js
18
18
  var require_shouldSkipPonyfill = __commonJS({
package/dist/index.js CHANGED
@@ -54,7 +54,7 @@ import {
54
54
  validateCrossReferences,
55
55
  verifyPassword,
56
56
  walkSchemaDirectory
57
- } from "./chunk-4LYEA2W3.js";
57
+ } from "./chunk-L36JYVXK.js";
58
58
 
59
59
  // ../../node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js
60
60
  var require_postgres_array = __commonJS({
@@ -16975,9 +16975,13 @@ function orderParagraphTypes(paragraphs) {
16975
16975
  }
16976
16976
  function generateJunctionTables(registry) {
16977
16977
  const tables = [];
16978
- for (const ct2 of Object.values(registry.content_types)) {
16979
- const leftVar = tableNameToVarName(ct2.db.table_name);
16980
- for (const jt of ct2.db.junction_tables) {
16978
+ const owners = [
16979
+ ...Object.values(registry.content_types),
16980
+ ...Object.values(registry.taxonomy_types)
16981
+ ];
16982
+ for (const owner of owners) {
16983
+ const leftVar = tableNameToVarName(owner.db.table_name);
16984
+ for (const jt of owner.db.junction_tables) {
16981
16985
  const varName = tableNameToVarName(jt.table_name);
16982
16986
  const rightVar = tableNameToVarName(jt.right_table);
16983
16987
  const orderLine = jt.order_column ? ` order: s.integer('order').notNull().default(0),` : null;
@@ -17526,11 +17530,18 @@ function fieldToZodSchema(field, registry) {
17526
17530
  return "z.unknown()";
17527
17531
  }
17528
17532
  }
17533
+ function acceptsNull(f) {
17534
+ if (relationCardinality(f) === "many") return false;
17535
+ return f.db_column ? f.db_column.nullable : f.nullable;
17536
+ }
17537
+ function fieldEntrySchema(f, registry) {
17538
+ let s = fieldToZodSchema(f, registry);
17539
+ if (acceptsNull(f)) s += ".nullable()";
17540
+ if (!f.required) s += ".optional()";
17541
+ return s;
17542
+ }
17529
17543
  function generateParagraphObjectSchema(paragraph, registry) {
17530
- const entries = paragraph.fields.map((f) => {
17531
- const zodType = fieldToZodSchema(f, registry);
17532
- return `${f.name}: ${f.required ? zodType : `${zodType}.optional()`}`;
17533
- });
17544
+ const entries = paragraph.fields.map((f) => `${f.name}: ${fieldEntrySchema(f, registry)}`);
17534
17545
  if (entries.length === 0) return "z.object({})";
17535
17546
  return `z.object({ ${entries.join(", ")} })`;
17536
17547
  }
@@ -17545,10 +17556,7 @@ function systemFieldEntries(schema) {
17545
17556
  return entries;
17546
17557
  }
17547
17558
  function schemaFieldEntries(fields, registry) {
17548
- return fields.map((f) => {
17549
- const zodType = fieldToZodSchema(f, registry);
17550
- return ` ${f.name}: ${f.required ? zodType : `${zodType}.optional()`}`;
17551
- });
17559
+ return fields.map((f) => ` ${f.name}: ${fieldEntrySchema(f, registry)}`);
17552
17560
  }
17553
17561
  function generateContentSchema(schema, registry) {
17554
17562
  const name = schemaVar(schema.name);
@@ -17611,6 +17619,10 @@ var ADMIN_LIST_QUERY = `z.object({
17611
17619
  })`;
17612
17620
  var SLUG_PARAMS = `z.object({ slug: z.string() })`;
17613
17621
  var ID_PARAMS = `z.object({ id: z.string().uuid() })`;
17622
+ var ITEM_IN_USE_RESPONSE = `{
17623
+ content: { 'application/json': { schema: ErrorResponseSchema } },
17624
+ description: 'ITEM_IN_USE: a required reference elsewhere still points at this item',
17625
+ }`;
17614
17626
  var INCLUDE_QUERY = `z.object({ include: z.string().optional().describe('Comma-separated relation field names to expand') })`;
17615
17627
  function generateContentRoutes(contentType) {
17616
17628
  const sVar = schemaVar(contentType.name);
@@ -17770,6 +17782,7 @@ function generateContentRoutes(contentType) {
17770
17782
  content: { 'application/json': { schema: ErrorResponseSchema } },
17771
17783
  description: '${label} not found',
17772
17784
  },
17785
+ 409: ${ITEM_IN_USE_RESPONSE},
17773
17786
  },
17774
17787
  })`
17775
17788
  );
@@ -18031,6 +18044,7 @@ function generateTaxonomyRoutes(taxonomyType) {
18031
18044
  content: { 'application/json': { schema: ErrorResponseSchema } },
18032
18045
  description: '${label} not found',
18033
18046
  },
18047
+ 409: ${ITEM_IN_USE_RESPONSE},
18034
18048
  },
18035
18049
  })`
18036
18050
  );
@@ -22939,6 +22953,46 @@ async function loadParagraphRows(db, registry, pType, parentId, fieldName) {
22939
22953
  }
22940
22954
  return rows;
22941
22955
  }
22956
+ async function loadRelationsForEdit(db, registry, fields, id, row) {
22957
+ for (const f of fields) {
22958
+ if (f.db_column === null) {
22959
+ const comp = f.ui_component;
22960
+ if (comp.component !== "paragraph-embed" || !comp.ref) continue;
22961
+ const pType = registry.paragraph_types[comp.ref];
22962
+ if (!pType) continue;
22963
+ row[f.name] = oneOrMany(relationCardinality(f) ?? "many", await loadParagraphRows(db, registry, pType, id, f.name));
22964
+ } else if (f.db_column.junction) {
22965
+ const j = f.db_column.junction;
22966
+ const r = await db.execute(
22967
+ sql`SELECT ${sql.raw(quoteIdent3(j.right_column))} FROM ${sql.raw(quoteIdent3(j.table_name))} WHERE ${sql.raw(quoteIdent3(j.left_column))} = ${id}`
22968
+ );
22969
+ row[f.name] = r.rows.map((linked) => linked[j.right_column]);
22970
+ }
22971
+ }
22972
+ }
22973
+ async function valuesForPublishCheck(db, registry, requiredFields, id, storedLabels, body) {
22974
+ const stored = { ...storedLabels };
22975
+ if (db) await loadRelationsForEdit(db, registry, requiredFields.filter((f) => !(f.name in body)), id, stored);
22976
+ return { ...stored, ...body };
22977
+ }
22978
+ async function persistRelationFields(db, registry, fields, body, itemId, ownerTable) {
22979
+ const deltas = [];
22980
+ for (const f of fields) {
22981
+ if (!(f.name in body)) continue;
22982
+ if (f.db_column === null) {
22983
+ const comp = f.ui_component;
22984
+ if (comp.component !== "paragraph-embed" || !comp.ref) continue;
22985
+ const pType = registry.paragraph_types[comp.ref];
22986
+ if (!pType) continue;
22987
+ deltas.push(await persistParagraphField(db, itemId, ownerTable, f.name, pType, paragraphItems(f, body[f.name]), registry));
22988
+ } else if (f.db_column.junction) {
22989
+ const value = body[f.name];
22990
+ const relatedIds = Array.isArray(value) ? value.filter((v2) => typeof v2 === "string") : [];
22991
+ await persistJunctionField(db, itemId, f.db_column.junction, relatedIds);
22992
+ }
22993
+ }
22994
+ return deltas;
22995
+ }
22942
22996
  async function lookupBasePathId(db, pathOrName) {
22943
22997
  const r = await db.execute(
22944
22998
  sql`SELECT id FROM base_paths WHERE path = ${pathOrName} OR name = ${pathOrName} LIMIT 1`
@@ -23094,26 +23148,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23094
23148
  404
23095
23149
  );
23096
23150
  }
23097
- if (db) {
23098
- const row = item;
23099
- for (const f of contentType.fields) {
23100
- if (f.db_column === null) {
23101
- const comp = f.ui_component;
23102
- if (comp.component !== "paragraph-embed" || !comp.ref) continue;
23103
- const pType = registry.paragraph_types[comp.ref];
23104
- if (!pType) continue;
23105
- row[f.name] = oneOrMany(relationCardinality(f) ?? "many", await loadParagraphRows(db, registry, pType, id, f.name));
23106
- } else if (f.db_column.junction) {
23107
- const j = f.db_column.junction;
23108
- const r = await db.execute(
23109
- sql`SELECT ${sql.raw(quoteIdent3(j.right_column))} FROM ${sql.raw(quoteIdent3(j.table_name))} WHERE ${sql.raw(quoteIdent3(j.left_column))} = ${id}`
23110
- );
23111
- row[f.name] = r.rows.map(
23112
- (row2) => row2[j.right_column]
23113
- );
23114
- }
23115
- }
23116
- }
23151
+ if (db) await loadRelationsForEdit(db, registry, contentType.fields, id, item);
23117
23152
  const data = projectRow(item, typeName, projectors);
23118
23153
  return c.json({ ok: true, data });
23119
23154
  }
@@ -23204,10 +23239,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23204
23239
  });
23205
23240
  if (publishDeny) return publishDeny;
23206
23241
  }
23207
- const paragraphFields = contentType.fields.filter((f) => f.db_column === null);
23208
- const junctionFields = contentType.fields.filter(
23209
- (f) => f.db_column !== null && f.db_column.junction !== void 0
23210
- );
23211
23242
  const columnBackedFields = contentType.fields.filter(isColumnBacked);
23212
23243
  const columnData = {
23213
23244
  published: body["published"] ?? false
@@ -23242,19 +23273,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23242
23273
  const itemId = item["id"];
23243
23274
  const mediaDeltas = [topLevelMediaDelta(mediaFields, null, storageBody)];
23244
23275
  if (db) {
23245
- for (const f of paragraphFields) {
23246
- const comp = f.ui_component;
23247
- if (comp.component !== "paragraph-embed" || !comp.ref) continue;
23248
- const pType = registry.paragraph_types[comp.ref];
23249
- if (!pType) continue;
23250
- const items = paragraphItems(f, body[f.name]);
23251
- mediaDeltas.push(await persistParagraphField(db, itemId, contentType.db.table_name, f.name, pType, items, registry));
23252
- }
23253
- for (const f of junctionFields) {
23254
- const junction = f.db_column.junction;
23255
- const relatedIds = Array.isArray(body[f.name]) ? body[f.name].filter((v2) => typeof v2 === "string") : [];
23256
- await persistJunctionField(db, itemId, junction, relatedIds);
23257
- }
23276
+ mediaDeltas.push(...await persistRelationFields(db, registry, contentType.fields, body, itemId, contentType.db.table_name));
23258
23277
  }
23259
23278
  await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
23260
23279
  const data = projectRow(item, typeName, projectors);
@@ -23325,7 +23344,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23325
23344
  const publishDeny = await requirePermission("content:edit")(c, async () => {
23326
23345
  });
23327
23346
  if (publishDeny) return publishDeny;
23328
- const merged = { ...fieldKeys.toLabels(existing), ...body };
23347
+ const merged = await valuesForPublishCheck(db, registry, requiredFields, id, fieldKeys.toLabels(existing), body);
23329
23348
  const fieldErrors = checkRequiredFields(requiredFields, merged);
23330
23349
  if (fieldErrors.length > 0) {
23331
23350
  return c.json(
@@ -23341,10 +23360,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23341
23360
  );
23342
23361
  }
23343
23362
  }
23344
- const patchParagraphFields = contentType.fields.filter((f) => f.db_column === null);
23345
- const patchJunctionFields = contentType.fields.filter(
23346
- (f) => f.db_column !== null && f.db_column.junction !== void 0
23347
- );
23348
23363
  const patchColumnBackedFields = contentType.fields.filter(isColumnBacked);
23349
23364
  const patchData = {};
23350
23365
  if (!contentType.only_one && "slug" in body) patchData["slug"] = body["slug"];
@@ -23365,21 +23380,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23365
23380
  topLevelMediaDelta(mediaFields, existing, storageBody)
23366
23381
  ];
23367
23382
  if (db) {
23368
- for (const f of patchParagraphFields) {
23369
- if (!(f.name in body)) continue;
23370
- const comp = f.ui_component;
23371
- if (comp.component !== "paragraph-embed" || !comp.ref) continue;
23372
- const pType = registry.paragraph_types[comp.ref];
23373
- if (!pType) continue;
23374
- const items = paragraphItems(f, body[f.name]);
23375
- mediaDeltas.push(await persistParagraphField(db, id, contentType.db.table_name, f.name, pType, items, registry));
23376
- }
23377
- for (const f of patchJunctionFields) {
23378
- if (!(f.name in body)) continue;
23379
- const junction = f.db_column.junction;
23380
- const relatedIds = Array.isArray(body[f.name]) ? body[f.name].filter((v2) => typeof v2 === "string") : [];
23381
- await persistJunctionField(db, id, junction, relatedIds);
23382
- }
23383
+ mediaDeltas.push(...await persistRelationFields(db, registry, contentType.fields, body, id, contentType.db.table_name));
23383
23384
  }
23384
23385
  await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
23385
23386
  const data = projectRow(updated, typeName, projectors);
@@ -23508,6 +23509,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23508
23509
  404
23509
23510
  );
23510
23511
  }
23512
+ if (db) await loadRelationsForEdit(db, registry, taxonomyType.fields, id, item);
23511
23513
  const data = projectRow(item, typeName, projectors);
23512
23514
  return c.json({ ok: true, data });
23513
23515
  }
@@ -23539,7 +23541,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23539
23541
  });
23540
23542
  if (publishDeny) return publishDeny;
23541
23543
  }
23542
- const taxParagraphFields = taxonomyType.fields.filter((f) => f.db_column === null);
23543
23544
  const taxColumnBackedFields = taxonomyType.fields.filter(isColumnBacked);
23544
23545
  const taxColumnData = {
23545
23546
  published: body["published"] ?? false
@@ -23553,14 +23554,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23553
23554
  const taxItemId = item["id"];
23554
23555
  const mediaDeltas = [topLevelMediaDelta(mediaFields, null, storageBody)];
23555
23556
  if (db) {
23556
- for (const f of taxParagraphFields) {
23557
- const comp = f.ui_component;
23558
- if (comp.component !== "paragraph-embed" || !comp.ref) continue;
23559
- const pType = registry.paragraph_types[comp.ref];
23560
- if (!pType) continue;
23561
- const items = paragraphItems(f, body[f.name]);
23562
- mediaDeltas.push(await persistParagraphField(db, taxItemId, taxonomyType.db.table_name, f.name, pType, items, registry));
23563
- }
23557
+ mediaDeltas.push(...await persistRelationFields(db, registry, taxonomyType.fields, body, taxItemId, taxonomyType.db.table_name));
23564
23558
  }
23565
23559
  await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
23566
23560
  const data = projectRow(item, typeName, projectors);
@@ -23587,7 +23581,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23587
23581
  const publishDeny = await requirePermission("content:edit")(c, async () => {
23588
23582
  });
23589
23583
  if (publishDeny) return publishDeny;
23590
- const merged = { ...fieldKeys.toLabels(existing), ...body };
23584
+ const merged = await valuesForPublishCheck(db, registry, requiredFields, id, fieldKeys.toLabels(existing), body);
23591
23585
  const fieldErrors = checkRequiredFields(requiredFields, merged);
23592
23586
  if (fieldErrors.length > 0) {
23593
23587
  return c.json(
@@ -23603,7 +23597,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23603
23597
  );
23604
23598
  }
23605
23599
  }
23606
- const taxPatchParagraphFields = taxonomyType.fields.filter((f) => f.db_column === null);
23607
23600
  const taxPatchColumnBackedFields = taxonomyType.fields.filter(isColumnBacked);
23608
23601
  const taxPatchData = {};
23609
23602
  if ("published" in body) taxPatchData["published"] = body["published"];
@@ -23623,15 +23616,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
23623
23616
  topLevelMediaDelta(mediaFields, existing, storageBody)
23624
23617
  ];
23625
23618
  if (db) {
23626
- for (const f of taxPatchParagraphFields) {
23627
- if (!(f.name in body)) continue;
23628
- const comp = f.ui_component;
23629
- if (comp.component !== "paragraph-embed" || !comp.ref) continue;
23630
- const pType = registry.paragraph_types[comp.ref];
23631
- if (!pType) continue;
23632
- const items = paragraphItems(f, body[f.name]);
23633
- mediaDeltas.push(await persistParagraphField(db, id, taxonomyType.db.table_name, f.name, pType, items, registry));
23634
- }
23619
+ mediaDeltas.push(...await persistRelationFields(db, registry, taxonomyType.fields, body, id, taxonomyType.db.table_name));
23635
23620
  }
23636
23621
  await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
23637
23622
  const data = projectRow(updated, typeName, projectors);
@@ -24619,7 +24604,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
24619
24604
  }
24620
24605
  var API_VERSION = (() => {
24621
24606
  try {
24622
- return "0.8.1";
24607
+ return "0.8.3";
24623
24608
  } catch {
24624
24609
  return "0.0.0";
24625
24610
  }
@@ -25302,7 +25287,7 @@ function createCmsApp(options) {
25302
25287
  handler: null,
25303
25288
  error: null
25304
25289
  }));
25305
- const ready = import("./handler-VEOWJRAL-AUA3O2FA.js").then(({ createGraphQLHandler }) => {
25290
+ const ready = import("./handler-VEOWJRAL-6WUFNANJ.js").then(({ createGraphQLHandler }) => {
25306
25291
  gqlPasses.forEach((pass, i) => {
25307
25292
  const mount = mounts[i];
25308
25293
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobbykim/manguito-cms-cli",
3
- "version": "0.8.2",
3
+ "version": "0.9.1",
4
4
  "license": "MIT",
5
5
  "author": "Bobby Kim <bobby.sihun.kim@gmail.com>",
6
6
  "homepage": "https://github.com/bobbykim89/manguito-cms",
@@ -29,10 +29,10 @@
29
29
  "tsup": "^8.5.1",
30
30
  "typescript": "^6.0.3",
31
31
  "vite": "^8.1.3",
32
- "@bobbykim/manguito-cms-admin": "0.5.1",
33
- "@bobbykim/manguito-cms-core": "0.7.0",
34
- "@bobbykim/manguito-cms-db": "0.1.7",
35
- "@bobbykim/manguito-cms-api": "0.8.1"
32
+ "@bobbykim/manguito-cms-admin": "0.5.2",
33
+ "@bobbykim/manguito-cms-core": "0.8.0",
34
+ "@bobbykim/manguito-cms-api": "0.8.3",
35
+ "@bobbykim/manguito-cms-db": "0.1.8"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^25.9.5",
@@ -43,7 +43,7 @@
43
43
  "build": "tsup",
44
44
  "dev": "tsup --watch",
45
45
  "test": "dotenv -e ../../.env.test -- vitest run",
46
- "lint": "eslint src",
46
+ "lint": "eslint src tests",
47
47
  "typecheck": "tsc --noEmit"
48
48
  }
49
49
  }