@bobbykim/manguito-cms-api 0.5.0 → 0.6.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/{chunk-D5AO4BTL.mjs → chunk-JVP33FI2.mjs} +16 -4
- package/dist/chunk-JVP33FI2.mjs.map +1 -0
- package/dist/{chunk-ARZXLNZS.mjs → chunk-XNXM4W4K.mjs} +147 -40
- package/dist/chunk-XNXM4W4K.mjs.map +1 -0
- package/dist/graphql/index.d.mts +7 -2
- package/dist/graphql/index.d.ts +7 -2
- package/dist/graphql/index.js +145 -38
- package/dist/graphql/index.js.map +1 -1
- package/dist/graphql/index.mjs +2 -2
- package/dist/handler-XZVDLKZ2.mjs +8 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +258 -70
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +93 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-ARZXLNZS.mjs.map +0 -1
- package/dist/chunk-D5AO4BTL.mjs.map +0 -1
- package/dist/handler-X7JDHJAL.mjs +0 -8
- /package/dist/{handler-X7JDHJAL.mjs.map → handler-XZVDLKZ2.mjs.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -45,7 +45,8 @@ function buildFieldKeyMap(pairs, allLabels, droppedKeys) {
|
|
|
45
45
|
labelToColumn.set(label, column);
|
|
46
46
|
columnToLabel.set(column, label);
|
|
47
47
|
}
|
|
48
|
-
for (const label of allLabels) {
|
|
48
|
+
for (const { label, ownColumn } of allLabels) {
|
|
49
|
+
if (ownColumn === label) continue;
|
|
49
50
|
const columnOwner = columnToLabel.get(label);
|
|
50
51
|
if (columnOwner !== void 0 && columnOwner !== label) {
|
|
51
52
|
throw new Error(
|
|
@@ -90,7 +91,12 @@ function createFieldKeyMap(fields) {
|
|
|
90
91
|
}
|
|
91
92
|
return buildFieldKeyMap(
|
|
92
93
|
pairs,
|
|
93
|
-
fields.map((f) =>
|
|
94
|
+
fields.map((f) => ({
|
|
95
|
+
label: f.name,
|
|
96
|
+
// Conditional spread, not a plain assignment — exactOptionalPropertyTypes
|
|
97
|
+
// forbids present-and-undefined.
|
|
98
|
+
...isColumnBacked(f) && { ownColumn: f.db_column.column_name }
|
|
99
|
+
})),
|
|
94
100
|
droppedKeys
|
|
95
101
|
);
|
|
96
102
|
}
|
|
@@ -107,7 +113,13 @@ function createFieldKeyMapFromProjection(projectionType, allFields) {
|
|
|
107
113
|
for (const label of projected) dropped.delete(label);
|
|
108
114
|
return buildFieldKeyMap(
|
|
109
115
|
pairs,
|
|
110
|
-
allFields.map((f) =>
|
|
116
|
+
allFields.map((f) => ({
|
|
117
|
+
label: f.name,
|
|
118
|
+
// See buildFieldKeyMap's collision check: this is what lets it tell a
|
|
119
|
+
// version exposing a field's own column under a new label (fine) from a
|
|
120
|
+
// label that reuses another field's column (not fine).
|
|
121
|
+
...isColumnBacked(f) && { ownColumn: f.db_column.column_name }
|
|
122
|
+
})),
|
|
111
123
|
dropped
|
|
112
124
|
);
|
|
113
125
|
}
|
|
@@ -537,10 +549,13 @@ var init_naming = __esm({
|
|
|
537
549
|
});
|
|
538
550
|
|
|
539
551
|
// src/graphql/filters.ts
|
|
540
|
-
function buildSortFieldEnum(typeName) {
|
|
552
|
+
function buildSortFieldEnum(typeName, fieldKeys) {
|
|
553
|
+
const resolvable = SORTABLE.filter(
|
|
554
|
+
(s) => s.gql === "createdAt" || s.gql === "updatedAt" || fieldKeys === void 0 || fieldKeys.columnFor(s.value) !== void 0
|
|
555
|
+
);
|
|
541
556
|
return new import_graphql3.GraphQLEnumType({
|
|
542
557
|
name: `${typeName}SortField`,
|
|
543
|
-
values: Object.fromEntries(
|
|
558
|
+
values: Object.fromEntries(resolvable.map((s) => [s.gql, { value: s.value }]))
|
|
544
559
|
});
|
|
545
560
|
}
|
|
546
561
|
function comparableFilterInput(name, scalar) {
|
|
@@ -575,18 +590,19 @@ function filterInputForField(fieldType) {
|
|
|
575
590
|
if (scalar === null) return null;
|
|
576
591
|
return StringFilter;
|
|
577
592
|
}
|
|
578
|
-
function buildFilterInputType(type) {
|
|
593
|
+
function buildFilterInputType(type, viewFields) {
|
|
579
594
|
const fields = {};
|
|
580
595
|
fields["id"] = { type: IDFilter };
|
|
581
596
|
fields["published"] = { type: BooleanFilter };
|
|
582
597
|
fields["createdAt"] = { type: DateTimeFilter };
|
|
583
598
|
fields["updatedAt"] = { type: DateTimeFilter };
|
|
584
599
|
if (type.schema_type === "content-type") fields["slug"] = { type: StringFilter };
|
|
585
|
-
|
|
600
|
+
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 }));
|
|
601
|
+
for (const { field: f, exposedAs } of entries) {
|
|
586
602
|
if (f.field_type === "programmatic" || f.field_type === "paragraph") continue;
|
|
587
603
|
if (f.field_type === "image" || f.field_type === "video" || f.field_type === "file") continue;
|
|
588
604
|
const input = filterInputForField(f.field_type);
|
|
589
|
-
if (input) fields[buildFieldNameMap([
|
|
605
|
+
if (input) fields[buildFieldNameMap([exposedAs]).toGraphql(exposedAs)] = { type: input };
|
|
590
606
|
}
|
|
591
607
|
if (Object.keys(fields).length === 0) return null;
|
|
592
608
|
return new import_graphql3.GraphQLInputObjectType({ name: `${graphqlTypeName(type.name)}Filter`, fields });
|
|
@@ -649,32 +665,41 @@ function codeError2(code, message) {
|
|
|
649
665
|
function isPublished2(item) {
|
|
650
666
|
return !!item && item["published"] === true;
|
|
651
667
|
}
|
|
652
|
-
function resolveFieldValue(field, row) {
|
|
668
|
+
function resolveFieldValue(field, row, fallback) {
|
|
653
669
|
const key = isColumnBacked(field) ? field.db_column.column_name : field.name;
|
|
654
|
-
|
|
670
|
+
const value = row[key];
|
|
671
|
+
if ((value === null || value === void 0) && fallback !== void 0) return fallback;
|
|
672
|
+
return value ?? null;
|
|
655
673
|
}
|
|
656
|
-
function scalarFieldResolver(field) {
|
|
657
|
-
return (parent) => resolveFieldValue(field, parent);
|
|
674
|
+
function scalarFieldResolver(field, fallback) {
|
|
675
|
+
return (parent) => resolveFieldValue(field, parent, fallback);
|
|
658
676
|
}
|
|
659
677
|
function relationFieldResolver(typeName, schemaFieldName) {
|
|
660
678
|
return (parent, _args, ctx) => ctx.loaders.load(typeName, schemaFieldName, parent);
|
|
661
679
|
}
|
|
662
|
-
function programmaticFieldResolver(typeName, schemaFieldName,
|
|
680
|
+
function programmaticFieldResolver(typeName, schemaFieldName, mediaFields = [], fieldKeys) {
|
|
663
681
|
return async (parent, _args, ctx) => {
|
|
664
682
|
let p = ctx.programmaticMemo.get(parent);
|
|
665
683
|
if (!p) {
|
|
666
|
-
p = resolveProgrammaticRow(typeName, parent, ctx,
|
|
684
|
+
p = resolveProgrammaticRow(typeName, parent, ctx, mediaFields, fieldKeys);
|
|
667
685
|
ctx.programmaticMemo.set(parent, p);
|
|
668
686
|
}
|
|
669
687
|
return (await p)[schemaFieldName];
|
|
670
688
|
};
|
|
671
689
|
}
|
|
672
|
-
async function resolveProgrammaticRow(typeName, parent, ctx,
|
|
690
|
+
async function resolveProgrammaticRow(typeName, parent, ctx, mediaFields, fieldKeys) {
|
|
673
691
|
const toLabels = (row) => fieldKeys ? fieldKeys.toLabels(row) : row;
|
|
674
|
-
if (
|
|
692
|
+
if (mediaFields.length === 0) return ctx.resolver.resolveItem(typeName, toLabels(parent));
|
|
675
693
|
const enriched = { ...parent };
|
|
676
|
-
await Promise.all(
|
|
677
|
-
return ctx.resolver.resolveItem(typeName, toLabels(enriched));
|
|
694
|
+
await Promise.all(mediaFields.map((m) => ctx.loaders.load(typeName, m.name, enriched)));
|
|
695
|
+
return ctx.resolver.resolveItem(typeName, relabelMedia(toLabels(enriched), enriched, mediaFields));
|
|
696
|
+
}
|
|
697
|
+
function relabelMedia(record, enriched, mediaFields) {
|
|
698
|
+
for (const { name, exposedAs } of mediaFields) {
|
|
699
|
+
if (!(name in enriched)) continue;
|
|
700
|
+
record[exposedAs] = enriched[name];
|
|
701
|
+
}
|
|
702
|
+
return record;
|
|
678
703
|
}
|
|
679
704
|
function collectionResolver(typeName, nameMap, fieldKeys) {
|
|
680
705
|
return async (_root, args, ctx) => {
|
|
@@ -732,7 +757,7 @@ var init_resolvers = __esm({
|
|
|
732
757
|
});
|
|
733
758
|
|
|
734
759
|
// src/graphql/schema.ts
|
|
735
|
-
function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
|
|
760
|
+
function buildGraphQLSchema(registry, fieldKeyMaps = {}, view) {
|
|
736
761
|
const objectTypes = /* @__PURE__ */ new Map();
|
|
737
762
|
const enumTypes = /* @__PURE__ */ new Map();
|
|
738
763
|
for (const [name, enumType] of Object.entries(registry.enum_types)) {
|
|
@@ -751,14 +776,14 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
|
|
|
751
776
|
);
|
|
752
777
|
}
|
|
753
778
|
}
|
|
754
|
-
function outputTypeForField(field) {
|
|
779
|
+
function outputTypeForField(field, required) {
|
|
755
780
|
const scalar = scalarOutputType(field.field_type);
|
|
756
|
-
if (scalar) return
|
|
781
|
+
if (scalar) return required ? new import_graphql5.GraphQLNonNull(scalar) : scalar;
|
|
757
782
|
if (field.field_type === "enum") {
|
|
758
783
|
const ref = field.ui_component.component === "select" ? field.ui_component.enum_ref : void 0;
|
|
759
784
|
const et = ref ? enumTypes.get(ref) : void 0;
|
|
760
785
|
const t = et ?? import_graphql5.GraphQLString;
|
|
761
|
-
return
|
|
786
|
+
return required ? new import_graphql5.GraphQLNonNull(t) : t;
|
|
762
787
|
}
|
|
763
788
|
if (field.field_type === "image" || field.field_type === "video" || field.field_type === "file") {
|
|
764
789
|
return MEDIA;
|
|
@@ -788,8 +813,17 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
|
|
|
788
813
|
}
|
|
789
814
|
return import_graphql5.GraphQLString;
|
|
790
815
|
}
|
|
816
|
+
function exposedFields(type) {
|
|
817
|
+
return type.fields.filter((f) => f.removed !== true);
|
|
818
|
+
}
|
|
819
|
+
function viewFieldsFor(machineName, type) {
|
|
820
|
+
return view?.[machineName]?.fields ?? exposedFields(type).map((f) => ({ field: f, exposedAs: f.name, required: f.required }));
|
|
821
|
+
}
|
|
791
822
|
function buildObjectType(machineName, type) {
|
|
792
|
-
const
|
|
823
|
+
const visible = viewFieldsFor(machineName, type);
|
|
824
|
+
const mediaFields = visible.filter(
|
|
825
|
+
(v) => v.field.field_type === "image" || v.field.field_type === "video" || v.field.field_type === "file"
|
|
826
|
+
).map((v) => ({ name: v.field.name, exposedAs: v.exposedAs }));
|
|
793
827
|
return new import_graphql5.GraphQLObjectType({
|
|
794
828
|
name: graphqlTypeName(machineName),
|
|
795
829
|
fields: () => {
|
|
@@ -803,23 +837,25 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
|
|
|
803
837
|
}
|
|
804
838
|
fields["createdAt"] = { type: new import_graphql5.GraphQLNonNull(DateTimeScalar), resolve: (p) => p["created_at"] };
|
|
805
839
|
fields["updatedAt"] = { type: new import_graphql5.GraphQLNonNull(DateTimeScalar), resolve: (p) => p["updated_at"] };
|
|
806
|
-
for (const
|
|
807
|
-
const
|
|
808
|
-
const
|
|
840
|
+
for (const vf of visible) {
|
|
841
|
+
const field = vf.field;
|
|
842
|
+
const gqlName = toCamelCase(vf.exposedAs);
|
|
843
|
+
const outType = outputTypeForField(field, vf.required);
|
|
809
844
|
let resolve;
|
|
810
845
|
if (field.field_type === "programmatic") {
|
|
811
|
-
resolve = programmaticFieldResolver(
|
|
812
|
-
machineName,
|
|
813
|
-
field.name,
|
|
814
|
-
mediaFieldNames,
|
|
815
|
-
fieldKeyMaps[machineName]
|
|
816
|
-
);
|
|
846
|
+
resolve = programmaticFieldResolver(machineName, field.name, mediaFields, fieldKeyMaps[machineName]);
|
|
817
847
|
} else if (field.field_type === "reference" || field.field_type === "paragraph" || field.field_type === "image" || field.field_type === "video" || field.field_type === "file") {
|
|
818
848
|
resolve = relationFieldResolver(machineName, field.name);
|
|
819
849
|
} else {
|
|
820
|
-
resolve = scalarFieldResolver(field);
|
|
850
|
+
resolve = scalarFieldResolver(field, vf.fallback);
|
|
821
851
|
}
|
|
822
|
-
fields[gqlName] = {
|
|
852
|
+
fields[gqlName] = {
|
|
853
|
+
type: outType,
|
|
854
|
+
resolve,
|
|
855
|
+
// Conditional spread, not `deprecationReason: vf.deprecationReason`
|
|
856
|
+
// — exactOptionalPropertyTypes forbids present-and-undefined.
|
|
857
|
+
...vf.deprecationReason !== void 0 && { deprecationReason: vf.deprecationReason }
|
|
858
|
+
};
|
|
823
859
|
}
|
|
824
860
|
return fields;
|
|
825
861
|
}
|
|
@@ -851,7 +887,8 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
|
|
|
851
887
|
const queryFields = {};
|
|
852
888
|
for (const [name, ct] of Object.entries(registry.content_types)) {
|
|
853
889
|
const objType = objectTypes.get(name);
|
|
854
|
-
const
|
|
890
|
+
const visible = viewFieldsFor(name, ct);
|
|
891
|
+
const nameMap = buildFieldNameMap(visible.map((v) => v.exposedAs));
|
|
855
892
|
if (ct.only_one) {
|
|
856
893
|
queryFields[singleQueryName(name)] = { type: objType, resolve: singletonResolver(name) };
|
|
857
894
|
continue;
|
|
@@ -863,13 +900,13 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
|
|
|
863
900
|
meta: { type: new import_graphql5.GraphQLNonNull(PAGE_META), resolve: (r) => r.meta }
|
|
864
901
|
}
|
|
865
902
|
});
|
|
866
|
-
const filterType = buildFilterInputType(ct);
|
|
903
|
+
const filterType = buildFilterInputType(ct, visible);
|
|
867
904
|
queryFields[collectionQueryName(name)] = {
|
|
868
905
|
type: new import_graphql5.GraphQLNonNull(listType),
|
|
869
906
|
args: {
|
|
870
907
|
page: { type: import_graphql5.GraphQLInt },
|
|
871
908
|
perPage: { type: import_graphql5.GraphQLInt },
|
|
872
|
-
sortBy: { type: buildSortFieldEnum(graphqlTypeName(name)) },
|
|
909
|
+
sortBy: { type: buildSortFieldEnum(graphqlTypeName(name), fieldKeyMaps[name]) },
|
|
873
910
|
sortOrder: { type: SortOrderEnum },
|
|
874
911
|
...filterType ? { filter: { type: filterType } } : {}
|
|
875
912
|
},
|
|
@@ -893,7 +930,11 @@ function buildGraphQLSchema(registry, fieldKeyMaps = {}) {
|
|
|
893
930
|
queryFields[collectionQueryName(name)] = {
|
|
894
931
|
type: new import_graphql5.GraphQLNonNull(listType),
|
|
895
932
|
args: { page: { type: import_graphql5.GraphQLInt }, perPage: { type: import_graphql5.GraphQLInt } },
|
|
896
|
-
resolve: collectionResolver(
|
|
933
|
+
resolve: collectionResolver(
|
|
934
|
+
name,
|
|
935
|
+
buildFieldNameMap(viewFieldsFor(name, tt).map((v) => v.exposedAs)),
|
|
936
|
+
fieldKeyMaps[name]
|
|
937
|
+
)
|
|
897
938
|
};
|
|
898
939
|
queryFields[singleQueryName(name)] = {
|
|
899
940
|
type: objType,
|
|
@@ -943,6 +984,76 @@ var init_schema = __esm({
|
|
|
943
984
|
}
|
|
944
985
|
});
|
|
945
986
|
|
|
987
|
+
// src/graphql/version-view.ts
|
|
988
|
+
function byColumn(fields) {
|
|
989
|
+
const out = /* @__PURE__ */ new Map();
|
|
990
|
+
for (const f of fields) {
|
|
991
|
+
if (!isColumnBacked(f)) continue;
|
|
992
|
+
out.set(f.db_column.column_name, f);
|
|
993
|
+
}
|
|
994
|
+
return out;
|
|
995
|
+
}
|
|
996
|
+
function deprecationFor(field, currentLabels, currentVersion) {
|
|
997
|
+
const currentLabel = currentLabels.get(field.column_name);
|
|
998
|
+
if (currentLabel === void 0) {
|
|
999
|
+
return `Removed in ${currentVersion}; column retained while this version is live.`;
|
|
1000
|
+
}
|
|
1001
|
+
if (currentLabel !== field.exposed_as) {
|
|
1002
|
+
return `Renamed to '${currentLabel}' in ${currentVersion}.`;
|
|
1003
|
+
}
|
|
1004
|
+
return void 0;
|
|
1005
|
+
}
|
|
1006
|
+
function buildVersionView(input) {
|
|
1007
|
+
const { registry, projection, currentProjection, currentVersion } = input;
|
|
1008
|
+
const out = {};
|
|
1009
|
+
const sources = [
|
|
1010
|
+
registry.content_types,
|
|
1011
|
+
registry.taxonomy_types,
|
|
1012
|
+
registry.paragraph_types
|
|
1013
|
+
];
|
|
1014
|
+
for (const source of sources) {
|
|
1015
|
+
for (const [typeName, type] of Object.entries(source)) {
|
|
1016
|
+
const projectionType = projection?.types[typeName];
|
|
1017
|
+
if (projectionType === void 0) {
|
|
1018
|
+
out[typeName] = {
|
|
1019
|
+
fields: type.fields.filter((f) => f.removed !== true).map((f) => ({ field: f, exposedAs: f.name, required: f.required }))
|
|
1020
|
+
};
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
const columns = byColumn(type.fields);
|
|
1024
|
+
const currentLabels = /* @__PURE__ */ new Map();
|
|
1025
|
+
for (const f of currentProjection?.types[typeName]?.fields ?? []) {
|
|
1026
|
+
currentLabels.set(f.column_name, f.exposed_as);
|
|
1027
|
+
}
|
|
1028
|
+
const fields = [];
|
|
1029
|
+
for (const pf of projectionType.fields) {
|
|
1030
|
+
const field = columns.get(pf.column_name);
|
|
1031
|
+
if (field === void 0) continue;
|
|
1032
|
+
const reason = deprecationFor(pf, currentLabels, currentVersion);
|
|
1033
|
+
fields.push({
|
|
1034
|
+
field,
|
|
1035
|
+
exposedAs: pf.exposed_as,
|
|
1036
|
+
required: pf.required,
|
|
1037
|
+
...pf.fallback !== void 0 && { fallback: pf.fallback },
|
|
1038
|
+
...reason !== void 0 && { deprecationReason: reason }
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
for (const f of type.fields) {
|
|
1042
|
+
if (isColumnBacked(f) || f.removed === true) continue;
|
|
1043
|
+
fields.push({ field: f, exposedAs: f.name, required: f.required });
|
|
1044
|
+
}
|
|
1045
|
+
out[typeName] = { fields };
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return out;
|
|
1049
|
+
}
|
|
1050
|
+
var init_version_view = __esm({
|
|
1051
|
+
"src/graphql/version-view.ts"() {
|
|
1052
|
+
"use strict";
|
|
1053
|
+
init_field_keys();
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
|
|
946
1057
|
// src/graphql/dataloaders.ts
|
|
947
1058
|
function createRelationLoaders(db, registry) {
|
|
948
1059
|
const relMaps = /* @__PURE__ */ new Map();
|
|
@@ -1035,15 +1146,29 @@ function isClientSafeGraphQLError(error) {
|
|
|
1035
1146
|
}
|
|
1036
1147
|
return false;
|
|
1037
1148
|
}
|
|
1038
|
-
function createGraphQLHandler(registry, repos, fieldKeyMaps, resolver, db, options) {
|
|
1039
|
-
|
|
1149
|
+
function createGraphQLHandler(registry, repos, fieldKeyMaps, resolver, db, options, versioning = {}) {
|
|
1150
|
+
let view;
|
|
1151
|
+
if (versioning.projection !== void 0) {
|
|
1152
|
+
if (versioning.currentVersion === void 0) {
|
|
1153
|
+
throw new Error(
|
|
1154
|
+
'createGraphQLHandler: currentVersion is required when a projection is supplied \u2014 deprecation reasons name it, and an empty one ships "Removed in ." into published SDL.'
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
view = buildVersionView({
|
|
1158
|
+
registry,
|
|
1159
|
+
projection: versioning.projection,
|
|
1160
|
+
currentProjection: versioning.currentProjection,
|
|
1161
|
+
currentVersion: versioning.currentVersion
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
const schema = buildGraphQLSchema(registry, fieldKeyMaps, view);
|
|
1040
1165
|
const { plugins } = buildArmorPlugin({
|
|
1041
1166
|
maxDepth: options.maxDepth,
|
|
1042
1167
|
maxComplexity: options.maxComplexity
|
|
1043
1168
|
});
|
|
1044
1169
|
const yoga = (0, import_graphql_yoga.createYoga)({
|
|
1045
1170
|
schema,
|
|
1046
|
-
graphqlEndpoint: "/graphql",
|
|
1171
|
+
graphqlEndpoint: versioning.endpoint ?? "/graphql",
|
|
1047
1172
|
graphiql: options.graphiql,
|
|
1048
1173
|
landingPage: false,
|
|
1049
1174
|
maskedErrors: {
|
|
@@ -1071,6 +1196,7 @@ var init_handler = __esm({
|
|
|
1071
1196
|
"use strict";
|
|
1072
1197
|
import_graphql_yoga = require("graphql-yoga");
|
|
1073
1198
|
init_schema();
|
|
1199
|
+
init_version_view();
|
|
1074
1200
|
init_dataloaders();
|
|
1075
1201
|
init_security();
|
|
1076
1202
|
}
|
|
@@ -1180,7 +1306,7 @@ function createSecurityHeadersMiddleware(options = {}) {
|
|
|
1180
1306
|
c.res.headers.set("X-Content-Type-Options", "nosniff");
|
|
1181
1307
|
c.res.headers.set("X-Frame-Options", "DENY");
|
|
1182
1308
|
c.res.headers.set("Referrer-Policy", "no-referrer");
|
|
1183
|
-
const isGraphiql = graphiqlPath !== void 0 && c.req.path === graphiqlPath;
|
|
1309
|
+
const isGraphiql = graphiqlPath !== void 0 && (c.req.path === graphiqlPath || c.req.path.startsWith(`${graphiqlPath}/`));
|
|
1184
1310
|
c.res.headers.set("Content-Security-Policy", isGraphiql ? graphiqlCsp : csp);
|
|
1185
1311
|
};
|
|
1186
1312
|
}
|
|
@@ -3816,7 +3942,7 @@ function registerUserRoutes(app, db, requirePermission, requireHierarchy) {
|
|
|
3816
3942
|
var import_drizzle_orm6 = require("drizzle-orm");
|
|
3817
3943
|
var API_VERSION = (() => {
|
|
3818
3944
|
try {
|
|
3819
|
-
return "0.
|
|
3945
|
+
return "0.6.0";
|
|
3820
3946
|
} catch {
|
|
3821
3947
|
return "0.0.0";
|
|
3822
3948
|
}
|
|
@@ -4414,20 +4540,6 @@ function createCmsApp(options) {
|
|
|
4414
4540
|
sortableColumns
|
|
4415
4541
|
});
|
|
4416
4542
|
};
|
|
4417
|
-
const graphqlRepos = Object.fromEntries([
|
|
4418
|
-
...Object.entries(registry.content_types).map(([typeName, ct]) => [
|
|
4419
|
-
typeName,
|
|
4420
|
-
createDrizzleContentRepository(db, ct.db.table_name, {
|
|
4421
|
-
sortableColumns: sortableColumnsFor(typeName)
|
|
4422
|
-
})
|
|
4423
|
-
]),
|
|
4424
|
-
...Object.entries(registry.taxonomy_types).map(([typeName, tt]) => [
|
|
4425
|
-
typeName,
|
|
4426
|
-
createDrizzleContentRepository(db, tt.db.table_name, {
|
|
4427
|
-
sortableColumns: sortableColumnsFor(typeName)
|
|
4428
|
-
})
|
|
4429
|
-
])
|
|
4430
|
-
]);
|
|
4431
4543
|
registerAuthRoutes(app, db);
|
|
4432
4544
|
app.use("/admin/api/*", authMiddleware);
|
|
4433
4545
|
app.use("/admin/api/*", mustChangePasswordCheck);
|
|
@@ -4525,31 +4637,107 @@ function createCmsApp(options) {
|
|
|
4525
4637
|
registerPublicMediaRoutes(app, mediaRepo, publicPaths, listRateLimit);
|
|
4526
4638
|
if (options.graphql?.enabled) {
|
|
4527
4639
|
const gqlOptions = options.graphql;
|
|
4528
|
-
|
|
4529
|
-
|
|
4640
|
+
const makeGraphqlRepo = (_typeName, tableName, sortableColumns) => createDrizzleContentRepository(db, tableName, { sortableColumns });
|
|
4641
|
+
const gqlPasses = [
|
|
4642
|
+
...options.versions !== void 0 ? model.live.map((v) => ({ version: v, projectionVersion: v })) : [],
|
|
4643
|
+
{ version: null, projectionVersion: model.current }
|
|
4644
|
+
];
|
|
4645
|
+
const mounts = gqlPasses.map((pass) => ({
|
|
4646
|
+
path: pass.version === null ? "/graphql" : `/graphql/${pass.version}`,
|
|
4647
|
+
version: pass.version,
|
|
4648
|
+
handler: null,
|
|
4649
|
+
error: null
|
|
4650
|
+
}));
|
|
4530
4651
|
const ready = Promise.resolve().then(() => (init_handler(), handler_exports)).then(({ createGraphQLHandler: createGraphQLHandler2 }) => {
|
|
4531
|
-
|
|
4652
|
+
gqlPasses.forEach((pass, i) => {
|
|
4653
|
+
const mount = mounts[i];
|
|
4654
|
+
try {
|
|
4655
|
+
const surface = buildVersionSurface({
|
|
4656
|
+
...pass,
|
|
4657
|
+
prefix,
|
|
4658
|
+
registry,
|
|
4659
|
+
model,
|
|
4660
|
+
makeRepo: makeGraphqlRepo
|
|
4661
|
+
});
|
|
4662
|
+
const projection = model.projections[pass.projectionVersion];
|
|
4663
|
+
const currentProjection = model.projections[model.current];
|
|
4664
|
+
mount.handler = createGraphQLHandler2(
|
|
4665
|
+
registry,
|
|
4666
|
+
surface.repos,
|
|
4667
|
+
surface.fieldKeyMaps,
|
|
4668
|
+
programmaticResolver,
|
|
4669
|
+
db,
|
|
4670
|
+
gqlOptions,
|
|
4671
|
+
{
|
|
4672
|
+
endpoint: mount.path,
|
|
4673
|
+
currentVersion: model.current,
|
|
4674
|
+
...projection !== void 0 && { projection },
|
|
4675
|
+
...currentProjection !== void 0 && { currentProjection }
|
|
4676
|
+
}
|
|
4677
|
+
);
|
|
4678
|
+
} catch (err) {
|
|
4679
|
+
mount.error = err;
|
|
4680
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4681
|
+
process.stderr.write(
|
|
4682
|
+
`\u2717 GraphQL schema failed to initialize for ${mount.path}; it will return 500. ${message}
|
|
4683
|
+
`
|
|
4684
|
+
);
|
|
4685
|
+
}
|
|
4686
|
+
});
|
|
4532
4687
|
}).catch((err) => {
|
|
4533
|
-
|
|
4688
|
+
for (const m of mounts) m.error = err;
|
|
4534
4689
|
const message = err instanceof Error ? err.message : String(err);
|
|
4535
|
-
|
|
4690
|
+
const paths = mounts.map((m) => m.path).join(", ");
|
|
4691
|
+
process.stderr.write(`\u2717 GraphQL module failed to load; ${paths} will return 500. ${message}
|
|
4536
4692
|
`);
|
|
4537
4693
|
});
|
|
4538
|
-
const
|
|
4539
|
-
if (!
|
|
4540
|
-
if (!
|
|
4694
|
+
const invokeFor = (mount) => async (c) => {
|
|
4695
|
+
if (!mount.handler && !mount.error) await ready;
|
|
4696
|
+
if (!mount.handler) {
|
|
4541
4697
|
return c.json(
|
|
4542
4698
|
{ ok: false, error: { code: "GRAPHQL_INIT_FAILED", message: "GraphQL schema failed to initialize" } },
|
|
4543
4699
|
500
|
|
4544
4700
|
);
|
|
4545
4701
|
}
|
|
4546
|
-
return
|
|
4702
|
+
return mount.handler(c, async () => {
|
|
4547
4703
|
});
|
|
4548
4704
|
};
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4705
|
+
for (const mount of mounts) {
|
|
4706
|
+
const headers = deprecationHeaders({
|
|
4707
|
+
requested: mount.version,
|
|
4708
|
+
model,
|
|
4709
|
+
successor: `/graphql/${model.current}`
|
|
4710
|
+
});
|
|
4711
|
+
if (headers === null) continue;
|
|
4712
|
+
const attach = async (c, next) => {
|
|
4713
|
+
await next();
|
|
4714
|
+
for (const [key, value] of Object.entries(headers)) c.res.headers.set(key, value);
|
|
4715
|
+
};
|
|
4716
|
+
app.use(mount.path, attach);
|
|
4717
|
+
}
|
|
4718
|
+
for (const mount of mounts) {
|
|
4719
|
+
if (listRateLimit) app.all(mount.path, listRateLimit, invokeFor(mount));
|
|
4720
|
+
else app.all(mount.path, invokeFor(mount));
|
|
4721
|
+
}
|
|
4722
|
+
if (options.versions !== void 0) {
|
|
4723
|
+
app.all("/graphql/:version", async (c, next) => {
|
|
4724
|
+
const segment = c.req.param("version");
|
|
4725
|
+
const kind = classifyVersion(segment, model);
|
|
4726
|
+
if (kind === "not-a-version" || kind === "live") return next();
|
|
4727
|
+
const live = model.live.join(", ");
|
|
4728
|
+
return c.json({
|
|
4729
|
+
errors: [
|
|
4730
|
+
{
|
|
4731
|
+
message: kind === "retired" ? `Version ${segment} is no longer served. Live versions: ${live}.` : `Version ${segment} does not exist. Live versions: ${live}.`,
|
|
4732
|
+
extensions: {
|
|
4733
|
+
code: kind === "retired" ? "VERSION_RETIRED" : "VERSION_UNKNOWN",
|
|
4734
|
+
current: model.current,
|
|
4735
|
+
live: model.live
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
]
|
|
4739
|
+
});
|
|
4740
|
+
});
|
|
4553
4741
|
}
|
|
4554
4742
|
}
|
|
4555
4743
|
registerConfigRoute(
|