@bobbykim/manguito-cms-api 0.5.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/{chunk-ARZXLNZS.mjs → chunk-4LLELFAZ.mjs} +139 -35
- package/dist/chunk-4LLELFAZ.mjs.map +1 -0
- package/dist/{chunk-D5AO4BTL.mjs → chunk-FXPKGPYF.mjs} +57 -42
- package/dist/chunk-FXPKGPYF.mjs.map +1 -0
- package/dist/graphql/index.d.mts +8 -2
- package/dist/graphql/index.d.ts +8 -2
- package/dist/graphql/index.js +145 -52
- package/dist/graphql/index.js.map +1 -1
- package/dist/graphql/index.mjs +2 -2
- package/dist/handler-GED3I7Z2.mjs +8 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +372 -154
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +173 -82
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- 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-GED3I7Z2.mjs.map} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
persistParagraphField,
|
|
9
9
|
resolveRelationBareIds,
|
|
10
10
|
resolveRelationField
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-FXPKGPYF.mjs";
|
|
12
12
|
|
|
13
13
|
// src/paths.ts
|
|
14
14
|
function normalizePrefix(prefix) {
|
|
@@ -102,7 +102,7 @@ function createSecurityHeadersMiddleware(options = {}) {
|
|
|
102
102
|
c.res.headers.set("X-Content-Type-Options", "nosniff");
|
|
103
103
|
c.res.headers.set("X-Frame-Options", "DENY");
|
|
104
104
|
c.res.headers.set("Referrer-Policy", "no-referrer");
|
|
105
|
-
const isGraphiql = graphiqlPath !== void 0 && c.req.path === graphiqlPath;
|
|
105
|
+
const isGraphiql = graphiqlPath !== void 0 && (c.req.path === graphiqlPath || c.req.path.startsWith(`${graphiqlPath}/`));
|
|
106
106
|
c.res.headers.set("Content-Security-Policy", isGraphiql ? graphiqlCsp : csp);
|
|
107
107
|
};
|
|
108
108
|
}
|
|
@@ -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
|
-
|
|
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
|
|
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)
|
|
796
|
+
if (label !== void 0) includeNames.set(label, f.name);
|
|
763
797
|
} else {
|
|
764
|
-
|
|
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
|
-
|
|
784
|
-
|
|
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
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
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
|
|
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
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
2764
|
+
return "0.7.0";
|
|
2737
2765
|
} catch {
|
|
2738
2766
|
return "0.0.0";
|
|
2739
2767
|
}
|
|
@@ -3323,20 +3351,6 @@ function createCmsApp(options) {
|
|
|
3323
3351
|
sortableColumns
|
|
3324
3352
|
});
|
|
3325
3353
|
};
|
|
3326
|
-
const graphqlRepos = Object.fromEntries([
|
|
3327
|
-
...Object.entries(registry.content_types).map(([typeName, ct]) => [
|
|
3328
|
-
typeName,
|
|
3329
|
-
createDrizzleContentRepository(db, ct.db.table_name, {
|
|
3330
|
-
sortableColumns: sortableColumnsFor(typeName)
|
|
3331
|
-
})
|
|
3332
|
-
]),
|
|
3333
|
-
...Object.entries(registry.taxonomy_types).map(([typeName, tt]) => [
|
|
3334
|
-
typeName,
|
|
3335
|
-
createDrizzleContentRepository(db, tt.db.table_name, {
|
|
3336
|
-
sortableColumns: sortableColumnsFor(typeName)
|
|
3337
|
-
})
|
|
3338
|
-
])
|
|
3339
|
-
]);
|
|
3340
3354
|
registerAuthRoutes(app, db);
|
|
3341
3355
|
app.use("/admin/api/*", authMiddleware);
|
|
3342
3356
|
app.use("/admin/api/*", mustChangePasswordCheck);
|
|
@@ -3403,7 +3417,7 @@ function createCmsApp(options) {
|
|
|
3403
3417
|
app.use(path, attachDeprecationHeaders);
|
|
3404
3418
|
}
|
|
3405
3419
|
}
|
|
3406
|
-
registerPublicContentRoutes(app, registry, surface.repos, surface.projectors, surface.paths, listRateLimit, programmaticResolver);
|
|
3420
|
+
registerPublicContentRoutes(app, registry, surface.repos, surface.projectors, surface.paths, listRateLimit, programmaticResolver, projectors);
|
|
3407
3421
|
}
|
|
3408
3422
|
if (options.versions !== void 0) {
|
|
3409
3423
|
app.all(`${prefix}/:version/*`, async (c, next) => {
|
|
@@ -3434,31 +3448,108 @@ function createCmsApp(options) {
|
|
|
3434
3448
|
registerPublicMediaRoutes(app, mediaRepo, publicPaths, listRateLimit);
|
|
3435
3449
|
if (options.graphql?.enabled) {
|
|
3436
3450
|
const gqlOptions = options.graphql;
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3451
|
+
const makeGraphqlRepo = (_typeName, tableName, sortableColumns) => createDrizzleContentRepository(db, tableName, { sortableColumns });
|
|
3452
|
+
const gqlPasses = [
|
|
3453
|
+
...options.versions !== void 0 ? model.live.map((v) => ({ version: v, projectionVersion: v })) : [],
|
|
3454
|
+
{ version: null, projectionVersion: model.current }
|
|
3455
|
+
];
|
|
3456
|
+
const mounts = gqlPasses.map((pass) => ({
|
|
3457
|
+
path: pass.version === null ? "/graphql" : `/graphql/${pass.version}`,
|
|
3458
|
+
version: pass.version,
|
|
3459
|
+
handler: null,
|
|
3460
|
+
error: null
|
|
3461
|
+
}));
|
|
3462
|
+
const ready = import("./handler-GED3I7Z2.mjs").then(({ createGraphQLHandler }) => {
|
|
3463
|
+
gqlPasses.forEach((pass, i) => {
|
|
3464
|
+
const mount = mounts[i];
|
|
3465
|
+
try {
|
|
3466
|
+
const surface = buildVersionSurface({
|
|
3467
|
+
...pass,
|
|
3468
|
+
prefix,
|
|
3469
|
+
registry,
|
|
3470
|
+
model,
|
|
3471
|
+
makeRepo: makeGraphqlRepo
|
|
3472
|
+
});
|
|
3473
|
+
const projection = model.projections[pass.projectionVersion];
|
|
3474
|
+
const currentProjection = model.projections[model.current];
|
|
3475
|
+
mount.handler = createGraphQLHandler(
|
|
3476
|
+
registry,
|
|
3477
|
+
surface.repos,
|
|
3478
|
+
surface.fieldKeyMaps,
|
|
3479
|
+
programmaticResolver,
|
|
3480
|
+
db,
|
|
3481
|
+
gqlOptions,
|
|
3482
|
+
{
|
|
3483
|
+
endpoint: mount.path,
|
|
3484
|
+
currentVersion: model.current,
|
|
3485
|
+
...projection !== void 0 && { projection },
|
|
3486
|
+
...currentProjection !== void 0 && { currentProjection },
|
|
3487
|
+
currentFieldKeyMaps: fieldKeyMaps
|
|
3488
|
+
}
|
|
3489
|
+
);
|
|
3490
|
+
} catch (err) {
|
|
3491
|
+
mount.error = err;
|
|
3492
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3493
|
+
process.stderr.write(
|
|
3494
|
+
`\u2717 GraphQL schema failed to initialize for ${mount.path}; it will return 500. ${message}
|
|
3495
|
+
`
|
|
3496
|
+
);
|
|
3497
|
+
}
|
|
3498
|
+
});
|
|
3441
3499
|
}).catch((err) => {
|
|
3442
|
-
|
|
3500
|
+
for (const m of mounts) m.error = err;
|
|
3443
3501
|
const message = err instanceof Error ? err.message : String(err);
|
|
3444
|
-
|
|
3502
|
+
const paths = mounts.map((m) => m.path).join(", ");
|
|
3503
|
+
process.stderr.write(`\u2717 GraphQL module failed to load; ${paths} will return 500. ${message}
|
|
3445
3504
|
`);
|
|
3446
3505
|
});
|
|
3447
|
-
const
|
|
3448
|
-
if (!
|
|
3449
|
-
if (!
|
|
3506
|
+
const invokeFor = (mount) => async (c) => {
|
|
3507
|
+
if (!mount.handler && !mount.error) await ready;
|
|
3508
|
+
if (!mount.handler) {
|
|
3450
3509
|
return c.json(
|
|
3451
3510
|
{ ok: false, error: { code: "GRAPHQL_INIT_FAILED", message: "GraphQL schema failed to initialize" } },
|
|
3452
3511
|
500
|
|
3453
3512
|
);
|
|
3454
3513
|
}
|
|
3455
|
-
return
|
|
3514
|
+
return mount.handler(c, async () => {
|
|
3456
3515
|
});
|
|
3457
3516
|
};
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3517
|
+
for (const mount of mounts) {
|
|
3518
|
+
const headers = deprecationHeaders({
|
|
3519
|
+
requested: mount.version,
|
|
3520
|
+
model,
|
|
3521
|
+
successor: `/graphql/${model.current}`
|
|
3522
|
+
});
|
|
3523
|
+
if (headers === null) continue;
|
|
3524
|
+
const attach = async (c, next) => {
|
|
3525
|
+
await next();
|
|
3526
|
+
for (const [key, value] of Object.entries(headers)) c.res.headers.set(key, value);
|
|
3527
|
+
};
|
|
3528
|
+
app.use(mount.path, attach);
|
|
3529
|
+
}
|
|
3530
|
+
for (const mount of mounts) {
|
|
3531
|
+
if (listRateLimit) app.all(mount.path, listRateLimit, invokeFor(mount));
|
|
3532
|
+
else app.all(mount.path, invokeFor(mount));
|
|
3533
|
+
}
|
|
3534
|
+
if (options.versions !== void 0) {
|
|
3535
|
+
app.all("/graphql/:version", async (c, next) => {
|
|
3536
|
+
const segment = c.req.param("version");
|
|
3537
|
+
const kind = classifyVersion(segment, model);
|
|
3538
|
+
if (kind === "not-a-version" || kind === "live") return next();
|
|
3539
|
+
const live = model.live.join(", ");
|
|
3540
|
+
return c.json({
|
|
3541
|
+
errors: [
|
|
3542
|
+
{
|
|
3543
|
+
message: kind === "retired" ? `Version ${segment} is no longer served. Live versions: ${live}.` : `Version ${segment} does not exist. Live versions: ${live}.`,
|
|
3544
|
+
extensions: {
|
|
3545
|
+
code: kind === "retired" ? "VERSION_RETIRED" : "VERSION_UNKNOWN",
|
|
3546
|
+
current: model.current,
|
|
3547
|
+
live: model.live
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
]
|
|
3551
|
+
});
|
|
3552
|
+
});
|
|
3462
3553
|
}
|
|
3463
3554
|
}
|
|
3464
3555
|
registerConfigRoute(
|