@bobbykim/manguito-cms-cli 0.6.0 → 0.6.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.
@@ -49,7 +49,7 @@ var init_esm_shims = __esm({
49
49
  }
50
50
  });
51
51
 
52
- // ../api/dist/chunk-D5AO4BTL.mjs
52
+ // ../api/dist/chunk-JVP33FI2.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-D5AO4BTL.mjs
1215
+ // ../api/dist/chunk-JVP33FI2.mjs
1216
1216
  function isColumnBacked(field) {
1217
1217
  const col = field.db_column;
1218
1218
  if (col === null) return false;
@@ -1227,7 +1227,8 @@ function buildFieldKeyMap(pairs, allLabels, droppedKeys) {
1227
1227
  labelToColumn.set(label, column);
1228
1228
  columnToLabel.set(column, label);
1229
1229
  }
1230
- for (const label of allLabels) {
1230
+ for (const { label, ownColumn } of allLabels) {
1231
+ if (ownColumn === label) continue;
1231
1232
  const columnOwner = columnToLabel.get(label);
1232
1233
  if (columnOwner !== void 0 && columnOwner !== label) {
1233
1234
  throw new Error(
@@ -1272,7 +1273,12 @@ function createFieldKeyMap(fields) {
1272
1273
  }
1273
1274
  return buildFieldKeyMap(
1274
1275
  pairs,
1275
- fields.map((f) => f.name),
1276
+ fields.map((f) => ({
1277
+ label: f.name,
1278
+ // Conditional spread, not a plain assignment — exactOptionalPropertyTypes
1279
+ // forbids present-and-undefined.
1280
+ ...isColumnBacked(f) && { ownColumn: f.db_column.column_name }
1281
+ })),
1276
1282
  droppedKeys
1277
1283
  );
1278
1284
  }
@@ -1289,7 +1295,13 @@ function createFieldKeyMapFromProjection(projectionType, allFields) {
1289
1295
  for (const label of projected) dropped.delete(label);
1290
1296
  return buildFieldKeyMap(
1291
1297
  pairs,
1292
- allFields.map((f) => f.name),
1298
+ allFields.map((f) => ({
1299
+ label: f.name,
1300
+ // See buildFieldKeyMap's collision check: this is what lets it tell a
1301
+ // version exposing a field's own column under a new label (fine) from a
1302
+ // label that reuses another field's column (not fine).
1303
+ ...isColumnBacked(f) && { ownColumn: f.db_column.column_name }
1304
+ })),
1293
1305
  dropped
1294
1306
  );
1295
1307
  }
@@ -11,7 +11,7 @@ import {
11
11
  init_esm_shims,
12
12
  isColumnBacked,
13
13
  resolveRelationField
14
- } from "./chunk-STFSR5LP.js";
14
+ } from "./chunk-SW3T2M64.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-X7JDHJAL.mjs
10789
+ // ../api/dist/handler-XZVDLKZ2.mjs
10790
10790
  init_esm_shims();
10791
10791
 
10792
- // ../api/dist/chunk-ARZXLNZS.mjs
10792
+ // ../api/dist/chunk-XNXM4W4K.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-ARZXLNZS.mjs
30125
+ // ../api/dist/chunk-XNXM4W4K.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-ARZXLNZS.mjs
30883
+ // ../api/dist/chunk-XNXM4W4K.mjs
30884
30884
  var DateTimeScalar = new GraphQLScalarType({
30885
30885
  name: "DateTime",
30886
30886
  description: "ISO-8601 date-time string",
@@ -30979,10 +30979,13 @@ var SORTABLE = [
30979
30979
  { gql: "createdAt", value: "created_at" },
30980
30980
  { gql: "updatedAt", value: "updated_at" }
30981
30981
  ];
30982
- function buildSortFieldEnum(typeName) {
30982
+ function buildSortFieldEnum(typeName, fieldKeys) {
30983
+ const resolvable = SORTABLE.filter(
30984
+ (s) => s.gql === "createdAt" || s.gql === "updatedAt" || fieldKeys === void 0 || fieldKeys.columnFor(s.value) !== void 0
30985
+ );
30983
30986
  return new GraphQLEnumType({
30984
30987
  name: `${typeName}SortField`,
30985
- values: Object.fromEntries(SORTABLE.map((s) => [s.gql, { value: s.value }]))
30988
+ values: Object.fromEntries(resolvable.map((s) => [s.gql, { value: s.value }]))
30986
30989
  });
30987
30990
  }
30988
30991
  function comparableFilterInput(name, scalar) {
@@ -31026,18 +31029,19 @@ function filterInputForField(fieldType) {
31026
31029
  if (scalar === null) return null;
31027
31030
  return StringFilter;
31028
31031
  }
31029
- function buildFilterInputType(type) {
31032
+ function buildFilterInputType(type, viewFields) {
31030
31033
  const fields = {};
31031
31034
  fields["id"] = { type: IDFilter };
31032
31035
  fields["published"] = { type: BooleanFilter };
31033
31036
  fields["createdAt"] = { type: DateTimeFilter };
31034
31037
  fields["updatedAt"] = { type: DateTimeFilter };
31035
31038
  if (type.schema_type === "content-type") fields["slug"] = { type: StringFilter };
31036
- for (const f of type.fields) {
31039
+ const entries = viewFields !== void 0 ? viewFields.map((v) => ({ field: v.field, exposedAs: v.exposedAs })) : type.fields.filter((f) => f.removed !== true).map((f) => ({ field: f, exposedAs: f.name }));
31040
+ for (const { field: f, exposedAs } of entries) {
31037
31041
  if (f.field_type === "programmatic" || f.field_type === "paragraph") continue;
31038
31042
  if (f.field_type === "image" || f.field_type === "video" || f.field_type === "file") continue;
31039
31043
  const input = filterInputForField(f.field_type);
31040
- if (input) fields[buildFieldNameMap([f.name]).toGraphql(f.name)] = { type: input };
31044
+ if (input) fields[buildFieldNameMap([exposedAs]).toGraphql(exposedAs)] = { type: input };
31041
31045
  }
31042
31046
  if (Object.keys(fields).length === 0) return null;
31043
31047
  return new GraphQLInputObjectType({ name: `${graphqlTypeName(type.name)}Filter`, fields });
@@ -31070,32 +31074,41 @@ function codeError(code, message) {
31070
31074
  function isPublished(item) {
31071
31075
  return !!item && item["published"] === true;
31072
31076
  }
31073
- function resolveFieldValue(field, row) {
31077
+ function resolveFieldValue(field, row, fallback) {
31074
31078
  const key = isColumnBacked(field) ? field.db_column.column_name : field.name;
31075
- return row[key] ?? null;
31079
+ const value = row[key];
31080
+ if ((value === null || value === void 0) && fallback !== void 0) return fallback;
31081
+ return value ?? null;
31076
31082
  }
31077
- function scalarFieldResolver(field) {
31078
- return (parent) => resolveFieldValue(field, parent);
31083
+ function scalarFieldResolver(field, fallback) {
31084
+ return (parent) => resolveFieldValue(field, parent, fallback);
31079
31085
  }
31080
31086
  function relationFieldResolver(typeName, schemaFieldName) {
31081
31087
  return (parent, _args, ctx) => ctx.loaders.load(typeName, schemaFieldName, parent);
31082
31088
  }
31083
- function programmaticFieldResolver(typeName, schemaFieldName, mediaFieldNames = [], fieldKeys) {
31089
+ function programmaticFieldResolver(typeName, schemaFieldName, mediaFields = [], fieldKeys) {
31084
31090
  return async (parent, _args, ctx) => {
31085
31091
  let p = ctx.programmaticMemo.get(parent);
31086
31092
  if (!p) {
31087
- p = resolveProgrammaticRow(typeName, parent, ctx, mediaFieldNames, fieldKeys);
31093
+ p = resolveProgrammaticRow(typeName, parent, ctx, mediaFields, fieldKeys);
31088
31094
  ctx.programmaticMemo.set(parent, p);
31089
31095
  }
31090
31096
  return (await p)[schemaFieldName];
31091
31097
  };
31092
31098
  }
31093
- async function resolveProgrammaticRow(typeName, parent, ctx, mediaFieldNames, fieldKeys) {
31099
+ async function resolveProgrammaticRow(typeName, parent, ctx, mediaFields, fieldKeys) {
31094
31100
  const toLabels = (row) => fieldKeys ? fieldKeys.toLabels(row) : row;
31095
- if (mediaFieldNames.length === 0) return ctx.resolver.resolveItem(typeName, toLabels(parent));
31101
+ if (mediaFields.length === 0) return ctx.resolver.resolveItem(typeName, toLabels(parent));
31096
31102
  const enriched = { ...parent };
31097
- await Promise.all(mediaFieldNames.map((name) => ctx.loaders.load(typeName, name, enriched)));
31098
- return ctx.resolver.resolveItem(typeName, toLabels(enriched));
31103
+ await Promise.all(mediaFields.map((m) => ctx.loaders.load(typeName, m.name, enriched)));
31104
+ return ctx.resolver.resolveItem(typeName, relabelMedia(toLabels(enriched), enriched, mediaFields));
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;
31099
31112
  }
31100
31113
  function collectionResolver(typeName, nameMap, fieldKeys) {
31101
31114
  return async (_root, args, ctx) => {
@@ -31167,7 +31180,7 @@ var MEDIA = new GraphQLObjectType({
31167
31180
  duration: { type: GraphQLInt, resolve: (m) => m.duration }
31168
31181
  }
31169
31182
  });
31170
- function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
31183
+ function buildGraphQLSchema(registry, fieldKeyMaps = {}, view) {
31171
31184
  const objectTypes = /* @__PURE__ */ new Map();
31172
31185
  const enumTypes = /* @__PURE__ */ new Map();
31173
31186
  for (const [name, enumType] of Object.entries(registry.enum_types)) {
@@ -31186,14 +31199,14 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
31186
31199
  );
31187
31200
  }
31188
31201
  }
31189
- function outputTypeForField(field) {
31202
+ function outputTypeForField(field, required) {
31190
31203
  const scalar = scalarOutputType(field.field_type);
31191
- if (scalar) return field.required ? new GraphQLNonNull(scalar) : scalar;
31204
+ if (scalar) return required ? new GraphQLNonNull(scalar) : scalar;
31192
31205
  if (field.field_type === "enum") {
31193
31206
  const ref = field.ui_component.component === "select" ? field.ui_component.enum_ref : void 0;
31194
31207
  const et = ref ? enumTypes.get(ref) : void 0;
31195
31208
  const t = et ?? GraphQLString;
31196
- return field.required ? new GraphQLNonNull(t) : t;
31209
+ return required ? new GraphQLNonNull(t) : t;
31197
31210
  }
31198
31211
  if (field.field_type === "image" || field.field_type === "video" || field.field_type === "file") {
31199
31212
  return MEDIA;
@@ -31223,8 +31236,17 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
31223
31236
  }
31224
31237
  return GraphQLString;
31225
31238
  }
31239
+ function exposedFields(type) {
31240
+ return type.fields.filter((f) => f.removed !== true);
31241
+ }
31242
+ function viewFieldsFor(machineName, type) {
31243
+ return view?.[machineName]?.fields ?? exposedFields(type).map((f) => ({ field: f, exposedAs: f.name, required: f.required }));
31244
+ }
31226
31245
  function buildObjectType(machineName, type) {
31227
- const mediaFieldNames = type.fields.filter((f) => f.field_type === "image" || f.field_type === "video" || f.field_type === "file").map((f) => f.name);
31246
+ const visible = viewFieldsFor(machineName, type);
31247
+ const mediaFields = visible.filter(
31248
+ (v) => v.field.field_type === "image" || v.field.field_type === "video" || v.field.field_type === "file"
31249
+ ).map((v) => ({ name: v.field.name, exposedAs: v.exposedAs }));
31228
31250
  return new GraphQLObjectType({
31229
31251
  name: graphqlTypeName(machineName),
31230
31252
  fields: () => {
@@ -31238,23 +31260,25 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
31238
31260
  }
31239
31261
  fields["createdAt"] = { type: new GraphQLNonNull(DateTimeScalar), resolve: (p) => p["created_at"] };
31240
31262
  fields["updatedAt"] = { type: new GraphQLNonNull(DateTimeScalar), resolve: (p) => p["updated_at"] };
31241
- for (const field of type.fields) {
31242
- const gqlName = toCamelCase(field.name);
31243
- const outType = outputTypeForField(field);
31263
+ for (const vf of visible) {
31264
+ const field = vf.field;
31265
+ const gqlName = toCamelCase(vf.exposedAs);
31266
+ const outType = outputTypeForField(field, vf.required);
31244
31267
  let resolve;
31245
31268
  if (field.field_type === "programmatic") {
31246
- resolve = programmaticFieldResolver(
31247
- machineName,
31248
- field.name,
31249
- mediaFieldNames,
31250
- fieldKeyMaps[machineName]
31251
- );
31269
+ resolve = programmaticFieldResolver(machineName, field.name, mediaFields, fieldKeyMaps[machineName]);
31252
31270
  } else if (field.field_type === "reference" || field.field_type === "paragraph" || field.field_type === "image" || field.field_type === "video" || field.field_type === "file") {
31253
31271
  resolve = relationFieldResolver(machineName, field.name);
31254
31272
  } else {
31255
- resolve = scalarFieldResolver(field);
31273
+ resolve = scalarFieldResolver(field, vf.fallback);
31256
31274
  }
31257
- fields[gqlName] = { type: outType, resolve };
31275
+ fields[gqlName] = {
31276
+ type: outType,
31277
+ resolve,
31278
+ // Conditional spread, not `deprecationReason: vf.deprecationReason`
31279
+ // — exactOptionalPropertyTypes forbids present-and-undefined.
31280
+ ...vf.deprecationReason !== void 0 && { deprecationReason: vf.deprecationReason }
31281
+ };
31258
31282
  }
31259
31283
  return fields;
31260
31284
  }
@@ -31286,7 +31310,8 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
31286
31310
  const queryFields = {};
31287
31311
  for (const [name, ct] of Object.entries(registry.content_types)) {
31288
31312
  const objType = objectTypes.get(name);
31289
- const nameMap = buildFieldNameMap(ct.fields.map((f) => f.name));
31313
+ const visible = viewFieldsFor(name, ct);
31314
+ const nameMap = buildFieldNameMap(visible.map((v) => v.exposedAs));
31290
31315
  if (ct.only_one) {
31291
31316
  queryFields[singleQueryName(name)] = { type: objType, resolve: singletonResolver(name) };
31292
31317
  continue;
@@ -31298,13 +31323,13 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
31298
31323
  meta: { type: new GraphQLNonNull(PAGE_META), resolve: (r) => r.meta }
31299
31324
  }
31300
31325
  });
31301
- const filterType = buildFilterInputType(ct);
31326
+ const filterType = buildFilterInputType(ct, visible);
31302
31327
  queryFields[collectionQueryName(name)] = {
31303
31328
  type: new GraphQLNonNull(listType),
31304
31329
  args: {
31305
31330
  page: { type: GraphQLInt },
31306
31331
  perPage: { type: GraphQLInt },
31307
- sortBy: { type: buildSortFieldEnum(graphqlTypeName(name)) },
31332
+ sortBy: { type: buildSortFieldEnum(graphqlTypeName(name), fieldKeyMaps[name]) },
31308
31333
  sortOrder: { type: SortOrderEnum },
31309
31334
  ...filterType ? { filter: { type: filterType } } : {}
31310
31335
  },
@@ -31328,7 +31353,11 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
31328
31353
  queryFields[collectionQueryName(name)] = {
31329
31354
  type: new GraphQLNonNull(listType),
31330
31355
  args: { page: { type: GraphQLInt }, perPage: { type: GraphQLInt } },
31331
- resolve: collectionResolver(name, buildFieldNameMap(tt.fields.map((f) => f.name)), fieldKeyMaps[name])
31356
+ resolve: collectionResolver(
31357
+ name,
31358
+ buildFieldNameMap(viewFieldsFor(name, tt).map((v) => v.exposedAs)),
31359
+ fieldKeyMaps[name]
31360
+ )
31332
31361
  };
31333
31362
  queryFields[singleQueryName(name)] = {
31334
31363
  type: objType,
@@ -31340,6 +31369,68 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
31340
31369
  query: new GraphQLObjectType({ name: "Query", fields: queryFields })
31341
31370
  });
31342
31371
  }
31372
+ function byColumn(fields) {
31373
+ const out = /* @__PURE__ */ new Map();
31374
+ for (const f of fields) {
31375
+ if (!isColumnBacked(f)) continue;
31376
+ out.set(f.db_column.column_name, f);
31377
+ }
31378
+ return out;
31379
+ }
31380
+ function deprecationFor(field, currentLabels, currentVersion) {
31381
+ const currentLabel = currentLabels.get(field.column_name);
31382
+ if (currentLabel === void 0) {
31383
+ return `Removed in ${currentVersion}; column retained while this version is live.`;
31384
+ }
31385
+ if (currentLabel !== field.exposed_as) {
31386
+ return `Renamed to '${currentLabel}' in ${currentVersion}.`;
31387
+ }
31388
+ return void 0;
31389
+ }
31390
+ function buildVersionView(input) {
31391
+ const { registry, projection, currentProjection, currentVersion } = input;
31392
+ const out = {};
31393
+ const sources = [
31394
+ registry.content_types,
31395
+ registry.taxonomy_types,
31396
+ registry.paragraph_types
31397
+ ];
31398
+ for (const source of sources) {
31399
+ for (const [typeName, type] of Object.entries(source)) {
31400
+ const projectionType = projection?.types[typeName];
31401
+ if (projectionType === void 0) {
31402
+ out[typeName] = {
31403
+ fields: type.fields.filter((f) => f.removed !== true).map((f) => ({ field: f, exposedAs: f.name, required: f.required }))
31404
+ };
31405
+ continue;
31406
+ }
31407
+ const columns = byColumn(type.fields);
31408
+ const currentLabels = /* @__PURE__ */ new Map();
31409
+ for (const f of currentProjection?.types[typeName]?.fields ?? []) {
31410
+ currentLabels.set(f.column_name, f.exposed_as);
31411
+ }
31412
+ const fields = [];
31413
+ for (const pf of projectionType.fields) {
31414
+ const field = columns.get(pf.column_name);
31415
+ if (field === void 0) continue;
31416
+ const reason = deprecationFor(pf, currentLabels, currentVersion);
31417
+ fields.push({
31418
+ field,
31419
+ exposedAs: pf.exposed_as,
31420
+ required: pf.required,
31421
+ ...pf.fallback !== void 0 && { fallback: pf.fallback },
31422
+ ...reason !== void 0 && { deprecationReason: reason }
31423
+ });
31424
+ }
31425
+ for (const f of type.fields) {
31426
+ if (isColumnBacked(f) || f.removed === true) continue;
31427
+ fields.push({ field: f, exposedAs: f.name, required: f.required });
31428
+ }
31429
+ out[typeName] = { fields };
31430
+ }
31431
+ }
31432
+ return out;
31433
+ }
31343
31434
  function createRelationLoaders(db, registry) {
31344
31435
  const relMaps = /* @__PURE__ */ new Map();
31345
31436
  const loaders = /* @__PURE__ */ new Map();
@@ -31407,15 +31498,29 @@ function isClientSafeGraphQLError(error) {
31407
31498
  }
31408
31499
  return false;
31409
31500
  }
31410
- function createGraphQLHandler(registry, repos, fieldKeyMaps, resolver, db, options) {
31411
- const schema = buildGraphQLSchema(registry, fieldKeyMaps);
31501
+ function createGraphQLHandler(registry, repos, fieldKeyMaps, resolver, db, options, versioning = {}) {
31502
+ let view;
31503
+ if (versioning.projection !== void 0) {
31504
+ if (versioning.currentVersion === void 0) {
31505
+ throw new Error(
31506
+ 'createGraphQLHandler: currentVersion is required when a projection is supplied \u2014 deprecation reasons name it, and an empty one ships "Removed in ." into published SDL.'
31507
+ );
31508
+ }
31509
+ view = buildVersionView({
31510
+ registry,
31511
+ projection: versioning.projection,
31512
+ currentProjection: versioning.currentProjection,
31513
+ currentVersion: versioning.currentVersion
31514
+ });
31515
+ }
31516
+ const schema = buildGraphQLSchema(registry, fieldKeyMaps, view);
31412
31517
  const { plugins } = buildArmorPlugin({
31413
31518
  maxDepth: options.maxDepth,
31414
31519
  maxComplexity: options.maxComplexity
31415
31520
  });
31416
31521
  const yoga = createYoga({
31417
31522
  schema,
31418
- graphqlEndpoint: "/graphql",
31523
+ graphqlEndpoint: versioning.endpoint ?? "/graphql",
31419
31524
  graphiql: options.graphiql,
31420
31525
  landingPage: false,
31421
31526
  maskedErrors: {
package/dist/index.js CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  resolveRelationField,
40
40
  sql,
41
41
  tracer
42
- } from "./chunk-STFSR5LP.js";
42
+ } from "./chunk-SW3T2M64.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({
@@ -30444,7 +30444,7 @@ function buildProjections(input2) {
30444
30444
  const fields = type.fields.filter((f) => isColumnBacked2(f) && f.removed !== true).map((f) => {
30445
30445
  const column_name = f.db_column.column_name;
30446
30446
  const fallback = fallbacks.get(`${typeName}.${column_name}`);
30447
- return fallback === void 0 ? { column_name, exposed_as: f.name } : { column_name, exposed_as: f.name, fallback };
30447
+ return fallback === void 0 ? { column_name, exposed_as: f.name, required: f.required } : { column_name, exposed_as: f.name, required: f.required, fallback };
30448
30448
  });
30449
30449
  types3[typeName] = { fields };
30450
30450
  }
@@ -47129,7 +47129,7 @@ function createSecurityHeadersMiddleware(options = {}) {
47129
47129
  c.res.headers.set("X-Content-Type-Options", "nosniff");
47130
47130
  c.res.headers.set("X-Frame-Options", "DENY");
47131
47131
  c.res.headers.set("Referrer-Policy", "no-referrer");
47132
- const isGraphiql = graphiqlPath !== void 0 && c.req.path === graphiqlPath;
47132
+ const isGraphiql = graphiqlPath !== void 0 && (c.req.path === graphiqlPath || c.req.path.startsWith(`${graphiqlPath}/`));
47133
47133
  c.res.headers.set("Content-Security-Policy", isGraphiql ? graphiqlCsp : csp);
47134
47134
  };
47135
47135
  }
@@ -49702,7 +49702,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
49702
49702
  }
49703
49703
  var API_VERSION = (() => {
49704
49704
  try {
49705
- return "0.5.0";
49705
+ return "0.6.0";
49706
49706
  } catch {
49707
49707
  return "0.0.0";
49708
49708
  }
@@ -50277,20 +50277,6 @@ function createCmsApp(options) {
50277
50277
  sortableColumns
50278
50278
  });
50279
50279
  };
50280
- const graphqlRepos = Object.fromEntries([
50281
- ...Object.entries(registry2.content_types).map(([typeName, ct2]) => [
50282
- typeName,
50283
- createDrizzleContentRepository(db, ct2.db.table_name, {
50284
- sortableColumns: sortableColumnsFor(typeName)
50285
- })
50286
- ]),
50287
- ...Object.entries(registry2.taxonomy_types).map(([typeName, tt2]) => [
50288
- typeName,
50289
- createDrizzleContentRepository(db, tt2.db.table_name, {
50290
- sortableColumns: sortableColumnsFor(typeName)
50291
- })
50292
- ])
50293
- ]);
50294
50280
  registerAuthRoutes(app, db);
50295
50281
  app.use("/admin/api/*", authMiddleware);
50296
50282
  app.use("/admin/api/*", mustChangePasswordCheck);
@@ -50388,31 +50374,107 @@ function createCmsApp(options) {
50388
50374
  registerPublicMediaRoutes(app, mediaRepo, publicPaths, listRateLimit);
50389
50375
  if (options.graphql?.enabled) {
50390
50376
  const gqlOptions = options.graphql;
50391
- let gqlHandler = null;
50392
- let initError = null;
50393
- const ready = import("./handler-X7JDHJAL-JEAY3P6W.js").then(({ createGraphQLHandler }) => {
50394
- gqlHandler = createGraphQLHandler(registry2, graphqlRepos, fieldKeyMaps, programmaticResolver, db, gqlOptions);
50377
+ const makeGraphqlRepo = (_typeName, tableName, sortableColumns) => createDrizzleContentRepository(db, tableName, { sortableColumns });
50378
+ const gqlPasses = [
50379
+ ...options.versions !== void 0 ? model.live.map((v2) => ({ version: v2, projectionVersion: v2 })) : [],
50380
+ { version: null, projectionVersion: model.current }
50381
+ ];
50382
+ const mounts = gqlPasses.map((pass) => ({
50383
+ path: pass.version === null ? "/graphql" : `/graphql/${pass.version}`,
50384
+ version: pass.version,
50385
+ handler: null,
50386
+ error: null
50387
+ }));
50388
+ const ready = import("./handler-XZVDLKZ2-2TIH4GG7.js").then(({ createGraphQLHandler }) => {
50389
+ gqlPasses.forEach((pass, i) => {
50390
+ const mount = mounts[i];
50391
+ try {
50392
+ const surface = buildVersionSurface({
50393
+ ...pass,
50394
+ prefix,
50395
+ registry: registry2,
50396
+ model,
50397
+ makeRepo: makeGraphqlRepo
50398
+ });
50399
+ const projection = model.projections[pass.projectionVersion];
50400
+ const currentProjection = model.projections[model.current];
50401
+ mount.handler = createGraphQLHandler(
50402
+ registry2,
50403
+ surface.repos,
50404
+ surface.fieldKeyMaps,
50405
+ programmaticResolver,
50406
+ db,
50407
+ gqlOptions,
50408
+ {
50409
+ endpoint: mount.path,
50410
+ currentVersion: model.current,
50411
+ ...projection !== void 0 && { projection },
50412
+ ...currentProjection !== void 0 && { currentProjection }
50413
+ }
50414
+ );
50415
+ } catch (err) {
50416
+ mount.error = err;
50417
+ const message = err instanceof Error ? err.message : String(err);
50418
+ process.stderr.write(
50419
+ `\u2717 GraphQL schema failed to initialize for ${mount.path}; it will return 500. ${message}
50420
+ `
50421
+ );
50422
+ }
50423
+ });
50395
50424
  }).catch((err) => {
50396
- initError = err;
50425
+ for (const m2 of mounts) m2.error = err;
50397
50426
  const message = err instanceof Error ? err.message : String(err);
50398
- process.stderr.write(`\u2717 GraphQL schema failed to initialize; /graphql will return 500. ${message}
50427
+ const paths = mounts.map((m2) => m2.path).join(", ");
50428
+ process.stderr.write(`\u2717 GraphQL module failed to load; ${paths} will return 500. ${message}
50399
50429
  `);
50400
50430
  });
50401
- const invokeGraphQL = async (c) => {
50402
- if (!gqlHandler && !initError) await ready;
50403
- if (!gqlHandler) {
50431
+ const invokeFor = (mount) => async (c) => {
50432
+ if (!mount.handler && !mount.error) await ready;
50433
+ if (!mount.handler) {
50404
50434
  return c.json(
50405
50435
  { ok: false, error: { code: "GRAPHQL_INIT_FAILED", message: "GraphQL schema failed to initialize" } },
50406
50436
  500
50407
50437
  );
50408
50438
  }
50409
- return gqlHandler(c, async () => {
50439
+ return mount.handler(c, async () => {
50410
50440
  });
50411
50441
  };
50412
- if (listRateLimit) {
50413
- app.all("/graphql", listRateLimit, invokeGraphQL);
50414
- } else {
50415
- app.all("/graphql", invokeGraphQL);
50442
+ for (const mount of mounts) {
50443
+ const headers = deprecationHeaders({
50444
+ requested: mount.version,
50445
+ model,
50446
+ successor: `/graphql/${model.current}`
50447
+ });
50448
+ if (headers === null) continue;
50449
+ const attach = async (c, next) => {
50450
+ await next();
50451
+ for (const [key, value] of Object.entries(headers)) c.res.headers.set(key, value);
50452
+ };
50453
+ app.use(mount.path, attach);
50454
+ }
50455
+ for (const mount of mounts) {
50456
+ if (listRateLimit) app.all(mount.path, listRateLimit, invokeFor(mount));
50457
+ else app.all(mount.path, invokeFor(mount));
50458
+ }
50459
+ if (options.versions !== void 0) {
50460
+ app.all("/graphql/:version", async (c, next) => {
50461
+ const segment = c.req.param("version");
50462
+ const kind = classifyVersion(segment, model);
50463
+ if (kind === "not-a-version" || kind === "live") return next();
50464
+ const live = model.live.join(", ");
50465
+ return c.json({
50466
+ errors: [
50467
+ {
50468
+ message: kind === "retired" ? `Version ${segment} is no longer served. Live versions: ${live}.` : `Version ${segment} does not exist. Live versions: ${live}.`,
50469
+ extensions: {
50470
+ code: kind === "retired" ? "VERSION_RETIRED" : "VERSION_UNKNOWN",
50471
+ current: model.current,
50472
+ live: model.live
50473
+ }
50474
+ }
50475
+ ]
50476
+ });
50477
+ });
50416
50478
  }
50417
50479
  }
50418
50480
  registerConfigRoute(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobbykim/manguito-cms-cli",
3
- "version": "0.6.0",
3
+ "version": "0.6.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.4.2",
33
- "@bobbykim/manguito-cms-core": "0.5.0",
34
- "@bobbykim/manguito-cms-db": "0.1.5",
35
- "@bobbykim/manguito-cms-api": "0.5.0"
32
+ "@bobbykim/manguito-cms-api": "0.6.0",
33
+ "@bobbykim/manguito-cms-db": "0.1.6",
34
+ "@bobbykim/manguito-cms-core": "0.6.0",
35
+ "@bobbykim/manguito-cms-admin": "0.4.3"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^25.9.5",