@bobbykim/manguito-cms-cli 0.10.1 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -9270,7 +9270,7 @@ var require_bcryptjs = __commonJS({
|
|
|
9270
9270
|
}
|
|
9271
9271
|
});
|
|
9272
9272
|
|
|
9273
|
-
// ../api/dist/chunk-
|
|
9273
|
+
// ../api/dist/chunk-GUZZQZSN.mjs
|
|
9274
9274
|
init_esm_shims();
|
|
9275
9275
|
import crypto from "crypto";
|
|
9276
9276
|
|
|
@@ -25795,7 +25795,8 @@ var fieldTypeRegistry = {
|
|
|
25795
25795
|
left_column: "left_id",
|
|
25796
25796
|
right_column: "right_id",
|
|
25797
25797
|
right_table: targetTableName,
|
|
25798
|
-
|
|
25798
|
+
// Every list keeps the order the editor gave it (#57).
|
|
25799
|
+
order_column: true
|
|
25799
25800
|
}
|
|
25800
25801
|
} : {
|
|
25801
25802
|
// FK column on the owning table. An optional reference is cleared when
|
|
@@ -26617,7 +26618,7 @@ function describeSchemaChange(input) {
|
|
|
26617
26618
|
return { from: from?.version ?? null, to: to.version, types, identical };
|
|
26618
26619
|
}
|
|
26619
26620
|
|
|
26620
|
-
// ../api/dist/chunk-
|
|
26621
|
+
// ../api/dist/chunk-GUZZQZSN.mjs
|
|
26621
26622
|
function isColumnBacked2(field) {
|
|
26622
26623
|
const col = field.db_column;
|
|
26623
26624
|
if (col === null) return false;
|
|
@@ -26845,10 +26846,28 @@ async function persistJunctionField(db, itemId, junction, relatedIds) {
|
|
|
26845
26846
|
const tbl = sql.raw(quoteIdent(junction.table_name));
|
|
26846
26847
|
const left = sql.raw(quoteIdent(junction.left_column));
|
|
26847
26848
|
const right = sql.raw(quoteIdent(junction.right_column));
|
|
26849
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26850
|
+
const ids = relatedIds.filter((id) => {
|
|
26851
|
+
const key = id.toLowerCase();
|
|
26852
|
+
if (seen.has(key)) return false;
|
|
26853
|
+
seen.add(key);
|
|
26854
|
+
return true;
|
|
26855
|
+
});
|
|
26848
26856
|
await db.execute(sql`DELETE FROM ${tbl} WHERE ${left} = ${itemId}`);
|
|
26849
|
-
|
|
26850
|
-
|
|
26851
|
-
|
|
26857
|
+
if (ids.length === 0) return;
|
|
26858
|
+
const rows = sql.join(
|
|
26859
|
+
ids.map(
|
|
26860
|
+
(rid, position) => junction.order_column ? sql`(${itemId}, ${rid}, ${position})` : sql`(${itemId}, ${rid})`
|
|
26861
|
+
),
|
|
26862
|
+
sql`, `
|
|
26863
|
+
);
|
|
26864
|
+
await db.execute(
|
|
26865
|
+
junction.order_column ? sql`INSERT INTO ${tbl} (${left}, ${right}, "order") VALUES ${rows}` : sql`INSERT INTO ${tbl} (${left}, ${right}) VALUES ${rows}`
|
|
26866
|
+
);
|
|
26867
|
+
}
|
|
26868
|
+
function junctionOrderBy(junction) {
|
|
26869
|
+
const right = sql.raw(quoteIdent(junction.right_column));
|
|
26870
|
+
return junction.order_column ? sql` ORDER BY "order" ASC, ${right} ASC` : sql` ORDER BY ${right} ASC`;
|
|
26852
26871
|
}
|
|
26853
26872
|
function oneOrMany(cardinality, list) {
|
|
26854
26873
|
return cardinality === "one" ? list[0] ?? null : list;
|
|
@@ -26950,9 +26969,8 @@ async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOn
|
|
|
26950
26969
|
} else if (rel.type === "junction") {
|
|
26951
26970
|
const parentIds = batch.map((r) => r["id"]);
|
|
26952
26971
|
const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `);
|
|
26953
|
-
const orderBy = rel.order_column ? sql` ORDER BY "order" ASC` : sql``;
|
|
26954
26972
|
const junctionResult = await db.execute(
|
|
26955
|
-
sql`SELECT * FROM ${sql.raw(quoteIdent(rel.junction_table))} WHERE ${sql.raw(quoteIdent(rel.left_column))} IN (${inList})${
|
|
26973
|
+
sql`SELECT * FROM ${sql.raw(quoteIdent(rel.junction_table))} WHERE ${sql.raw(quoteIdent(rel.left_column))} IN (${inList})${junctionOrderBy(rel)}`
|
|
26956
26974
|
);
|
|
26957
26975
|
const jRows = junctionResult.rows;
|
|
26958
26976
|
const rightIds = [...new Set(jRows.map((r) => r[rel.right_column]))];
|
|
@@ -27011,7 +27029,7 @@ async function resolveRelationBareIds(db, rows, fieldName, rel) {
|
|
|
27011
27029
|
const parentIds = rows.map((r) => r["id"]);
|
|
27012
27030
|
const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `);
|
|
27013
27031
|
const result = await db.execute(
|
|
27014
|
-
sql`SELECT ${sql.raw(quoteIdent(rel.left_column))} AS left_id, ${sql.raw(quoteIdent(rel.right_column))} AS right_id FROM ${sql.raw(quoteIdent(rel.junction_table))} WHERE ${sql.raw(quoteIdent(rel.left_column))} IN (${inList})`
|
|
27032
|
+
sql`SELECT ${sql.raw(quoteIdent(rel.left_column))} AS left_id, ${sql.raw(quoteIdent(rel.right_column))} AS right_id FROM ${sql.raw(quoteIdent(rel.junction_table))} WHERE ${sql.raw(quoteIdent(rel.left_column))} IN (${inList})${junctionOrderBy(rel)}`
|
|
27015
27033
|
);
|
|
27016
27034
|
const byLeft = groupBy(result.rows, "left_id");
|
|
27017
27035
|
for (const row of rows) {
|
|
@@ -27071,6 +27089,7 @@ export {
|
|
|
27071
27089
|
persistParagraphField,
|
|
27072
27090
|
deleteParagraphField,
|
|
27073
27091
|
persistJunctionField,
|
|
27092
|
+
junctionOrderBy,
|
|
27074
27093
|
oneOrMany,
|
|
27075
27094
|
paragraphItems,
|
|
27076
27095
|
buildRelationsMap,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
isColumnBacked,
|
|
13
13
|
relationCardinality,
|
|
14
14
|
resolveRelationField
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-WTZBHFCV.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-MFF32JB4.mjs
|
|
10791
10791
|
init_esm_shims();
|
|
10792
10792
|
|
|
10793
|
-
// ../api/dist/chunk-
|
|
10793
|
+
// ../api/dist/chunk-ITOSMGDT.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-ITOSMGDT.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-ITOSMGDT.mjs
|
|
30885
30885
|
var DateTimeScalar = new GraphQLScalarType({
|
|
30886
30886
|
name: "DateTime",
|
|
30887
30887
|
description: "ISO-8601 date-time string",
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
isColumnBacked,
|
|
38
38
|
isDriverValueEncoder,
|
|
39
39
|
isSQLWrapper,
|
|
40
|
+
junctionOrderBy,
|
|
40
41
|
loadSchemaFile,
|
|
41
42
|
loadVersionSnapshots,
|
|
42
43
|
oneOrMany,
|
|
@@ -54,7 +55,7 @@ import {
|
|
|
54
55
|
validateCrossReferences,
|
|
55
56
|
verifyPassword,
|
|
56
57
|
walkSchemaDirectory
|
|
57
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-WTZBHFCV.js";
|
|
58
59
|
|
|
59
60
|
// ../../node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js
|
|
60
61
|
var require_postgres_array = __commonJS({
|
|
@@ -17216,27 +17217,59 @@ async function runDevMigration(configPath) {
|
|
|
17216
17217
|
env: resolvedPathEnv()
|
|
17217
17218
|
});
|
|
17218
17219
|
}
|
|
17219
|
-
async function
|
|
17220
|
+
async function runGenerate(configPath, outFolder, cwd) {
|
|
17220
17221
|
let beforeFiles = [];
|
|
17221
17222
|
try {
|
|
17222
|
-
const entries = await fs.readdir(
|
|
17223
|
+
const entries = await fs.readdir(outFolder);
|
|
17223
17224
|
beforeFiles = entries.filter((f) => f.endsWith(".sql"));
|
|
17224
17225
|
} catch {
|
|
17225
17226
|
}
|
|
17226
17227
|
execSync(`drizzle-kit generate --config=${configPath}`, {
|
|
17227
17228
|
stdio: "inherit",
|
|
17228
|
-
cwd
|
|
17229
|
+
cwd,
|
|
17229
17230
|
env: resolvedPathEnv()
|
|
17230
17231
|
});
|
|
17231
17232
|
let afterFiles = [];
|
|
17232
17233
|
try {
|
|
17233
|
-
const entries = await fs.readdir(
|
|
17234
|
+
const entries = await fs.readdir(outFolder);
|
|
17234
17235
|
afterFiles = entries.filter((f) => f.endsWith(".sql"));
|
|
17235
17236
|
} catch {
|
|
17236
17237
|
}
|
|
17237
17238
|
const beforeSet = new Set(beforeFiles);
|
|
17238
17239
|
return afterFiles.filter((f) => !beforeSet.has(f));
|
|
17239
17240
|
}
|
|
17241
|
+
async function generateMigration(configPath, migrationsFolder) {
|
|
17242
|
+
return runGenerate(configPath, migrationsFolder, path.dirname(configPath));
|
|
17243
|
+
}
|
|
17244
|
+
async function previewMigration(configPath, migrationsFolder) {
|
|
17245
|
+
const configDir = path.dirname(configPath);
|
|
17246
|
+
const tmp = await fs.mkdtemp(path.join(configDir, ".migration-preview-"));
|
|
17247
|
+
try {
|
|
17248
|
+
const out = path.join(tmp, "migrations");
|
|
17249
|
+
try {
|
|
17250
|
+
await fs.cp(migrationsFolder, out, { recursive: true });
|
|
17251
|
+
} catch (err) {
|
|
17252
|
+
if (err.code !== "ENOENT") throw err;
|
|
17253
|
+
}
|
|
17254
|
+
const base = `./${path.basename(configPath).replace(/\.[cm]?[jt]s$/, "")}`;
|
|
17255
|
+
const tmpConfig = path.join(tmp, "drizzle.config.ts");
|
|
17256
|
+
await fs.writeFile(
|
|
17257
|
+
tmpConfig,
|
|
17258
|
+
[
|
|
17259
|
+
`import base from '.${base}'`,
|
|
17260
|
+
// Relative to the real config's folder, where drizzle-kit runs.
|
|
17261
|
+
`export default { ...base, out: './${path.relative(configDir, out).split(path.sep).join("/")}' }`,
|
|
17262
|
+
""
|
|
17263
|
+
].join("\n")
|
|
17264
|
+
);
|
|
17265
|
+
const names = await runGenerate(tmpConfig, out, configDir);
|
|
17266
|
+
return await Promise.all(
|
|
17267
|
+
names.map(async (name) => ({ name, sql: await fs.readFile(path.join(out, name), "utf8") }))
|
|
17268
|
+
);
|
|
17269
|
+
} finally {
|
|
17270
|
+
await fs.rm(tmp, { recursive: true, force: true });
|
|
17271
|
+
}
|
|
17272
|
+
}
|
|
17240
17273
|
async function applyMigrations(configPath, db, options) {
|
|
17241
17274
|
const { migrationsTable, migrationsFolder } = options;
|
|
17242
17275
|
await db.execute(
|
|
@@ -17378,11 +17411,14 @@ function formatAlterColumnType(line2) {
|
|
|
17378
17411
|
return `ALTER COLUMN ${tableName}.${colName} TYPE ${newType}`;
|
|
17379
17412
|
}
|
|
17380
17413
|
function scanMigrationFiles(filePaths) {
|
|
17414
|
+
return scanMigrationSql(
|
|
17415
|
+
filePaths.map((filePath) => ({ name: path2.basename(filePath), sql: readFileSync(filePath, "utf-8") }))
|
|
17416
|
+
);
|
|
17417
|
+
}
|
|
17418
|
+
function scanMigrationSql(migrations) {
|
|
17381
17419
|
const operations = [];
|
|
17382
|
-
for (const
|
|
17383
|
-
const content = readFileSync(filePath, "utf-8");
|
|
17420
|
+
for (const { name: filename, sql: content } of migrations) {
|
|
17384
17421
|
const lines = content.split("\n");
|
|
17385
|
-
const filename = path2.basename(filePath);
|
|
17386
17422
|
for (const line2 of lines) {
|
|
17387
17423
|
const upper = line2.trim().toUpperCase();
|
|
17388
17424
|
if (upper.includes("DROP COLUMN")) {
|
|
@@ -22848,8 +22884,7 @@ function checkRelationInput(fields, body, registry, path5 = "") {
|
|
|
22848
22884
|
});
|
|
22849
22885
|
continue;
|
|
22850
22886
|
}
|
|
22851
|
-
const
|
|
22852
|
-
const pType = comp.component === "paragraph-embed" ? registry.paragraph_types[comp.ref] : void 0;
|
|
22887
|
+
const pType = paragraphTypeOf(field, registry);
|
|
22853
22888
|
if (!pType) continue;
|
|
22854
22889
|
items.forEach((item, i) => {
|
|
22855
22890
|
const prefix = cardinality === "one" ? `${name}.` : `${name}[${i}].`;
|
|
@@ -22858,12 +22893,15 @@ function checkRelationInput(fields, body, registry, path5 = "") {
|
|
|
22858
22893
|
}
|
|
22859
22894
|
return errors;
|
|
22860
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
|
+
}
|
|
22861
22900
|
function paragraphItemsIn(fields, body, registry, path5) {
|
|
22862
22901
|
const out = [];
|
|
22863
22902
|
for (const field of fields) {
|
|
22864
22903
|
if (field.field_type !== "paragraph" || !(field.name in body)) continue;
|
|
22865
|
-
const
|
|
22866
|
-
const pType = comp.component === "paragraph-embed" ? registry.paragraph_types[comp.ref] : void 0;
|
|
22904
|
+
const pType = paragraphTypeOf(field, registry);
|
|
22867
22905
|
if (!pType) continue;
|
|
22868
22906
|
const value = body[field.name];
|
|
22869
22907
|
const name = `${path5}${field.name}`;
|
|
@@ -22934,6 +22972,131 @@ async function findMissingReferences(db, fields, body, registry) {
|
|
|
22934
22972
|
const found = new Map(await Promise.all(lookups));
|
|
22935
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.` }));
|
|
22936
22974
|
}
|
|
22975
|
+
var VARCHAR_DEFAULT_LIMIT = 255;
|
|
22976
|
+
var INTEGER_MIN = -2147483648;
|
|
22977
|
+
var INTEGER_MAX = 2147483647;
|
|
22978
|
+
var DECIMAL_LIMIT = 1e6;
|
|
22979
|
+
var ISO_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
22980
|
+
var DECIMAL_STRING = /^-?\d+(?:\.\d+)?$/;
|
|
22981
|
+
var EMPTY_MEANS_NULL = /* @__PURE__ */ new Set(["date", "integer", "float", "enum"]);
|
|
22982
|
+
function emptyValuesToNull(fields, body, registry) {
|
|
22983
|
+
const out = { ...body };
|
|
22984
|
+
for (const f of fields) {
|
|
22985
|
+
if (!(f.name in out)) continue;
|
|
22986
|
+
const value = out[f.name];
|
|
22987
|
+
if (value === "" && EMPTY_MEANS_NULL.has(f.field_type)) {
|
|
22988
|
+
out[f.name] = null;
|
|
22989
|
+
continue;
|
|
22990
|
+
}
|
|
22991
|
+
if (f.field_type !== "paragraph") continue;
|
|
22992
|
+
const pType = paragraphTypeOf(f, registry);
|
|
22993
|
+
if (!pType) continue;
|
|
22994
|
+
if (isPlainObject(value)) out[f.name] = emptyValuesToNull(pType.fields, value, registry);
|
|
22995
|
+
else if (Array.isArray(value)) {
|
|
22996
|
+
out[f.name] = value.map((item) => isPlainObject(item) ? emptyValuesToNull(pType.fields, item, registry) : item);
|
|
22997
|
+
}
|
|
22998
|
+
}
|
|
22999
|
+
return out;
|
|
23000
|
+
}
|
|
23001
|
+
var compiled = /* @__PURE__ */ new Map();
|
|
23002
|
+
function patternOf(source) {
|
|
23003
|
+
if (!compiled.has(source)) {
|
|
23004
|
+
let re;
|
|
23005
|
+
try {
|
|
23006
|
+
re = new RegExp(source);
|
|
23007
|
+
} catch {
|
|
23008
|
+
re = null;
|
|
23009
|
+
}
|
|
23010
|
+
compiled.set(source, re);
|
|
23011
|
+
}
|
|
23012
|
+
return compiled.get(source) ?? null;
|
|
23013
|
+
}
|
|
23014
|
+
function isRealDate(value) {
|
|
23015
|
+
const m2 = ISO_TIMESTAMP.exec(value);
|
|
23016
|
+
if (!m2) return false;
|
|
23017
|
+
const [y, mo, d2, h = 0, mi2 = 0, sec = 0] = m2.slice(1).map((part) => part === void 0 ? void 0 : Number(part));
|
|
23018
|
+
if (y === void 0 || mo === void 0 || d2 === void 0 || y < 1) return false;
|
|
23019
|
+
const date2 = new Date(Date.UTC(y, mo - 1, d2));
|
|
23020
|
+
date2.setUTCFullYear(y);
|
|
23021
|
+
return date2.getUTCMonth() === mo - 1 && date2.getUTCDate() === d2 && h <= 23 && mi2 <= 59 && sec <= 59;
|
|
23022
|
+
}
|
|
23023
|
+
function valueError(f, value, name) {
|
|
23024
|
+
const v2 = f.validation;
|
|
23025
|
+
if (Array.isArray(value) && (f.field_type === "paragraph" || f.field_type === "reference")) {
|
|
23026
|
+
if (v2.max_items !== void 0 && value.length > v2.max_items) {
|
|
23027
|
+
return `${name} can hold at most ${v2.max_items} items.`;
|
|
23028
|
+
}
|
|
23029
|
+
return null;
|
|
23030
|
+
}
|
|
23031
|
+
const col = f.db_column;
|
|
23032
|
+
if (!col || col.junction || col.column_type === "uuid") return null;
|
|
23033
|
+
if (value === null || value === void 0) {
|
|
23034
|
+
if (col.nullable) return null;
|
|
23035
|
+
return f.required ? `${name} is required.` : `${name} cannot be empty.`;
|
|
23036
|
+
}
|
|
23037
|
+
switch (f.field_type) {
|
|
23038
|
+
case "text/plain": {
|
|
23039
|
+
if (typeof value !== "string") return `${name} must be text.`;
|
|
23040
|
+
if (value.includes("\0")) return `${name} contains a character that cannot be stored (U+0000).`;
|
|
23041
|
+
const limit = v2.limit ?? VARCHAR_DEFAULT_LIMIT;
|
|
23042
|
+
if ([...value].length > limit) return `${name} can be at most ${limit} characters.`;
|
|
23043
|
+
const pattern = v2.pattern === void 0 ? null : patternOf(v2.pattern);
|
|
23044
|
+
if (value !== "" && pattern && !pattern.test(value)) return `${name} is not in the correct format.`;
|
|
23045
|
+
return null;
|
|
23046
|
+
}
|
|
23047
|
+
case "text/rich":
|
|
23048
|
+
if (typeof value !== "string") return `${name} must be text.`;
|
|
23049
|
+
return value.includes("\0") ? `${name} contains a character that cannot be stored (U+0000).` : null;
|
|
23050
|
+
case "integer":
|
|
23051
|
+
if (typeof value !== "number" || !Number.isInteger(value)) return `${name} must be a whole number.`;
|
|
23052
|
+
if (value < INTEGER_MIN || value > INTEGER_MAX) {
|
|
23053
|
+
return `${name} must be between ${INTEGER_MIN} and ${INTEGER_MAX}.`;
|
|
23054
|
+
}
|
|
23055
|
+
return rangeError(name, value, v2.min, v2.max);
|
|
23056
|
+
case "float": {
|
|
23057
|
+
const n = typeof value === "number" ? value : typeof value === "string" && DECIMAL_STRING.test(value) ? Number(value) : NaN;
|
|
23058
|
+
if (!Number.isFinite(n)) return `${name} must be a number.`;
|
|
23059
|
+
if (roundedSize(typeof value === "string" ? value : String(n)) >= DECIMAL_LIMIT) {
|
|
23060
|
+
return `${name} must be less than ${DECIMAL_LIMIT} in size.`;
|
|
23061
|
+
}
|
|
23062
|
+
return rangeError(name, n, v2.min, v2.max);
|
|
23063
|
+
}
|
|
23064
|
+
case "boolean":
|
|
23065
|
+
return typeof value === "boolean" ? null : `${name} must be true or false.`;
|
|
23066
|
+
case "date":
|
|
23067
|
+
return typeof value === "string" && isRealDate(value) ? null : `${name} must be a date, such as 2026-10-11 or 2026-10-11T09:30:00Z.`;
|
|
23068
|
+
case "enum": {
|
|
23069
|
+
const allowed = v2.allowed_values ?? [];
|
|
23070
|
+
return typeof value === "string" && allowed.includes(value) ? null : `${name} must be one of: ${allowed.join(", ")}.`;
|
|
23071
|
+
}
|
|
23072
|
+
default:
|
|
23073
|
+
return null;
|
|
23074
|
+
}
|
|
23075
|
+
}
|
|
23076
|
+
function roundedSize(text2) {
|
|
23077
|
+
if (text2.includes("e")) return Math.abs(Number(text2));
|
|
23078
|
+
const [int = "0", frac = ""] = text2.replace("-", "").split(".");
|
|
23079
|
+
const kept = Number(`${int}.${frac.slice(0, 4) || "0"}`);
|
|
23080
|
+
return frac.length > 4 && frac.charAt(4) >= "5" ? kept + 1e-4 : kept;
|
|
23081
|
+
}
|
|
23082
|
+
function rangeError(name, value, min, max) {
|
|
23083
|
+
if (min !== void 0 && value < min) return `${name} must be at least ${min}.`;
|
|
23084
|
+
if (max !== void 0 && value > max) return `${name} must be at most ${max}.`;
|
|
23085
|
+
return null;
|
|
23086
|
+
}
|
|
23087
|
+
function checkFieldValues(fields, body, registry, path5 = "") {
|
|
23088
|
+
const errors = [];
|
|
23089
|
+
for (const f of fields) {
|
|
23090
|
+
if (!(f.name in body) || f.removed === true) continue;
|
|
23091
|
+
const name = `${path5}${f.name}`;
|
|
23092
|
+
const message = valueError(f, body[f.name], name);
|
|
23093
|
+
if (message) errors.push({ field: name, message });
|
|
23094
|
+
}
|
|
23095
|
+
for (const { item, fields: itemFields, prefix } of paragraphItemsIn(fields, body, registry, path5)) {
|
|
23096
|
+
errors.push(...checkFieldValues(itemFields, item, registry, prefix));
|
|
23097
|
+
}
|
|
23098
|
+
return errors;
|
|
23099
|
+
}
|
|
22937
23100
|
function quoteIdent3(name) {
|
|
22938
23101
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`);
|
|
22939
23102
|
return `"${name}"`;
|
|
@@ -22969,7 +23132,7 @@ async function loadRelationsForEdit(db, registry, fields, id, row) {
|
|
|
22969
23132
|
} else if (f.db_column.junction) {
|
|
22970
23133
|
const j = f.db_column.junction;
|
|
22971
23134
|
const r = await db.execute(
|
|
22972
|
-
sql`SELECT ${sql.raw(quoteIdent3(j.right_column))} FROM ${sql.raw(quoteIdent3(j.table_name))} WHERE ${sql.raw(quoteIdent3(j.left_column))} = ${id}`
|
|
23135
|
+
sql`SELECT ${sql.raw(quoteIdent3(j.right_column))} FROM ${sql.raw(quoteIdent3(j.table_name))} WHERE ${sql.raw(quoteIdent3(j.left_column))} = ${id}${junctionOrderBy(j)}`
|
|
22973
23136
|
);
|
|
22974
23137
|
row[f.name] = r.rows.map((linked) => linked[j.right_column]);
|
|
22975
23138
|
}
|
|
@@ -23012,6 +23175,8 @@ async function relationInputError(c, fields, body, registry, db) {
|
|
|
23012
23175
|
const refuse = (message, details) => c.json({ ok: false, error: { code: "VALIDATION_ERROR", message, details } }, 422);
|
|
23013
23176
|
const shape = checkRelationInput(fields, body, registry);
|
|
23014
23177
|
if (shape.length > 0) return refuse("Relation fields have the wrong shape", shape);
|
|
23178
|
+
const values = checkFieldValues(fields, body, registry);
|
|
23179
|
+
if (values.length > 0) return refuse("Some values are not valid", values);
|
|
23015
23180
|
const required = checkRequiredInParagraphItems(fields, body, registry);
|
|
23016
23181
|
if (required.length > 0) return refuse("Required fields are missing", required);
|
|
23017
23182
|
if (db) {
|
|
@@ -23162,7 +23327,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23162
23327
|
`/admin/api/${basePath}`,
|
|
23163
23328
|
requirePermission("content:create"),
|
|
23164
23329
|
async (c) => {
|
|
23165
|
-
const body = await c.req.json();
|
|
23330
|
+
const body = emptyValuesToNull(contentType.fields, await c.req.json(), registry);
|
|
23166
23331
|
if (contentType.only_one) {
|
|
23167
23332
|
const existing = await repo.findMany({ page: 1, per_page: 1 });
|
|
23168
23333
|
if (existing.meta.total > 0) {
|
|
@@ -23289,7 +23454,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23289
23454
|
requirePermission("content:edit"),
|
|
23290
23455
|
async (c) => {
|
|
23291
23456
|
const id = c.req.param("id");
|
|
23292
|
-
const body = await c.req.json();
|
|
23457
|
+
const body = emptyValuesToNull(contentType.fields, await c.req.json(), registry);
|
|
23293
23458
|
const existing = await repo.findOne(id);
|
|
23294
23459
|
if (!existing) {
|
|
23295
23460
|
return c.json(
|
|
@@ -23396,7 +23561,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23396
23561
|
`/admin/api/${basePath}`,
|
|
23397
23562
|
requirePermission("content:edit"),
|
|
23398
23563
|
async (c) => {
|
|
23399
|
-
const body = await c.req.json();
|
|
23564
|
+
const body = emptyValuesToNull(contentType.fields, await c.req.json(), registry);
|
|
23400
23565
|
const existingResult = await repo.findMany({ page: 1, per_page: 1 });
|
|
23401
23566
|
const existing = existingResult.data[0];
|
|
23402
23567
|
if (!existing) {
|
|
@@ -23523,7 +23688,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23523
23688
|
`/admin/api/taxonomy/${typeName}`,
|
|
23524
23689
|
requirePermission("content:create"),
|
|
23525
23690
|
async (c) => {
|
|
23526
|
-
const body = await c.req.json();
|
|
23691
|
+
const body = emptyValuesToNull(taxonomyType.fields, await c.req.json(), registry);
|
|
23527
23692
|
const inputError = await relationInputError(c, taxonomyType.fields, body, registry, db);
|
|
23528
23693
|
if (inputError) return inputError;
|
|
23529
23694
|
const storageBody = fieldKeys.toStorage(body);
|
|
@@ -23571,7 +23736,7 @@ function registerAdminContentRoutes(app, registry, repos, projectors, mediaRepo,
|
|
|
23571
23736
|
requirePermission("content:edit"),
|
|
23572
23737
|
async (c) => {
|
|
23573
23738
|
const id = c.req.param("id");
|
|
23574
|
-
const body = await c.req.json();
|
|
23739
|
+
const body = emptyValuesToNull(taxonomyType.fields, await c.req.json(), registry);
|
|
23575
23740
|
const storageBody = fieldKeys.toStorage(body);
|
|
23576
23741
|
const existing = await repo.findOne(id);
|
|
23577
23742
|
if (!existing) {
|
|
@@ -24609,7 +24774,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
24609
24774
|
}
|
|
24610
24775
|
var API_VERSION = (() => {
|
|
24611
24776
|
try {
|
|
24612
|
-
return "0.
|
|
24777
|
+
return "0.9.1";
|
|
24613
24778
|
} catch {
|
|
24614
24779
|
return "0.0.0";
|
|
24615
24780
|
}
|
|
@@ -25292,7 +25457,7 @@ function createCmsApp(options) {
|
|
|
25292
25457
|
handler: null,
|
|
25293
25458
|
error: null
|
|
25294
25459
|
}));
|
|
25295
|
-
const ready = import("./handler-
|
|
25460
|
+
const ready = import("./handler-MFF32JB4-MEMKPHHQ.js").then(({ createGraphQLHandler }) => {
|
|
25296
25461
|
gqlPasses.forEach((pass, i) => {
|
|
25297
25462
|
const mount = mounts[i];
|
|
25298
25463
|
try {
|
|
@@ -25994,6 +26159,16 @@ function buildDeps(options, cwd) {
|
|
|
25994
26159
|
prompt: createPromptAdapter()
|
|
25995
26160
|
};
|
|
25996
26161
|
}
|
|
26162
|
+
function printDestructiveOperations(scan) {
|
|
26163
|
+
process.stdout.write("\u26A0 This migration contains destructive operations:\n");
|
|
26164
|
+
for (const op of scan.operations) process.stdout.write(` - ${op.operation}
|
|
26165
|
+
`);
|
|
26166
|
+
process.stdout.write("\n");
|
|
26167
|
+
}
|
|
26168
|
+
async function readPendingMigrations(db, migrationsTable, migrationsFolder) {
|
|
26169
|
+
const { pending } = await getMigrationStatus(db.getDb(), { migrationsTable, migrationsFolder });
|
|
26170
|
+
return Promise.all(pending.map(async (name) => ({ name, sql: await readFile2(join12(migrationsFolder, name), "utf-8") })));
|
|
26171
|
+
}
|
|
25997
26172
|
async function runMigrate(options, deps) {
|
|
25998
26173
|
if (options.status) {
|
|
25999
26174
|
loadEnvFile(options.env);
|
|
@@ -26059,19 +26234,39 @@ async function runMigrate(options, deps) {
|
|
|
26059
26234
|
printSuccess("Build complete");
|
|
26060
26235
|
const generatedDir = resolve8(cwd, "dist/generated");
|
|
26061
26236
|
await generateDrizzleConfig(config2, generatedDir);
|
|
26237
|
+
if (options.dryRun) {
|
|
26238
|
+
const pending2 = await readPendingMigrations(deps.db, migrationsTable, migrationsFolder);
|
|
26239
|
+
const preview = await previewMigration(deps.configPath, migrationsFolder);
|
|
26240
|
+
const all = [...pending2, ...preview];
|
|
26241
|
+
if (pending2.length > 0) {
|
|
26242
|
+
printSuccess(`Generated earlier, not yet applied: ${pending2.map((m2) => m2.name).join(", ")}`);
|
|
26243
|
+
}
|
|
26244
|
+
printSuccess(
|
|
26245
|
+
preview.length === 0 ? "No new migration would be generated" : `Would generate ${preview.length} migration file${preview.length !== 1 ? "s" : ""}: ${preview.map((m2) => m2.name).join(", ")}`
|
|
26246
|
+
);
|
|
26247
|
+
process.stdout.write("\nDry run \u2014 no migration files written, no changes applied.\n\n");
|
|
26248
|
+
if (all.length > 0) {
|
|
26249
|
+
process.stdout.write("SQL `manguito migrate` would apply:\n");
|
|
26250
|
+
process.stdout.write("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
26251
|
+
for (const m2 of all) process.stdout.write(`-- ${m2.name}
|
|
26252
|
+
${m2.sql}
|
|
26253
|
+
`);
|
|
26254
|
+
process.stdout.write("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n");
|
|
26255
|
+
const scan = scanMigrationSql(all);
|
|
26256
|
+
if (scan.hasDestructiveOperations) printDestructiveOperations(scan);
|
|
26257
|
+
else process.stdout.write("No destructive operations detected.\n\n");
|
|
26258
|
+
process.stdout.write("Run `manguito migrate` to generate and apply these.\n");
|
|
26259
|
+
}
|
|
26260
|
+
process.exit(0);
|
|
26261
|
+
}
|
|
26062
26262
|
const newFileNames = await generateMigration(deps.configPath, migrationsFolder);
|
|
26063
|
-
const newFilePaths = newFileNames.map((f) => join12(migrationsFolder, f));
|
|
26064
26263
|
printSuccess(
|
|
26065
26264
|
newFileNames.length === 0 ? "No new migration files generated" : `Generated ${newFileNames.length} migration file${newFileNames.length !== 1 ? "s" : ""}: ${newFileNames.join(", ")}`
|
|
26066
26265
|
);
|
|
26067
|
-
const
|
|
26266
|
+
const { pending } = await getMigrationStatus(deps.db.getDb(), { migrationsTable, migrationsFolder });
|
|
26267
|
+
const scanResult = scanMigrationFiles(pending.map((f) => join12(migrationsFolder, f)));
|
|
26068
26268
|
if (scanResult.hasDestructiveOperations) {
|
|
26069
|
-
|
|
26070
|
-
for (const op of scanResult.operations) {
|
|
26071
|
-
process.stdout.write(` - ${op.operation}
|
|
26072
|
-
`);
|
|
26073
|
-
}
|
|
26074
|
-
process.stdout.write("\n");
|
|
26269
|
+
printDestructiveOperations(scanResult);
|
|
26075
26270
|
process.stdout.write(" These changes are irreversible and may cause data loss.\n");
|
|
26076
26271
|
const uniqueFiles = [...new Set(scanResult.operations.map((op) => op.file))];
|
|
26077
26272
|
for (const file of uniqueFiles) {
|
|
@@ -26089,25 +26284,6 @@ async function runMigrate(options, deps) {
|
|
|
26089
26284
|
}
|
|
26090
26285
|
}
|
|
26091
26286
|
}
|
|
26092
|
-
if (options.dryRun) {
|
|
26093
|
-
process.stdout.write("\nDry run \u2014 no files will be written, no changes applied.\n\n");
|
|
26094
|
-
if (newFilePaths.length > 0) {
|
|
26095
|
-
process.stdout.write("Generated SQL:\n");
|
|
26096
|
-
process.stdout.write("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
|
|
26097
|
-
for (const filePath of newFilePaths) {
|
|
26098
|
-
const sql2 = await readFile2(filePath, "utf-8");
|
|
26099
|
-
process.stdout.write(sql2);
|
|
26100
|
-
}
|
|
26101
|
-
process.stdout.write("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n");
|
|
26102
|
-
} else {
|
|
26103
|
-
process.stdout.write("No SQL changes generated.\n\n");
|
|
26104
|
-
}
|
|
26105
|
-
if (!scanResult.hasDestructiveOperations) {
|
|
26106
|
-
process.stdout.write("No destructive operations detected.\n\n");
|
|
26107
|
-
}
|
|
26108
|
-
process.stdout.write("Run `manguito migrate` to generate and apply this migration.\n");
|
|
26109
|
-
process.exit(0);
|
|
26110
|
-
}
|
|
26111
26287
|
const migrationResult = await applyMigrations(deps.configPath, deps.db.getDb(), {
|
|
26112
26288
|
migrationsTable,
|
|
26113
26289
|
migrationsFolder
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobbykim/manguito-cms-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Bobby Kim <bobby.sihun.kim@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/bobbykim89/manguito-cms",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"tsup": "^8.5.1",
|
|
30
30
|
"typescript": "^6.0.3",
|
|
31
31
|
"vite": "^8.1.3",
|
|
32
|
-
"@bobbykim/manguito-cms-admin": "0.5.
|
|
33
|
-
"@bobbykim/manguito-cms-api": "0.
|
|
34
|
-
"@bobbykim/manguito-cms-db": "0.
|
|
35
|
-
"@bobbykim/manguito-cms-core": "0.
|
|
32
|
+
"@bobbykim/manguito-cms-admin": "0.5.4",
|
|
33
|
+
"@bobbykim/manguito-cms-api": "0.9.1",
|
|
34
|
+
"@bobbykim/manguito-cms-db": "0.2.0",
|
|
35
|
+
"@bobbykim/manguito-cms-core": "0.10.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^25.9.5",
|