@bobbykim/manguito-cms-cli 0.11.3 → 0.11.4
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.
|
@@ -9270,7 +9270,7 @@ var require_bcryptjs = __commonJS({
|
|
|
9270
9270
|
}
|
|
9271
9271
|
});
|
|
9272
9272
|
|
|
9273
|
-
// ../api/dist/chunk-
|
|
9273
|
+
// ../api/dist/chunk-IFE2L4VM.mjs
|
|
9274
9274
|
init_esm_shims();
|
|
9275
9275
|
import crypto from "crypto";
|
|
9276
9276
|
|
|
@@ -26681,7 +26681,7 @@ function describeSchemaChange(input) {
|
|
|
26681
26681
|
return { from: from?.version ?? null, to: to.version, types, identical };
|
|
26682
26682
|
}
|
|
26683
26683
|
|
|
26684
|
-
// ../api/dist/chunk-
|
|
26684
|
+
// ../api/dist/chunk-IFE2L4VM.mjs
|
|
26685
26685
|
function isColumnBacked2(field) {
|
|
26686
26686
|
const col = field.db_column;
|
|
26687
26687
|
if (col === null) return false;
|
|
@@ -26788,10 +26788,131 @@ function createFieldKeyMapFromProjection(projectionType, allFields) {
|
|
|
26788
26788
|
droppedLabels
|
|
26789
26789
|
);
|
|
26790
26790
|
}
|
|
26791
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
26792
|
+
function isUuid(v) {
|
|
26793
|
+
return typeof v === "string" && UUID_RE.test(v);
|
|
26794
|
+
}
|
|
26795
|
+
function isPlainObject2(v) {
|
|
26796
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
26797
|
+
}
|
|
26798
|
+
function checkRelationInput(fields, body, registry2, path4 = "") {
|
|
26799
|
+
const errors = [];
|
|
26800
|
+
for (const field of fields) {
|
|
26801
|
+
if (!Object.hasOwn(body, field.name)) continue;
|
|
26802
|
+
const cardinality = relationCardinality(field);
|
|
26803
|
+
if (cardinality === null) continue;
|
|
26804
|
+
const value = body[field.name];
|
|
26805
|
+
const name = `${path4}${field.name}`;
|
|
26806
|
+
if (field.field_type === "reference") {
|
|
26807
|
+
const ok = cardinality === "one" ? value === null || isUuid(value) : Array.isArray(value) && value.every(isUuid);
|
|
26808
|
+
if (!ok) {
|
|
26809
|
+
errors.push({
|
|
26810
|
+
field: name,
|
|
26811
|
+
message: cardinality === "one" ? `${name} holds one item: send a UUID string or null.` : `${name} holds a list: send an array of UUID strings.`
|
|
26812
|
+
});
|
|
26813
|
+
}
|
|
26814
|
+
continue;
|
|
26815
|
+
}
|
|
26816
|
+
const items = cardinality === "one" ? value === null ? [] : isPlainObject2(value) ? [value] : void 0 : Array.isArray(value) && value.every(isPlainObject2) ? value : void 0;
|
|
26817
|
+
if (items === void 0) {
|
|
26818
|
+
errors.push({
|
|
26819
|
+
field: name,
|
|
26820
|
+
message: cardinality === "one" ? `${name} holds one item: send an object or null.` : `${name} holds a list: send an array of objects.`
|
|
26821
|
+
});
|
|
26822
|
+
continue;
|
|
26823
|
+
}
|
|
26824
|
+
const pType = paragraphTypeOf(field, registry2);
|
|
26825
|
+
if (!pType) continue;
|
|
26826
|
+
items.forEach((item, i) => {
|
|
26827
|
+
const prefix = cardinality === "one" ? `${name}.` : `${name}[${i}].`;
|
|
26828
|
+
errors.push(...checkRelationInput(pType.fields, item, registry2, prefix));
|
|
26829
|
+
});
|
|
26830
|
+
}
|
|
26831
|
+
return errors;
|
|
26832
|
+
}
|
|
26833
|
+
function paragraphTypeOf(field, registry2) {
|
|
26834
|
+
const comp = field.ui_component;
|
|
26835
|
+
return comp.component === "paragraph-embed" ? registry2.paragraph_types[comp.ref] : void 0;
|
|
26836
|
+
}
|
|
26837
|
+
function paragraphItemsIn(fields, body, registry2, path4) {
|
|
26838
|
+
const out = [];
|
|
26839
|
+
for (const field of fields) {
|
|
26840
|
+
if (field.field_type !== "paragraph" || !Object.hasOwn(body, field.name)) continue;
|
|
26841
|
+
const pType = paragraphTypeOf(field, registry2);
|
|
26842
|
+
if (!pType) continue;
|
|
26843
|
+
const value = body[field.name];
|
|
26844
|
+
const name = `${path4}${field.name}`;
|
|
26845
|
+
if (relationCardinality(field) === "one") {
|
|
26846
|
+
if (isPlainObject2(value)) out.push({ item: value, fields: pType.fields, prefix: `${name}.` });
|
|
26847
|
+
} else if (Array.isArray(value)) {
|
|
26848
|
+
value.forEach((item, i) => {
|
|
26849
|
+
if (isPlainObject2(item)) out.push({ item, fields: pType.fields, prefix: `${name}[${i}].` });
|
|
26850
|
+
});
|
|
26851
|
+
}
|
|
26852
|
+
}
|
|
26853
|
+
return out;
|
|
26854
|
+
}
|
|
26855
|
+
function isMissing(value) {
|
|
26856
|
+
if (value === null || value === void 0) return true;
|
|
26857
|
+
if (typeof value === "string" && value.trim() === "") return true;
|
|
26858
|
+
if (Array.isArray(value) && value.length === 0) return true;
|
|
26859
|
+
return false;
|
|
26860
|
+
}
|
|
26861
|
+
function checkRequiredInParagraphItems(fields, body, registry2, path4 = "") {
|
|
26862
|
+
const errors = [];
|
|
26863
|
+
for (const { item, fields: itemFields, prefix } of paragraphItemsIn(fields, body, registry2, path4)) {
|
|
26864
|
+
for (const f of itemFields) {
|
|
26865
|
+
if (!f.required || f.removed === true || !f.db_column || f.db_column.junction) continue;
|
|
26866
|
+
if (isMissing(item[f.name])) {
|
|
26867
|
+
errors.push({ field: `${prefix}${f.name}`, message: `${prefix}${f.name} is required.` });
|
|
26868
|
+
}
|
|
26869
|
+
}
|
|
26870
|
+
errors.push(...checkRequiredInParagraphItems(itemFields, item, registry2, prefix));
|
|
26871
|
+
}
|
|
26872
|
+
return errors;
|
|
26873
|
+
}
|
|
26791
26874
|
function quoteIdent(name) {
|
|
26792
26875
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`);
|
|
26793
26876
|
return `"${name}"`;
|
|
26794
26877
|
}
|
|
26878
|
+
function collectReferences(fields, body, registry2, path4, out) {
|
|
26879
|
+
for (const f of fields) {
|
|
26880
|
+
if (!Object.hasOwn(body, f.name) || !f.db_column) continue;
|
|
26881
|
+
const table = f.db_column.junction ? f.db_column.junction.right_table : f.db_column.foreign_key?.table;
|
|
26882
|
+
if (!table) continue;
|
|
26883
|
+
const value = body[f.name];
|
|
26884
|
+
const name = `${path4}${f.name}`;
|
|
26885
|
+
if (typeof value === "string") {
|
|
26886
|
+
out.push({ table, id: value, field: name });
|
|
26887
|
+
} else if (Array.isArray(value)) {
|
|
26888
|
+
value.forEach((v, i) => {
|
|
26889
|
+
if (typeof v === "string") out.push({ table, id: v, field: `${name}[${i}]` });
|
|
26890
|
+
});
|
|
26891
|
+
}
|
|
26892
|
+
}
|
|
26893
|
+
for (const { item, fields: itemFields, prefix } of paragraphItemsIn(fields, body, registry2, path4)) {
|
|
26894
|
+
collectReferences(itemFields, item, registry2, prefix, out);
|
|
26895
|
+
}
|
|
26896
|
+
}
|
|
26897
|
+
async function findMissingReferences(db, fields, body, registry2) {
|
|
26898
|
+
const uses = [];
|
|
26899
|
+
collectReferences(fields, body, registry2, "", uses);
|
|
26900
|
+
if (uses.length === 0) return [];
|
|
26901
|
+
const lookups = [...new Set(uses.map((u) => u.table))].map(async (table) => {
|
|
26902
|
+
const ids = [...new Set(uses.filter((u) => u.table === table && isUuid(u.id)).map((u) => u.id.toLowerCase()))];
|
|
26903
|
+
if (ids.length === 0) return [table, /* @__PURE__ */ new Set()];
|
|
26904
|
+
const result = await db.execute(
|
|
26905
|
+
sql`SELECT id FROM ${sql.raw(quoteIdent(table))} WHERE id IN (${sql.join(ids.map((id) => sql`${id}`), sql`, `)})`
|
|
26906
|
+
);
|
|
26907
|
+
return [table, new Set(result.rows.map((r) => String(r.id).toLowerCase()))];
|
|
26908
|
+
});
|
|
26909
|
+
const found = new Map(await Promise.all(lookups));
|
|
26910
|
+
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.` }));
|
|
26911
|
+
}
|
|
26912
|
+
function quoteIdent2(name) {
|
|
26913
|
+
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`);
|
|
26914
|
+
return `"${name}"`;
|
|
26915
|
+
}
|
|
26795
26916
|
function paragraphMediaFieldNames(pType) {
|
|
26796
26917
|
return new Set(
|
|
26797
26918
|
pType.fields.filter((f) => f.field_type === "image" || f.field_type === "video" || f.field_type === "file").map((f) => f.name)
|
|
@@ -26800,8 +26921,8 @@ function paragraphMediaFieldNames(pType) {
|
|
|
26800
26921
|
async function fetchParagraphMediaIds(db, pType, itemId, fieldName) {
|
|
26801
26922
|
const mediaColumns = pType.fields.filter((f) => f.field_type === "image" || f.field_type === "video" || f.field_type === "file").map((f) => f.db_column.column_name);
|
|
26802
26923
|
if (mediaColumns.length === 0) return [];
|
|
26803
|
-
const tbl = sql.raw(
|
|
26804
|
-
const cols = sql.join(mediaColumns.map((c) => sql.raw(
|
|
26924
|
+
const tbl = sql.raw(quoteIdent2(pType.db.table_name));
|
|
26925
|
+
const cols = sql.join(mediaColumns.map((c) => sql.raw(quoteIdent2(c))), sql`, `);
|
|
26805
26926
|
const result = await db.execute(
|
|
26806
26927
|
sql`SELECT ${cols} FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`
|
|
26807
26928
|
);
|
|
@@ -26819,20 +26940,37 @@ function nestedParagraphFields(pType, registry2) {
|
|
|
26819
26940
|
const out = [];
|
|
26820
26941
|
for (const f of pType.fields) {
|
|
26821
26942
|
if (f.db_column !== null) continue;
|
|
26822
|
-
const
|
|
26823
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
26824
|
-
const nType = registry2.paragraph_types[comp.ref];
|
|
26943
|
+
const nType = paragraphTypeOf(f, registry2);
|
|
26825
26944
|
if (nType) out.push({ fieldName: f.name, nType, field: f });
|
|
26826
26945
|
}
|
|
26827
26946
|
return out;
|
|
26828
26947
|
}
|
|
26829
26948
|
async function fetchParagraphRowIds(db, pType, itemId, fieldName) {
|
|
26830
|
-
const tbl = sql.raw(
|
|
26949
|
+
const tbl = sql.raw(quoteIdent2(pType.db.table_name));
|
|
26831
26950
|
const result = await db.execute(
|
|
26832
26951
|
sql`SELECT id FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`
|
|
26833
26952
|
);
|
|
26834
26953
|
return result.rows.map((r) => r["id"]).filter((id) => typeof id === "string");
|
|
26835
26954
|
}
|
|
26955
|
+
async function ownParagraphRowIds(db, registry2, fields, itemId) {
|
|
26956
|
+
const ids = [];
|
|
26957
|
+
for (const f of fields) {
|
|
26958
|
+
const pType = paragraphTypeOf(f, registry2);
|
|
26959
|
+
if (!pType) continue;
|
|
26960
|
+
const rowIds = await fetchParagraphRowIds(db, pType, itemId, f.name);
|
|
26961
|
+
ids.push(...rowIds);
|
|
26962
|
+
if (rowIds.length === 0) continue;
|
|
26963
|
+
for (const n of nestedParagraphFields(pType, registry2)) {
|
|
26964
|
+
const result = await db.execute(
|
|
26965
|
+
sql`SELECT id FROM ${sql.raw(quoteIdent2(n.nType.db.table_name))} WHERE parent_id = ANY(${sql.param(rowIds)}::uuid[]) AND parent_field = ${n.fieldName}`
|
|
26966
|
+
);
|
|
26967
|
+
for (const r of result.rows) {
|
|
26968
|
+
if (typeof r["id"] === "string") ids.push(r["id"]);
|
|
26969
|
+
}
|
|
26970
|
+
}
|
|
26971
|
+
}
|
|
26972
|
+
return ids;
|
|
26973
|
+
}
|
|
26836
26974
|
async function persistParagraphField(db, itemId, parentType, fieldName, pType, items, registry2) {
|
|
26837
26975
|
const removed = await fetchParagraphMediaIds(db, pType, itemId, fieldName);
|
|
26838
26976
|
const nested = nestedParagraphFields(pType, registry2);
|
|
@@ -26845,7 +26983,7 @@ async function persistParagraphField(db, itemId, parentType, fieldName, pType, i
|
|
|
26845
26983
|
}
|
|
26846
26984
|
}
|
|
26847
26985
|
}
|
|
26848
|
-
const tbl = sql.raw(
|
|
26986
|
+
const tbl = sql.raw(quoteIdent2(pType.db.table_name));
|
|
26849
26987
|
await db.execute(sql`DELETE FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`);
|
|
26850
26988
|
const mediaFieldNames = paragraphMediaFieldNames(pType);
|
|
26851
26989
|
const added = [];
|
|
@@ -26865,7 +27003,7 @@ async function persistParagraphField(db, itemId, parentType, fieldName, pType, i
|
|
|
26865
27003
|
if (!pf.db_column || pf.db_column.junction) continue;
|
|
26866
27004
|
data[pf.db_column.column_name] = pItem[pf.name] ?? null;
|
|
26867
27005
|
}
|
|
26868
|
-
const cols = sql.join(Object.keys(data).map((k) => sql.raw(
|
|
27006
|
+
const cols = sql.join(Object.keys(data).map((k) => sql.raw(quoteIdent2(k))), sql`, `);
|
|
26869
27007
|
const vals = sql.join(Object.values(data).map((v) => sql`${v}`), sql`, `);
|
|
26870
27008
|
await db.execute(sql`INSERT INTO ${tbl} (${cols}) VALUES (${vals})`);
|
|
26871
27009
|
for (const name of mediaFieldNames) {
|
|
@@ -26901,14 +27039,14 @@ async function deleteParagraphField(db, itemId, fieldName, pType, registry2) {
|
|
|
26901
27039
|
}
|
|
26902
27040
|
}
|
|
26903
27041
|
}
|
|
26904
|
-
const tbl = sql.raw(
|
|
27042
|
+
const tbl = sql.raw(quoteIdent2(pType.db.table_name));
|
|
26905
27043
|
await db.execute(sql`DELETE FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`);
|
|
26906
27044
|
return { added: [], removed };
|
|
26907
27045
|
}
|
|
26908
27046
|
async function persistJunctionField(db, itemId, junction, relatedIds) {
|
|
26909
|
-
const tbl = sql.raw(
|
|
26910
|
-
const left = sql.raw(
|
|
26911
|
-
const right = sql.raw(
|
|
27047
|
+
const tbl = sql.raw(quoteIdent2(junction.table_name));
|
|
27048
|
+
const left = sql.raw(quoteIdent2(junction.left_column));
|
|
27049
|
+
const right = sql.raw(quoteIdent2(junction.right_column));
|
|
26912
27050
|
const seen = /* @__PURE__ */ new Set();
|
|
26913
27051
|
const ids = relatedIds.filter((id) => {
|
|
26914
27052
|
const key = id.toLowerCase();
|
|
@@ -26929,9 +27067,18 @@ async function persistJunctionField(db, itemId, junction, relatedIds) {
|
|
|
26929
27067
|
);
|
|
26930
27068
|
}
|
|
26931
27069
|
function junctionOrderBy(junction) {
|
|
26932
|
-
const right = sql.raw(
|
|
27070
|
+
const right = sql.raw(quoteIdent2(junction.right_column));
|
|
26933
27071
|
return junction.order_column ? sql` ORDER BY "order" ASC, ${right} ASC` : sql` ORDER BY ${right} ASC`;
|
|
26934
27072
|
}
|
|
27073
|
+
function firstOfEach(items, key) {
|
|
27074
|
+
const seen = /* @__PURE__ */ new Set();
|
|
27075
|
+
return items.filter((item) => {
|
|
27076
|
+
const k = key(item);
|
|
27077
|
+
if (seen.has(k)) return false;
|
|
27078
|
+
seen.add(k);
|
|
27079
|
+
return true;
|
|
27080
|
+
});
|
|
27081
|
+
}
|
|
26935
27082
|
function oneOrMany(cardinality, list) {
|
|
26936
27083
|
return cardinality === "one" ? list[0] ?? null : list;
|
|
26937
27084
|
}
|
|
@@ -26999,7 +27146,7 @@ async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOn
|
|
|
26999
27146
|
const parentIds = batch.map((r) => r["id"]);
|
|
27000
27147
|
const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `);
|
|
27001
27148
|
const result = await db.execute(
|
|
27002
|
-
sql`SELECT * FROM ${sql.raw(
|
|
27149
|
+
sql`SELECT * FROM ${sql.raw(quoteIdent2(rel.table))} WHERE parent_id IN (${inList}) AND parent_field = ${fieldName} ORDER BY "order" ASC, id ASC`
|
|
27003
27150
|
);
|
|
27004
27151
|
const byParent = groupBy(result.rows, "parent_id");
|
|
27005
27152
|
for (const row of batch) {
|
|
@@ -27019,7 +27166,7 @@ async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOn
|
|
|
27019
27166
|
const inList = sql.join(uncached.map((id) => sql`${id}`), sql`, `);
|
|
27020
27167
|
const publishedCond = publishedOnly ? sql` AND published = true` : sql``;
|
|
27021
27168
|
const result = await db.execute(
|
|
27022
|
-
sql`SELECT * FROM ${sql.raw(
|
|
27169
|
+
sql`SELECT * FROM ${sql.raw(quoteIdent2(rel.table))} WHERE id IN (${inList})${publishedCond}`
|
|
27023
27170
|
);
|
|
27024
27171
|
for (const item of result.rows) {
|
|
27025
27172
|
cache.set(`${rel.table}:${item["id"]}`, item);
|
|
@@ -27033,7 +27180,7 @@ async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOn
|
|
|
27033
27180
|
const parentIds = batch.map((r) => r["id"]);
|
|
27034
27181
|
const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `);
|
|
27035
27182
|
const junctionResult = await db.execute(
|
|
27036
|
-
sql`SELECT * FROM ${sql.raw(
|
|
27183
|
+
sql`SELECT * FROM ${sql.raw(quoteIdent2(rel.junction_table))} WHERE ${sql.raw(quoteIdent2(rel.left_column))} IN (${inList})${junctionOrderBy(rel)}`
|
|
27037
27184
|
);
|
|
27038
27185
|
const jRows = junctionResult.rows;
|
|
27039
27186
|
const rightIds = [...new Set(jRows.map((r) => r[rel.right_column]))];
|
|
@@ -27042,7 +27189,7 @@ async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOn
|
|
|
27042
27189
|
const rightInList = sql.join(uncached.map((id) => sql`${id}`), sql`, `);
|
|
27043
27190
|
const publishedCond = publishedOnly ? sql` AND published = true` : sql``;
|
|
27044
27191
|
const entitiesResult = await db.execute(
|
|
27045
|
-
sql`SELECT * FROM ${sql.raw(
|
|
27192
|
+
sql`SELECT * FROM ${sql.raw(quoteIdent2(rel.table))} WHERE id IN (${rightInList})${publishedCond}`
|
|
27046
27193
|
);
|
|
27047
27194
|
for (const item of entitiesResult.rows) {
|
|
27048
27195
|
cache.set(`${rel.table}:${item["id"]}`, item);
|
|
@@ -27050,7 +27197,7 @@ async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOn
|
|
|
27050
27197
|
}
|
|
27051
27198
|
const byLeft = groupBy(jRows, rel.left_column);
|
|
27052
27199
|
for (const row of batch) {
|
|
27053
|
-
const jEntries = byLeft[row["id"]] ?? [];
|
|
27200
|
+
const jEntries = firstOfEach(byLeft[row["id"]] ?? [], (jr) => jr[rel.right_column]);
|
|
27054
27201
|
row[fieldName] = jEntries.map((jr) => cache.get(`${rel.table}:${jr[rel.right_column]}`)).filter(Boolean);
|
|
27055
27202
|
}
|
|
27056
27203
|
} else if (rel.type === "media") {
|
|
@@ -27082,7 +27229,7 @@ async function resolveRelationBareIds(db, rows, fieldName, rel) {
|
|
|
27082
27229
|
const parentIds = rows.map((r) => r["id"]);
|
|
27083
27230
|
const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `);
|
|
27084
27231
|
const result = await db.execute(
|
|
27085
|
-
sql`SELECT id, parent_id FROM ${sql.raw(
|
|
27232
|
+
sql`SELECT id, parent_id FROM ${sql.raw(quoteIdent2(rel.table))} WHERE parent_id IN (${inList}) AND parent_field = ${fieldName} ORDER BY "order" ASC, id ASC`
|
|
27086
27233
|
);
|
|
27087
27234
|
const byParent = groupBy(result.rows, "parent_id");
|
|
27088
27235
|
for (const row of rows) {
|
|
@@ -27092,11 +27239,11 @@ async function resolveRelationBareIds(db, rows, fieldName, rel) {
|
|
|
27092
27239
|
const parentIds = rows.map((r) => r["id"]);
|
|
27093
27240
|
const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `);
|
|
27094
27241
|
const result = await db.execute(
|
|
27095
|
-
sql`SELECT ${sql.raw(
|
|
27242
|
+
sql`SELECT ${sql.raw(quoteIdent2(rel.left_column))} AS left_id, ${sql.raw(quoteIdent2(rel.right_column))} AS right_id FROM ${sql.raw(quoteIdent2(rel.junction_table))} WHERE ${sql.raw(quoteIdent2(rel.left_column))} IN (${inList})${junctionOrderBy(rel)}`
|
|
27096
27243
|
);
|
|
27097
27244
|
const byLeft = groupBy(result.rows, "left_id");
|
|
27098
27245
|
for (const row of rows) {
|
|
27099
|
-
row[fieldName] = (byLeft[row["id"]] ?? []).map((r) => r["right_id"]);
|
|
27246
|
+
row[fieldName] = firstOfEach(byLeft[row["id"]] ?? [], (r) => r["right_id"]).map((r) => r["right_id"]);
|
|
27100
27247
|
}
|
|
27101
27248
|
}
|
|
27102
27249
|
}
|
|
@@ -27149,6 +27296,14 @@ export {
|
|
|
27149
27296
|
isColumnBacked2 as isColumnBacked,
|
|
27150
27297
|
createFieldKeyMap,
|
|
27151
27298
|
createFieldKeyMapFromProjection,
|
|
27299
|
+
isPlainObject2 as isPlainObject,
|
|
27300
|
+
checkRelationInput,
|
|
27301
|
+
paragraphTypeOf,
|
|
27302
|
+
paragraphItemsIn,
|
|
27303
|
+
isMissing,
|
|
27304
|
+
checkRequiredInParagraphItems,
|
|
27305
|
+
findMissingReferences,
|
|
27306
|
+
ownParagraphRowIds,
|
|
27152
27307
|
persistParagraphField,
|
|
27153
27308
|
deleteParagraphField,
|
|
27154
27309
|
persistJunctionField,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
isColumnBacked,
|
|
13
13
|
relationCardinality,
|
|
14
14
|
resolveRelationField
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-44VXH5BN.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({
|
|
@@ -10787,10 +10787,10 @@ var require_parser = __commonJS({
|
|
|
10787
10787
|
}
|
|
10788
10788
|
});
|
|
10789
10789
|
|
|
10790
|
-
// ../api/dist/handler-
|
|
10790
|
+
// ../api/dist/handler-N5IMQJBP.mjs
|
|
10791
10791
|
init_esm_shims();
|
|
10792
10792
|
|
|
10793
|
-
// ../api/dist/chunk-
|
|
10793
|
+
// ../api/dist/chunk-IMDC3NGM.mjs
|
|
10794
10794
|
init_esm_shims();
|
|
10795
10795
|
|
|
10796
10796
|
// ../../node_modules/.pnpm/graphql-yoga@5.21.2_graphql@16.14.2/node_modules/graphql-yoga/esm/index.js
|
|
@@ -30123,7 +30123,7 @@ function createYoga(options) {
|
|
|
30123
30123
|
});
|
|
30124
30124
|
}
|
|
30125
30125
|
|
|
30126
|
-
// ../api/dist/chunk-
|
|
30126
|
+
// ../api/dist/chunk-IMDC3NGM.mjs
|
|
30127
30127
|
var import_dataloader = __toESM(require_dataloader(), 1);
|
|
30128
30128
|
|
|
30129
30129
|
// ../../node_modules/.pnpm/@escape.tech+graphql-armor@3.2.0_@envelop+core@5.5.1_@escape.tech+graphql-armor-types@0.7.0/node_modules/@escape.tech/graphql-armor/dist/graphql-armor.esm.js
|
|
@@ -30881,7 +30881,7 @@ var EnvelopArmor = class {
|
|
|
30881
30881
|
}
|
|
30882
30882
|
};
|
|
30883
30883
|
|
|
30884
|
-
// ../api/dist/chunk-
|
|
30884
|
+
// ../api/dist/chunk-IMDC3NGM.mjs
|
|
30885
30885
|
var DateTimeScalar = new GraphQLScalarType({
|
|
30886
30886
|
name: "DateTime",
|
|
30887
30887
|
description: "ISO-8601 date-time string",
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
__toESM,
|
|
22
22
|
buildRelationsMap,
|
|
23
23
|
buildSchemaRegistry,
|
|
24
|
+
checkRelationInput,
|
|
25
|
+
checkRequiredInParagraphItems,
|
|
24
26
|
computeVersionModel,
|
|
25
27
|
createFieldKeyMap,
|
|
26
28
|
createFieldKeyMapFromProjection,
|
|
@@ -28,6 +30,7 @@ import {
|
|
|
28
30
|
describeSchemaChange,
|
|
29
31
|
entityKind,
|
|
30
32
|
fillPlaceholders,
|
|
33
|
+
findMissingReferences,
|
|
31
34
|
findSchemaDeprecations,
|
|
32
35
|
getTableName,
|
|
33
36
|
getTableUniqueName,
|
|
@@ -36,12 +39,17 @@ import {
|
|
|
36
39
|
is,
|
|
37
40
|
isColumnBacked,
|
|
38
41
|
isDriverValueEncoder,
|
|
42
|
+
isMissing,
|
|
43
|
+
isPlainObject,
|
|
39
44
|
isSQLWrapper,
|
|
40
45
|
junctionOrderBy,
|
|
41
46
|
loadSchemaFile,
|
|
42
47
|
loadVersionSnapshots,
|
|
43
48
|
oneOrMany,
|
|
49
|
+
ownParagraphRowIds,
|
|
44
50
|
paragraphItems,
|
|
51
|
+
paragraphItemsIn,
|
|
52
|
+
paragraphTypeOf,
|
|
45
53
|
parseRoles,
|
|
46
54
|
parseRoutes,
|
|
47
55
|
parseSchema,
|
|
@@ -55,7 +63,7 @@ import {
|
|
|
55
63
|
validateCrossReferences,
|
|
56
64
|
verifyPassword,
|
|
57
65
|
walkSchemaDirectory
|
|
58
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-44VXH5BN.js";
|
|
59
67
|
|
|
60
68
|
// ../../node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js
|
|
61
69
|
var require_postgres_array = __commonJS({
|
|
@@ -21901,7 +21909,7 @@ var ERROR_STATUS_MAP = {
|
|
|
21901
21909
|
var errorHandler2 = (err, c) => {
|
|
21902
21910
|
console.error(err.stack ?? err.message);
|
|
21903
21911
|
const raw2 = err.code;
|
|
21904
|
-
const deliberate = raw2 !== void 0 && raw2 !== "INTERNAL_ERROR" && raw2
|
|
21912
|
+
const deliberate = raw2 !== void 0 && raw2 !== "INTERNAL_ERROR" && Object.hasOwn(ERROR_STATUS_MAP, raw2);
|
|
21905
21913
|
if (!deliberate) {
|
|
21906
21914
|
return c.json({ ok: false, error: { code: "INTERNAL_ERROR", message: "Internal server error" } }, 500);
|
|
21907
21915
|
}
|
|
@@ -22464,16 +22472,16 @@ function parseFilters(url, validFields, columnFor) {
|
|
|
22464
22472
|
}
|
|
22465
22473
|
return { ok: true, filters };
|
|
22466
22474
|
}
|
|
22475
|
+
function pickProgrammatic(resolved, names) {
|
|
22476
|
+
const out = {};
|
|
22477
|
+
for (const name of names) if (Object.hasOwn(resolved, name)) out[name] = resolved[name];
|
|
22478
|
+
return out;
|
|
22479
|
+
}
|
|
22467
22480
|
function isPublished(item) {
|
|
22468
22481
|
return item["published"] === true;
|
|
22469
22482
|
}
|
|
22470
22483
|
function registerPublicContentRoutes(app, registry, repos, projectors, paths, listRateLimit, resolver, resolverProjectors) {
|
|
22471
22484
|
const forResolver = resolverProjectors ?? projectors;
|
|
22472
|
-
function pickProgrammatic(resolved, names) {
|
|
22473
|
-
const out = {};
|
|
22474
|
-
for (const name of names) if (name in resolved) out[name] = resolved[name];
|
|
22475
|
-
return out;
|
|
22476
|
-
}
|
|
22477
22485
|
async function respondItem(row, typeName, programmatic) {
|
|
22478
22486
|
const data = projectRow(row, typeName, projectors);
|
|
22479
22487
|
if (!resolver?.hasSchema(typeName)) return data;
|
|
@@ -22785,25 +22793,42 @@ function quoteIdent(name) {
|
|
|
22785
22793
|
function isRequiredSingleReferenceTo(f, targetType) {
|
|
22786
22794
|
return f.field_type === "reference" && f.required && relationCardinality(f) === "one" && f.db_column?.foreign_key !== void 0 && f.ui_component.component === "typeahead-select" && f.ui_component.ref === targetType;
|
|
22787
22795
|
}
|
|
22796
|
+
function requiredReferencesTo(owners, targetType) {
|
|
22797
|
+
return owners.flatMap(
|
|
22798
|
+
(owner) => owner.fields.filter((f) => isRequiredSingleReferenceTo(f, targetType)).map((f) => ({ owner, f }))
|
|
22799
|
+
);
|
|
22800
|
+
}
|
|
22788
22801
|
async function findRequiredReferrers(db, registry, targetType, id) {
|
|
22789
|
-
const
|
|
22790
|
-
|
|
22791
|
-
|
|
22792
|
-
|
|
22793
|
-
];
|
|
22794
|
-
const
|
|
22795
|
-
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
|
|
22799
|
-
|
|
22800
|
-
|
|
22801
|
-
|
|
22802
|
-
|
|
22803
|
-
|
|
22804
|
-
|
|
22802
|
+
const targetFields = registry.content_types[targetType]?.fields ?? registry.taxonomy_types[targetType]?.fields ?? [];
|
|
22803
|
+
const itemOwners = [...Object.values(registry.content_types), ...Object.values(registry.taxonomy_types)];
|
|
22804
|
+
const itemFields = requiredReferencesTo(itemOwners, targetType);
|
|
22805
|
+
const paragraphFields = requiredReferencesTo(Object.values(registry.paragraph_types), targetType);
|
|
22806
|
+
const ownIds = paragraphFields.length > 0 ? await ownParagraphRowIds(db, registry, targetFields, id) : [];
|
|
22807
|
+
const [itemCounts, paragraphCounts] = await Promise.all([
|
|
22808
|
+
Promise.all(
|
|
22809
|
+
itemFields.map(async ({ owner, f }) => {
|
|
22810
|
+
const notSelf = owner.name === targetType ? sql` AND id <> ${id}` : sql``;
|
|
22811
|
+
const r = await db.execute(
|
|
22812
|
+
sql`SELECT count(*)::int AS n FROM ${sql.raw(quoteIdent(owner.db.table_name))} WHERE ${sql.raw(quoteIdent(f.db_column.column_name))} = ${id}${notSelf}`
|
|
22813
|
+
);
|
|
22814
|
+
return { owner, f, n: r.rows[0].n, own: 0 };
|
|
22815
|
+
})
|
|
22816
|
+
),
|
|
22817
|
+
Promise.all(
|
|
22818
|
+
paragraphFields.map(async ({ owner, f }) => {
|
|
22819
|
+
const r = await db.execute(
|
|
22820
|
+
sql`SELECT count(*)::int AS n, count(*) FILTER (WHERE id = ANY(${sql.param(ownIds)}::uuid[]))::int AS own FROM ${sql.raw(quoteIdent(owner.db.table_name))} WHERE ${sql.raw(quoteIdent(f.db_column.column_name))} = ${id}`
|
|
22821
|
+
);
|
|
22822
|
+
const { n, own } = r.rows[0];
|
|
22823
|
+
return { owner, f, n, own };
|
|
22824
|
+
})
|
|
22825
|
+
)
|
|
22826
|
+
]);
|
|
22827
|
+
const others = [];
|
|
22828
|
+
for (const { owner, f, n, own } of [...itemCounts, ...paragraphCounts]) {
|
|
22829
|
+
if (n - own > 0) others.push({ type_label: owner.label, field_label: f.label, count: n - own });
|
|
22805
22830
|
}
|
|
22806
|
-
return
|
|
22831
|
+
return { others, fromOwnParagraphs: paragraphCounts.some((c) => c.own > 0) };
|
|
22807
22832
|
}
|
|
22808
22833
|
function inUseMessage(referrers) {
|
|
22809
22834
|
const total = referrers.reduce((sum, r) => sum + r.count, 0);
|
|
@@ -22828,7 +22853,7 @@ function topLevelMediaDelta(mediaFields, before, after) {
|
|
|
22828
22853
|
for (const f of mediaFields) {
|
|
22829
22854
|
const key = f.db_column?.column_name;
|
|
22830
22855
|
if (key === void 0 || key === "") continue;
|
|
22831
|
-
if (after !== null && !(key
|
|
22856
|
+
if (after !== null && !Object.hasOwn(after, key)) continue;
|
|
22832
22857
|
const oldId = before ? extractMediaId(before[key]) : null;
|
|
22833
22858
|
const newId = after ? extractMediaId(after[key]) : null;
|
|
22834
22859
|
if (newId === oldId) continue;
|
|
@@ -22851,127 +22876,6 @@ async function applyMediaReferenceDelta(delta, mediaRepo) {
|
|
|
22851
22876
|
if (netAdd.length > 0) await mediaRepo.incrementReferenceCount(netAdd);
|
|
22852
22877
|
if (netRemove.length > 0) await mediaRepo.decrementReferenceCount(netRemove);
|
|
22853
22878
|
}
|
|
22854
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
22855
|
-
function isUuid(v2) {
|
|
22856
|
-
return typeof v2 === "string" && UUID_RE.test(v2);
|
|
22857
|
-
}
|
|
22858
|
-
function isPlainObject(v2) {
|
|
22859
|
-
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
22860
|
-
}
|
|
22861
|
-
function checkRelationInput(fields, body, registry, path5 = "") {
|
|
22862
|
-
const errors = [];
|
|
22863
|
-
for (const field of fields) {
|
|
22864
|
-
if (!(field.name in body)) continue;
|
|
22865
|
-
const cardinality = relationCardinality(field);
|
|
22866
|
-
if (cardinality === null) continue;
|
|
22867
|
-
const value = body[field.name];
|
|
22868
|
-
const name = `${path5}${field.name}`;
|
|
22869
|
-
if (field.field_type === "reference") {
|
|
22870
|
-
const ok = cardinality === "one" ? value === null || isUuid(value) : Array.isArray(value) && value.every(isUuid);
|
|
22871
|
-
if (!ok) {
|
|
22872
|
-
errors.push({
|
|
22873
|
-
field: name,
|
|
22874
|
-
message: cardinality === "one" ? `${name} holds one item: send a UUID string or null.` : `${name} holds a list: send an array of UUID strings.`
|
|
22875
|
-
});
|
|
22876
|
-
}
|
|
22877
|
-
continue;
|
|
22878
|
-
}
|
|
22879
|
-
const items = cardinality === "one" ? value === null ? [] : isPlainObject(value) ? [value] : void 0 : Array.isArray(value) && value.every(isPlainObject) ? value : void 0;
|
|
22880
|
-
if (items === void 0) {
|
|
22881
|
-
errors.push({
|
|
22882
|
-
field: name,
|
|
22883
|
-
message: cardinality === "one" ? `${name} holds one item: send an object or null.` : `${name} holds a list: send an array of objects.`
|
|
22884
|
-
});
|
|
22885
|
-
continue;
|
|
22886
|
-
}
|
|
22887
|
-
const pType = paragraphTypeOf(field, registry);
|
|
22888
|
-
if (!pType) continue;
|
|
22889
|
-
items.forEach((item, i) => {
|
|
22890
|
-
const prefix = cardinality === "one" ? `${name}.` : `${name}[${i}].`;
|
|
22891
|
-
errors.push(...checkRelationInput(pType.fields, item, registry, prefix));
|
|
22892
|
-
});
|
|
22893
|
-
}
|
|
22894
|
-
return errors;
|
|
22895
|
-
}
|
|
22896
|
-
function paragraphTypeOf(field, registry) {
|
|
22897
|
-
const comp = field.ui_component;
|
|
22898
|
-
return comp.component === "paragraph-embed" ? registry.paragraph_types[comp.ref] : void 0;
|
|
22899
|
-
}
|
|
22900
|
-
function paragraphItemsIn(fields, body, registry, path5) {
|
|
22901
|
-
const out = [];
|
|
22902
|
-
for (const field of fields) {
|
|
22903
|
-
if (field.field_type !== "paragraph" || !(field.name in body)) continue;
|
|
22904
|
-
const pType = paragraphTypeOf(field, registry);
|
|
22905
|
-
if (!pType) continue;
|
|
22906
|
-
const value = body[field.name];
|
|
22907
|
-
const name = `${path5}${field.name}`;
|
|
22908
|
-
if (relationCardinality(field) === "one") {
|
|
22909
|
-
if (isPlainObject(value)) out.push({ item: value, fields: pType.fields, prefix: `${name}.` });
|
|
22910
|
-
} else if (Array.isArray(value)) {
|
|
22911
|
-
value.forEach((item, i) => {
|
|
22912
|
-
if (isPlainObject(item)) out.push({ item, fields: pType.fields, prefix: `${name}[${i}].` });
|
|
22913
|
-
});
|
|
22914
|
-
}
|
|
22915
|
-
}
|
|
22916
|
-
return out;
|
|
22917
|
-
}
|
|
22918
|
-
function isMissing(value) {
|
|
22919
|
-
if (value === null || value === void 0) return true;
|
|
22920
|
-
if (typeof value === "string" && value.trim() === "") return true;
|
|
22921
|
-
if (Array.isArray(value) && value.length === 0) return true;
|
|
22922
|
-
return false;
|
|
22923
|
-
}
|
|
22924
|
-
function checkRequiredInParagraphItems(fields, body, registry, path5 = "") {
|
|
22925
|
-
const errors = [];
|
|
22926
|
-
for (const { item, fields: itemFields, prefix } of paragraphItemsIn(fields, body, registry, path5)) {
|
|
22927
|
-
for (const f of itemFields) {
|
|
22928
|
-
if (!f.required || f.removed === true || !f.db_column || f.db_column.junction) continue;
|
|
22929
|
-
if (isMissing(item[f.name])) {
|
|
22930
|
-
errors.push({ field: `${prefix}${f.name}`, message: `${prefix}${f.name} is required.` });
|
|
22931
|
-
}
|
|
22932
|
-
}
|
|
22933
|
-
errors.push(...checkRequiredInParagraphItems(itemFields, item, registry, prefix));
|
|
22934
|
-
}
|
|
22935
|
-
return errors;
|
|
22936
|
-
}
|
|
22937
|
-
function quoteIdent2(name) {
|
|
22938
|
-
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`);
|
|
22939
|
-
return `"${name}"`;
|
|
22940
|
-
}
|
|
22941
|
-
function collectReferences(fields, body, registry, path5, out) {
|
|
22942
|
-
for (const f of fields) {
|
|
22943
|
-
if (!(f.name in body) || !f.db_column) continue;
|
|
22944
|
-
const table = f.db_column.junction ? f.db_column.junction.right_table : f.db_column.foreign_key?.table;
|
|
22945
|
-
if (!table) continue;
|
|
22946
|
-
const value = body[f.name];
|
|
22947
|
-
const name = `${path5}${f.name}`;
|
|
22948
|
-
if (typeof value === "string") {
|
|
22949
|
-
out.push({ table, id: value, field: name });
|
|
22950
|
-
} else if (Array.isArray(value)) {
|
|
22951
|
-
value.forEach((v2, i) => {
|
|
22952
|
-
if (typeof v2 === "string") out.push({ table, id: v2, field: `${name}[${i}]` });
|
|
22953
|
-
});
|
|
22954
|
-
}
|
|
22955
|
-
}
|
|
22956
|
-
for (const { item, fields: itemFields, prefix } of paragraphItemsIn(fields, body, registry, path5)) {
|
|
22957
|
-
collectReferences(itemFields, item, registry, prefix, out);
|
|
22958
|
-
}
|
|
22959
|
-
}
|
|
22960
|
-
async function findMissingReferences(db, fields, body, registry) {
|
|
22961
|
-
const uses = [];
|
|
22962
|
-
collectReferences(fields, body, registry, "", uses);
|
|
22963
|
-
if (uses.length === 0) return [];
|
|
22964
|
-
const lookups = [...new Set(uses.map((u) => u.table))].map(async (table) => {
|
|
22965
|
-
const ids = [...new Set(uses.filter((u) => u.table === table && isUuid(u.id)).map((u) => u.id.toLowerCase()))];
|
|
22966
|
-
if (ids.length === 0) return [table, /* @__PURE__ */ new Set()];
|
|
22967
|
-
const result = await db.execute(
|
|
22968
|
-
sql`SELECT id FROM ${sql.raw(quoteIdent2(table))} WHERE id IN (${sql.join(ids.map((id) => sql`${id}`), sql`, `)})`
|
|
22969
|
-
);
|
|
22970
|
-
return [table, new Set(result.rows.map((r) => String(r.id).toLowerCase()))];
|
|
22971
|
-
});
|
|
22972
|
-
const found = new Map(await Promise.all(lookups));
|
|
22973
|
-
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.` }));
|
|
22974
|
-
}
|
|
22975
22879
|
var VARCHAR_DEFAULT_LIMIT = 255;
|
|
22976
22880
|
var INTEGER_MIN = -2147483648;
|
|
22977
22881
|
var INTEGER_MAX = 2147483647;
|
|
@@ -22982,7 +22886,7 @@ var EMPTY_MEANS_NULL = /* @__PURE__ */ new Set(["date", "integer", "float", "enu
|
|
|
22982
22886
|
function emptyValuesToNull(fields, body, registry) {
|
|
22983
22887
|
const out = { ...body };
|
|
22984
22888
|
for (const f of fields) {
|
|
22985
|
-
if (!(f.name
|
|
22889
|
+
if (!Object.hasOwn(out, f.name)) continue;
|
|
22986
22890
|
const value = out[f.name];
|
|
22987
22891
|
if (value === "" && EMPTY_MEANS_NULL.has(f.field_type)) {
|
|
22988
22892
|
out[f.name] = null;
|
|
@@ -23087,7 +22991,7 @@ function rangeError(name, value, min, max) {
|
|
|
23087
22991
|
function checkFieldValues(fields, body, registry, path5 = "") {
|
|
23088
22992
|
const errors = [];
|
|
23089
22993
|
for (const f of fields) {
|
|
23090
|
-
if (!(f.name
|
|
22994
|
+
if (!Object.hasOwn(body, f.name) || f.removed === true) continue;
|
|
23091
22995
|
const name = `${path5}${f.name}`;
|
|
23092
22996
|
const message = valueError(f, body[f.name], name);
|
|
23093
22997
|
if (message) errors.push({ field: name, message });
|
|
@@ -23097,20 +23001,18 @@ function checkFieldValues(fields, body, registry, path5 = "") {
|
|
|
23097
23001
|
}
|
|
23098
23002
|
return errors;
|
|
23099
23003
|
}
|
|
23100
|
-
function
|
|
23004
|
+
function quoteIdent2(name) {
|
|
23101
23005
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`);
|
|
23102
23006
|
return `"${name}"`;
|
|
23103
23007
|
}
|
|
23104
23008
|
async function loadParagraphRows(db, registry, pType, parentId, fieldName) {
|
|
23105
23009
|
const result = await db.execute(
|
|
23106
|
-
sql`SELECT * FROM ${sql.raw(
|
|
23010
|
+
sql`SELECT * FROM ${sql.raw(quoteIdent2(pType.db.table_name))} WHERE parent_id = ${parentId} AND parent_field = ${fieldName} ORDER BY "order" ASC, id ASC`
|
|
23107
23011
|
);
|
|
23108
23012
|
const rows = result.rows;
|
|
23109
23013
|
for (const nf of pType.fields) {
|
|
23110
23014
|
if (nf.db_column !== null) continue;
|
|
23111
|
-
const
|
|
23112
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
23113
|
-
const nType = registry.paragraph_types[comp.ref];
|
|
23015
|
+
const nType = paragraphTypeOf(nf, registry);
|
|
23114
23016
|
if (!nType) continue;
|
|
23115
23017
|
for (const row of rows) {
|
|
23116
23018
|
row[nf.name] = oneOrMany(
|
|
@@ -23122,35 +23024,36 @@ async function loadParagraphRows(db, registry, pType, parentId, fieldName) {
|
|
|
23122
23024
|
return rows;
|
|
23123
23025
|
}
|
|
23124
23026
|
async function loadRelationsForEdit(db, registry, fields, id, row) {
|
|
23125
|
-
|
|
23126
|
-
|
|
23127
|
-
|
|
23128
|
-
|
|
23129
|
-
|
|
23130
|
-
|
|
23131
|
-
|
|
23132
|
-
|
|
23133
|
-
|
|
23134
|
-
|
|
23135
|
-
|
|
23136
|
-
|
|
23137
|
-
|
|
23138
|
-
|
|
23139
|
-
|
|
23027
|
+
const loaded = await Promise.all(
|
|
23028
|
+
fields.map(async (f) => {
|
|
23029
|
+
if (f.db_column === null) {
|
|
23030
|
+
const pType = paragraphTypeOf(f, registry);
|
|
23031
|
+
if (!pType) return null;
|
|
23032
|
+
return [f.name, oneOrMany(relationCardinality(f) ?? "many", await loadParagraphRows(db, registry, pType, id, f.name))];
|
|
23033
|
+
}
|
|
23034
|
+
if (f.db_column.junction) {
|
|
23035
|
+
const j = f.db_column.junction;
|
|
23036
|
+
const r = await db.execute(
|
|
23037
|
+
sql`SELECT ${sql.raw(quoteIdent2(j.right_column))} FROM ${sql.raw(quoteIdent2(j.table_name))} WHERE ${sql.raw(quoteIdent2(j.left_column))} = ${id}${junctionOrderBy(j)}`
|
|
23038
|
+
);
|
|
23039
|
+
return [f.name, [...new Set(r.rows.map((linked) => linked[j.right_column]))]];
|
|
23040
|
+
}
|
|
23041
|
+
return null;
|
|
23042
|
+
})
|
|
23043
|
+
);
|
|
23044
|
+
for (const entry of loaded) if (entry) row[entry[0]] = entry[1];
|
|
23140
23045
|
}
|
|
23141
23046
|
async function valuesForPublishCheck(db, registry, requiredFields, id, storedLabels, body) {
|
|
23142
23047
|
const stored = { ...storedLabels };
|
|
23143
|
-
if (db) await loadRelationsForEdit(db, registry, requiredFields.filter((f) => !(f.name
|
|
23048
|
+
if (db) await loadRelationsForEdit(db, registry, requiredFields.filter((f) => !Object.hasOwn(body, f.name)), id, stored);
|
|
23144
23049
|
return { ...stored, ...body };
|
|
23145
23050
|
}
|
|
23146
23051
|
async function persistRelationFields(db, registry, fields, body, itemId, ownerTable) {
|
|
23147
23052
|
const deltas = [];
|
|
23148
23053
|
for (const f of fields) {
|
|
23149
|
-
if (!(f.name
|
|
23054
|
+
if (!Object.hasOwn(body, f.name)) continue;
|
|
23150
23055
|
if (f.db_column === null) {
|
|
23151
|
-
const
|
|
23152
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
23153
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
23056
|
+
const pType = paragraphTypeOf(f, registry);
|
|
23154
23057
|
if (!pType) continue;
|
|
23155
23058
|
deltas.push(await persistParagraphField(db, itemId, ownerTable, f.name, pType, paragraphItems(f, body[f.name]), registry));
|
|
23156
23059
|
} else if (f.db_column.junction) {
|
|
@@ -23161,6 +23064,46 @@ async function persistRelationFields(db, registry, fields, body, itemId, ownerTa
|
|
|
23161
23064
|
}
|
|
23162
23065
|
return deltas;
|
|
23163
23066
|
}
|
|
23067
|
+
async function deleteOwnParagraphs(db, registry, fields, itemId) {
|
|
23068
|
+
const deltas = [];
|
|
23069
|
+
for (const f of fields) {
|
|
23070
|
+
const pType = paragraphTypeOf(f, registry);
|
|
23071
|
+
if (!pType) continue;
|
|
23072
|
+
deltas.push(await deleteParagraphField(db, itemId, f.name, pType, registry));
|
|
23073
|
+
}
|
|
23074
|
+
return deltas;
|
|
23075
|
+
}
|
|
23076
|
+
function releasedFromParagraphsOnly(paragraphDeltas, topLevel) {
|
|
23077
|
+
const stillHeld = new Set(topLevel.removed);
|
|
23078
|
+
return { added: [], removed: mergeMediaDeltas(...paragraphDeltas).removed.filter((mediaId) => !stillHeld.has(mediaId)) };
|
|
23079
|
+
}
|
|
23080
|
+
async function deleteItemCascade(db, registry, mediaRepo, target) {
|
|
23081
|
+
const { typeName, fields, mediaFields, repo, id, row } = target;
|
|
23082
|
+
const referrers = db ? await findRequiredReferrers(db, registry, typeName, id) : { others: [], fromOwnParagraphs: false };
|
|
23083
|
+
if (referrers.others.length > 0) return { ok: false, message: inUseMessage(referrers.others) };
|
|
23084
|
+
const topLevel = topLevelMediaDelta(mediaFields, row, null);
|
|
23085
|
+
if (!db || !referrers.fromOwnParagraphs) {
|
|
23086
|
+
try {
|
|
23087
|
+
await repo.delete(id);
|
|
23088
|
+
} catch (err) {
|
|
23089
|
+
if (isForeignKeyViolation(err)) return { ok: false, message: IN_USE_RACE_MESSAGE };
|
|
23090
|
+
throw err;
|
|
23091
|
+
}
|
|
23092
|
+
const paragraphDeltas2 = db ? await deleteOwnParagraphs(db, registry, fields, id) : [];
|
|
23093
|
+
await applyMediaReferenceDelta(mergeMediaDeltas(topLevel, ...paragraphDeltas2), mediaRepo);
|
|
23094
|
+
return { ok: true };
|
|
23095
|
+
}
|
|
23096
|
+
const paragraphDeltas = await deleteOwnParagraphs(db, registry, fields, id);
|
|
23097
|
+
try {
|
|
23098
|
+
await repo.delete(id);
|
|
23099
|
+
} catch (err) {
|
|
23100
|
+
await applyMediaReferenceDelta(releasedFromParagraphsOnly(paragraphDeltas, topLevel), mediaRepo);
|
|
23101
|
+
if (isForeignKeyViolation(err)) return { ok: false, message: IN_USE_RACE_MESSAGE };
|
|
23102
|
+
throw err;
|
|
23103
|
+
}
|
|
23104
|
+
await applyMediaReferenceDelta(mergeMediaDeltas(topLevel, ...paragraphDeltas), mediaRepo);
|
|
23105
|
+
return { ok: true };
|
|
23106
|
+
}
|
|
23164
23107
|
async function lookupBasePathId(db, pathOrName) {
|
|
23165
23108
|
const r = await db.execute(
|
|
23166
23109
|
sql`SELECT id FROM base_paths WHERE path = ${pathOrName} OR name = ${pathOrName} LIMIT 1`
|
|
@@ -23270,7 +23213,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23270
23213
|
const mediaFields = contentType.fields.filter(
|
|
23271
23214
|
(f) => f.field_type === "image" || f.field_type === "video" || f.field_type === "file"
|
|
23272
23215
|
);
|
|
23273
|
-
const paragraphFieldDefs = contentType.fields.filter((f) => f.db_column === null);
|
|
23274
23216
|
const searchableColumns = [
|
|
23275
23217
|
...contentType.fields.filter((f) => f.field_type === "text/plain" && f.db_column !== null).map((f) => f.db_column.column_name),
|
|
23276
23218
|
...contentType.system_fields.some((f) => f.name === "slug") ? ["slug"] : []
|
|
@@ -23420,7 +23362,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23420
23362
|
}
|
|
23421
23363
|
for (const f of columnBackedFields) {
|
|
23422
23364
|
const key = f.db_column.column_name;
|
|
23423
|
-
if (!(key
|
|
23365
|
+
if (!Object.hasOwn(storageBody, key)) continue;
|
|
23424
23366
|
columnData[key] = storageBody[key];
|
|
23425
23367
|
}
|
|
23426
23368
|
if (db) {
|
|
@@ -23462,7 +23404,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23462
23404
|
404
|
|
23463
23405
|
);
|
|
23464
23406
|
}
|
|
23465
|
-
if (!contentType.only_one && "slug"
|
|
23407
|
+
if (!contentType.only_one && Object.hasOwn(body, "slug")) {
|
|
23466
23408
|
const slug = body["slug"];
|
|
23467
23409
|
if (typeof slug !== "string" || slug.trim() === "") {
|
|
23468
23410
|
return c.json(
|
|
@@ -23532,11 +23474,11 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23532
23474
|
}
|
|
23533
23475
|
const patchColumnBackedFields = contentType.fields.filter(isColumnBacked);
|
|
23534
23476
|
const patchData = {};
|
|
23535
|
-
if (!contentType.only_one && "slug"
|
|
23536
|
-
if ("published"
|
|
23477
|
+
if (!contentType.only_one && Object.hasOwn(body, "slug")) patchData["slug"] = body["slug"];
|
|
23478
|
+
if (Object.hasOwn(body, "published")) patchData["published"] = body["published"];
|
|
23537
23479
|
for (const f of patchColumnBackedFields) {
|
|
23538
23480
|
const key = f.db_column.column_name;
|
|
23539
|
-
if (!(key
|
|
23481
|
+
if (!Object.hasOwn(storageBody, key)) continue;
|
|
23540
23482
|
patchData[key] = storageBody[key];
|
|
23541
23483
|
}
|
|
23542
23484
|
const updated = await repo.update(id, patchData);
|
|
@@ -23586,31 +23528,17 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23586
23528
|
404
|
|
23587
23529
|
);
|
|
23588
23530
|
}
|
|
23589
|
-
const
|
|
23590
|
-
|
|
23591
|
-
|
|
23592
|
-
|
|
23593
|
-
|
|
23594
|
-
|
|
23595
|
-
|
|
23596
|
-
|
|
23597
|
-
|
|
23598
|
-
}
|
|
23599
|
-
throw err;
|
|
23600
|
-
}
|
|
23601
|
-
const mediaDeltas = [
|
|
23602
|
-
topLevelMediaDelta(mediaFields, item, null)
|
|
23603
|
-
];
|
|
23604
|
-
if (db) {
|
|
23605
|
-
for (const f of paragraphFieldDefs) {
|
|
23606
|
-
const comp = f.ui_component;
|
|
23607
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
23608
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
23609
|
-
if (!pType) continue;
|
|
23610
|
-
mediaDeltas.push(await deleteParagraphField(db, id, f.name, pType, registry));
|
|
23611
|
-
}
|
|
23531
|
+
const outcome = await deleteItemCascade(db, registry, mediaRepo, {
|
|
23532
|
+
typeName,
|
|
23533
|
+
fields: contentType.fields,
|
|
23534
|
+
mediaFields,
|
|
23535
|
+
repo,
|
|
23536
|
+
id,
|
|
23537
|
+
row: item
|
|
23538
|
+
});
|
|
23539
|
+
if (!outcome.ok) {
|
|
23540
|
+
return c.json({ ok: false, error: { code: "ITEM_IN_USE", message: outcome.message } }, 409);
|
|
23612
23541
|
}
|
|
23613
|
-
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
23614
23542
|
return c.json({ ok: true });
|
|
23615
23543
|
}
|
|
23616
23544
|
);
|
|
@@ -23631,7 +23559,6 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23631
23559
|
const mediaFields = taxonomyType.fields.filter(
|
|
23632
23560
|
(f) => f.field_type === "image" || f.field_type === "video" || f.field_type === "file"
|
|
23633
23561
|
);
|
|
23634
|
-
const paragraphFieldDefs = taxonomyType.fields.filter((f) => f.db_column === null);
|
|
23635
23562
|
const searchableColumns = [
|
|
23636
23563
|
...taxonomyType.fields.filter((f) => f.field_type === "text/plain" && f.db_column !== null).map((f) => f.db_column.column_name),
|
|
23637
23564
|
...taxonomyType.system_fields.some((f) => f.name === "slug") ? ["slug"] : []
|
|
@@ -23717,7 +23644,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23717
23644
|
};
|
|
23718
23645
|
for (const f of taxColumnBackedFields) {
|
|
23719
23646
|
const key = f.db_column.column_name;
|
|
23720
|
-
if (!(key
|
|
23647
|
+
if (!Object.hasOwn(storageBody, key)) continue;
|
|
23721
23648
|
taxColumnData[key] = storageBody[key];
|
|
23722
23649
|
}
|
|
23723
23650
|
const item = await repo.create(taxColumnData);
|
|
@@ -23769,10 +23696,10 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23769
23696
|
}
|
|
23770
23697
|
const taxPatchColumnBackedFields = taxonomyType.fields.filter(isColumnBacked);
|
|
23771
23698
|
const taxPatchData = {};
|
|
23772
|
-
if ("published"
|
|
23699
|
+
if (Object.hasOwn(body, "published")) taxPatchData["published"] = body["published"];
|
|
23773
23700
|
for (const f of taxPatchColumnBackedFields) {
|
|
23774
23701
|
const key = f.db_column.column_name;
|
|
23775
|
-
if (!(key
|
|
23702
|
+
if (!Object.hasOwn(storageBody, key)) continue;
|
|
23776
23703
|
taxPatchData[key] = storageBody[key];
|
|
23777
23704
|
}
|
|
23778
23705
|
const updated = await repo.update(id, taxPatchData);
|
|
@@ -23805,31 +23732,17 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23805
23732
|
404
|
|
23806
23733
|
);
|
|
23807
23734
|
}
|
|
23808
|
-
const
|
|
23809
|
-
|
|
23810
|
-
|
|
23811
|
-
|
|
23812
|
-
|
|
23813
|
-
|
|
23814
|
-
|
|
23815
|
-
|
|
23816
|
-
|
|
23817
|
-
}
|
|
23818
|
-
throw err;
|
|
23819
|
-
}
|
|
23820
|
-
const mediaDeltas = [
|
|
23821
|
-
topLevelMediaDelta(mediaFields, item, null)
|
|
23822
|
-
];
|
|
23823
|
-
if (db) {
|
|
23824
|
-
for (const f of paragraphFieldDefs) {
|
|
23825
|
-
const comp = f.ui_component;
|
|
23826
|
-
if (comp.component !== "paragraph-embed" || !comp.ref) continue;
|
|
23827
|
-
const pType = registry.paragraph_types[comp.ref];
|
|
23828
|
-
if (!pType) continue;
|
|
23829
|
-
mediaDeltas.push(await deleteParagraphField(db, id, f.name, pType, registry));
|
|
23830
|
-
}
|
|
23735
|
+
const outcome = await deleteItemCascade(db, registry, mediaRepo, {
|
|
23736
|
+
typeName,
|
|
23737
|
+
fields: taxonomyType.fields,
|
|
23738
|
+
mediaFields,
|
|
23739
|
+
repo,
|
|
23740
|
+
id,
|
|
23741
|
+
row: item
|
|
23742
|
+
});
|
|
23743
|
+
if (!outcome.ok) {
|
|
23744
|
+
return c.json({ ok: false, error: { code: "ITEM_IN_USE", message: outcome.message } }, 409);
|
|
23831
23745
|
}
|
|
23832
|
-
await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo);
|
|
23833
23746
|
return c.json({ ok: true });
|
|
23834
23747
|
}
|
|
23835
23748
|
);
|
|
@@ -24774,7 +24687,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
24774
24687
|
}
|
|
24775
24688
|
var API_VERSION = (() => {
|
|
24776
24689
|
try {
|
|
24777
|
-
return "0.9.
|
|
24690
|
+
return "0.9.3";
|
|
24778
24691
|
} catch {
|
|
24779
24692
|
return "0.0.0";
|
|
24780
24693
|
}
|
|
@@ -24883,7 +24796,7 @@ function codeError(code, message) {
|
|
|
24883
24796
|
err.code = code;
|
|
24884
24797
|
return err;
|
|
24885
24798
|
}
|
|
24886
|
-
function
|
|
24799
|
+
function quoteIdent3(name) {
|
|
24887
24800
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) {
|
|
24888
24801
|
throw new Error(`Unsafe SQL identifier: ${name}`);
|
|
24889
24802
|
}
|
|
@@ -24934,7 +24847,7 @@ function whereFragment(conds) {
|
|
|
24934
24847
|
function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
24935
24848
|
const { relations = {}, publishedRelations = false, sortableColumns } = options;
|
|
24936
24849
|
function tableRaw() {
|
|
24937
|
-
return sql.raw(
|
|
24850
|
+
return sql.raw(quoteIdent3(tableName));
|
|
24938
24851
|
}
|
|
24939
24852
|
async function resolveRows(rows, include, cache) {
|
|
24940
24853
|
if (rows.length === 0) return rows;
|
|
@@ -24977,7 +24890,7 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
24977
24890
|
const offset = (page - 1) * per_page;
|
|
24978
24891
|
const conds = buildConditions(published_only, filters, search);
|
|
24979
24892
|
const where = whereFragment(conds);
|
|
24980
|
-
const sortCol = sql.raw(
|
|
24893
|
+
const sortCol = sql.raw(quoteIdent3(sort_by));
|
|
24981
24894
|
const sortDir = sql.raw(sort_order === "desc" ? "DESC" : "ASC");
|
|
24982
24895
|
const countResult = await db.execute(
|
|
24983
24896
|
sql`SELECT COUNT(*) AS total FROM ${tableRaw()}${where}`
|
|
@@ -25031,7 +24944,7 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
25031
24944
|
updated_at: /* @__PURE__ */ new Date()
|
|
25032
24945
|
};
|
|
25033
24946
|
const entries = Object.entries(record).filter(([, v2]) => v2 !== void 0);
|
|
25034
|
-
const colsSql = sql.join(entries.map(([k]) => sql.raw(
|
|
24947
|
+
const colsSql = sql.join(entries.map(([k]) => sql.raw(quoteIdent3(k))), sql`, `);
|
|
25035
24948
|
const valsSql = sql.join(entries.map(([, v2]) => sql`${v2}`), sql`, `);
|
|
25036
24949
|
const result = await db.execute(
|
|
25037
24950
|
sql`INSERT INTO ${tableRaw()} (${colsSql}) VALUES (${valsSql}) RETURNING *`
|
|
@@ -25048,7 +24961,7 @@ function createDrizzleContentRepository(db, tableName, options = {}) {
|
|
|
25048
24961
|
return this.findOne(id);
|
|
25049
24962
|
}
|
|
25050
24963
|
const setClauses = sql.join(
|
|
25051
|
-
entries.map(([k, v2]) => sql`${sql.raw(
|
|
24964
|
+
entries.map(([k, v2]) => sql`${sql.raw(quoteIdent3(k))} = ${v2}`),
|
|
25052
24965
|
sql`, `
|
|
25053
24966
|
);
|
|
25054
24967
|
const result = await db.execute(
|
|
@@ -25457,7 +25370,7 @@ function createCmsApp(options) {
|
|
|
25457
25370
|
handler: null,
|
|
25458
25371
|
error: null
|
|
25459
25372
|
}));
|
|
25460
|
-
const ready = import("./handler-
|
|
25373
|
+
const ready = import("./handler-N5IMQJBP-USVWSLZP.js").then(({ createGraphQLHandler }) => {
|
|
25461
25374
|
gqlPasses.forEach((pass, i) => {
|
|
25462
25375
|
const mount = mounts[i];
|
|
25463
25376
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobbykim/manguito-cms-cli",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.4",
|
|
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-
|
|
32
|
+
"@bobbykim/manguito-cms-admin": "0.6.2",
|
|
33
|
+
"@bobbykim/manguito-cms-api": "0.9.3",
|
|
33
34
|
"@bobbykim/manguito-cms-db": "0.2.1",
|
|
34
|
-
"@bobbykim/manguito-cms-
|
|
35
|
-
"@bobbykim/manguito-cms-admin": "0.6.1"
|
|
35
|
+
"@bobbykim/manguito-cms-core": "0.11.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^25.9.5",
|