@bobbykim/manguito-cms-api 0.6.0 → 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.
package/dist/index.mjs CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  persistParagraphField,
9
9
  resolveRelationBareIds,
10
10
  resolveRelationField
11
- } from "./chunk-JVP33FI2.mjs";
11
+ } from "./chunk-FXPKGPYF.mjs";
12
12
 
13
13
  // src/paths.ts
14
14
  function normalizePrefix(prefix) {
@@ -617,14 +617,16 @@ function isPlainRow(v) {
617
617
  return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof Date);
618
618
  }
619
619
  var MEDIA_FIELD_TYPES = /* @__PURE__ */ new Set(["image", "video", "file"]);
620
- function nestedTargets(fields) {
620
+ function nestedTargets(fields, map) {
621
621
  const out = [];
622
622
  for (const f of fields) {
623
623
  if (MEDIA_FIELD_TYPES.has(f.field_type)) continue;
624
624
  if (f.field_type !== "paragraph" && f.field_type !== "reference") continue;
625
625
  const ref = f.ui_component.ref;
626
626
  if (!ref) continue;
627
- out.push({ label: f.name, target: ref });
627
+ const label = isColumnBacked(f) ? map.labelFor(f.db_column.column_name) : f.name;
628
+ if (label === void 0) continue;
629
+ out.push({ label, target: ref });
628
630
  }
629
631
  return out;
630
632
  }
@@ -642,7 +644,7 @@ function buildProjectors(registry, fieldKeyMaps, fallbacks) {
642
644
  const typeFallbacks = fallbacks?.[typeName];
643
645
  projectors[typeName] = {
644
646
  map,
645
- nested: nestedTargets(type.fields),
647
+ nested: nestedTargets(type.fields, map),
646
648
  // Spread rather than assign: with exactOptionalPropertyTypes, an optional
647
649
  // property may be ABSENT but not present-and-undefined.
648
650
  ...typeFallbacks !== void 0 && { fallbacks: typeFallbacks }
@@ -726,7 +728,28 @@ function parseFilters(url, validFields, columnFor) {
726
728
  function isPublished(item) {
727
729
  return item["published"] === true;
728
730
  }
729
- function registerPublicContentRoutes(app, registry, repos, projectors, paths, listRateLimit, resolver) {
731
+ function registerPublicContentRoutes(app, registry, repos, projectors, paths, listRateLimit, resolver, resolverProjectors) {
732
+ const forResolver = resolverProjectors ?? projectors;
733
+ function pickProgrammatic(resolved, names) {
734
+ const out = {};
735
+ for (const name of names) if (name in resolved) out[name] = resolved[name];
736
+ return out;
737
+ }
738
+ async function respondItem(row, typeName, programmatic) {
739
+ const data = projectRow(row, typeName, projectors);
740
+ if (!resolver?.hasSchema(typeName)) return data;
741
+ const resolved = await resolver.resolveItem(typeName, projectRow(row, typeName, forResolver));
742
+ return { ...data, ...pickProgrammatic(resolved, programmatic) };
743
+ }
744
+ async function respondList(rows, typeName, programmatic) {
745
+ const data = rows.map((row) => projectRow(row, typeName, projectors));
746
+ if (!resolver?.hasSchema(typeName)) return data;
747
+ const resolved = await resolver.resolveList(
748
+ typeName,
749
+ rows.map((row) => projectRow(row, typeName, forResolver))
750
+ );
751
+ return data.map((d, i) => ({ ...d, ...pickProgrammatic(resolved[i], programmatic) }));
752
+ }
730
753
  function registerListRoute(path, handler) {
731
754
  if (listRateLimit) {
732
755
  app.get(path, listRateLimit, handler);
@@ -750,18 +773,29 @@ function registerPublicContentRoutes(app, registry, repos, projectors, paths, li
750
773
  return c.json({ ok: true, data });
751
774
  });
752
775
  for (const [typeName, contentType] of Object.entries(registry.content_types)) {
776
+ let translateInclude2 = function(requested) {
777
+ const include = [];
778
+ for (const field of requested) {
779
+ const registryName = includeNames.get(field);
780
+ if (registryName === void 0) return { ok: false, field };
781
+ include.push(registryName);
782
+ }
783
+ return { ok: true, include };
784
+ };
785
+ var translateInclude = translateInclude2;
753
786
  const basePath = contentType.default_base_path;
754
787
  const repo = repos[typeName];
755
788
  if (!repo) continue;
756
789
  const fieldKeys = projectors[typeName].map;
757
- const relationFieldNames = /* @__PURE__ */ new Set();
790
+ const programmatic = contentType.fields.filter((f) => f.field_type === "programmatic").map((f) => f.name);
791
+ const includeNames = /* @__PURE__ */ new Map();
758
792
  for (const f of contentType.fields) {
759
793
  if (!RELATION_FIELD_TYPES.has(f.field_type)) continue;
760
794
  if (isColumnBacked(f)) {
761
795
  const label = fieldKeys.labelFor(f.db_column.column_name);
762
- if (label !== void 0) relationFieldNames.add(label);
796
+ if (label !== void 0) includeNames.set(label, f.name);
763
797
  } else {
764
- relationFieldNames.add(f.name);
798
+ includeNames.set(f.name, f.name);
765
799
  }
766
800
  }
767
801
  const filterableFieldNames = /* @__PURE__ */ new Set([
@@ -780,8 +814,11 @@ function registerPublicContentRoutes(app, registry, repos, projectors, paths, li
780
814
  404
781
815
  );
782
816
  }
783
- let data = projectRow(result.data[0], typeName, projectors);
784
- if (resolver?.hasSchema(typeName)) data = await resolver.resolveItem(typeName, data);
817
+ const data = await respondItem(
818
+ result.data[0],
819
+ typeName,
820
+ programmatic
821
+ );
785
822
  return c.json({ ok: true, data });
786
823
  });
787
824
  } else {
@@ -838,21 +875,20 @@ function registerPublicContentRoutes(app, registry, repos, projectors, paths, li
838
875
  400
839
876
  );
840
877
  }
841
- const include = parseInclude(c.req.query("include"));
842
- for (const field of include) {
843
- if (!relationFieldNames.has(field)) {
844
- return c.json(
845
- {
846
- ok: false,
847
- error: {
848
- code: "INVALID_INCLUDE_FIELD",
849
- message: `'${field}' is not a valid relation field`
850
- }
851
- },
852
- 400
853
- );
854
- }
878
+ const translated = translateInclude2(parseInclude(c.req.query("include")));
879
+ if (!translated.ok) {
880
+ return c.json(
881
+ {
882
+ ok: false,
883
+ error: {
884
+ code: "INVALID_INCLUDE_FIELD",
885
+ message: `'${translated.field}' is not a valid relation field`
886
+ }
887
+ },
888
+ 400
889
+ );
855
890
  }
891
+ const include = translated.include;
856
892
  const sortColumn = fieldKeys.labels.includes(sortBy) ? fieldKeys.columnFor(sortBy) : sortBy;
857
893
  const result = await repo.findMany({
858
894
  published_only: true,
@@ -863,29 +899,25 @@ function registerPublicContentRoutes(app, registry, repos, projectors, paths, li
863
899
  filters: filtersResult.filters,
864
900
  include
865
901
  });
866
- const labeled = result.data.map(
867
- (row) => projectRow(row, typeName, projectors)
868
- );
869
- const data = resolver?.hasSchema(typeName) ? await resolver.resolveList(typeName, labeled) : labeled;
902
+ const data = await respondList(result.data, typeName, programmatic);
870
903
  return c.json({ ...result, data });
871
904
  });
872
905
  app.get(paths.item(basePath), async (c) => {
873
906
  const slug = c.req.param("slug");
874
- const include = parseInclude(c.req.query("include"));
875
- for (const field of include) {
876
- if (!relationFieldNames.has(field)) {
877
- return c.json(
878
- {
879
- ok: false,
880
- error: {
881
- code: "INVALID_INCLUDE_FIELD",
882
- message: `'${field}' is not a valid relation field`
883
- }
884
- },
885
- 400
886
- );
887
- }
907
+ const translated = translateInclude2(parseInclude(c.req.query("include")));
908
+ if (!translated.ok) {
909
+ return c.json(
910
+ {
911
+ ok: false,
912
+ error: {
913
+ code: "INVALID_INCLUDE_FIELD",
914
+ message: `'${translated.field}' is not a valid relation field`
915
+ }
916
+ },
917
+ 400
918
+ );
888
919
  }
920
+ const include = translated.include;
889
921
  const item = await repo.findBySlug(slug, include);
890
922
  if (!item || !isPublished(item)) {
891
923
  return c.json(
@@ -896,15 +928,15 @@ function registerPublicContentRoutes(app, registry, repos, projectors, paths, li
896
928
  404
897
929
  );
898
930
  }
899
- let data = projectRow(item, typeName, projectors);
900
- if (resolver?.hasSchema(typeName)) data = await resolver.resolveItem(typeName, data);
931
+ const data = await respondItem(item, typeName, programmatic);
901
932
  return c.json({ ok: true, data });
902
933
  });
903
934
  }
904
935
  }
905
- for (const [typeName] of Object.entries(registry.taxonomy_types)) {
936
+ for (const [typeName, taxonomyType] of Object.entries(registry.taxonomy_types)) {
906
937
  const repo = repos[typeName];
907
938
  if (!repo) continue;
939
+ const programmatic = taxonomyType.fields.filter((f) => f.field_type === "programmatic").map((f) => f.name);
908
940
  registerListRoute(paths.taxonomyCollection(typeName), async (c) => {
909
941
  const pagination = parsePagination(c.req.query("page"), c.req.query("per_page"));
910
942
  if (!pagination.ok) {
@@ -924,10 +956,7 @@ function registerPublicContentRoutes(app, registry, repos, projectors, paths, li
924
956
  page: pagination.page,
925
957
  per_page: pagination.per_page
926
958
  });
927
- const labeled = result.data.map(
928
- (row) => projectRow(row, typeName, projectors)
929
- );
930
- const data = resolver?.hasSchema(typeName) ? await resolver.resolveList(typeName, labeled) : labeled;
959
+ const data = await respondList(result.data, typeName, programmatic);
931
960
  return c.json({ ...result, data });
932
961
  });
933
962
  app.get(paths.taxonomyItem(typeName), async (c) => {
@@ -942,8 +971,7 @@ function registerPublicContentRoutes(app, registry, repos, projectors, paths, li
942
971
  404
943
972
  );
944
973
  }
945
- let data = projectRow(item, typeName, projectors);
946
- if (resolver?.hasSchema(typeName)) data = await resolver.resolveItem(typeName, data);
974
+ const data = await respondItem(item, typeName, programmatic);
947
975
  return c.json({ ok: true, data });
948
976
  });
949
977
  }
@@ -2733,7 +2761,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
2733
2761
  import { sql as sql5 } from "drizzle-orm";
2734
2762
  var API_VERSION = (() => {
2735
2763
  try {
2736
- return "0.6.0";
2764
+ return "0.7.0";
2737
2765
  } catch {
2738
2766
  return "0.0.0";
2739
2767
  }
@@ -3389,7 +3417,7 @@ function createCmsApp(options) {
3389
3417
  app.use(path, attachDeprecationHeaders);
3390
3418
  }
3391
3419
  }
3392
- registerPublicContentRoutes(app, registry, surface.repos, surface.projectors, surface.paths, listRateLimit, programmaticResolver);
3420
+ registerPublicContentRoutes(app, registry, surface.repos, surface.projectors, surface.paths, listRateLimit, programmaticResolver, projectors);
3393
3421
  }
3394
3422
  if (options.versions !== void 0) {
3395
3423
  app.all(`${prefix}/:version/*`, async (c, next) => {
@@ -3431,7 +3459,7 @@ function createCmsApp(options) {
3431
3459
  handler: null,
3432
3460
  error: null
3433
3461
  }));
3434
- const ready = import("./handler-XZVDLKZ2.mjs").then(({ createGraphQLHandler }) => {
3462
+ const ready = import("./handler-GED3I7Z2.mjs").then(({ createGraphQLHandler }) => {
3435
3463
  gqlPasses.forEach((pass, i) => {
3436
3464
  const mount = mounts[i];
3437
3465
  try {
@@ -3455,7 +3483,8 @@ function createCmsApp(options) {
3455
3483
  endpoint: mount.path,
3456
3484
  currentVersion: model.current,
3457
3485
  ...projection !== void 0 && { projection },
3458
- ...currentProjection !== void 0 && { currentProjection }
3486
+ ...currentProjection !== void 0 && { currentProjection },
3487
+ currentFieldKeyMaps: fieldKeyMaps
3459
3488
  }
3460
3489
  );
3461
3490
  } catch (err) {