@bobbykim/manguito-cms-cli 0.6.1 → 0.7.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.
|
@@ -49,7 +49,7 @@ var init_esm_shims = __esm({
|
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
// ../api/dist/chunk-
|
|
52
|
+
// ../api/dist/chunk-FXPKGPYF.mjs
|
|
53
53
|
init_esm_shims();
|
|
54
54
|
import crypto from "crypto";
|
|
55
55
|
|
|
@@ -1212,7 +1212,7 @@ Subquery.prototype.getSQL = function() {
|
|
|
1212
1212
|
return new SQL([this]);
|
|
1213
1213
|
};
|
|
1214
1214
|
|
|
1215
|
-
// ../api/dist/chunk-
|
|
1215
|
+
// ../api/dist/chunk-FXPKGPYF.mjs
|
|
1216
1216
|
function isColumnBacked(field) {
|
|
1217
1217
|
const col = field.db_column;
|
|
1218
1218
|
if (col === null) return false;
|
|
@@ -1220,7 +1220,7 @@ function isColumnBacked(field) {
|
|
|
1220
1220
|
if (col.junction) return false;
|
|
1221
1221
|
return true;
|
|
1222
1222
|
}
|
|
1223
|
-
function buildFieldKeyMap(pairs, allLabels,
|
|
1223
|
+
function buildFieldKeyMap(pairs, allLabels, droppedColumns, droppedLabels) {
|
|
1224
1224
|
const labelToColumn = /* @__PURE__ */ new Map();
|
|
1225
1225
|
const columnToLabel = /* @__PURE__ */ new Map();
|
|
1226
1226
|
for (const { label, column } of pairs) {
|
|
@@ -1228,7 +1228,7 @@ function buildFieldKeyMap(pairs, allLabels, droppedKeys) {
|
|
|
1228
1228
|
columnToLabel.set(column, label);
|
|
1229
1229
|
}
|
|
1230
1230
|
for (const { label, ownColumn } of allLabels) {
|
|
1231
|
-
if (ownColumn
|
|
1231
|
+
if (ownColumn !== void 0) continue;
|
|
1232
1232
|
const columnOwner = columnToLabel.get(label);
|
|
1233
1233
|
if (columnOwner !== void 0 && columnOwner !== label) {
|
|
1234
1234
|
throw new Error(
|
|
@@ -1237,23 +1237,33 @@ function buildFieldKeyMap(pairs, allLabels, droppedKeys) {
|
|
|
1237
1237
|
}
|
|
1238
1238
|
}
|
|
1239
1239
|
for (const label of labelToColumn.keys()) {
|
|
1240
|
-
if (
|
|
1240
|
+
if (droppedLabels.has(label)) labelToColumn.delete(label);
|
|
1241
1241
|
}
|
|
1242
1242
|
for (const column of columnToLabel.keys()) {
|
|
1243
|
-
if (
|
|
1243
|
+
if (droppedColumns.has(column)) columnToLabel.delete(column);
|
|
1244
1244
|
}
|
|
1245
1245
|
const diverges = [...labelToColumn].some(([label, column]) => label !== column);
|
|
1246
|
-
function remap(input, lookup) {
|
|
1246
|
+
function remap(input, lookup, dropped, droppedIfUnmapped) {
|
|
1247
1247
|
const out = {};
|
|
1248
1248
|
for (const key of Object.keys(input)) {
|
|
1249
|
-
|
|
1250
|
-
|
|
1249
|
+
const mapped = lookup.get(key);
|
|
1250
|
+
if (mapped !== void 0) {
|
|
1251
|
+
out[mapped] = input[key];
|
|
1252
|
+
continue;
|
|
1253
|
+
}
|
|
1254
|
+
if (dropped.has(key) || droppedIfUnmapped?.has(key)) continue;
|
|
1255
|
+
out[key] = input[key];
|
|
1251
1256
|
}
|
|
1252
1257
|
return out;
|
|
1253
1258
|
}
|
|
1254
1259
|
return {
|
|
1255
|
-
toStorage
|
|
1256
|
-
|
|
1260
|
+
// toStorage reads LABEL-keyed input: a live label maps and writes
|
|
1261
|
+
// regardless of either drop set; an unmapped key is refused if it is
|
|
1262
|
+
// EITHER the tombstone's own name (droppedLabels) or a raw, unexposed
|
|
1263
|
+
// column (droppedColumns) — see remap's comment.
|
|
1264
|
+
// toLabels reads COLUMN-keyed input, so it consults only droppedColumns.
|
|
1265
|
+
toStorage: (input) => remap(input, labelToColumn, droppedLabels, droppedColumns),
|
|
1266
|
+
toLabels: (row) => remap(row, columnToLabel, droppedColumns),
|
|
1257
1267
|
columnFor: (label) => labelToColumn.get(label),
|
|
1258
1268
|
labelFor: (column) => columnToLabel.get(column),
|
|
1259
1269
|
labels: [...labelToColumn.keys()],
|
|
@@ -1261,14 +1271,15 @@ function buildFieldKeyMap(pairs, allLabels, droppedKeys) {
|
|
|
1261
1271
|
};
|
|
1262
1272
|
}
|
|
1263
1273
|
function createFieldKeyMap(fields) {
|
|
1264
|
-
const
|
|
1274
|
+
const droppedColumns = /* @__PURE__ */ new Set();
|
|
1275
|
+
const droppedLabels = /* @__PURE__ */ new Set();
|
|
1265
1276
|
const pairs = [];
|
|
1266
1277
|
for (const f of fields) {
|
|
1267
1278
|
if (!isColumnBacked(f)) continue;
|
|
1268
1279
|
pairs.push({ label: f.name, column: f.db_column.column_name });
|
|
1269
1280
|
if (f.removed === true) {
|
|
1270
|
-
|
|
1271
|
-
|
|
1281
|
+
droppedLabels.add(f.name);
|
|
1282
|
+
droppedColumns.add(f.db_column.column_name);
|
|
1272
1283
|
}
|
|
1273
1284
|
}
|
|
1274
1285
|
return buildFieldKeyMap(
|
|
@@ -1279,30 +1290,33 @@ function createFieldKeyMap(fields) {
|
|
|
1279
1290
|
// forbids present-and-undefined.
|
|
1280
1291
|
...isColumnBacked(f) && { ownColumn: f.db_column.column_name }
|
|
1281
1292
|
})),
|
|
1282
|
-
|
|
1293
|
+
droppedColumns,
|
|
1294
|
+
droppedLabels
|
|
1283
1295
|
);
|
|
1284
1296
|
}
|
|
1285
1297
|
function createFieldKeyMapFromProjection(projectionType, allFields) {
|
|
1286
1298
|
const pairs = projectionType.fields.map((f) => ({ label: f.exposed_as, column: f.column_name }));
|
|
1287
|
-
const
|
|
1288
|
-
const
|
|
1299
|
+
const projectedColumns = new Set(pairs.map((p) => p.column));
|
|
1300
|
+
const projectedLabels = new Set(pairs.map((p) => p.label));
|
|
1301
|
+
const droppedColumns = /* @__PURE__ */ new Set();
|
|
1302
|
+
const droppedLabels = /* @__PURE__ */ new Set();
|
|
1289
1303
|
for (const f of allFields) {
|
|
1290
1304
|
if (!isColumnBacked(f)) continue;
|
|
1291
|
-
|
|
1292
|
-
|
|
1305
|
+
if (!projectedColumns.has(f.db_column.column_name)) droppedColumns.add(f.db_column.column_name);
|
|
1306
|
+
if (!projectedLabels.has(f.name)) droppedLabels.add(f.name);
|
|
1293
1307
|
}
|
|
1294
|
-
for (const p of pairs) dropped.delete(p.column);
|
|
1295
|
-
for (const label of projected) dropped.delete(label);
|
|
1296
1308
|
return buildFieldKeyMap(
|
|
1297
1309
|
pairs,
|
|
1298
1310
|
allFields.map((f) => ({
|
|
1299
1311
|
label: f.name,
|
|
1300
|
-
// See buildFieldKeyMap's collision check:
|
|
1301
|
-
//
|
|
1302
|
-
// label
|
|
1312
|
+
// See buildFieldKeyMap's collision check: `ownColumn` marks every
|
|
1313
|
+
// column-backed field so the check skips it entirely — only a
|
|
1314
|
+
// paragraph/junction/programmatic label (no column of its own) can
|
|
1315
|
+
// still collide.
|
|
1303
1316
|
...isColumnBacked(f) && { ownColumn: f.db_column.column_name }
|
|
1304
1317
|
})),
|
|
1305
|
-
|
|
1318
|
+
droppedColumns,
|
|
1319
|
+
droppedLabels
|
|
1306
1320
|
);
|
|
1307
1321
|
}
|
|
1308
1322
|
function quoteIdent(name) {
|
|
@@ -1474,9 +1488,8 @@ function groupBy(items, key) {
|
|
|
1474
1488
|
}
|
|
1475
1489
|
return result;
|
|
1476
1490
|
}
|
|
1477
|
-
function needsFkResolution(row, fkColumn
|
|
1478
|
-
|
|
1479
|
-
const current = row[fieldName];
|
|
1491
|
+
function needsFkResolution(row, fkColumn) {
|
|
1492
|
+
const current = row[fkColumn];
|
|
1480
1493
|
return !(typeof current === "object" && current !== null);
|
|
1481
1494
|
}
|
|
1482
1495
|
async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOnly = false) {
|
|
@@ -1493,15 +1506,11 @@ async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOn
|
|
|
1493
1506
|
row[fieldName] = byParent[row["id"]] ?? [];
|
|
1494
1507
|
}
|
|
1495
1508
|
} else if (rel.type === "reference") {
|
|
1496
|
-
const
|
|
1497
|
-
const pending = batch.filter((r) => needsFkResolution(r, rel.fk_column, fieldName));
|
|
1509
|
+
const pending = batch.filter((r) => needsFkResolution(r, rel.fk_column));
|
|
1498
1510
|
if (pending.length === 0) return;
|
|
1499
1511
|
const fkValues = pending.map((r) => r[rel.fk_column]).filter(Boolean);
|
|
1500
1512
|
if (fkValues.length === 0) {
|
|
1501
|
-
for (const row of pending)
|
|
1502
|
-
row[fieldName] = null;
|
|
1503
|
-
if (dropFk) delete row[rel.fk_column];
|
|
1504
|
-
}
|
|
1513
|
+
for (const row of pending) row[rel.fk_column] = null;
|
|
1505
1514
|
return;
|
|
1506
1515
|
}
|
|
1507
1516
|
const unique = [...new Set(fkValues)];
|
|
@@ -1518,8 +1527,7 @@ async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOn
|
|
|
1518
1527
|
}
|
|
1519
1528
|
for (const row of pending) {
|
|
1520
1529
|
const fkVal = row[rel.fk_column];
|
|
1521
|
-
row[
|
|
1522
|
-
if (dropFk) delete row[rel.fk_column];
|
|
1530
|
+
row[rel.fk_column] = fkVal ? cache.get(`${rel.table}:${fkVal}`) ?? null : null;
|
|
1523
1531
|
}
|
|
1524
1532
|
} else if (rel.type === "junction") {
|
|
1525
1533
|
const parentIds = batch.map((r) => r["id"]);
|
|
@@ -1547,15 +1555,11 @@ async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOn
|
|
|
1547
1555
|
row[fieldName] = jEntries.map((jr) => cache.get(`${rel.table}:${jr[rel.right_column]}`)).filter(Boolean);
|
|
1548
1556
|
}
|
|
1549
1557
|
} else if (rel.type === "media") {
|
|
1550
|
-
const
|
|
1551
|
-
const pending = batch.filter((r) => needsFkResolution(r, rel.fk_column, fieldName));
|
|
1558
|
+
const pending = batch.filter((r) => needsFkResolution(r, rel.fk_column));
|
|
1552
1559
|
if (pending.length === 0) return;
|
|
1553
1560
|
const fkValues = pending.map((r) => r[rel.fk_column]).filter(Boolean);
|
|
1554
1561
|
if (fkValues.length === 0) {
|
|
1555
|
-
for (const row of pending)
|
|
1556
|
-
row[fieldName] = null;
|
|
1557
|
-
if (dropFk) delete row[rel.fk_column];
|
|
1558
|
-
}
|
|
1562
|
+
for (const row of pending) row[rel.fk_column] = null;
|
|
1559
1563
|
return;
|
|
1560
1564
|
}
|
|
1561
1565
|
const unique = [...new Set(fkValues)];
|
|
@@ -1569,8 +1573,7 @@ async function resolveRelationField(db, rows, fieldName, rel, cache, publishedOn
|
|
|
1569
1573
|
}
|
|
1570
1574
|
for (const row of pending) {
|
|
1571
1575
|
const fkVal = row[rel.fk_column];
|
|
1572
|
-
row[
|
|
1573
|
-
if (dropFk) delete row[rel.fk_column];
|
|
1576
|
+
row[rel.fk_column] = fkVal ? cache.get(`media:${fkVal}`) ?? null : null;
|
|
1574
1577
|
}
|
|
1575
1578
|
}
|
|
1576
1579
|
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
init_esm_shims,
|
|
12
12
|
isColumnBacked,
|
|
13
13
|
resolveRelationField
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-52I54C3X.js";
|
|
15
15
|
|
|
16
16
|
// ../../node_modules/.pnpm/@whatwg-node+fetch@0.10.13/node_modules/@whatwg-node/fetch/dist/shouldSkipPonyfill.js
|
|
17
17
|
var require_shouldSkipPonyfill = __commonJS({
|
|
@@ -10786,10 +10786,10 @@ var require_parser = __commonJS({
|
|
|
10786
10786
|
}
|
|
10787
10787
|
});
|
|
10788
10788
|
|
|
10789
|
-
// ../api/dist/handler-
|
|
10789
|
+
// ../api/dist/handler-GED3I7Z2.mjs
|
|
10790
10790
|
init_esm_shims();
|
|
10791
10791
|
|
|
10792
|
-
// ../api/dist/chunk-
|
|
10792
|
+
// ../api/dist/chunk-4LLELFAZ.mjs
|
|
10793
10793
|
init_esm_shims();
|
|
10794
10794
|
|
|
10795
10795
|
// ../../node_modules/.pnpm/graphql-yoga@5.21.2_graphql@16.14.2/node_modules/graphql-yoga/esm/index.js
|
|
@@ -30122,7 +30122,7 @@ function createYoga(options) {
|
|
|
30122
30122
|
});
|
|
30123
30123
|
}
|
|
30124
30124
|
|
|
30125
|
-
// ../api/dist/chunk-
|
|
30125
|
+
// ../api/dist/chunk-4LLELFAZ.mjs
|
|
30126
30126
|
var import_dataloader = __toESM(require_dataloader(), 1);
|
|
30127
30127
|
|
|
30128
30128
|
// ../../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
|
|
@@ -30880,7 +30880,7 @@ var EnvelopArmor = class {
|
|
|
30880
30880
|
}
|
|
30881
30881
|
};
|
|
30882
30882
|
|
|
30883
|
-
// ../api/dist/chunk-
|
|
30883
|
+
// ../api/dist/chunk-4LLELFAZ.mjs
|
|
30884
30884
|
var DateTimeScalar = new GraphQLScalarType({
|
|
30885
30885
|
name: "DateTime",
|
|
30886
30886
|
description: "ISO-8601 date-time string",
|
|
@@ -31086,29 +31086,22 @@ function scalarFieldResolver(field, fallback) {
|
|
|
31086
31086
|
function relationFieldResolver(typeName, schemaFieldName) {
|
|
31087
31087
|
return (parent, _args, ctx) => ctx.loaders.load(typeName, schemaFieldName, parent);
|
|
31088
31088
|
}
|
|
31089
|
-
function programmaticFieldResolver(typeName, schemaFieldName,
|
|
31089
|
+
function programmaticFieldResolver(typeName, schemaFieldName, mediaFieldNames = [], fieldKeys) {
|
|
31090
31090
|
return async (parent, _args, ctx) => {
|
|
31091
31091
|
let p = ctx.programmaticMemo.get(parent);
|
|
31092
31092
|
if (!p) {
|
|
31093
|
-
p = resolveProgrammaticRow(typeName, parent, ctx,
|
|
31093
|
+
p = resolveProgrammaticRow(typeName, parent, ctx, mediaFieldNames, fieldKeys);
|
|
31094
31094
|
ctx.programmaticMemo.set(parent, p);
|
|
31095
31095
|
}
|
|
31096
31096
|
return (await p)[schemaFieldName];
|
|
31097
31097
|
};
|
|
31098
31098
|
}
|
|
31099
|
-
async function resolveProgrammaticRow(typeName, parent, ctx,
|
|
31099
|
+
async function resolveProgrammaticRow(typeName, parent, ctx, mediaFieldNames, fieldKeys) {
|
|
31100
31100
|
const toLabels = (row) => fieldKeys ? fieldKeys.toLabels(row) : row;
|
|
31101
|
-
if (
|
|
31101
|
+
if (mediaFieldNames.length === 0) return ctx.resolver.resolveItem(typeName, toLabels(parent));
|
|
31102
31102
|
const enriched = { ...parent };
|
|
31103
|
-
await Promise.all(
|
|
31104
|
-
return ctx.resolver.resolveItem(typeName,
|
|
31105
|
-
}
|
|
31106
|
-
function relabelMedia(record, enriched, mediaFields) {
|
|
31107
|
-
for (const { name, exposedAs } of mediaFields) {
|
|
31108
|
-
if (!(name in enriched)) continue;
|
|
31109
|
-
record[exposedAs] = enriched[name];
|
|
31110
|
-
}
|
|
31111
|
-
return record;
|
|
31103
|
+
await Promise.all(mediaFieldNames.map((name) => ctx.loaders.load(typeName, name, enriched)));
|
|
31104
|
+
return ctx.resolver.resolveItem(typeName, toLabels(enriched));
|
|
31112
31105
|
}
|
|
31113
31106
|
function collectionResolver(typeName, nameMap, fieldKeys) {
|
|
31114
31107
|
return async (_root, args, ctx) => {
|
|
@@ -31180,7 +31173,7 @@ var MEDIA = new GraphQLObjectType({
|
|
|
31180
31173
|
duration: { type: GraphQLInt, resolve: (m) => m.duration }
|
|
31181
31174
|
}
|
|
31182
31175
|
});
|
|
31183
|
-
function buildGraphQLSchema(registry, fieldKeyMaps = {}, view) {
|
|
31176
|
+
function buildGraphQLSchema(registry, fieldKeyMaps = {}, view, programmaticKeyMaps = fieldKeyMaps) {
|
|
31184
31177
|
const objectTypes = /* @__PURE__ */ new Map();
|
|
31185
31178
|
const enumTypes = /* @__PURE__ */ new Map();
|
|
31186
31179
|
for (const [name, enumType] of Object.entries(registry.enum_types)) {
|
|
@@ -31244,9 +31237,9 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}, view) {
|
|
|
31244
31237
|
}
|
|
31245
31238
|
function buildObjectType(machineName, type) {
|
|
31246
31239
|
const visible = viewFieldsFor(machineName, type);
|
|
31247
|
-
const
|
|
31248
|
-
(
|
|
31249
|
-
).map((
|
|
31240
|
+
const mediaFieldNames = type.fields.filter(
|
|
31241
|
+
(f) => f.removed !== true && (f.field_type === "image" || f.field_type === "video" || f.field_type === "file")
|
|
31242
|
+
).map((f) => f.name);
|
|
31250
31243
|
return new GraphQLObjectType({
|
|
31251
31244
|
name: graphqlTypeName(machineName),
|
|
31252
31245
|
fields: () => {
|
|
@@ -31266,7 +31259,7 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}, view) {
|
|
|
31266
31259
|
const outType = outputTypeForField(field, vf.required);
|
|
31267
31260
|
let resolve;
|
|
31268
31261
|
if (field.field_type === "programmatic") {
|
|
31269
|
-
resolve = programmaticFieldResolver(machineName, field.name,
|
|
31262
|
+
resolve = programmaticFieldResolver(machineName, field.name, mediaFieldNames, programmaticKeyMaps[machineName]);
|
|
31270
31263
|
} else if (field.field_type === "reference" || field.field_type === "paragraph" || field.field_type === "image" || field.field_type === "video" || field.field_type === "file") {
|
|
31271
31264
|
resolve = relationFieldResolver(machineName, field.name);
|
|
31272
31265
|
} else {
|
|
@@ -31311,7 +31304,10 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}, view) {
|
|
|
31311
31304
|
for (const [name, ct] of Object.entries(registry.content_types)) {
|
|
31312
31305
|
const objType = objectTypes.get(name);
|
|
31313
31306
|
const visible = viewFieldsFor(name, ct);
|
|
31314
|
-
const nameMap = buildFieldNameMap(
|
|
31307
|
+
const nameMap = buildFieldNameMap([
|
|
31308
|
+
...visible.map((v) => v.exposedAs),
|
|
31309
|
+
...ct.system_fields.map((s) => s.name)
|
|
31310
|
+
]);
|
|
31315
31311
|
if (ct.only_one) {
|
|
31316
31312
|
queryFields[singleQueryName(name)] = { type: objType, resolve: singletonResolver(name) };
|
|
31317
31313
|
continue;
|
|
@@ -31452,7 +31448,8 @@ function createRelationLoaders(db, registry) {
|
|
|
31452
31448
|
async (parents) => {
|
|
31453
31449
|
const rows = parents;
|
|
31454
31450
|
await resolveRelationField(db, rows, fieldName, rel, cache, true);
|
|
31455
|
-
|
|
31451
|
+
const resultKey = rel.type === "reference" || rel.type === "media" ? rel.fk_column : fieldName;
|
|
31452
|
+
return rows.map((r) => r[resultKey]);
|
|
31456
31453
|
},
|
|
31457
31454
|
// Batch within a tick; do not memoize by parent identity (rows are mutated
|
|
31458
31455
|
// and may recur across nesting levels).
|
|
@@ -31513,7 +31510,7 @@ function createGraphQLHandler(registry, repos, fieldKeyMaps, resolver, db, optio
|
|
|
31513
31510
|
currentVersion: versioning.currentVersion
|
|
31514
31511
|
});
|
|
31515
31512
|
}
|
|
31516
|
-
const schema = buildGraphQLSchema(registry, fieldKeyMaps, view);
|
|
31513
|
+
const schema = buildGraphQLSchema(registry, fieldKeyMaps, view, versioning.currentFieldKeyMaps ?? fieldKeyMaps);
|
|
31517
31514
|
const { plugins } = buildArmorPlugin({
|
|
31518
31515
|
maxDepth: options.maxDepth,
|
|
31519
31516
|
maxComplexity: options.maxComplexity
|
package/dist/index.js
CHANGED
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
resolveRelationField,
|
|
40
40
|
sql,
|
|
41
41
|
tracer
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-52I54C3X.js";
|
|
43
43
|
|
|
44
44
|
// ../../node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js
|
|
45
45
|
var require_identity = __commonJS({
|
|
@@ -43958,6 +43958,16 @@ function loadEnvFile(filePath) {
|
|
|
43958
43958
|
config2({ path: filePath });
|
|
43959
43959
|
}
|
|
43960
43960
|
|
|
43961
|
+
// src/utils/project-version-model.ts
|
|
43962
|
+
init_esm_shims();
|
|
43963
|
+
function loadProjectVersionModel(schema, registry2) {
|
|
43964
|
+
const snapshots = loadVersionSnapshots(schema, registry2);
|
|
43965
|
+
if (!snapshots.ok) return snapshots;
|
|
43966
|
+
const model = computeVersionModel({ current: registry2, snapshots: snapshots.value });
|
|
43967
|
+
if (!model.ok) return model;
|
|
43968
|
+
return { ok: true, value: { snapshots: snapshots.value, model: model.value } };
|
|
43969
|
+
}
|
|
43970
|
+
|
|
43961
43971
|
// src/commands/build.ts
|
|
43962
43972
|
function registerBuild(program2) {
|
|
43963
43973
|
program2.command("build").description("Run codegen and compile project to dist/").option("--env <path>", "path to .env file to load").action(async (options) => {
|
|
@@ -44018,6 +44028,11 @@ async function runBuild(options, deps) {
|
|
|
44018
44028
|
parsedRoutesDef,
|
|
44019
44029
|
parsedRoles
|
|
44020
44030
|
);
|
|
44031
|
+
const versions = loadProjectVersionModel(resolveSchemaConfig(cwd, config3), registry2);
|
|
44032
|
+
if (!versions.ok) {
|
|
44033
|
+
printValidationErrors(versions.errors, "Version model errors", "manguito build");
|
|
44034
|
+
process.exit(1);
|
|
44035
|
+
}
|
|
44021
44036
|
printSuccess("Config loaded");
|
|
44022
44037
|
const contentCount = parsedSchemas.filter((s) => s.schema_type === "content-type").length;
|
|
44023
44038
|
const paragraphCount = parsedSchemas.filter((s) => s.schema_type === "paragraph-type").length;
|
|
@@ -44033,19 +44048,8 @@ async function runBuild(options, deps) {
|
|
|
44033
44048
|
await generateSchemaRegistry(registry2, generatedDir);
|
|
44034
44049
|
await generateRoutes2(registry2, generatedDir);
|
|
44035
44050
|
await generateForms(registry2, join9(generatedDir, "forms"));
|
|
44036
|
-
|
|
44037
|
-
|
|
44038
|
-
if (!snapshots.ok) {
|
|
44039
|
-
printValidationErrors(snapshots.errors, "Snapshot errors", "manguito build");
|
|
44040
|
-
process.exit(1);
|
|
44041
|
-
}
|
|
44042
|
-
const versionModel = computeVersionModel({ current: registry2, snapshots: snapshots.value });
|
|
44043
|
-
if (!versionModel.ok) {
|
|
44044
|
-
printValidationErrors(versionModel.errors, "Version model errors", "manguito build");
|
|
44045
|
-
process.exit(1);
|
|
44046
|
-
}
|
|
44047
|
-
await generateVersionModel(versionModel.value, generatedDir);
|
|
44048
|
-
printSuccess(`Version model baked (live: ${versionModel.value.live.join(", ")})`);
|
|
44051
|
+
await generateVersionModel(versions.value.model, generatedDir);
|
|
44052
|
+
printSuccess(`Version model baked (live: ${versions.value.model.live.join(", ")})`);
|
|
44049
44053
|
const programmaticDir = resolve4(cwd, config3.programmatic.dir);
|
|
44050
44054
|
const resolverFiles = existsSync3(programmaticDir) ? walkFiles(programmaticDir).filter((f) => /\.(ts|mjs|js)$/.test(f)) : [];
|
|
44051
44055
|
await generateProgrammaticRegistry(resolverFiles, generatedDir);
|
|
@@ -47618,14 +47622,16 @@ function isPlainRow(v2) {
|
|
|
47618
47622
|
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2) && !(v2 instanceof Date);
|
|
47619
47623
|
}
|
|
47620
47624
|
var MEDIA_FIELD_TYPES = /* @__PURE__ */ new Set(["image", "video", "file"]);
|
|
47621
|
-
function nestedTargets(fields) {
|
|
47625
|
+
function nestedTargets(fields, map2) {
|
|
47622
47626
|
const out = [];
|
|
47623
47627
|
for (const f of fields) {
|
|
47624
47628
|
if (MEDIA_FIELD_TYPES.has(f.field_type)) continue;
|
|
47625
47629
|
if (f.field_type !== "paragraph" && f.field_type !== "reference") continue;
|
|
47626
47630
|
const ref = f.ui_component.ref;
|
|
47627
47631
|
if (!ref) continue;
|
|
47628
|
-
|
|
47632
|
+
const label = isColumnBacked(f) ? map2.labelFor(f.db_column.column_name) : f.name;
|
|
47633
|
+
if (label === void 0) continue;
|
|
47634
|
+
out.push({ label, target: ref });
|
|
47629
47635
|
}
|
|
47630
47636
|
return out;
|
|
47631
47637
|
}
|
|
@@ -47643,7 +47649,7 @@ function buildProjectors(registry2, fieldKeyMaps, fallbacks) {
|
|
|
47643
47649
|
const typeFallbacks = fallbacks?.[typeName];
|
|
47644
47650
|
projectors[typeName] = {
|
|
47645
47651
|
map: map2,
|
|
47646
|
-
nested: nestedTargets(type.fields),
|
|
47652
|
+
nested: nestedTargets(type.fields, map2),
|
|
47647
47653
|
// Spread rather than assign: with exactOptionalPropertyTypes, an optional
|
|
47648
47654
|
// property may be ABSENT but not present-and-undefined.
|
|
47649
47655
|
...typeFallbacks !== void 0 && { fallbacks: typeFallbacks }
|
|
@@ -47723,7 +47729,28 @@ function parseFilters(url2, validFields, columnFor) {
|
|
|
47723
47729
|
function isPublished(item) {
|
|
47724
47730
|
return item["published"] === true;
|
|
47725
47731
|
}
|
|
47726
|
-
function registerPublicContentRoutes(app, registry2, repos, projectors, paths, listRateLimit, resolver) {
|
|
47732
|
+
function registerPublicContentRoutes(app, registry2, repos, projectors, paths, listRateLimit, resolver, resolverProjectors) {
|
|
47733
|
+
const forResolver = resolverProjectors ?? projectors;
|
|
47734
|
+
function pickProgrammatic(resolved, names) {
|
|
47735
|
+
const out = {};
|
|
47736
|
+
for (const name of names) if (name in resolved) out[name] = resolved[name];
|
|
47737
|
+
return out;
|
|
47738
|
+
}
|
|
47739
|
+
async function respondItem(row, typeName, programmatic) {
|
|
47740
|
+
const data = projectRow(row, typeName, projectors);
|
|
47741
|
+
if (!resolver?.hasSchema(typeName)) return data;
|
|
47742
|
+
const resolved = await resolver.resolveItem(typeName, projectRow(row, typeName, forResolver));
|
|
47743
|
+
return { ...data, ...pickProgrammatic(resolved, programmatic) };
|
|
47744
|
+
}
|
|
47745
|
+
async function respondList(rows, typeName, programmatic) {
|
|
47746
|
+
const data = rows.map((row) => projectRow(row, typeName, projectors));
|
|
47747
|
+
if (!resolver?.hasSchema(typeName)) return data;
|
|
47748
|
+
const resolved = await resolver.resolveList(
|
|
47749
|
+
typeName,
|
|
47750
|
+
rows.map((row) => projectRow(row, typeName, forResolver))
|
|
47751
|
+
);
|
|
47752
|
+
return data.map((d2, i) => ({ ...d2, ...pickProgrammatic(resolved[i], programmatic) }));
|
|
47753
|
+
}
|
|
47727
47754
|
function registerListRoute(path7, handler) {
|
|
47728
47755
|
if (listRateLimit) {
|
|
47729
47756
|
app.get(path7, listRateLimit, handler);
|
|
@@ -47747,18 +47774,29 @@ function registerPublicContentRoutes(app, registry2, repos, projectors, paths, l
|
|
|
47747
47774
|
return c.json({ ok: true, data });
|
|
47748
47775
|
});
|
|
47749
47776
|
for (const [typeName, contentType] of Object.entries(registry2.content_types)) {
|
|
47777
|
+
let translateInclude2 = function(requested) {
|
|
47778
|
+
const include = [];
|
|
47779
|
+
for (const field of requested) {
|
|
47780
|
+
const registryName = includeNames.get(field);
|
|
47781
|
+
if (registryName === void 0) return { ok: false, field };
|
|
47782
|
+
include.push(registryName);
|
|
47783
|
+
}
|
|
47784
|
+
return { ok: true, include };
|
|
47785
|
+
};
|
|
47786
|
+
var translateInclude = translateInclude2;
|
|
47750
47787
|
const basePath = contentType.default_base_path;
|
|
47751
47788
|
const repo = repos[typeName];
|
|
47752
47789
|
if (!repo) continue;
|
|
47753
47790
|
const fieldKeys = projectors[typeName].map;
|
|
47754
|
-
const
|
|
47791
|
+
const programmatic = contentType.fields.filter((f) => f.field_type === "programmatic").map((f) => f.name);
|
|
47792
|
+
const includeNames = /* @__PURE__ */ new Map();
|
|
47755
47793
|
for (const f of contentType.fields) {
|
|
47756
47794
|
if (!RELATION_FIELD_TYPES.has(f.field_type)) continue;
|
|
47757
47795
|
if (isColumnBacked(f)) {
|
|
47758
47796
|
const label = fieldKeys.labelFor(f.db_column.column_name);
|
|
47759
|
-
if (label !== void 0)
|
|
47797
|
+
if (label !== void 0) includeNames.set(label, f.name);
|
|
47760
47798
|
} else {
|
|
47761
|
-
|
|
47799
|
+
includeNames.set(f.name, f.name);
|
|
47762
47800
|
}
|
|
47763
47801
|
}
|
|
47764
47802
|
const filterableFieldNames = /* @__PURE__ */ new Set([
|
|
@@ -47777,8 +47815,11 @@ function registerPublicContentRoutes(app, registry2, repos, projectors, paths, l
|
|
|
47777
47815
|
404
|
|
47778
47816
|
);
|
|
47779
47817
|
}
|
|
47780
|
-
|
|
47781
|
-
|
|
47818
|
+
const data = await respondItem(
|
|
47819
|
+
result.data[0],
|
|
47820
|
+
typeName,
|
|
47821
|
+
programmatic
|
|
47822
|
+
);
|
|
47782
47823
|
return c.json({ ok: true, data });
|
|
47783
47824
|
});
|
|
47784
47825
|
} else {
|
|
@@ -47835,21 +47876,20 @@ function registerPublicContentRoutes(app, registry2, repos, projectors, paths, l
|
|
|
47835
47876
|
400
|
|
47836
47877
|
);
|
|
47837
47878
|
}
|
|
47838
|
-
const
|
|
47839
|
-
|
|
47840
|
-
|
|
47841
|
-
|
|
47842
|
-
|
|
47843
|
-
|
|
47844
|
-
|
|
47845
|
-
|
|
47846
|
-
|
|
47847
|
-
|
|
47848
|
-
|
|
47849
|
-
|
|
47850
|
-
);
|
|
47851
|
-
}
|
|
47879
|
+
const translated = translateInclude2(parseInclude(c.req.query("include")));
|
|
47880
|
+
if (!translated.ok) {
|
|
47881
|
+
return c.json(
|
|
47882
|
+
{
|
|
47883
|
+
ok: false,
|
|
47884
|
+
error: {
|
|
47885
|
+
code: "INVALID_INCLUDE_FIELD",
|
|
47886
|
+
message: `'${translated.field}' is not a valid relation field`
|
|
47887
|
+
}
|
|
47888
|
+
},
|
|
47889
|
+
400
|
|
47890
|
+
);
|
|
47852
47891
|
}
|
|
47892
|
+
const include = translated.include;
|
|
47853
47893
|
const sortColumn = fieldKeys.labels.includes(sortBy) ? fieldKeys.columnFor(sortBy) : sortBy;
|
|
47854
47894
|
const result = await repo.findMany({
|
|
47855
47895
|
published_only: true,
|
|
@@ -47860,29 +47900,25 @@ function registerPublicContentRoutes(app, registry2, repos, projectors, paths, l
|
|
|
47860
47900
|
filters: filtersResult.filters,
|
|
47861
47901
|
include
|
|
47862
47902
|
});
|
|
47863
|
-
const
|
|
47864
|
-
(row) => projectRow(row, typeName, projectors)
|
|
47865
|
-
);
|
|
47866
|
-
const data = resolver?.hasSchema(typeName) ? await resolver.resolveList(typeName, labeled) : labeled;
|
|
47903
|
+
const data = await respondList(result.data, typeName, programmatic);
|
|
47867
47904
|
return c.json({ ...result, data });
|
|
47868
47905
|
});
|
|
47869
47906
|
app.get(paths.item(basePath), async (c) => {
|
|
47870
47907
|
const slug = c.req.param("slug");
|
|
47871
|
-
const
|
|
47872
|
-
|
|
47873
|
-
|
|
47874
|
-
|
|
47875
|
-
|
|
47876
|
-
|
|
47877
|
-
|
|
47878
|
-
|
|
47879
|
-
|
|
47880
|
-
|
|
47881
|
-
|
|
47882
|
-
|
|
47883
|
-
);
|
|
47884
|
-
}
|
|
47908
|
+
const translated = translateInclude2(parseInclude(c.req.query("include")));
|
|
47909
|
+
if (!translated.ok) {
|
|
47910
|
+
return c.json(
|
|
47911
|
+
{
|
|
47912
|
+
ok: false,
|
|
47913
|
+
error: {
|
|
47914
|
+
code: "INVALID_INCLUDE_FIELD",
|
|
47915
|
+
message: `'${translated.field}' is not a valid relation field`
|
|
47916
|
+
}
|
|
47917
|
+
},
|
|
47918
|
+
400
|
|
47919
|
+
);
|
|
47885
47920
|
}
|
|
47921
|
+
const include = translated.include;
|
|
47886
47922
|
const item = await repo.findBySlug(slug, include);
|
|
47887
47923
|
if (!item || !isPublished(item)) {
|
|
47888
47924
|
return c.json(
|
|
@@ -47893,15 +47929,15 @@ function registerPublicContentRoutes(app, registry2, repos, projectors, paths, l
|
|
|
47893
47929
|
404
|
|
47894
47930
|
);
|
|
47895
47931
|
}
|
|
47896
|
-
|
|
47897
|
-
if (resolver?.hasSchema(typeName)) data = await resolver.resolveItem(typeName, data);
|
|
47932
|
+
const data = await respondItem(item, typeName, programmatic);
|
|
47898
47933
|
return c.json({ ok: true, data });
|
|
47899
47934
|
});
|
|
47900
47935
|
}
|
|
47901
47936
|
}
|
|
47902
|
-
for (const [typeName] of Object.entries(registry2.taxonomy_types)) {
|
|
47937
|
+
for (const [typeName, taxonomyType] of Object.entries(registry2.taxonomy_types)) {
|
|
47903
47938
|
const repo = repos[typeName];
|
|
47904
47939
|
if (!repo) continue;
|
|
47940
|
+
const programmatic = taxonomyType.fields.filter((f) => f.field_type === "programmatic").map((f) => f.name);
|
|
47905
47941
|
registerListRoute(paths.taxonomyCollection(typeName), async (c) => {
|
|
47906
47942
|
const pagination = parsePagination(c.req.query("page"), c.req.query("per_page"));
|
|
47907
47943
|
if (!pagination.ok) {
|
|
@@ -47921,10 +47957,7 @@ function registerPublicContentRoutes(app, registry2, repos, projectors, paths, l
|
|
|
47921
47957
|
page: pagination.page,
|
|
47922
47958
|
per_page: pagination.per_page
|
|
47923
47959
|
});
|
|
47924
|
-
const
|
|
47925
|
-
(row) => projectRow(row, typeName, projectors)
|
|
47926
|
-
);
|
|
47927
|
-
const data = resolver?.hasSchema(typeName) ? await resolver.resolveList(typeName, labeled) : labeled;
|
|
47960
|
+
const data = await respondList(result.data, typeName, programmatic);
|
|
47928
47961
|
return c.json({ ...result, data });
|
|
47929
47962
|
});
|
|
47930
47963
|
app.get(paths.taxonomyItem(typeName), async (c) => {
|
|
@@ -47939,8 +47972,7 @@ function registerPublicContentRoutes(app, registry2, repos, projectors, paths, l
|
|
|
47939
47972
|
404
|
|
47940
47973
|
);
|
|
47941
47974
|
}
|
|
47942
|
-
|
|
47943
|
-
if (resolver?.hasSchema(typeName)) data = await resolver.resolveItem(typeName, data);
|
|
47975
|
+
const data = await respondItem(item, typeName, programmatic);
|
|
47944
47976
|
return c.json({ ok: true, data });
|
|
47945
47977
|
});
|
|
47946
47978
|
}
|
|
@@ -49702,7 +49734,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
49702
49734
|
}
|
|
49703
49735
|
var API_VERSION = (() => {
|
|
49704
49736
|
try {
|
|
49705
|
-
return "0.
|
|
49737
|
+
return "0.7.0";
|
|
49706
49738
|
} catch {
|
|
49707
49739
|
return "0.0.0";
|
|
49708
49740
|
}
|
|
@@ -50343,7 +50375,7 @@ function createCmsApp(options) {
|
|
|
50343
50375
|
app.use(path7, attachDeprecationHeaders);
|
|
50344
50376
|
}
|
|
50345
50377
|
}
|
|
50346
|
-
registerPublicContentRoutes(app, registry2, surface.repos, surface.projectors, surface.paths, listRateLimit, programmaticResolver);
|
|
50378
|
+
registerPublicContentRoutes(app, registry2, surface.repos, surface.projectors, surface.paths, listRateLimit, programmaticResolver, projectors);
|
|
50347
50379
|
}
|
|
50348
50380
|
if (options.versions !== void 0) {
|
|
50349
50381
|
app.all(`${prefix}/:version/*`, async (c, next) => {
|
|
@@ -50385,7 +50417,7 @@ function createCmsApp(options) {
|
|
|
50385
50417
|
handler: null,
|
|
50386
50418
|
error: null
|
|
50387
50419
|
}));
|
|
50388
|
-
const ready = import("./handler-
|
|
50420
|
+
const ready = import("./handler-GED3I7Z2-HZAD77OI.js").then(({ createGraphQLHandler }) => {
|
|
50389
50421
|
gqlPasses.forEach((pass, i) => {
|
|
50390
50422
|
const mount = mounts[i];
|
|
50391
50423
|
try {
|
|
@@ -50409,7 +50441,8 @@ function createCmsApp(options) {
|
|
|
50409
50441
|
endpoint: mount.path,
|
|
50410
50442
|
currentVersion: model.current,
|
|
50411
50443
|
...projection !== void 0 && { projection },
|
|
50412
|
-
...currentProjection !== void 0 && { currentProjection }
|
|
50444
|
+
...currentProjection !== void 0 && { currentProjection },
|
|
50445
|
+
currentFieldKeyMaps: fieldKeyMaps
|
|
50413
50446
|
}
|
|
50414
50447
|
);
|
|
50415
50448
|
} catch (err) {
|
|
@@ -50712,18 +50745,12 @@ async function runDev(options, deps) {
|
|
|
50712
50745
|
await generateRoutes2(registry2, manguitoDir);
|
|
50713
50746
|
await generateForms(registry2, join13(manguitoDir, "forms"));
|
|
50714
50747
|
await generateNav(registry2, manguitoDir);
|
|
50715
|
-
const
|
|
50716
|
-
|
|
50717
|
-
|
|
50718
|
-
printValidationErrors(snapshotsResult.errors, "Version snapshot errors", "manguito version:diff");
|
|
50748
|
+
const versionsResult = loadProjectVersionModel(resolveSchemaConfig(cwd, config3), registry2);
|
|
50749
|
+
if (!versionsResult.ok) {
|
|
50750
|
+
printValidationErrors(versionsResult.errors, "Version model errors", "manguito validate");
|
|
50719
50751
|
process.exit(1);
|
|
50720
50752
|
}
|
|
50721
|
-
const
|
|
50722
|
-
if (!versionModelResult.ok) {
|
|
50723
|
-
printValidationErrors(versionModelResult.errors, "Version model errors", "manguito version:diff");
|
|
50724
|
-
process.exit(1);
|
|
50725
|
-
}
|
|
50726
|
-
const versionModel = reduceVersionModel(versionModelResult.value);
|
|
50753
|
+
const versionModel = reduceVersionModel(versionsResult.value.model);
|
|
50727
50754
|
const resolverMap = await loadProgrammaticResolvers(cwd, config3.programmatic.dir);
|
|
50728
50755
|
const adapter = createCmsApp({
|
|
50729
50756
|
name: config3.name,
|
|
@@ -50862,20 +50889,13 @@ async function onSchemaFileChange(args) {
|
|
|
50862
50889
|
await generateRoutes2(registry2, manguitoDir);
|
|
50863
50890
|
await generateForms(registry2, join13(manguitoDir, "forms"));
|
|
50864
50891
|
await generateNav(registry2, manguitoDir);
|
|
50865
|
-
const
|
|
50866
|
-
|
|
50867
|
-
|
|
50868
|
-
printValidationErrors(snapshotsResult.errors, "Version snapshot errors", "manguito version:diff");
|
|
50892
|
+
const versionsResult = loadProjectVersionModel(resolveSchemaConfig(cwd, config3), registry2);
|
|
50893
|
+
if (!versionsResult.ok) {
|
|
50894
|
+
printValidationErrors(versionsResult.errors, "Version model errors", "manguito validate");
|
|
50869
50895
|
process.stderr.write("\u26A0 Changes not applied.\n");
|
|
50870
50896
|
return;
|
|
50871
50897
|
}
|
|
50872
|
-
const
|
|
50873
|
-
if (!versionModelResult.ok) {
|
|
50874
|
-
printValidationErrors(versionModelResult.errors, "Version model errors", "manguito version:diff");
|
|
50875
|
-
process.stderr.write("\u26A0 Changes not applied.\n");
|
|
50876
|
-
return;
|
|
50877
|
-
}
|
|
50878
|
-
const versionModel = reduceVersionModel(versionModelResult.value);
|
|
50898
|
+
const versionModel = reduceVersionModel(versionsResult.value.model);
|
|
50879
50899
|
try {
|
|
50880
50900
|
await runDevMigration(drizzleConfigPath);
|
|
50881
50901
|
} catch (err) {
|
|
@@ -51333,10 +51353,16 @@ async function runValidate(options, deps) {
|
|
|
51333
51353
|
parsedRoutesDef = routesParseResult.value;
|
|
51334
51354
|
}
|
|
51335
51355
|
}
|
|
51356
|
+
let liveVersions = null;
|
|
51336
51357
|
if (allErrors.length === 0 && parsedRoles !== null && parsedRoutesDef !== null) {
|
|
51337
51358
|
const registry2 = buildSchemaRegistry(parsedSchemas, parsedRoutesDef, parsedRoles);
|
|
51338
51359
|
const crossRefErrors = validateCrossReferences(registry2, config3.api.media?.max_file_size);
|
|
51339
51360
|
allErrors.push(...crossRefErrors);
|
|
51361
|
+
if (crossRefErrors.length === 0) {
|
|
51362
|
+
const versions = loadProjectVersionModel(resolveSchemaConfig(cwd, config3), registry2);
|
|
51363
|
+
if (versions.ok) liveVersions = versions.value.model.live;
|
|
51364
|
+
else allErrors.push(...versions.errors);
|
|
51365
|
+
}
|
|
51340
51366
|
}
|
|
51341
51367
|
if (allErrors.length > 0) {
|
|
51342
51368
|
printValidationErrors(allErrors, "Validation errors", "manguito validate");
|
|
@@ -51351,6 +51377,7 @@ async function runValidate(options, deps) {
|
|
|
51351
51377
|
);
|
|
51352
51378
|
printSuccess("roles.json valid");
|
|
51353
51379
|
printSuccess("routes.json valid");
|
|
51380
|
+
if (liveVersions !== null) printSuccess(`Versions valid (live: ${liveVersions.join(", ")})`);
|
|
51354
51381
|
process.stdout.write("\nNo errors found.\n");
|
|
51355
51382
|
}
|
|
51356
51383
|
|
|
@@ -51664,7 +51691,7 @@ function formatField(change) {
|
|
|
51664
51691
|
}
|
|
51665
51692
|
}
|
|
51666
51693
|
function formatSchemaChange(change) {
|
|
51667
|
-
const header = change.from === null ? `Working schema \u2014 nothing has been
|
|
51694
|
+
const header = change.from === null ? `Working schema \u2014 nothing has been created yet, so ${change.to} would be the first version` : `Working schema vs ${change.from} (highest snapshot) \u2014 would become ${change.to}`;
|
|
51668
51695
|
if (change.identical) {
|
|
51669
51696
|
return `${header}
|
|
51670
51697
|
|
|
@@ -51678,6 +51705,38 @@ No column added, renamed, tombstoned or restored.`;
|
|
|
51678
51705
|
});
|
|
51679
51706
|
return [header, "", ...blocks].join("\n");
|
|
51680
51707
|
}
|
|
51708
|
+
function driftSummary(change) {
|
|
51709
|
+
const counts = { renamed: 0, tombstoned: 0, added: 0, restored: 0 };
|
|
51710
|
+
for (const type of change.types) {
|
|
51711
|
+
for (const field of type.fields) counts[field.kind]++;
|
|
51712
|
+
}
|
|
51713
|
+
const parts = [];
|
|
51714
|
+
if (counts.renamed > 0) parts.push(`${counts.renamed} renamed`);
|
|
51715
|
+
if (counts.tombstoned > 0) parts.push(`${counts.tombstoned} removed`);
|
|
51716
|
+
if (counts.added > 0) parts.push(`${counts.added} added`);
|
|
51717
|
+
if (counts.restored > 0) parts.push(`${counts.restored} restored`);
|
|
51718
|
+
if (parts.length > 0) return `${parts.join(", ")} since`;
|
|
51719
|
+
if (change.identical) return "identical to current";
|
|
51720
|
+
const newTypes = change.types.filter((t) => t.status === "added").length;
|
|
51721
|
+
return `${newTypes} new type${newTypes === 1 ? "" : "s"} since`;
|
|
51722
|
+
}
|
|
51723
|
+
function formatVersionList(input2) {
|
|
51724
|
+
const { prefix, current, older } = input2;
|
|
51725
|
+
if (older.length === 0) {
|
|
51726
|
+
return `No versions created yet. Your working schema is ${current}, served at ${prefix}/${current} and ${prefix}.
|
|
51727
|
+
Run \`manguito version:create\` before making a breaking change.`;
|
|
51728
|
+
}
|
|
51729
|
+
const rows = [
|
|
51730
|
+
...older.map((o) => [o.version, `${prefix}/${o.version}`, driftSummary(o.change)]),
|
|
51731
|
+
[current, `${prefix}/${current}`, `current \u2014 also ${prefix}`]
|
|
51732
|
+
];
|
|
51733
|
+
const nameWidth = Math.max(...rows.map((r) => r[0].length));
|
|
51734
|
+
const pathWidth = Math.max(...rows.map((r) => r[1].length));
|
|
51735
|
+
return [
|
|
51736
|
+
`Live versions (${rows.length}):`,
|
|
51737
|
+
...rows.map(([name, url2, note]) => ` ${name.padEnd(nameWidth)} ${url2.padEnd(pathWidth)} ${note}`)
|
|
51738
|
+
].join("\n");
|
|
51739
|
+
}
|
|
51681
51740
|
|
|
51682
51741
|
// src/commands/version-fs.ts
|
|
51683
51742
|
init_esm_shims();
|
|
@@ -51735,17 +51794,19 @@ async function loadVersionContext(options, deps, command) {
|
|
|
51735
51794
|
const config3 = await resolveConfig(deps.cwd);
|
|
51736
51795
|
const schema = resolveSchemaConfig(deps.cwd, config3);
|
|
51737
51796
|
const registry2 = loadWorkingRegistry(deps.cwd, config3, command);
|
|
51738
|
-
const
|
|
51739
|
-
if (!
|
|
51740
|
-
printValidationErrors(
|
|
51797
|
+
const versions = loadProjectVersionModel(schema, registry2);
|
|
51798
|
+
if (!versions.ok) {
|
|
51799
|
+
printValidationErrors(versions.errors, "Version model errors", command);
|
|
51741
51800
|
process.exit(1);
|
|
51742
51801
|
}
|
|
51743
|
-
|
|
51744
|
-
|
|
51745
|
-
|
|
51746
|
-
|
|
51747
|
-
|
|
51748
|
-
|
|
51802
|
+
return {
|
|
51803
|
+
schema,
|
|
51804
|
+
registry: registry2,
|
|
51805
|
+
snapshots: versions.value.snapshots,
|
|
51806
|
+
model: versions.value.model,
|
|
51807
|
+
// Same default `dev` uses: the prefix the served routes actually carry.
|
|
51808
|
+
apiPrefix: config3.api.prefix ?? "/api"
|
|
51809
|
+
};
|
|
51749
51810
|
}
|
|
51750
51811
|
function orphanedTombstoneErrors(input2) {
|
|
51751
51812
|
const remaining = input2.snapshots.filter((s) => s.version !== input2.retiring);
|
|
@@ -51753,14 +51814,22 @@ function orphanedTombstoneErrors(input2) {
|
|
|
51753
51814
|
if (model.ok) return [];
|
|
51754
51815
|
return model.errors.filter((e) => e.code === "ORPHANED_TOMBSTONE");
|
|
51755
51816
|
}
|
|
51817
|
+
var VERSION_CUT_DEPRECATION = "`version:cut` is deprecated \u2014 use `version:create`. It will be removed in a future release.\n";
|
|
51756
51818
|
function registerVersion(program2) {
|
|
51757
|
-
program2.command("version:diff").description("Show what
|
|
51819
|
+
program2.command("version:diff").description("Show what creating a new version would freeze").option("--env <path>", "path to .env file to load").action(async (options) => {
|
|
51758
51820
|
await runVersionDiff(options, { cwd: process.cwd() });
|
|
51759
51821
|
});
|
|
51760
|
-
program2.command("version:
|
|
51761
|
-
await
|
|
51822
|
+
program2.command("version:create").description("Create a version: freeze the working schema so it keeps being served unchanged").option("--env <path>", "path to .env file to load").option("--yes", "skip the confirmation prompt").action(async (options) => {
|
|
51823
|
+
await runVersionCreate(options, { cwd: process.cwd(), prompt: createPromptAdapter() });
|
|
51824
|
+
});
|
|
51825
|
+
program2.command("version:list").description("List the versions being served and how far each is behind the working schema").option("--env <path>", "path to .env file to load").action(async (options) => {
|
|
51826
|
+
await runVersionList(options, { cwd: process.cwd() });
|
|
51827
|
+
});
|
|
51828
|
+
program2.command("version:cut", { hidden: true }).option("--env <path>", "path to .env file to load").option("--yes", "skip the confirmation prompt").action(async (options) => {
|
|
51829
|
+
process.stderr.write(VERSION_CUT_DEPRECATION);
|
|
51830
|
+
await runVersionCreate(options, { cwd: process.cwd(), prompt: createPromptAdapter() });
|
|
51762
51831
|
});
|
|
51763
|
-
program2.command("version:retire <version>").description("Stop serving a
|
|
51832
|
+
program2.command("version:retire <version>").description("Stop serving a created version and delete its snapshot").option("--env <path>", "path to .env file to load").option("--yes", "skip the confirmation prompt").action(async (version3, options) => {
|
|
51764
51833
|
await runVersionRetire(version3, options, { cwd: process.cwd(), prompt: createPromptAdapter() });
|
|
51765
51834
|
});
|
|
51766
51835
|
}
|
|
@@ -51776,15 +51845,15 @@ async function runVersionDiff(options, deps) {
|
|
|
51776
51845
|
if (change.identical) {
|
|
51777
51846
|
process.stdout.write(
|
|
51778
51847
|
`
|
|
51779
|
-
Nothing to
|
|
51848
|
+
Nothing to create \u2014 no column was added, renamed, tombstoned or restored, so ${ctx.model.current} would expose the same contract.
|
|
51780
51849
|
`
|
|
51781
51850
|
);
|
|
51782
51851
|
return;
|
|
51783
51852
|
}
|
|
51784
|
-
printSuccess(`
|
|
51853
|
+
printSuccess(`Creating a version now would write ${ctx.schema.base_path}/versions/${ctx.model.current}/`);
|
|
51785
51854
|
}
|
|
51786
|
-
async function
|
|
51787
|
-
const ctx = await loadVersionContext(options, deps, "manguito version:
|
|
51855
|
+
async function runVersionCreate(options, deps) {
|
|
51856
|
+
const ctx = await loadVersionContext(options, deps, "manguito version:create");
|
|
51788
51857
|
const from = highestSnapshot(ctx.snapshots);
|
|
51789
51858
|
const version3 = ctx.model.current;
|
|
51790
51859
|
const change = describeSchemaChange({
|
|
@@ -51793,8 +51862,8 @@ async function runVersionCut(options, deps) {
|
|
|
51793
51862
|
});
|
|
51794
51863
|
if (change.identical) {
|
|
51795
51864
|
printGuidedError(
|
|
51796
|
-
`No column was added, renamed, tombstoned or restored since ${from?.version ?? "the
|
|
51797
|
-
"Changes to paragraph, programmatic, many-to-many and enum definitions are not versioned and need no new version. A live version commits you to retaining every column it exposes, so
|
|
51865
|
+
`No column was added, renamed, tombstoned or restored since ${from?.version ?? "the working schema began"} \u2014 creating ${version3} would freeze an identical contract.`,
|
|
51866
|
+
"Changes to paragraph, programmatic, many-to-many and enum definitions are not versioned and need no new version. A live version commits you to retaining every column it exposes, so create one only when a column actually changed \u2014 or run `manguito version:retire <version>` if you meant to shrink the live set."
|
|
51798
51867
|
);
|
|
51799
51868
|
process.exit(1);
|
|
51800
51869
|
}
|
|
@@ -51803,22 +51872,32 @@ async function runVersionCut(options, deps) {
|
|
|
51803
51872
|
if (fs6.existsSync(target)) {
|
|
51804
51873
|
printGuidedError(
|
|
51805
51874
|
`${target} already exists.`,
|
|
51806
|
-
"A snapshot directory is never overwritten. Remove or rename it, then run version:
|
|
51875
|
+
"A snapshot directory is never overwritten. Remove or rename it, then run version:create again."
|
|
51807
51876
|
);
|
|
51808
51877
|
process.exit(1);
|
|
51809
51878
|
}
|
|
51810
51879
|
process.stdout.write(`${formatSchemaChange(change)}
|
|
51811
51880
|
|
|
51812
51881
|
`);
|
|
51813
|
-
const
|
|
51882
|
+
const next = `v${Number.parseInt(version3.slice(1), 10) + 1}`;
|
|
51883
|
+
const liveAfter = [...ctx.model.live.filter((v2) => v2 !== version3), version3, next];
|
|
51884
|
+
if (from === null) {
|
|
51885
|
+
const prefix = ctx.apiPrefix;
|
|
51886
|
+
process.stdout.write(
|
|
51887
|
+
`This creates ${version3} from your working schema. ${prefix}/${version3} keeps serving it
|
|
51888
|
+
unchanged; your working schema becomes ${next}, served at ${prefix}/${next} and ${prefix}.
|
|
51889
|
+
|
|
51890
|
+
`
|
|
51891
|
+
);
|
|
51892
|
+
}
|
|
51814
51893
|
process.stdout.write(
|
|
51815
|
-
`After
|
|
51816
|
-
schema \u2014 as a live field or a tombstone \u2014 until you retire them.
|
|
51894
|
+
`After creating ${version3}, these versions are live: ${liveAfter.join(" ")}. Every column they
|
|
51895
|
+
expose must stay in the schema \u2014 as a live field or a tombstone \u2014 until you retire them.
|
|
51817
51896
|
|
|
51818
51897
|
`
|
|
51819
51898
|
);
|
|
51820
51899
|
if (options.yes !== true) {
|
|
51821
|
-
const ok = await deps.prompt.confirm(`
|
|
51900
|
+
const ok = await deps.prompt.confirm(`Create ${version3} from the working schema?`);
|
|
51822
51901
|
if (!ok) {
|
|
51823
51902
|
process.stdout.write("Cancelled. Nothing was written.\n");
|
|
51824
51903
|
return;
|
|
@@ -51833,8 +51912,19 @@ schema \u2014 as a live field or a tombstone \u2014 until you retire them.
|
|
|
51833
51912
|
);
|
|
51834
51913
|
process.exit(1);
|
|
51835
51914
|
}
|
|
51836
|
-
printSuccess(`
|
|
51837
|
-
process.stdout.write(`Live: ${
|
|
51915
|
+
printSuccess(`Created ${version3} at ${target}`);
|
|
51916
|
+
process.stdout.write(`Live: ${liveAfter.join(" ")}. Working schema is now ${next}.
|
|
51917
|
+
`);
|
|
51918
|
+
}
|
|
51919
|
+
async function runVersionList(options, deps) {
|
|
51920
|
+
const ctx = await loadVersionContext(options, deps, "manguito version:list");
|
|
51921
|
+
const current = ctx.model.current;
|
|
51922
|
+
const versionNumber3 = (v2) => Number.parseInt(v2.slice(1), 10);
|
|
51923
|
+
const older = [...ctx.snapshots].sort((a2, b2) => versionNumber3(a2.version) - versionNumber3(b2.version)).map((s) => ({
|
|
51924
|
+
version: s.version,
|
|
51925
|
+
change: describeSchemaChange({ from: s, to: { version: current, registry: ctx.registry } })
|
|
51926
|
+
}));
|
|
51927
|
+
process.stdout.write(`${formatVersionList({ prefix: ctx.apiPrefix, current, older })}
|
|
51838
51928
|
`);
|
|
51839
51929
|
}
|
|
51840
51930
|
async function runVersionRetire(version3, options, deps) {
|
|
@@ -51846,8 +51936,8 @@ async function runVersionRetire(version3, options, deps) {
|
|
|
51846
51936
|
if (!ctx.snapshots.some((s) => s.version === version3)) {
|
|
51847
51937
|
const existing = ctx.snapshots.map((s) => s.version).join(", ");
|
|
51848
51938
|
printGuidedError(
|
|
51849
|
-
`${version3} is not a
|
|
51850
|
-
existing === "" ? "No versions have been
|
|
51939
|
+
`${version3} is not a created version.`,
|
|
51940
|
+
existing === "" ? "No versions have been created yet \u2014 run `manguito version:create` first." : `Created versions: ${existing}.`
|
|
51851
51941
|
);
|
|
51852
51942
|
process.exit(1);
|
|
51853
51943
|
}
|
|
@@ -51855,8 +51945,8 @@ async function runVersionRetire(version3, options, deps) {
|
|
|
51855
51945
|
if (highest !== null && highest.version === version3) {
|
|
51856
51946
|
const target = path6.join(ctx.schema.base_path, "versions", version3);
|
|
51857
51947
|
printGuidedError(
|
|
51858
|
-
`${version3} is the newest
|
|
51859
|
-
`The working schema is ${ctx.model.current} because ${version3} is the highest snapshot \u2014 retiring it would renumber the working schema back onto ${version3}, and anyone pinned to ${version3} would get a different contract.
|
|
51948
|
+
`${version3} is the newest created version and cannot be retired.`,
|
|
51949
|
+
`The working schema is ${ctx.model.current} because ${version3} is the highest snapshot \u2014 retiring it would renumber the working schema back onto ${version3}, and anyone pinned to ${version3} would get a different contract. Creating a newer version with a real contract change makes ${version3} retirable. If ${version3} was created in error, delete ${target} by hand \u2014 safe only while nothing yet consumes ${version3}, since deleting the newest snapshot renumbers the working schema back onto it.`
|
|
51860
51950
|
);
|
|
51861
51951
|
process.exit(1);
|
|
51862
51952
|
}
|
|
@@ -51882,7 +51972,7 @@ longer exposed by any live version. You must then delete these fields:
|
|
|
51882
51972
|
`);
|
|
51883
51973
|
}
|
|
51884
51974
|
process.stdout.write(
|
|
51885
|
-
"Until you do, `manguito validate` will
|
|
51975
|
+
"Until you do, `manguito validate` and `manguito build` will fail. Deleting them\nshrinks the union and lets the next migration DROP those columns.\n\n"
|
|
51886
51976
|
);
|
|
51887
51977
|
}
|
|
51888
51978
|
if (options.yes !== true) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobbykim/manguito-cms-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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-
|
|
32
|
+
"@bobbykim/manguito-cms-admin": "0.4.3",
|
|
33
|
+
"@bobbykim/manguito-cms-api": "0.7.0",
|
|
33
34
|
"@bobbykim/manguito-cms-db": "0.1.6",
|
|
34
|
-
"@bobbykim/manguito-cms-core": "0.6.0"
|
|
35
|
-
"@bobbykim/manguito-cms-admin": "0.4.3"
|
|
35
|
+
"@bobbykim/manguito-cms-core": "0.6.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^25.9.5",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "tsup",
|
|
44
44
|
"dev": "tsup --watch",
|
|
45
|
-
"test": "dotenv -e
|
|
45
|
+
"test": "dotenv -e ../../.env.test -- vitest run",
|
|
46
46
|
"lint": "eslint src",
|
|
47
47
|
"typecheck": "tsc --noEmit"
|
|
48
48
|
}
|