@bobbykim/manguito-cms-cli 0.11.4 → 0.12.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.
|
@@ -9270,7 +9270,7 @@ var require_bcryptjs = __commonJS({
|
|
|
9270
9270
|
}
|
|
9271
9271
|
});
|
|
9272
9272
|
|
|
9273
|
-
// ../api/dist/chunk-
|
|
9273
|
+
// ../api/dist/chunk-EFTMAG7N.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-EFTMAG7N.mjs
|
|
26685
26685
|
function isColumnBacked2(field) {
|
|
26686
26686
|
const col = field.db_column;
|
|
26687
26687
|
if (col === null) return false;
|
|
@@ -27063,7 +27063,7 @@ async function persistJunctionField(db, itemId, junction, relatedIds) {
|
|
|
27063
27063
|
sql`, `
|
|
27064
27064
|
);
|
|
27065
27065
|
await db.execute(
|
|
27066
|
-
junction.order_column ? sql`INSERT INTO ${tbl} (${left}, ${right}, "order") VALUES ${rows}` : sql`INSERT INTO ${tbl} (${left}, ${right}) VALUES ${rows}`
|
|
27066
|
+
junction.order_column ? sql`INSERT INTO ${tbl} (${left}, ${right}, "order") VALUES ${rows} ON CONFLICT DO NOTHING` : sql`INSERT INTO ${tbl} (${left}, ${right}) VALUES ${rows} ON CONFLICT DO NOTHING`
|
|
27067
27067
|
);
|
|
27068
27068
|
}
|
|
27069
27069
|
function junctionOrderBy(junction) {
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
isColumnBacked,
|
|
13
13
|
relationCardinality,
|
|
14
14
|
resolveRelationField
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-EVB2E4A5.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-5OTVPIAF.mjs
|
|
10791
10791
|
init_esm_shims();
|
|
10792
10792
|
|
|
10793
|
-
// ../api/dist/chunk-
|
|
10793
|
+
// ../api/dist/chunk-DHOVO42E.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-DHOVO42E.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-DHOVO42E.mjs
|
|
30885
30885
|
var DateTimeScalar = new GraphQLScalarType({
|
|
30886
30886
|
name: "DateTime",
|
|
30887
30887
|
description: "ISO-8601 date-time string",
|
package/dist/index.js
CHANGED
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
validateCrossReferences,
|
|
64
64
|
verifyPassword,
|
|
65
65
|
walkSchemaDirectory
|
|
66
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-EVB2E4A5.js";
|
|
67
67
|
|
|
68
68
|
// ../../node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js
|
|
69
69
|
var require_postgres_array = __commonJS({
|
|
@@ -5272,9 +5272,10 @@ import { Command } from "commander";
|
|
|
5272
5272
|
|
|
5273
5273
|
// src/commands/build.ts
|
|
5274
5274
|
init_esm_shims();
|
|
5275
|
-
import { mkdirSync, statSync, existsSync as existsSync3, readdirSync, writeFileSync } from "fs";
|
|
5275
|
+
import { mkdirSync, statSync, existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
5276
5276
|
import { resolve as resolve3, join as join7, dirname } from "path";
|
|
5277
5277
|
import { createRequire } from "module";
|
|
5278
|
+
import { fileURLToPath } from "url";
|
|
5278
5279
|
import { build as viteBuild } from "vite";
|
|
5279
5280
|
import { build as tsupBuild } from "tsup";
|
|
5280
5281
|
|
|
@@ -16721,6 +16722,7 @@ function drizzle2(...params) {
|
|
|
16721
16722
|
})(drizzle2 || (drizzle2 = {}));
|
|
16722
16723
|
|
|
16723
16724
|
// ../db/dist/index.mjs
|
|
16725
|
+
import { createHash } from "crypto";
|
|
16724
16726
|
import { execSync, spawnSync } from "child_process";
|
|
16725
16727
|
import crypto2 from "crypto";
|
|
16726
16728
|
import path from "path";
|
|
@@ -16809,6 +16811,256 @@ function createPostgresAdapter(options = {}) {
|
|
|
16809
16811
|
}
|
|
16810
16812
|
};
|
|
16811
16813
|
}
|
|
16814
|
+
var MAX_IDENTIFIER = 63;
|
|
16815
|
+
var SUFFIX = "_unique";
|
|
16816
|
+
function linkUniqueName(tableName) {
|
|
16817
|
+
const plain = `${tableName}${SUFFIX}`;
|
|
16818
|
+
if (plain.length <= MAX_IDENTIFIER) return plain;
|
|
16819
|
+
const hash = createHash("sha256").update(tableName).digest("hex").slice(0, 8);
|
|
16820
|
+
return `${tableName.slice(0, 47)}_${hash}${SUFFIX}`;
|
|
16821
|
+
}
|
|
16822
|
+
function linkDedupeSql(table, left, right, hasOrder) {
|
|
16823
|
+
const order = hasOrder ? '(a."order", a.ctid) > (b."order", b.ctid)' : "a.ctid > b.ctid";
|
|
16824
|
+
return `DELETE FROM "${table}" a USING "${table}" b WHERE a."${left}" = b."${left}" AND a."${right}" = b."${right}" AND ${order}`;
|
|
16825
|
+
}
|
|
16826
|
+
var ADD_LINK_UNIQUE = /ALTER TABLE "([^"]+)" ADD CONSTRAINT "([^"]+)" UNIQUE\("left_id","right_id"\);/g;
|
|
16827
|
+
function addLinkDedupe(sql6) {
|
|
16828
|
+
return sql6.replace(
|
|
16829
|
+
ADD_LINK_UNIQUE,
|
|
16830
|
+
(statement, table, name) => [
|
|
16831
|
+
"-- Removes only repeated (left, right) pairs, keeping the lowest order, before adding the constraint.",
|
|
16832
|
+
"DO $$ BEGIN",
|
|
16833
|
+
`IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = '"${table}"'::regclass AND conname = '${name}') THEN`,
|
|
16834
|
+
`LOCK TABLE "${table}" IN SHARE ROW EXCLUSIVE MODE;`,
|
|
16835
|
+
`${linkDedupeSql(table, "left_id", "right_id", true)};`,
|
|
16836
|
+
statement,
|
|
16837
|
+
"END IF;",
|
|
16838
|
+
"END $$;"
|
|
16839
|
+
].join("\n")
|
|
16840
|
+
);
|
|
16841
|
+
}
|
|
16842
|
+
async function ensureLinkConstraints(db, registry) {
|
|
16843
|
+
const owners = [
|
|
16844
|
+
...Object.values(registry.content_types),
|
|
16845
|
+
...Object.values(registry.taxonomy_types)
|
|
16846
|
+
];
|
|
16847
|
+
const added = [];
|
|
16848
|
+
for (const owner of owners) {
|
|
16849
|
+
for (const jt of owner.db.junction_tables) {
|
|
16850
|
+
const table = jt.table_name;
|
|
16851
|
+
const name = linkUniqueName(table);
|
|
16852
|
+
const exists2 = await db.execute(
|
|
16853
|
+
sql`SELECT to_regclass(${`"${table}"`}) IS NOT NULL AS found`
|
|
16854
|
+
);
|
|
16855
|
+
if (!exists2.rows[0].found) continue;
|
|
16856
|
+
const constrained = await db.execute(
|
|
16857
|
+
sql`SELECT 1 FROM pg_constraint WHERE conrelid = ${`"${table}"`}::regclass AND conname = ${name}`
|
|
16858
|
+
);
|
|
16859
|
+
if (constrained.rows.length > 0) continue;
|
|
16860
|
+
const orderColumn = await db.execute(
|
|
16861
|
+
sql`SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = ${table} AND column_name = 'order'`
|
|
16862
|
+
);
|
|
16863
|
+
const deleted = await db.execute(
|
|
16864
|
+
sql.raw(
|
|
16865
|
+
`${linkDedupeSql(table, jt.left_column, jt.right_column, orderColumn.rows.length > 0)} RETURNING 1`
|
|
16866
|
+
)
|
|
16867
|
+
);
|
|
16868
|
+
await db.execute(
|
|
16869
|
+
sql.raw(
|
|
16870
|
+
`ALTER TABLE "${table}" ADD CONSTRAINT "${name}" UNIQUE("${jt.left_column}", "${jt.right_column}")`
|
|
16871
|
+
)
|
|
16872
|
+
);
|
|
16873
|
+
added.push({ table, removed: deleted.rows.length });
|
|
16874
|
+
}
|
|
16875
|
+
}
|
|
16876
|
+
return added;
|
|
16877
|
+
}
|
|
16878
|
+
var LIMIT = 63;
|
|
16879
|
+
var stored = (name) => name.slice(0, LIMIT);
|
|
16880
|
+
var SYSTEM_TABLES = ["media", "base_paths", "roles", "users"];
|
|
16881
|
+
var SYSTEM_UNIQUES = [
|
|
16882
|
+
{ name: "base_paths_name_unique", table: "base_paths" },
|
|
16883
|
+
{ name: "base_paths_path_unique", table: "base_paths" },
|
|
16884
|
+
{ name: "roles_name_unique", table: "roles" },
|
|
16885
|
+
{ name: "roles_hierarchy_level_unique", table: "roles" },
|
|
16886
|
+
{ name: "users_email_unique", table: "users" }
|
|
16887
|
+
];
|
|
16888
|
+
function foreignKeyName(table, column, targetTable, targetColumn) {
|
|
16889
|
+
return `${table}_${column}_${targetTable}_${targetColumn}_fk`;
|
|
16890
|
+
}
|
|
16891
|
+
function checkConstraintName(column) {
|
|
16892
|
+
return `${column}_check`;
|
|
16893
|
+
}
|
|
16894
|
+
function primaryKeyName(table) {
|
|
16895
|
+
const name = `${table}_pkey`;
|
|
16896
|
+
return name.length <= LIMIT ? name : `${table.slice(0, LIMIT - "_pkey".length)}_pkey`;
|
|
16897
|
+
}
|
|
16898
|
+
function rawFieldPath(owner, field) {
|
|
16899
|
+
if (owner.schema_type !== "content-type") return `fields[${field.order}].name`;
|
|
16900
|
+
const t = owner.ui.tabs.findIndex((tab) => tab.fields.includes(field.name));
|
|
16901
|
+
const i = t < 0 ? -1 : owner.ui.tabs[t].fields.indexOf(field.name);
|
|
16902
|
+
return t < 0 ? `fields[${field.order}].name` : `fields[${t}].tab.fields[${i}].name`;
|
|
16903
|
+
}
|
|
16904
|
+
function declaredColumn(field) {
|
|
16905
|
+
const column = field.db_column?.column_name;
|
|
16906
|
+
return column !== void 0 && column !== field.name ? column : void 0;
|
|
16907
|
+
}
|
|
16908
|
+
function describeField(field) {
|
|
16909
|
+
const column = declaredColumn(field);
|
|
16910
|
+
return column !== void 0 ? `field "${field.name}" (column "${column}")` : `field "${field.name}"`;
|
|
16911
|
+
}
|
|
16912
|
+
function renameOf(field) {
|
|
16913
|
+
const column = declaredColumn(field);
|
|
16914
|
+
return column !== void 0 ? `change the column "${column}" of field "${field.name}"` : `rename field "${field.name}"`;
|
|
16915
|
+
}
|
|
16916
|
+
function shortenOf(field, room) {
|
|
16917
|
+
const column = declaredColumn(field);
|
|
16918
|
+
return column !== void 0 ? `shorten the column "${column}" of field "${field.name}" to at most ${room} characters` : `shorten field "${field.name}" to at most ${room} characters`;
|
|
16919
|
+
}
|
|
16920
|
+
var commonPrefixLength = (a2, b2) => {
|
|
16921
|
+
let i = 0;
|
|
16922
|
+
while (i < a2.length && i < b2.length && a2[i] === b2[i]) i++;
|
|
16923
|
+
return i;
|
|
16924
|
+
};
|
|
16925
|
+
function typeTableConstraints(owner) {
|
|
16926
|
+
const table = owner.db.table_name;
|
|
16927
|
+
const names = [];
|
|
16928
|
+
for (const field of owner.fields) {
|
|
16929
|
+
const col = field.db_column;
|
|
16930
|
+
if (field.field_type === "paragraph" || col === null || col.junction) continue;
|
|
16931
|
+
if (col.foreign_key) {
|
|
16932
|
+
const fk = col.foreign_key;
|
|
16933
|
+
names.push({ name: foreignKeyName(table, col.column_name, fk.table, fk.column), kind: "foreign key", field });
|
|
16934
|
+
}
|
|
16935
|
+
if ((col.check_constraint?.length ?? 0) > 0) {
|
|
16936
|
+
names.push({ name: checkConstraintName(col.column_name), kind: "check", field });
|
|
16937
|
+
}
|
|
16938
|
+
}
|
|
16939
|
+
return names;
|
|
16940
|
+
}
|
|
16941
|
+
function typeTableMessage(owner, a2, b2) {
|
|
16942
|
+
const table = owner.db.table_name;
|
|
16943
|
+
const clash = `Postgres keeps only the first ${LIMIT} characters of a name, which are the same, so the migration would fail with "constraint already exists".`;
|
|
16944
|
+
if (a2.kind === "foreign key" && b2.kind === "foreign key") {
|
|
16945
|
+
const room2 = LIMIT - 1 - table.length;
|
|
16946
|
+
const shared = commonPrefixLength(a2.name.slice(table.length), b2.name.slice(table.length));
|
|
16947
|
+
const maxType = LIMIT - 1 - shared + (owner.name.length - table.length);
|
|
16948
|
+
const fix = room2 < 1 ? `shorten the name of type "${owner.name}" to at most ${maxType} characters: its table name "${table}" leaves no room for a column in the first ${LIMIT} characters` : `${renameOf(a2.field)} or ${renameOf(b2.field)} so that the two differ within their first ${room2} characters, or shorten the name of type "${owner.name}"`;
|
|
16949
|
+
return `The ${describeField(a2.field)} and the ${describeField(b2.field)} of "${owner.name}" get foreign keys "${a2.name}" and "${b2.name}". ${clash} To fix it, ${fix}.`;
|
|
16950
|
+
}
|
|
16951
|
+
if (a2.kind === "check" && b2.kind === "check") {
|
|
16952
|
+
const room2 = LIMIT - "_check".length;
|
|
16953
|
+
return `The ${describeField(a2.field)} and the ${describeField(b2.field)} of "${owner.name}" get check constraints "${a2.name}" and "${b2.name}". ${clash} To fix it, ${renameOf(a2.field)} or ${renameOf(b2.field)} so that the two differ within their first ${room2} characters.`;
|
|
16954
|
+
}
|
|
16955
|
+
const check = a2.kind === "check" ? a2 : b2;
|
|
16956
|
+
const room = LIMIT - 1 - "_check".length;
|
|
16957
|
+
return `The ${a2.kind} "${a2.name}" of the ${describeField(a2.field)} and the ${b2.kind} "${b2.name}" of the ${describeField(b2.field)}, both on "${owner.name}", are the same in their first ${LIMIT} characters. ${clash} To fix it, ${shortenOf(check.field, room)}.`;
|
|
16958
|
+
}
|
|
16959
|
+
function checkTypeTable(owner) {
|
|
16960
|
+
const errors = [];
|
|
16961
|
+
const first = /* @__PURE__ */ new Map();
|
|
16962
|
+
for (const constraint of typeTableConstraints(owner)) {
|
|
16963
|
+
const earlier = first.get(stored(constraint.name));
|
|
16964
|
+
if (!earlier) {
|
|
16965
|
+
first.set(stored(constraint.name), constraint);
|
|
16966
|
+
continue;
|
|
16967
|
+
}
|
|
16968
|
+
errors.push({
|
|
16969
|
+
file: owner.source_file,
|
|
16970
|
+
code: "CONSTRAINT_NAME_COLLISION",
|
|
16971
|
+
message: typeTableMessage(owner, earlier, constraint),
|
|
16972
|
+
path: rawFieldPath(owner, constraint.field)
|
|
16973
|
+
});
|
|
16974
|
+
}
|
|
16975
|
+
return errors;
|
|
16976
|
+
}
|
|
16977
|
+
function checkLinkTable(link, left, right, rightTable) {
|
|
16978
|
+
const { owner, field, table } = link;
|
|
16979
|
+
const leftFk = foreignKeyName(table, left, owner.db.table_name, "id");
|
|
16980
|
+
const rightFk = foreignKeyName(table, right, rightTable, "id");
|
|
16981
|
+
const names = [leftFk, rightFk, link.unique];
|
|
16982
|
+
const collides = names.some((n, i) => names.some((m2, j) => i < j && stored(n) === stored(m2)));
|
|
16983
|
+
if (!collides) return null;
|
|
16984
|
+
const base = { file: owner.source_file, code: "CONSTRAINT_NAME_COLLISION", path: rawFieldPath(owner, field) };
|
|
16985
|
+
const truncated = `Postgres keeps only the first ${LIMIT} characters of a name`;
|
|
16986
|
+
if (stored(leftFk) === stored(rightFk)) {
|
|
16987
|
+
const prefix = `junction_${owner.db.table_name}_`.length;
|
|
16988
|
+
const room = LIMIT - 2 - prefix;
|
|
16989
|
+
const fix = room < 1 ? `shorten the name of type "${owner.name}": its link table names already have ${prefix} characters before the field name` : `shorten field "${field.name}" to at most ${room} characters, or the name of type "${owner.name}"`;
|
|
16990
|
+
return {
|
|
16991
|
+
...base,
|
|
16992
|
+
message: `The link table of field "${field.name}" of "${owner.name}", "${table}", has ${table.length} characters, so its foreign keys "${leftFk}" and "${rightFk}" are the same in their first ${LIMIT} characters. ${truncated}, so the migration would fail with "constraint already exists". To keep the link table within ${LIMIT - 2} characters, ${fix}.`
|
|
16993
|
+
};
|
|
16994
|
+
}
|
|
16995
|
+
return {
|
|
16996
|
+
...base,
|
|
16997
|
+
message: `The link table of field "${field.name}" of "${owner.name}", "${table}", gets constraints ${names.map((n) => `"${n}"`).join(", ")}, two of which are the same in their first ${LIMIT} characters. ${truncated}, so the migration would fail with "constraint already exists". To fix it, rename field "${field.name}" of type "${owner.name}".`
|
|
16998
|
+
};
|
|
16999
|
+
}
|
|
17000
|
+
function takenNames(registry, links) {
|
|
17001
|
+
const taken = [];
|
|
17002
|
+
for (const table of SYSTEM_TABLES) {
|
|
17003
|
+
taken.push({ name: table, what: `system table "${table}"` });
|
|
17004
|
+
taken.push({ name: primaryKeyName(table), what: `the primary key of system table "${table}"` });
|
|
17005
|
+
}
|
|
17006
|
+
for (const u of SYSTEM_UNIQUES) taken.push({ name: u.name, what: `a unique constraint of system table "${u.table}"` });
|
|
17007
|
+
const types3 = [
|
|
17008
|
+
...Object.values(registry.taxonomy_types),
|
|
17009
|
+
...Object.values(registry.paragraph_types),
|
|
17010
|
+
...Object.values(registry.content_types)
|
|
17011
|
+
];
|
|
17012
|
+
for (const t of types3) {
|
|
17013
|
+
taken.push({ name: t.db.table_name, what: `the table of type "${t.name}"` });
|
|
17014
|
+
taken.push({ name: primaryKeyName(t.db.table_name), what: `the primary key of type "${t.name}"` });
|
|
17015
|
+
}
|
|
17016
|
+
for (const l of links) taken.push({ name: l.table, what: `the link table of field "${l.field.name}" of "${l.owner.name}"` });
|
|
17017
|
+
return taken;
|
|
17018
|
+
}
|
|
17019
|
+
function checkLinkUniques(registry, links) {
|
|
17020
|
+
const first = /* @__PURE__ */ new Map();
|
|
17021
|
+
for (const t of takenNames(registry, links)) {
|
|
17022
|
+
if (!first.has(stored(t.name))) first.set(stored(t.name), t);
|
|
17023
|
+
}
|
|
17024
|
+
const errors = [];
|
|
17025
|
+
for (const link of links) {
|
|
17026
|
+
const key = stored(link.unique);
|
|
17027
|
+
const earlier = first.get(key);
|
|
17028
|
+
if (!earlier) {
|
|
17029
|
+
first.set(key, { name: link.unique, what: `the unique constraint of the link table of field "${link.field.name}" of "${link.owner.name}"` });
|
|
17030
|
+
continue;
|
|
17031
|
+
}
|
|
17032
|
+
const same = earlier.name === link.unique ? `which is also the name of ${earlier.what}` : `which is the same as ${earlier.what}, "${earlier.name}", in its first ${LIMIT} characters. Postgres keeps only the first ${LIMIT} characters of a name`;
|
|
17033
|
+
errors.push({
|
|
17034
|
+
file: link.owner.source_file,
|
|
17035
|
+
code: "CONSTRAINT_NAME_COLLISION",
|
|
17036
|
+
message: `The link table of field "${link.field.name}" of "${link.owner.name}" gets unique constraint "${link.unique}", ${same}. A unique constraint shares its name with its index, and indexes share one namespace with tables, so the migration would fail with "relation already exists". Rename field "${link.field.name}" of type "${link.owner.name}".`,
|
|
17037
|
+
path: rawFieldPath(link.owner, link.field)
|
|
17038
|
+
});
|
|
17039
|
+
}
|
|
17040
|
+
return errors;
|
|
17041
|
+
}
|
|
17042
|
+
function findConstraintNameCollisions(registry) {
|
|
17043
|
+
const errors = [];
|
|
17044
|
+
const typeTables = [
|
|
17045
|
+
...Object.values(registry.taxonomy_types),
|
|
17046
|
+
...Object.values(registry.paragraph_types),
|
|
17047
|
+
...Object.values(registry.content_types)
|
|
17048
|
+
];
|
|
17049
|
+
for (const owner of typeTables) errors.push(...checkTypeTable(owner));
|
|
17050
|
+
const links = [];
|
|
17051
|
+
for (const owner of [...Object.values(registry.content_types), ...Object.values(registry.taxonomy_types)]) {
|
|
17052
|
+
for (const jt of owner.db.junction_tables) {
|
|
17053
|
+
const field = owner.fields.find((f) => f.db_column?.junction?.table_name === jt.table_name);
|
|
17054
|
+
if (!field) continue;
|
|
17055
|
+
const link = { owner, field, table: jt.table_name, unique: linkUniqueName(jt.table_name) };
|
|
17056
|
+
links.push(link);
|
|
17057
|
+
const error = checkLinkTable(link, jt.left_column, jt.right_column, jt.right_table);
|
|
17058
|
+
if (error) errors.push(error);
|
|
17059
|
+
}
|
|
17060
|
+
}
|
|
17061
|
+
errors.push(...checkLinkUniques(registry, links));
|
|
17062
|
+
return errors;
|
|
17063
|
+
}
|
|
16812
17064
|
function generateFileHeader() {
|
|
16813
17065
|
return "import * as s from 'drizzle-orm/pg-core'\nimport { sql } from 'drizzle-orm'";
|
|
16814
17066
|
}
|
|
@@ -16943,7 +17195,7 @@ ${colBlock2}
|
|
|
16943
17195
|
const constraints = enumFields.map((f) => {
|
|
16944
17196
|
const col = f.db_column;
|
|
16945
17197
|
const values = col.check_constraint.map((v2) => `'${v2}'`).join(", ");
|
|
16946
|
-
const cname =
|
|
17198
|
+
const cname = checkConstraintName(col.column_name);
|
|
16947
17199
|
return [
|
|
16948
17200
|
` ${cname}: s.check(`,
|
|
16949
17201
|
` '${cname}',`,
|
|
@@ -17005,7 +17257,10 @@ function generateJunctionTables(registry) {
|
|
|
17005
17257
|
` .notNull()`,
|
|
17006
17258
|
` .references(() => ${rightVar}.id, { onDelete: 'cascade' }),`,
|
|
17007
17259
|
...orderLine ? [orderLine] : [],
|
|
17008
|
-
` }
|
|
17260
|
+
` },`,
|
|
17261
|
+
` (table) => ({`,
|
|
17262
|
+
` pair: s.unique('${linkUniqueName(jt.table_name)}').on(table.${jt.left_column}, table.${jt.right_column}),`,
|
|
17263
|
+
` })`,
|
|
17009
17264
|
`)`
|
|
17010
17265
|
].join("\n");
|
|
17011
17266
|
tables.push(def);
|
|
@@ -17244,7 +17499,14 @@ async function runGenerate(configPath, outFolder, cwd) {
|
|
|
17244
17499
|
} catch {
|
|
17245
17500
|
}
|
|
17246
17501
|
const beforeSet = new Set(beforeFiles);
|
|
17247
|
-
|
|
17502
|
+
const added = afterFiles.filter((f) => !beforeSet.has(f));
|
|
17503
|
+
for (const name of added) {
|
|
17504
|
+
const file = path.join(outFolder, name);
|
|
17505
|
+
const text2 = await fs.readFile(file, "utf8");
|
|
17506
|
+
const rewritten = addLinkDedupe(text2);
|
|
17507
|
+
if (rewritten !== text2) await fs.writeFile(file, rewritten);
|
|
17508
|
+
}
|
|
17509
|
+
return added;
|
|
17248
17510
|
}
|
|
17249
17511
|
async function generateMigration(configPath, migrationsFolder) {
|
|
17250
17512
|
return runGenerate(configPath, migrationsFolder, path.dirname(configPath));
|
|
@@ -18678,6 +18940,14 @@ function resolveSchemaConfig(cwd, config2) {
|
|
|
18678
18940
|
};
|
|
18679
18941
|
}
|
|
18680
18942
|
|
|
18943
|
+
// src/utils/schema-checks.ts
|
|
18944
|
+
init_esm_shims();
|
|
18945
|
+
function checkRegistry(registry, maxFileSize) {
|
|
18946
|
+
const crossRefErrors = validateCrossReferences(registry, maxFileSize);
|
|
18947
|
+
if (crossRefErrors.length > 0) return crossRefErrors;
|
|
18948
|
+
return findConstraintNameCollisions(registry);
|
|
18949
|
+
}
|
|
18950
|
+
|
|
18681
18951
|
// src/utils/env.ts
|
|
18682
18952
|
init_esm_shims();
|
|
18683
18953
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -18765,7 +19035,7 @@ async function runBuild(options, deps) {
|
|
|
18765
19035
|
parsedRoles
|
|
18766
19036
|
);
|
|
18767
19037
|
printSchemaDeprecations(findSchemaDeprecations(registry));
|
|
18768
|
-
const crossRefErrors =
|
|
19038
|
+
const crossRefErrors = checkRegistry(registry, config2.api.media?.max_file_size);
|
|
18769
19039
|
if (crossRefErrors.length > 0) {
|
|
18770
19040
|
printValidationErrors(crossRefErrors, "Schema reference errors", "manguito build");
|
|
18771
19041
|
process.exit(1);
|
|
@@ -18799,6 +19069,11 @@ async function runBuild(options, deps) {
|
|
|
18799
19069
|
{ adminPrefix: config2.admin.prefix, apiPrefix: config2.api.prefix },
|
|
18800
19070
|
generatedDir
|
|
18801
19071
|
);
|
|
19072
|
+
writeFileSync(
|
|
19073
|
+
join7(generatedDir, VERSIONS_RECORD),
|
|
19074
|
+
JSON.stringify(installedManguitoVersions(cwd), null, 2) + "\n",
|
|
19075
|
+
"utf8"
|
|
19076
|
+
);
|
|
18802
19077
|
printSuccess("Codegen complete");
|
|
18803
19078
|
try {
|
|
18804
19079
|
await viteBuild({
|
|
@@ -18847,11 +19122,76 @@ async function runBuild(options, deps) {
|
|
|
18847
19122
|
printSuccess("Server compiled");
|
|
18848
19123
|
process.stdout.write("\nBuild complete \u2192 dist/\n");
|
|
18849
19124
|
}
|
|
18850
|
-
|
|
19125
|
+
var VERSIONS_RECORD = "manguito-versions.json";
|
|
19126
|
+
var MANGUITO_PACKAGES = [
|
|
19127
|
+
"@bobbykim/manguito-cms-core",
|
|
19128
|
+
"@bobbykim/manguito-cms-db",
|
|
19129
|
+
"@bobbykim/manguito-cms-api",
|
|
19130
|
+
"@bobbykim/manguito-cms-admin",
|
|
19131
|
+
"@bobbykim/manguito-cms-cli"
|
|
19132
|
+
];
|
|
19133
|
+
function installedManguitoVersions(cwd) {
|
|
19134
|
+
const cwdRequire = createRequire(join7(cwd, "package.json"));
|
|
19135
|
+
const versions = {};
|
|
19136
|
+
for (const name of MANGUITO_PACKAGES) {
|
|
19137
|
+
try {
|
|
19138
|
+
let dir = dirname(cwdRequire.resolve(name));
|
|
19139
|
+
for (; ; ) {
|
|
19140
|
+
const pkgPath = join7(dir, "package.json");
|
|
19141
|
+
if (existsSync3(pkgPath)) {
|
|
19142
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
|
|
19143
|
+
if (pkg.name === name && typeof pkg.version === "string") versions[name] = pkg.version;
|
|
19144
|
+
break;
|
|
19145
|
+
}
|
|
19146
|
+
const parent = dirname(dir);
|
|
19147
|
+
if (parent === dir) break;
|
|
19148
|
+
dir = parent;
|
|
19149
|
+
}
|
|
19150
|
+
} catch {
|
|
19151
|
+
}
|
|
19152
|
+
}
|
|
19153
|
+
const running = runningCliVersion();
|
|
19154
|
+
if (running !== void 0) versions[RUNNING_CLI_KEY] = running;
|
|
19155
|
+
return versions;
|
|
19156
|
+
}
|
|
19157
|
+
var RUNNING_CLI_KEY = "running-cli";
|
|
19158
|
+
var CLI_PACKAGE = "@bobbykim/manguito-cms-cli";
|
|
19159
|
+
function runningCliVersion() {
|
|
19160
|
+
try {
|
|
19161
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
19162
|
+
for (; ; ) {
|
|
19163
|
+
const pkgPath = join7(dir, "package.json");
|
|
19164
|
+
if (existsSync3(pkgPath)) {
|
|
19165
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
|
|
19166
|
+
if (pkg.name === CLI_PACKAGE && typeof pkg.version === "string") return pkg.version;
|
|
19167
|
+
}
|
|
19168
|
+
const parent = dirname(dir);
|
|
19169
|
+
if (parent === dir) return void 0;
|
|
19170
|
+
dir = parent;
|
|
19171
|
+
}
|
|
19172
|
+
} catch {
|
|
19173
|
+
return void 0;
|
|
19174
|
+
}
|
|
19175
|
+
}
|
|
19176
|
+
function recordedVersions(cwd) {
|
|
19177
|
+
try {
|
|
19178
|
+
return JSON.parse(readFileSync2(resolve3(cwd, "dist/generated", VERSIONS_RECORD), "utf8"));
|
|
19179
|
+
} catch {
|
|
19180
|
+
return null;
|
|
19181
|
+
}
|
|
19182
|
+
}
|
|
19183
|
+
function sameVersions(a2, b2) {
|
|
19184
|
+
const keys = Object.keys(a2);
|
|
19185
|
+
return keys.length === Object.keys(b2).length && keys.every((k) => a2[k] === b2[k]);
|
|
19186
|
+
}
|
|
19187
|
+
async function needsRebuild(cwd, options = {}) {
|
|
19188
|
+
const installed = options.installed ?? installedManguitoVersions(cwd);
|
|
18851
19189
|
const sentinel = resolve3(cwd, "dist/generated/schema.ts");
|
|
18852
19190
|
if (!existsSync3(sentinel)) return true;
|
|
19191
|
+
const recorded = recordedVersions(cwd);
|
|
19192
|
+
if (recorded === null || !sameVersions(recorded, installed)) return true;
|
|
18853
19193
|
const sentinelMtime = statSync(sentinel).mtimeMs;
|
|
18854
|
-
const schemasDir = resolve3(cwd, "schemas");
|
|
19194
|
+
const schemasDir = options.schemaDir ?? resolve3(cwd, "schemas");
|
|
18855
19195
|
if (!existsSync3(schemasDir)) return false;
|
|
18856
19196
|
return walkFiles(schemasDir).some(
|
|
18857
19197
|
(file) => statSync(file).mtimeMs > sentinelMtime
|
|
@@ -23044,9 +23384,9 @@ async function loadRelationsForEdit(db, registry, fields, id, row) {
|
|
|
23044
23384
|
for (const entry of loaded) if (entry) row[entry[0]] = entry[1];
|
|
23045
23385
|
}
|
|
23046
23386
|
async function valuesForPublishCheck(db, registry, requiredFields, id, storedLabels, body) {
|
|
23047
|
-
const
|
|
23048
|
-
if (db) await loadRelationsForEdit(db, registry, requiredFields.filter((f) => !Object.hasOwn(body, f.name)), id,
|
|
23049
|
-
return { ...
|
|
23387
|
+
const stored2 = { ...storedLabels };
|
|
23388
|
+
if (db) await loadRelationsForEdit(db, registry, requiredFields.filter((f) => !Object.hasOwn(body, f.name)), id, stored2);
|
|
23389
|
+
return { ...stored2, ...body };
|
|
23050
23390
|
}
|
|
23051
23391
|
async function persistRelationFields(db, registry, fields, body, itemId, ownerTable) {
|
|
23052
23392
|
const deltas = [];
|
|
@@ -24687,7 +25027,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
24687
25027
|
}
|
|
24688
25028
|
var API_VERSION = (() => {
|
|
24689
25029
|
try {
|
|
24690
|
-
return "0.9.
|
|
25030
|
+
return "0.9.4";
|
|
24691
25031
|
} catch {
|
|
24692
25032
|
return "0.0.0";
|
|
24693
25033
|
}
|
|
@@ -25370,7 +25710,7 @@ function createCmsApp(options) {
|
|
|
25370
25710
|
handler: null,
|
|
25371
25711
|
error: null
|
|
25372
25712
|
}));
|
|
25373
|
-
const ready = import("./handler-
|
|
25713
|
+
const ready = import("./handler-5OTVPIAF-JVSJWO2S.js").then(({ createGraphQLHandler }) => {
|
|
25374
25714
|
gqlPasses.forEach((pass, i) => {
|
|
25375
25715
|
const mount = mounts[i];
|
|
25376
25716
|
try {
|
|
@@ -25656,6 +25996,7 @@ async function runDev(options, deps) {
|
|
|
25656
25996
|
const { registry, versionModel } = loaded.value;
|
|
25657
25997
|
await generateDrizzleConfig(config2, manguitoDir);
|
|
25658
25998
|
await writeFile9(join11(manguitoDir, "schema.ts"), generateSchemaFile(registry), "utf8");
|
|
25999
|
+
await addLinkConstraints(db.getDb(), registry);
|
|
25659
26000
|
await runDevMigration(drizzleConfigPath);
|
|
25660
26001
|
printSuccess(needsSeed ? "Schema tables created" : "Schema tables up to date");
|
|
25661
26002
|
if (needsSeed) {
|
|
@@ -25851,6 +26192,13 @@ async function onSchemaFileChange(args) {
|
|
|
25851
26192
|
await generateRoutes2(registry, manguitoDir);
|
|
25852
26193
|
await generateForms(registry, join11(manguitoDir, "forms"));
|
|
25853
26194
|
await generateNav(registry, manguitoDir);
|
|
26195
|
+
try {
|
|
26196
|
+
await addLinkConstraints(db.getDb(), registry);
|
|
26197
|
+
} catch (err) {
|
|
26198
|
+
process.stderr.write(`\u26A0 Adding link constraints failed: ${err instanceof Error ? err.message : String(err)}
|
|
26199
|
+
`);
|
|
26200
|
+
return;
|
|
26201
|
+
}
|
|
25854
26202
|
try {
|
|
25855
26203
|
await runDevMigration(drizzleConfigPath);
|
|
25856
26204
|
} catch (err) {
|
|
@@ -25875,11 +26223,17 @@ async function onSchemaFileChange(args) {
|
|
|
25875
26223
|
updateFetch((req) => newAdapter.app.fetch(req));
|
|
25876
26224
|
printSuccess("Schema updated");
|
|
25877
26225
|
}
|
|
26226
|
+
async function addLinkConstraints(database, registry) {
|
|
26227
|
+
const results = await ensureLinkConstraints(database, registry);
|
|
26228
|
+
for (const { table, removed } of results) {
|
|
26229
|
+
if (removed > 0) printSuccess(`Removed ${removed} duplicate links from ${table}`);
|
|
26230
|
+
}
|
|
26231
|
+
}
|
|
25878
26232
|
function loadValidatedSchema(cwd, config2) {
|
|
25879
26233
|
const parsed = parseAllSchemas(cwd, config2);
|
|
25880
26234
|
if (!parsed.ok) return parsed;
|
|
25881
26235
|
const registry = parsed.value;
|
|
25882
|
-
const crossRefErrors =
|
|
26236
|
+
const crossRefErrors = checkRegistry(registry, config2.api.media?.max_file_size);
|
|
25883
26237
|
if (crossRefErrors.length > 0) return { ok: false, errors: crossRefErrors };
|
|
25884
26238
|
const versions = loadProjectVersionModel(resolveSchemaConfig(cwd, config2), registry);
|
|
25885
26239
|
if (!versions.ok) return { ok: false, errors: versions.errors };
|
|
@@ -26065,7 +26419,7 @@ function buildDeps(options, cwd) {
|
|
|
26065
26419
|
loadEnvFile(options.env);
|
|
26066
26420
|
return {
|
|
26067
26421
|
buildRunner: async () => runBuild(options.env ? { env: options.env } : {}, { cwd }),
|
|
26068
|
-
needsRebuild: () => needsRebuild(cwd),
|
|
26422
|
+
needsRebuild: (schemaDir) => needsRebuild(cwd, { schemaDir }),
|
|
26069
26423
|
db: createPostgresAdapter(),
|
|
26070
26424
|
migrationsFolder: resolve8(cwd, "./migrations"),
|
|
26071
26425
|
configPath: resolve8(cwd, "dist/generated/drizzle.config.ts"),
|
|
@@ -26133,7 +26487,7 @@ async function runMigrate(options, deps) {
|
|
|
26133
26487
|
printGuidedError("DB_URL not set or database unreachable");
|
|
26134
26488
|
process.exit(1);
|
|
26135
26489
|
}
|
|
26136
|
-
if (await deps.needsRebuild()) {
|
|
26490
|
+
if (await deps.needsRebuild(resolve8(cwd, config2.schema.base_path))) {
|
|
26137
26491
|
try {
|
|
26138
26492
|
await deps.buildRunner();
|
|
26139
26493
|
} catch (err) {
|
|
@@ -26319,7 +26673,7 @@ async function runValidate(options, deps) {
|
|
|
26319
26673
|
if (allErrors.length === 0 && parsedRoles !== null && parsedRoutesDef !== null) {
|
|
26320
26674
|
const registry = buildSchemaRegistry(parsedSchemas, parsedRoutesDef, parsedRoles);
|
|
26321
26675
|
printSchemaDeprecations(findSchemaDeprecations(registry));
|
|
26322
|
-
const crossRefErrors =
|
|
26676
|
+
const crossRefErrors = checkRegistry(registry, config2.api.media?.max_file_size);
|
|
26323
26677
|
allErrors.push(...crossRefErrors);
|
|
26324
26678
|
if (crossRefErrors.length === 0) {
|
|
26325
26679
|
const versions = loadProjectVersionModel(resolveSchemaConfig(cwd, config2), registry);
|
|
@@ -26635,7 +26989,7 @@ function loadWorkingRegistry(cwd, config2, command) {
|
|
|
26635
26989
|
process.exit(1);
|
|
26636
26990
|
}
|
|
26637
26991
|
const registry = buildSchemaRegistry(parsedSchemas, parsedRoutes, parsedRoles);
|
|
26638
|
-
const crossRefErrors =
|
|
26992
|
+
const crossRefErrors = checkRegistry(registry, config2.api.media?.max_file_size);
|
|
26639
26993
|
if (crossRefErrors.length > 0) {
|
|
26640
26994
|
printValidationErrors(crossRefErrors, "Schema reference errors", command);
|
|
26641
26995
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobbykim/manguito-cms-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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.6.
|
|
33
|
-
"@bobbykim/manguito-cms-
|
|
34
|
-
"@bobbykim/manguito-cms-db": "0.
|
|
35
|
-
"@bobbykim/manguito-cms-
|
|
32
|
+
"@bobbykim/manguito-cms-admin": "0.6.3",
|
|
33
|
+
"@bobbykim/manguito-cms-core": "0.11.1",
|
|
34
|
+
"@bobbykim/manguito-cms-db": "0.3.0",
|
|
35
|
+
"@bobbykim/manguito-cms-api": "0.9.4"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^25.9.5",
|