@bobbykim/manguito-cms-cli 0.8.1 → 0.9.0
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:
|
|
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-
|
|
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-
|
|
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
|
-
|
|
16979
|
-
|
|
16980
|
-
|
|
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;
|
|
@@ -22839,13 +22843,89 @@ function checkRelationInput(fields, body, registry, path5 = "") {
|
|
|
22839
22843
|
}
|
|
22840
22844
|
return errors;
|
|
22841
22845
|
}
|
|
22846
|
+
function paragraphItemsIn(fields, body, registry, path5) {
|
|
22847
|
+
const out = [];
|
|
22848
|
+
for (const field of fields) {
|
|
22849
|
+
if (field.field_type !== "paragraph" || !(field.name in body)) continue;
|
|
22850
|
+
const comp = field.ui_component;
|
|
22851
|
+
const pType = comp.component === "paragraph-embed" ? registry.paragraph_types[comp.ref] : void 0;
|
|
22852
|
+
if (!pType) continue;
|
|
22853
|
+
const value = body[field.name];
|
|
22854
|
+
const name = `${path5}${field.name}`;
|
|
22855
|
+
if (relationCardinality(field) === "one") {
|
|
22856
|
+
if (isPlainObject(value)) out.push({ item: value, fields: pType.fields, prefix: `${name}.` });
|
|
22857
|
+
} else if (Array.isArray(value)) {
|
|
22858
|
+
value.forEach((item, i) => {
|
|
22859
|
+
if (isPlainObject(item)) out.push({ item, fields: pType.fields, prefix: `${name}[${i}].` });
|
|
22860
|
+
});
|
|
22861
|
+
}
|
|
22862
|
+
}
|
|
22863
|
+
return out;
|
|
22864
|
+
}
|
|
22865
|
+
function isMissing(value) {
|
|
22866
|
+
if (value === null || value === void 0) return true;
|
|
22867
|
+
if (typeof value === "string" && value.trim() === "") return true;
|
|
22868
|
+
if (Array.isArray(value) && value.length === 0) return true;
|
|
22869
|
+
return false;
|
|
22870
|
+
}
|
|
22871
|
+
function checkRequiredInParagraphItems(fields, body, registry, path5 = "") {
|
|
22872
|
+
const errors = [];
|
|
22873
|
+
for (const { item, fields: itemFields, prefix } of paragraphItemsIn(fields, body, registry, path5)) {
|
|
22874
|
+
for (const f of itemFields) {
|
|
22875
|
+
if (!f.required || f.removed === true || !f.db_column || f.db_column.junction) continue;
|
|
22876
|
+
if (isMissing(item[f.name])) {
|
|
22877
|
+
errors.push({ field: `${prefix}${f.name}`, message: `${prefix}${f.name} is required.` });
|
|
22878
|
+
}
|
|
22879
|
+
}
|
|
22880
|
+
errors.push(...checkRequiredInParagraphItems(itemFields, item, registry, prefix));
|
|
22881
|
+
}
|
|
22882
|
+
return errors;
|
|
22883
|
+
}
|
|
22842
22884
|
function quoteIdent2(name) {
|
|
22843
22885
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`);
|
|
22844
22886
|
return `"${name}"`;
|
|
22845
22887
|
}
|
|
22888
|
+
function collectReferences(fields, body, registry, path5, out) {
|
|
22889
|
+
for (const f of fields) {
|
|
22890
|
+
if (!(f.name in body) || !f.db_column) continue;
|
|
22891
|
+
const table = f.db_column.junction ? f.db_column.junction.right_table : f.db_column.foreign_key?.table;
|
|
22892
|
+
if (!table) continue;
|
|
22893
|
+
const value = body[f.name];
|
|
22894
|
+
const name = `${path5}${f.name}`;
|
|
22895
|
+
if (typeof value === "string") {
|
|
22896
|
+
out.push({ table, id: value, field: name });
|
|
22897
|
+
} else if (Array.isArray(value)) {
|
|
22898
|
+
value.forEach((v2, i) => {
|
|
22899
|
+
if (typeof v2 === "string") out.push({ table, id: v2, field: `${name}[${i}]` });
|
|
22900
|
+
});
|
|
22901
|
+
}
|
|
22902
|
+
}
|
|
22903
|
+
for (const { item, fields: itemFields, prefix } of paragraphItemsIn(fields, body, registry, path5)) {
|
|
22904
|
+
collectReferences(itemFields, item, registry, prefix, out);
|
|
22905
|
+
}
|
|
22906
|
+
}
|
|
22907
|
+
async function findMissingReferences(db, fields, body, registry) {
|
|
22908
|
+
const uses = [];
|
|
22909
|
+
collectReferences(fields, body, registry, "", uses);
|
|
22910
|
+
if (uses.length === 0) return [];
|
|
22911
|
+
const lookups = [...new Set(uses.map((u) => u.table))].map(async (table) => {
|
|
22912
|
+
const ids = [...new Set(uses.filter((u) => u.table === table && isUuid(u.id)).map((u) => u.id.toLowerCase()))];
|
|
22913
|
+
if (ids.length === 0) return [table, /* @__PURE__ */ new Set()];
|
|
22914
|
+
const result = await db.execute(
|
|
22915
|
+
sql`SELECT id FROM ${sql.raw(quoteIdent2(table))} WHERE id IN (${sql.join(ids.map((id) => sql`${id}`), sql`, `)})`
|
|
22916
|
+
);
|
|
22917
|
+
return [table, new Set(result.rows.map((r) => String(r.id).toLowerCase()))];
|
|
22918
|
+
});
|
|
22919
|
+
const found = new Map(await Promise.all(lookups));
|
|
22920
|
+
return uses.filter((u) => !found.get(u.table).has(u.id.toLowerCase())).map((u) => ({ field: u.field, message: `${u.field} refers to an item that does not exist.` }));
|
|
22921
|
+
}
|
|
22922
|
+
function quoteIdent3(name) {
|
|
22923
|
+
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`);
|
|
22924
|
+
return `"${name}"`;
|
|
22925
|
+
}
|
|
22846
22926
|
async function loadParagraphRows(db, registry, pType, parentId, fieldName) {
|
|
22847
22927
|
const result = await db.execute(
|
|
22848
|
-
sql`SELECT * FROM ${sql.raw(
|
|
22928
|
+
sql`SELECT * FROM ${sql.raw(quoteIdent3(pType.db.table_name))} WHERE parent_id = ${parentId} AND parent_field = ${fieldName} ORDER BY "order" ASC`
|
|
22849
22929
|
);
|
|
22850
22930
|
const rows = result.rows;
|
|
22851
22931
|
for (const nf of pType.fields) {
|
|
@@ -22863,6 +22943,46 @@ async function loadParagraphRows(db, registry, pType, parentId, fieldName) {
|
|
|
22863
22943
|
}
|
|
22864
22944
|
return rows;
|
|
22865
22945
|
}
|
|
22946
|
+
async function loadRelationsForEdit(db, registry, fields, id, row) {
|
|
22947
|
+
for (const f of fields) {
|
|
22948
|
+
if (f.db_column === null) {
|
|
22949
|
+
const comp = f.ui_component;
|
|
22950
|
+
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
22951
|
+
const pType = registry.paragraph_types[comp.ref];
|
|
22952
|
+
if (!pType) continue;
|
|
22953
|
+
row[f.name] = oneOrMany(relationCardinality(f) ?? "many", await loadParagraphRows(db, registry, pType, id, f.name));
|
|
22954
|
+
} else if (f.db_column.junction) {
|
|
22955
|
+
const j = f.db_column.junction;
|
|
22956
|
+
const r = await db.execute(
|
|
22957
|
+
sql`SELECT ${sql.raw(quoteIdent3(j.right_column))} FROM ${sql.raw(quoteIdent3(j.table_name))} WHERE ${sql.raw(quoteIdent3(j.left_column))} = ${id}`
|
|
22958
|
+
);
|
|
22959
|
+
row[f.name] = r.rows.map((linked) => linked[j.right_column]);
|
|
22960
|
+
}
|
|
22961
|
+
}
|
|
22962
|
+
}
|
|
22963
|
+
async function valuesForPublishCheck(db, registry, requiredFields, id, storedLabels, body) {
|
|
22964
|
+
const stored = { ...storedLabels };
|
|
22965
|
+
if (db) await loadRelationsForEdit(db, registry, requiredFields.filter((f) => !(f.name in body)), id, stored);
|
|
22966
|
+
return { ...stored, ...body };
|
|
22967
|
+
}
|
|
22968
|
+
async function persistRelationFields(db, registry, fields, body, itemId, ownerTable) {
|
|
22969
|
+
const deltas = [];
|
|
22970
|
+
for (const f of fields) {
|
|
22971
|
+
if (!(f.name in body)) continue;
|
|
22972
|
+
if (f.db_column === null) {
|
|
22973
|
+
const comp = f.ui_component;
|
|
22974
|
+
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
22975
|
+
const pType = registry.paragraph_types[comp.ref];
|
|
22976
|
+
if (!pType) continue;
|
|
22977
|
+
deltas.push(await persistParagraphField(db, itemId, ownerTable, f.name, pType, paragraphItems(f, body[f.name]), registry));
|
|
22978
|
+
} else if (f.db_column.junction) {
|
|
22979
|
+
const value = body[f.name];
|
|
22980
|
+
const relatedIds = Array.isArray(value) ? value.filter((v2) => typeof v2 === "string") : [];
|
|
22981
|
+
await persistJunctionField(db, itemId, f.db_column.junction, relatedIds);
|
|
22982
|
+
}
|
|
22983
|
+
}
|
|
22984
|
+
return deltas;
|
|
22985
|
+
}
|
|
22866
22986
|
async function lookupBasePathId(db, pathOrName) {
|
|
22867
22987
|
const r = await db.execute(
|
|
22868
22988
|
sql`SELECT id FROM base_paths WHERE path = ${pathOrName} OR name = ${pathOrName} LIMIT 1`
|
|
@@ -22870,22 +22990,20 @@ async function lookupBasePathId(db, pathOrName) {
|
|
|
22870
22990
|
return r.rows[0]?.id ?? null;
|
|
22871
22991
|
}
|
|
22872
22992
|
var SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
22873
|
-
function isEmpty(value) {
|
|
22874
|
-
if (value === null || value === void 0) return true;
|
|
22875
|
-
if (typeof value === "string" && value.trim() === "") return true;
|
|
22876
|
-
if (Array.isArray(value) && value.length === 0) return true;
|
|
22877
|
-
return false;
|
|
22878
|
-
}
|
|
22879
22993
|
function checkRequiredFields(fields, data) {
|
|
22880
|
-
return fields.filter((f) => f.required &&
|
|
22881
|
-
}
|
|
22882
|
-
function
|
|
22883
|
-
const
|
|
22884
|
-
|
|
22885
|
-
return
|
|
22886
|
-
|
|
22887
|
-
|
|
22888
|
-
)
|
|
22994
|
+
return fields.filter((f) => f.required && isMissing(data[f.name])).map((f) => ({ field: f.name, message: `${f.label} is required` }));
|
|
22995
|
+
}
|
|
22996
|
+
async function relationInputError(c, fields, body, registry, db) {
|
|
22997
|
+
const refuse = (message, details) => c.json({ ok: false, error: { code: "VALIDATION_ERROR", message, details } }, 422);
|
|
22998
|
+
const shape = checkRelationInput(fields, body, registry);
|
|
22999
|
+
if (shape.length > 0) return refuse("Relation fields have the wrong shape", shape);
|
|
23000
|
+
const required = checkRequiredInParagraphItems(fields, body, registry);
|
|
23001
|
+
if (required.length > 0) return refuse("Required fields are missing", required);
|
|
23002
|
+
if (db) {
|
|
23003
|
+
const missing = await findMissingReferences(db, fields, body, registry);
|
|
23004
|
+
if (missing.length > 0) return refuse("Relation fields refer to items that do not exist", missing);
|
|
23005
|
+
}
|
|
23006
|
+
return null;
|
|
22889
23007
|
}
|
|
22890
23008
|
function parseListQuery(url, schemaFieldNames, relationFieldNames, columnFor) {
|
|
22891
23009
|
const searchParams = new URL(url).searchParams;
|
|
@@ -23020,26 +23138,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23020
23138
|
404
|
|
23021
23139
|
);
|
|
23022
23140
|
}
|
|
23023
|
-
if (db)
|
|
23024
|
-
const row = item;
|
|
23025
|
-
for (const f of contentType.fields) {
|
|
23026
|
-
if (f.db_column === null) {
|
|
23027
|
-
const comp = f.ui_component;
|
|
23028
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
23029
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
23030
|
-
if (!pType) continue;
|
|
23031
|
-
row[f.name] = oneOrMany(relationCardinality(f) ?? "many", await loadParagraphRows(db, registry, pType, id, f.name));
|
|
23032
|
-
} else if (f.db_column.junction) {
|
|
23033
|
-
const j = f.db_column.junction;
|
|
23034
|
-
const r = await db.execute(
|
|
23035
|
-
sql`SELECT ${sql.raw(quoteIdent2(j.right_column))} FROM ${sql.raw(quoteIdent2(j.table_name))} WHERE ${sql.raw(quoteIdent2(j.left_column))} = ${id}`
|
|
23036
|
-
);
|
|
23037
|
-
row[f.name] = r.rows.map(
|
|
23038
|
-
(row2) => row2[j.right_column]
|
|
23039
|
-
);
|
|
23040
|
-
}
|
|
23041
|
-
}
|
|
23042
|
-
}
|
|
23141
|
+
if (db) await loadRelationsForEdit(db, registry, contentType.fields, id, item);
|
|
23043
23142
|
const data = projectRow(item, typeName, projectors);
|
|
23044
23143
|
return c.json({ ok: true, data });
|
|
23045
23144
|
}
|
|
@@ -23108,8 +23207,8 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23108
23207
|
}
|
|
23109
23208
|
);
|
|
23110
23209
|
const writeNewItem = async (c, body) => {
|
|
23111
|
-
const
|
|
23112
|
-
if (
|
|
23210
|
+
const inputError = await relationInputError(c, contentType.fields, body, registry, db);
|
|
23211
|
+
if (inputError) return inputError;
|
|
23113
23212
|
const storageBody = fieldKeys.toStorage(body);
|
|
23114
23213
|
const fieldErrors = checkRequiredFields(requiredFields, body);
|
|
23115
23214
|
if (fieldErrors.length > 0) {
|
|
@@ -23130,10 +23229,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23130
23229
|
});
|
|
23131
23230
|
if (publishDeny) return publishDeny;
|
|
23132
23231
|
}
|
|
23133
|
-
const paragraphFields = contentType.fields.filter((f) => f.db_column === null);
|
|
23134
|
-
const junctionFields = contentType.fields.filter(
|
|
23135
|
-
(f) => f.db_column !== null && f.db_column.junction !== void 0
|
|
23136
|
-
);
|
|
23137
23232
|
const columnBackedFields = contentType.fields.filter(isColumnBacked);
|
|
23138
23233
|
const columnData = {
|
|
23139
23234
|
published: body["published"] ?? false
|
|
@@ -23168,19 +23263,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23168
23263
|
const itemId = item["id"];
|
|
23169
23264
|
const mediaDeltas = [topLevelMediaDelta(mediaFields, null, storageBody)];
|
|
23170
23265
|
if (db) {
|
|
23171
|
-
|
|
23172
|
-
const comp = f.ui_component;
|
|
23173
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
23174
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
23175
|
-
if (!pType) continue;
|
|
23176
|
-
const items = paragraphItems(f, body[f.name]);
|
|
23177
|
-
mediaDeltas.push(await persistParagraphField(db, itemId, contentType.db.table_name, f.name, pType, items, registry));
|
|
23178
|
-
}
|
|
23179
|
-
for (const f of junctionFields) {
|
|
23180
|
-
const junction = f.db_column.junction;
|
|
23181
|
-
const relatedIds = Array.isArray(body[f.name]) ? body[f.name].filter((v2) => typeof v2 === "string") : [];
|
|
23182
|
-
await persistJunctionField(db, itemId, junction, relatedIds);
|
|
23183
|
-
}
|
|
23266
|
+
mediaDeltas.push(...await persistRelationFields(db, registry, contentType.fields, body, itemId, contentType.db.table_name));
|
|
23184
23267
|
}
|
|
23185
23268
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
23186
23269
|
const data = projectRow(item, typeName, projectors);
|
|
@@ -23244,14 +23327,14 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23244
23327
|
}
|
|
23245
23328
|
);
|
|
23246
23329
|
const writeExistingItem = async (c, id, existing, body) => {
|
|
23247
|
-
const
|
|
23248
|
-
if (
|
|
23330
|
+
const inputError = await relationInputError(c, contentType.fields, body, registry, db);
|
|
23331
|
+
if (inputError) return inputError;
|
|
23249
23332
|
const storageBody = fieldKeys.toStorage(body);
|
|
23250
23333
|
if (body["published"] === true) {
|
|
23251
23334
|
const publishDeny = await requirePermission("content:edit")(c, async () => {
|
|
23252
23335
|
});
|
|
23253
23336
|
if (publishDeny) return publishDeny;
|
|
23254
|
-
const merged =
|
|
23337
|
+
const merged = await valuesForPublishCheck(db, registry, requiredFields, id, fieldKeys.toLabels(existing), body);
|
|
23255
23338
|
const fieldErrors = checkRequiredFields(requiredFields, merged);
|
|
23256
23339
|
if (fieldErrors.length > 0) {
|
|
23257
23340
|
return c.json(
|
|
@@ -23267,10 +23350,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23267
23350
|
);
|
|
23268
23351
|
}
|
|
23269
23352
|
}
|
|
23270
|
-
const patchParagraphFields = contentType.fields.filter((f) => f.db_column === null);
|
|
23271
|
-
const patchJunctionFields = contentType.fields.filter(
|
|
23272
|
-
(f) => f.db_column !== null && f.db_column.junction !== void 0
|
|
23273
|
-
);
|
|
23274
23353
|
const patchColumnBackedFields = contentType.fields.filter(isColumnBacked);
|
|
23275
23354
|
const patchData = {};
|
|
23276
23355
|
if (!contentType.only_one && "slug" in body) patchData["slug"] = body["slug"];
|
|
@@ -23291,21 +23370,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23291
23370
|
topLevelMediaDelta(mediaFields, existing, storageBody)
|
|
23292
23371
|
];
|
|
23293
23372
|
if (db) {
|
|
23294
|
-
|
|
23295
|
-
if (!(f.name in body)) continue;
|
|
23296
|
-
const comp = f.ui_component;
|
|
23297
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
23298
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
23299
|
-
if (!pType) continue;
|
|
23300
|
-
const items = paragraphItems(f, body[f.name]);
|
|
23301
|
-
mediaDeltas.push(await persistParagraphField(db, id, contentType.db.table_name, f.name, pType, items, registry));
|
|
23302
|
-
}
|
|
23303
|
-
for (const f of patchJunctionFields) {
|
|
23304
|
-
if (!(f.name in body)) continue;
|
|
23305
|
-
const junction = f.db_column.junction;
|
|
23306
|
-
const relatedIds = Array.isArray(body[f.name]) ? body[f.name].filter((v2) => typeof v2 === "string") : [];
|
|
23307
|
-
await persistJunctionField(db, id, junction, relatedIds);
|
|
23308
|
-
}
|
|
23373
|
+
mediaDeltas.push(...await persistRelationFields(db, registry, contentType.fields, body, id, contentType.db.table_name));
|
|
23309
23374
|
}
|
|
23310
23375
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
23311
23376
|
const data = projectRow(updated, typeName, projectors);
|
|
@@ -23434,6 +23499,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23434
23499
|
404
|
|
23435
23500
|
);
|
|
23436
23501
|
}
|
|
23502
|
+
if (db) await loadRelationsForEdit(db, registry, taxonomyType.fields, id, item);
|
|
23437
23503
|
const data = projectRow(item, typeName, projectors);
|
|
23438
23504
|
return c.json({ ok: true, data });
|
|
23439
23505
|
}
|
|
@@ -23443,8 +23509,8 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23443
23509
|
requirePermission("content:create"),
|
|
23444
23510
|
async (c) => {
|
|
23445
23511
|
const body = await c.req.json();
|
|
23446
|
-
const
|
|
23447
|
-
if (
|
|
23512
|
+
const inputError = await relationInputError(c, taxonomyType.fields, body, registry, db);
|
|
23513
|
+
if (inputError) return inputError;
|
|
23448
23514
|
const storageBody = fieldKeys.toStorage(body);
|
|
23449
23515
|
const fieldErrors = checkRequiredFields(requiredFields, body);
|
|
23450
23516
|
if (fieldErrors.length > 0) {
|
|
@@ -23465,7 +23531,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23465
23531
|
});
|
|
23466
23532
|
if (publishDeny) return publishDeny;
|
|
23467
23533
|
}
|
|
23468
|
-
const taxParagraphFields = taxonomyType.fields.filter((f) => f.db_column === null);
|
|
23469
23534
|
const taxColumnBackedFields = taxonomyType.fields.filter(isColumnBacked);
|
|
23470
23535
|
const taxColumnData = {
|
|
23471
23536
|
published: body["published"] ?? false
|
|
@@ -23479,14 +23544,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23479
23544
|
const taxItemId = item["id"];
|
|
23480
23545
|
const mediaDeltas = [topLevelMediaDelta(mediaFields, null, storageBody)];
|
|
23481
23546
|
if (db) {
|
|
23482
|
-
|
|
23483
|
-
const comp = f.ui_component;
|
|
23484
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
23485
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
23486
|
-
if (!pType) continue;
|
|
23487
|
-
const items = paragraphItems(f, body[f.name]);
|
|
23488
|
-
mediaDeltas.push(await persistParagraphField(db, taxItemId, taxonomyType.db.table_name, f.name, pType, items, registry));
|
|
23489
|
-
}
|
|
23547
|
+
mediaDeltas.push(...await persistRelationFields(db, registry, taxonomyType.fields, body, taxItemId, taxonomyType.db.table_name));
|
|
23490
23548
|
}
|
|
23491
23549
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
23492
23550
|
const data = projectRow(item, typeName, projectors);
|
|
@@ -23507,13 +23565,13 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23507
23565
|
404
|
|
23508
23566
|
);
|
|
23509
23567
|
}
|
|
23510
|
-
const
|
|
23511
|
-
if (
|
|
23568
|
+
const inputError = await relationInputError(c, taxonomyType.fields, body, registry, db);
|
|
23569
|
+
if (inputError) return inputError;
|
|
23512
23570
|
if (body["published"] === true) {
|
|
23513
23571
|
const publishDeny = await requirePermission("content:edit")(c, async () => {
|
|
23514
23572
|
});
|
|
23515
23573
|
if (publishDeny) return publishDeny;
|
|
23516
|
-
const merged =
|
|
23574
|
+
const merged = await valuesForPublishCheck(db, registry, requiredFields, id, fieldKeys.toLabels(existing), body);
|
|
23517
23575
|
const fieldErrors = checkRequiredFields(requiredFields, merged);
|
|
23518
23576
|
if (fieldErrors.length > 0) {
|
|
23519
23577
|
return c.json(
|
|
@@ -23529,7 +23587,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23529
23587
|
);
|
|
23530
23588
|
}
|
|
23531
23589
|
}
|
|
23532
|
-
const taxPatchParagraphFields = taxonomyType.fields.filter((f) => f.db_column === null);
|
|
23533
23590
|
const taxPatchColumnBackedFields = taxonomyType.fields.filter(isColumnBacked);
|
|
23534
23591
|
const taxPatchData = {};
|
|
23535
23592
|
if ("published" in body) taxPatchData["published"] = body["published"];
|
|
@@ -23549,15 +23606,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23549
23606
|
topLevelMediaDelta(mediaFields, existing, storageBody)
|
|
23550
23607
|
];
|
|
23551
23608
|
if (db) {
|
|
23552
|
-
|
|
23553
|
-
if (!(f.name in body)) continue;
|
|
23554
|
-
const comp = f.ui_component;
|
|
23555
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
23556
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
23557
|
-
if (!pType) continue;
|
|
23558
|
-
const items = paragraphItems(f, body[f.name]);
|
|
23559
|
-
mediaDeltas.push(await persistParagraphField(db, id, taxonomyType.db.table_name, f.name, pType, items, registry));
|
|
23560
|
-
}
|
|
23609
|
+
mediaDeltas.push(...await persistRelationFields(db, registry, taxonomyType.fields, body, id, taxonomyType.db.table_name));
|
|
23561
23610
|
}
|
|
23562
23611
|
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
23563
23612
|
const data = projectRow(updated, typeName, projectors);
|
|
@@ -24545,7 +24594,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
24545
24594
|
}
|
|
24546
24595
|
var API_VERSION = (() => {
|
|
24547
24596
|
try {
|
|
24548
|
-
return "0.8.
|
|
24597
|
+
return "0.8.2";
|
|
24549
24598
|
} catch {
|
|
24550
24599
|
return "0.0.0";
|
|
24551
24600
|
}
|
|
@@ -24654,7 +24703,7 @@ function codeError(code, message) {
|
|
|
24654
24703
|
err.code = code;
|
|
24655
24704
|
return err;
|
|
24656
24705
|
}
|
|
24657
|
-
function
|
|
24706
|
+
function quoteIdent4(name) {
|
|
24658
24707
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) {
|
|
24659
24708
|
throw new Error(`Unsafe SQL identifier: ${name}`);
|
|
24660
24709
|
}
|
|
@@ -24705,7 +24754,7 @@ function whereFragment(conds) {
|
|
|
24705
24754
|
function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
24706
24755
|
const { relations = {}, publishedRelations = false, sortableColumns } = options;
|
|
24707
24756
|
function tableRaw() {
|
|
24708
|
-
return sql.raw(
|
|
24757
|
+
return sql.raw(quoteIdent4(tableName));
|
|
24709
24758
|
}
|
|
24710
24759
|
async function resolveRows(rows, include, cache) {
|
|
24711
24760
|
if (rows.length === 0) return rows;
|
|
@@ -24748,7 +24797,7 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
24748
24797
|
const offset = (page - 1) * per_page;
|
|
24749
24798
|
const conds = buildConditions(published_only, filters, search);
|
|
24750
24799
|
const where = whereFragment(conds);
|
|
24751
|
-
const sortCol = sql.raw(
|
|
24800
|
+
const sortCol = sql.raw(quoteIdent4(sort_by));
|
|
24752
24801
|
const sortDir = sql.raw(sort_order === "desc" ? "DESC" : "ASC");
|
|
24753
24802
|
const countResult = await db.execute(
|
|
24754
24803
|
sql`SELECT COUNT(*) AS total FROM ${tableRaw()}${where}`
|
|
@@ -24802,7 +24851,7 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
24802
24851
|
updated_at: /* @__PURE__ */ new Date()
|
|
24803
24852
|
};
|
|
24804
24853
|
const entries = Object.entries(record).filter(([, v2]) => v2 !== void 0);
|
|
24805
|
-
const colsSql = sql.join(entries.map(([k]) => sql.raw(
|
|
24854
|
+
const colsSql = sql.join(entries.map(([k]) => sql.raw(quoteIdent4(k))), sql`, `);
|
|
24806
24855
|
const valsSql = sql.join(entries.map(([, v2]) => sql`${v2}`), sql`, `);
|
|
24807
24856
|
const result = await db.execute(
|
|
24808
24857
|
sql`INSERT INTO ${tableRaw()} (${colsSql}) VALUES (${valsSql}) RETURNING *`
|
|
@@ -24819,7 +24868,7 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
24819
24868
|
return this.findOne(id);
|
|
24820
24869
|
}
|
|
24821
24870
|
const setClauses = sql.join(
|
|
24822
|
-
entries.map(([k, v2]) => sql`${sql.raw(
|
|
24871
|
+
entries.map(([k, v2]) => sql`${sql.raw(quoteIdent4(k))} = ${v2}`),
|
|
24823
24872
|
sql`, `
|
|
24824
24873
|
);
|
|
24825
24874
|
const result = await db.execute(
|
|
@@ -25228,7 +25277,7 @@ function createCmsApp(options) {
|
|
|
25228
25277
|
handler: null,
|
|
25229
25278
|
error: null
|
|
25230
25279
|
}));
|
|
25231
|
-
const ready = import("./handler-VEOWJRAL-
|
|
25280
|
+
const ready = import("./handler-VEOWJRAL-6WUFNANJ.js").then(({ createGraphQLHandler }) => {
|
|
25232
25281
|
gqlPasses.forEach((pass, i) => {
|
|
25233
25282
|
const mount = mounts[i];
|
|
25234
25283
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobbykim/manguito-cms-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
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.
|
|
33
|
-
"@bobbykim/manguito-cms-
|
|
34
|
-
"@bobbykim/manguito-cms-
|
|
35
|
-
"@bobbykim/manguito-cms-
|
|
32
|
+
"@bobbykim/manguito-cms-admin": "0.5.2",
|
|
33
|
+
"@bobbykim/manguito-cms-api": "0.8.2",
|
|
34
|
+
"@bobbykim/manguito-cms-db": "0.1.8",
|
|
35
|
+
"@bobbykim/manguito-cms-core": "0.8.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^25.9.5",
|