@se-studio/contentful-rest-api 1.0.46 → 1.0.47
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/CHANGELOG.md +416 -0
- package/dist/api/article-type.d.ts +37 -0
- package/dist/api/article-type.d.ts.map +1 -0
- package/dist/api/article-type.js +134 -0
- package/dist/api/article-type.js.map +1 -0
- package/dist/api/article.d.ts +32 -0
- package/dist/api/article.d.ts.map +1 -0
- package/dist/api/article.js +43 -0
- package/dist/api/article.js.map +1 -0
- package/dist/api/asset.d.ts +28 -0
- package/dist/api/asset.d.ts.map +1 -0
- package/dist/api/asset.js +55 -0
- package/dist/api/asset.js.map +1 -0
- package/dist/api/context.d.ts +24 -0
- package/dist/api/context.d.ts.map +1 -0
- package/dist/api/context.js +62 -0
- package/dist/api/context.js.map +1 -0
- package/dist/api/custom-type.d.ts +37 -0
- package/dist/api/custom-type.d.ts.map +1 -0
- package/dist/api/custom-type.js +44 -0
- package/dist/api/custom-type.js.map +1 -0
- package/dist/api/helpers.d.ts +73 -0
- package/dist/api/helpers.d.ts.map +1 -0
- package/dist/api/helpers.js +296 -0
- package/dist/api/helpers.js.map +1 -0
- package/dist/api/index.d.ts +22 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +28 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/links.d.ts +109 -0
- package/dist/api/links.d.ts.map +1 -0
- package/dist/api/links.js +199 -0
- package/dist/api/links.js.map +1 -0
- package/dist/api/page.d.ts +33 -0
- package/dist/api/page.d.ts.map +1 -0
- package/dist/api/page.js +40 -0
- package/dist/api/page.js.map +1 -0
- package/dist/api/person.d.ts +37 -0
- package/dist/api/person.d.ts.map +1 -0
- package/dist/api/person.js +134 -0
- package/dist/api/person.js.map +1 -0
- package/dist/api/preview.d.ts +55 -0
- package/dist/api/preview.d.ts.map +1 -0
- package/dist/api/preview.js +160 -0
- package/dist/api/preview.js.map +1 -0
- package/dist/api/related-articles.d.ts +22 -0
- package/dist/api/related-articles.d.ts.map +1 -0
- package/dist/api/related-articles.js +82 -0
- package/dist/api/related-articles.js.map +1 -0
- package/dist/api/server-asset.d.ts +40 -0
- package/dist/api/server-asset.d.ts.map +1 -0
- package/dist/api/server-asset.js +65 -0
- package/dist/api/server-asset.js.map +1 -0
- package/dist/api/sitemap.d.ts +131 -0
- package/dist/api/sitemap.d.ts.map +1 -0
- package/dist/api/sitemap.js +199 -0
- package/dist/api/sitemap.js.map +1 -0
- package/dist/api/tag.d.ts +37 -0
- package/dist/api/tag.d.ts.map +1 -0
- package/dist/api/tag.js +131 -0
- package/dist/api/tag.js.map +1 -0
- package/dist/api/template.d.ts +49 -0
- package/dist/api/template.d.ts.map +1 -0
- package/dist/api/template.js +88 -0
- package/dist/api/template.js.map +1 -0
- package/dist/api/types.d.ts +98 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +2 -0
- package/dist/api/types.js.map +1 -0
- package/dist/baseTypes/baseAlternatePageContent.d.ts +24 -0
- package/dist/baseTypes/baseAlternatePageContent.d.ts.map +1 -0
- package/dist/baseTypes/baseAlternatePageContent.js +2 -0
- package/dist/baseTypes/baseAlternatePageContent.js.map +1 -0
- package/dist/baseTypes/baseArticle.d.ts +179 -0
- package/dist/baseTypes/baseArticle.d.ts.map +1 -0
- package/dist/baseTypes/baseArticle.js +2 -0
- package/dist/baseTypes/baseArticle.js.map +1 -0
- package/dist/baseTypes/baseArticleType.d.ts +156 -0
- package/dist/baseTypes/baseArticleType.d.ts.map +1 -0
- package/dist/baseTypes/baseArticleType.js +2 -0
- package/dist/baseTypes/baseArticleType.js.map +1 -0
- package/dist/baseTypes/baseBanner.d.ts +76 -0
- package/dist/baseTypes/baseBanner.d.ts.map +1 -0
- package/dist/baseTypes/baseBanner.js +2 -0
- package/dist/baseTypes/baseBanner.js.map +1 -0
- package/dist/baseTypes/baseCollection.d.ts +132 -0
- package/dist/baseTypes/baseCollection.d.ts.map +1 -0
- package/dist/baseTypes/baseCollection.js +2 -0
- package/dist/baseTypes/baseCollection.js.map +1 -0
- package/dist/baseTypes/baseComponent.d.ts +130 -0
- package/dist/baseTypes/baseComponent.d.ts.map +1 -0
- package/dist/baseTypes/baseComponent.js +2 -0
- package/dist/baseTypes/baseComponent.js.map +1 -0
- package/dist/baseTypes/baseCustomType.d.ts +126 -0
- package/dist/baseTypes/baseCustomType.d.ts.map +1 -0
- package/dist/baseTypes/baseCustomType.js +2 -0
- package/dist/baseTypes/baseCustomType.js.map +1 -0
- package/dist/baseTypes/baseExternalComponent.d.ts +66 -0
- package/dist/baseTypes/baseExternalComponent.d.ts.map +1 -0
- package/dist/baseTypes/baseExternalComponent.js +2 -0
- package/dist/baseTypes/baseExternalComponent.js.map +1 -0
- package/dist/baseTypes/baseExternalVideo.d.ts +85 -0
- package/dist/baseTypes/baseExternalVideo.d.ts.map +1 -0
- package/dist/baseTypes/baseExternalVideo.js +2 -0
- package/dist/baseTypes/baseExternalVideo.js.map +1 -0
- package/dist/baseTypes/baseLink.d.ts +90 -0
- package/dist/baseTypes/baseLink.d.ts.map +1 -0
- package/dist/baseTypes/baseLink.js +2 -0
- package/dist/baseTypes/baseLink.js.map +1 -0
- package/dist/baseTypes/baseMedia.d.ts +92 -0
- package/dist/baseTypes/baseMedia.d.ts.map +1 -0
- package/dist/baseTypes/baseMedia.js +2 -0
- package/dist/baseTypes/baseMedia.js.map +1 -0
- package/dist/baseTypes/baseNavigation.d.ts +36 -0
- package/dist/baseTypes/baseNavigation.d.ts.map +1 -0
- package/dist/baseTypes/baseNavigation.js +2 -0
- package/dist/baseTypes/baseNavigation.js.map +1 -0
- package/dist/baseTypes/baseNavigationItem.d.ts +96 -0
- package/dist/baseTypes/baseNavigationItem.d.ts.map +1 -0
- package/dist/baseTypes/baseNavigationItem.js +2 -0
- package/dist/baseTypes/baseNavigationItem.js.map +1 -0
- package/dist/baseTypes/basePage.d.ts +120 -0
- package/dist/baseTypes/basePage.d.ts.map +1 -0
- package/dist/baseTypes/basePage.js +2 -0
- package/dist/baseTypes/basePage.js.map +1 -0
- package/dist/baseTypes/basePageTest.d.ts +66 -0
- package/dist/baseTypes/basePageTest.d.ts.map +1 -0
- package/dist/baseTypes/basePageTest.js +2 -0
- package/dist/baseTypes/basePageTest.js.map +1 -0
- package/dist/baseTypes/basePageVariant.d.ts +123 -0
- package/dist/baseTypes/basePageVariant.d.ts.map +1 -0
- package/dist/baseTypes/basePageVariant.js +2 -0
- package/dist/baseTypes/basePageVariant.js.map +1 -0
- package/dist/baseTypes/basePerson.d.ts +111 -0
- package/dist/baseTypes/basePerson.d.ts.map +1 -0
- package/dist/baseTypes/basePerson.js +2 -0
- package/dist/baseTypes/basePerson.js.map +1 -0
- package/dist/baseTypes/baseSchema.d.ts +18 -0
- package/dist/baseTypes/baseSchema.d.ts.map +1 -0
- package/dist/baseTypes/baseSchema.js +2 -0
- package/dist/baseTypes/baseSchema.js.map +1 -0
- package/dist/baseTypes/baseShared.d.ts +30 -0
- package/dist/baseTypes/baseShared.d.ts.map +1 -0
- package/dist/baseTypes/baseShared.js +2 -0
- package/dist/baseTypes/baseShared.js.map +1 -0
- package/dist/baseTypes/baseTag.d.ts +108 -0
- package/dist/baseTypes/baseTag.d.ts.map +1 -0
- package/dist/baseTypes/baseTag.js +2 -0
- package/dist/baseTypes/baseTag.js.map +1 -0
- package/dist/baseTypes/baseTagType.d.ts +11 -0
- package/dist/baseTypes/baseTagType.d.ts.map +1 -0
- package/dist/baseTypes/baseTagType.js +2 -0
- package/dist/baseTypes/baseTagType.js.map +1 -0
- package/dist/baseTypes/baseTemplate.d.ts +55 -0
- package/dist/baseTypes/baseTemplate.d.ts.map +1 -0
- package/dist/baseTypes/baseTemplate.js +2 -0
- package/dist/baseTypes/baseTemplate.js.map +1 -0
- package/dist/client.d.ts +143 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +268 -0
- package/dist/client.js.map +1 -0
- package/dist/converters/article.d.ts +42 -0
- package/dist/converters/article.d.ts.map +1 -0
- package/dist/converters/article.js +220 -0
- package/dist/converters/article.js.map +1 -0
- package/dist/converters/asset.d.ts +22 -0
- package/dist/converters/asset.d.ts.map +1 -0
- package/dist/converters/asset.js +282 -0
- package/dist/converters/asset.js.map +1 -0
- package/dist/converters/collection.d.ts +26 -0
- package/dist/converters/collection.d.ts.map +1 -0
- package/dist/converters/collection.js +50 -0
- package/dist/converters/collection.js.map +1 -0
- package/dist/converters/component.d.ts +26 -0
- package/dist/converters/component.d.ts.map +1 -0
- package/dist/converters/component.js +54 -0
- package/dist/converters/component.js.map +1 -0
- package/dist/converters/customType.d.ts +24 -0
- package/dist/converters/customType.d.ts.map +1 -0
- package/dist/converters/customType.js +71 -0
- package/dist/converters/customType.js.map +1 -0
- package/dist/converters/externalComponent.d.ts +22 -0
- package/dist/converters/externalComponent.d.ts.map +1 -0
- package/dist/converters/externalComponent.js +34 -0
- package/dist/converters/externalComponent.js.map +1 -0
- package/dist/converters/helpers.d.ts +116 -0
- package/dist/converters/helpers.d.ts.map +1 -0
- package/dist/converters/helpers.js +128 -0
- package/dist/converters/helpers.js.map +1 -0
- package/dist/converters/iconCollector.d.ts +65 -0
- package/dist/converters/iconCollector.d.ts.map +1 -0
- package/dist/converters/iconCollector.js +282 -0
- package/dist/converters/iconCollector.js.map +1 -0
- package/dist/converters/index.d.ts +18 -0
- package/dist/converters/index.d.ts.map +1 -0
- package/dist/converters/index.js +18 -0
- package/dist/converters/index.js.map +1 -0
- package/dist/converters/link.d.ts +11 -0
- package/dist/converters/link.d.ts.map +1 -0
- package/dist/converters/link.js +96 -0
- package/dist/converters/link.js.map +1 -0
- package/dist/converters/navigationItem.d.ts +11 -0
- package/dist/converters/navigationItem.d.ts.map +1 -0
- package/dist/converters/navigationItem.js +73 -0
- package/dist/converters/navigationItem.js.map +1 -0
- package/dist/converters/page.d.ts +44 -0
- package/dist/converters/page.d.ts.map +1 -0
- package/dist/converters/page.js +121 -0
- package/dist/converters/page.js.map +1 -0
- package/dist/converters/person.d.ts +40 -0
- package/dist/converters/person.d.ts.map +1 -0
- package/dist/converters/person.js +109 -0
- package/dist/converters/person.js.map +1 -0
- package/dist/converters/resolver.d.ts +29 -0
- package/dist/converters/resolver.d.ts.map +1 -0
- package/dist/converters/resolver.js +317 -0
- package/dist/converters/resolver.js.map +1 -0
- package/dist/converters/schema.d.ts +14 -0
- package/dist/converters/schema.d.ts.map +1 -0
- package/dist/converters/schema.js +18 -0
- package/dist/converters/schema.js.map +1 -0
- package/dist/converters/svgProcessor.d.ts +23 -0
- package/dist/converters/svgProcessor.d.ts.map +1 -0
- package/dist/converters/svgProcessor.js +47 -0
- package/dist/converters/svgProcessor.js.map +1 -0
- package/dist/converters/tag.d.ts +25 -0
- package/dist/converters/tag.d.ts.map +1 -0
- package/dist/converters/tag.js +98 -0
- package/dist/converters/tag.js.map +1 -0
- package/dist/converters/template.d.ts +26 -0
- package/dist/converters/template.d.ts.map +1 -0
- package/dist/converters/template.js +44 -0
- package/dist/converters/template.js.map +1 -0
- package/dist/index.d.ts +28 -721
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -3634
- package/dist/index.js.map +1 -1
- package/dist/revalidation/handlers.d.ts +52 -0
- package/dist/revalidation/handlers.d.ts.map +1 -0
- package/dist/revalidation/handlers.js +130 -0
- package/dist/revalidation/handlers.js.map +1 -0
- package/dist/revalidation/index.d.ts +3 -0
- package/dist/revalidation/index.d.ts.map +1 -0
- package/dist/revalidation/index.js +4 -0
- package/dist/revalidation/index.js.map +1 -0
- package/dist/revalidation/nextjs-route.d.ts +31 -0
- package/dist/revalidation/nextjs-route.d.ts.map +1 -0
- package/dist/revalidation/nextjs-route.js +34 -0
- package/dist/revalidation/nextjs-route.js.map +1 -0
- package/dist/revalidation/route.d.ts +3 -0
- package/dist/revalidation/route.d.ts.map +1 -0
- package/dist/revalidation/route.js +97 -0
- package/dist/revalidation/route.js.map +1 -0
- package/dist/revalidation/server-utils.d.ts +22 -0
- package/dist/revalidation/server-utils.d.ts.map +1 -0
- package/dist/revalidation/server-utils.js +41 -0
- package/dist/revalidation/server-utils.js.map +1 -0
- package/dist/revalidation/tags.d.ts +81 -0
- package/dist/revalidation/tags.d.ts.map +1 -0
- package/dist/revalidation/tags.js +117 -0
- package/dist/revalidation/tags.js.map +1 -0
- package/dist/revalidation/utils.d.ts +21 -0
- package/dist/revalidation/utils.d.ts.map +1 -0
- package/dist/revalidation/utils.js +51 -0
- package/dist/revalidation/utils.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +10 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/arrayUtils.d.ts +3 -0
- package/dist/utils/arrayUtils.d.ts.map +1 -0
- package/dist/utils/arrayUtils.js +12 -0
- package/dist/utils/arrayUtils.js.map +1 -0
- package/dist/utils/dateUtils.d.ts +9 -0
- package/dist/utils/dateUtils.d.ts.map +1 -0
- package/dist/utils/dateUtils.js +26 -0
- package/dist/utils/dateUtils.js.map +1 -0
- package/dist/utils/errors.d.ts +56 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +100 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/json-utils.d.ts +17 -0
- package/dist/utils/json-utils.d.ts.map +1 -0
- package/dist/utils/json-utils.js +43 -0
- package/dist/utils/json-utils.js.map +1 -0
- package/dist/utils/retry.d.ts +112 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +221 -0
- package/dist/utils/retry.js.map +1 -0
- package/package.json +12 -6
package/dist/index.js
CHANGED
|
@@ -1,3635 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return `${AssetTag}#${assetId}`;
|
|
39
|
-
}
|
|
40
|
-
function templateTag(label) {
|
|
41
|
-
return `${TemplateTag}#${label}`;
|
|
42
|
-
}
|
|
43
|
-
function locationTag(slug) {
|
|
44
|
-
return `${LocationTag}#${slug}`;
|
|
45
|
-
}
|
|
46
|
-
var PageTag, ArticleTag, ArticleTypeTag, ArticleTypeIndexTag, CustomTypeTag, TagTag, PersonTag, AssetTag, TemplateTag, NavigationTag, BannerTag, LocationTag, GlobalTag, AllTags;
|
|
47
|
-
var init_tags = __esm({
|
|
48
|
-
"src/revalidation/tags.ts"() {
|
|
49
|
-
PageTag = "page";
|
|
50
|
-
ArticleTag = "article";
|
|
51
|
-
ArticleTypeTag = "articleType";
|
|
52
|
-
ArticleTypeIndexTag = "articleTypeIndex";
|
|
53
|
-
CustomTypeTag = "customType";
|
|
54
|
-
TagTag = "tag";
|
|
55
|
-
PersonTag = "person";
|
|
56
|
-
AssetTag = "asset";
|
|
57
|
-
TemplateTag = "template";
|
|
58
|
-
NavigationTag = "navigation";
|
|
59
|
-
BannerTag = "banner";
|
|
60
|
-
LocationTag = "location";
|
|
61
|
-
GlobalTag = "global";
|
|
62
|
-
AllTags = [
|
|
63
|
-
PageTag,
|
|
64
|
-
ArticleTag,
|
|
65
|
-
ArticleTypeTag,
|
|
66
|
-
ArticleTypeIndexTag,
|
|
67
|
-
CustomTypeTag,
|
|
68
|
-
TagTag,
|
|
69
|
-
PersonTag,
|
|
70
|
-
AssetTag,
|
|
71
|
-
TemplateTag,
|
|
72
|
-
NavigationTag,
|
|
73
|
-
BannerTag,
|
|
74
|
-
LocationTag,
|
|
75
|
-
"hubspot-forms"
|
|
76
|
-
// Additional tag from the old implementation
|
|
77
|
-
];
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// src/revalidation/utils.ts
|
|
82
|
-
var utils_exports = {};
|
|
83
|
-
__export(utils_exports, {
|
|
84
|
-
getCacheTags: () => getCacheTags,
|
|
85
|
-
getCacheTagsForPreview: () => getCacheTagsForPreview,
|
|
86
|
-
getCacheTagsForProduction: () => getCacheTagsForProduction,
|
|
87
|
-
revalidateSingleTag: () => revalidateSingleTag,
|
|
88
|
-
revalidateTags: () => revalidateTags
|
|
89
|
-
});
|
|
90
|
-
function getCacheTagsForPreview() {
|
|
91
|
-
return [GlobalTag];
|
|
92
|
-
}
|
|
93
|
-
function getCacheTagsForProduction(contentType, slug) {
|
|
94
|
-
switch (contentType) {
|
|
95
|
-
case "page":
|
|
96
|
-
return slug ? [pageTag(slug), PageTag] : [PageTag];
|
|
97
|
-
case "article":
|
|
98
|
-
return slug ? [articleTag(slug), ArticleTag] : [ArticleTag];
|
|
99
|
-
case "articleType":
|
|
100
|
-
return slug ? [articleTypeTag(slug), ArticleTypeTag] : [ArticleTypeTag];
|
|
101
|
-
case "customType":
|
|
102
|
-
return slug ? [customTypeTag(slug), CustomTypeTag] : [CustomTypeTag];
|
|
103
|
-
case "tag":
|
|
104
|
-
return slug ? [tagTag(slug), TagTag] : [TagTag];
|
|
105
|
-
case "person":
|
|
106
|
-
return slug ? [personTag(slug), PersonTag] : [PersonTag];
|
|
107
|
-
case "asset":
|
|
108
|
-
return slug ? [assetTag(slug), AssetTag] : [AssetTag];
|
|
109
|
-
case "location":
|
|
110
|
-
return slug ? [locationTag(slug), LocationTag] : [LocationTag];
|
|
111
|
-
default:
|
|
112
|
-
return [contentType];
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function getCacheTags(contentType, slug, preview = false) {
|
|
116
|
-
if (preview) {
|
|
117
|
-
return getCacheTagsForPreview();
|
|
118
|
-
}
|
|
119
|
-
return getCacheTagsForProduction(contentType, slug);
|
|
120
|
-
}
|
|
121
|
-
async function revalidateTags(tags, reason) {
|
|
122
|
-
await delay(500);
|
|
123
|
-
for (const tag of tags) {
|
|
124
|
-
console.log(`Revalidating tag: ${tag} - ${reason}`);
|
|
125
|
-
revalidateTag(tag);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
async function revalidateSingleTag(tag, reason) {
|
|
129
|
-
await delay(500);
|
|
130
|
-
console.log(`Revalidating single tag: ${tag} - ${reason}`);
|
|
131
|
-
revalidateTag(tag);
|
|
132
|
-
}
|
|
133
|
-
function delay(ms) {
|
|
134
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
135
|
-
}
|
|
136
|
-
var init_utils = __esm({
|
|
137
|
-
"src/revalidation/utils.ts"() {
|
|
138
|
-
init_tags();
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// src/utils/errors.ts
|
|
143
|
-
var ContentfulError = class _ContentfulError extends Error {
|
|
144
|
-
constructor(message, statusCode, details) {
|
|
145
|
-
super(message);
|
|
146
|
-
this.statusCode = statusCode;
|
|
147
|
-
this.details = details;
|
|
148
|
-
this.name = "ContentfulError";
|
|
149
|
-
Object.setPrototypeOf(this, _ContentfulError.prototype);
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
var RateLimitError = class _RateLimitError extends ContentfulError {
|
|
153
|
-
constructor(message, retryAfter, details) {
|
|
154
|
-
super(message, 429, details);
|
|
155
|
-
this.retryAfter = retryAfter;
|
|
156
|
-
this.name = "RateLimitError";
|
|
157
|
-
Object.setPrototypeOf(this, _RateLimitError.prototype);
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
var EntryNotFoundError = class _EntryNotFoundError extends ContentfulError {
|
|
161
|
-
constructor(entryId, contentType) {
|
|
162
|
-
super(`Entry not found: ${entryId}${contentType ? ` (${contentType})` : ""}`, 404);
|
|
163
|
-
this.entryId = entryId;
|
|
164
|
-
this.contentType = contentType;
|
|
165
|
-
this.name = "EntryNotFoundError";
|
|
166
|
-
Object.setPrototypeOf(this, _EntryNotFoundError.prototype);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
var AuthenticationError = class _AuthenticationError extends ContentfulError {
|
|
170
|
-
constructor(message = "Authentication failed") {
|
|
171
|
-
super(message, 401);
|
|
172
|
-
this.name = "AuthenticationError";
|
|
173
|
-
Object.setPrototypeOf(this, _AuthenticationError.prototype);
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
var ValidationError = class _ValidationError extends ContentfulError {
|
|
177
|
-
constructor(message, validationErrors) {
|
|
178
|
-
super(message, 400, validationErrors);
|
|
179
|
-
this.validationErrors = validationErrors;
|
|
180
|
-
this.name = "ValidationError";
|
|
181
|
-
Object.setPrototypeOf(this, _ValidationError.prototype);
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
function isContentfulError(error) {
|
|
185
|
-
return error instanceof ContentfulError;
|
|
186
|
-
}
|
|
187
|
-
function isRateLimitError(error) {
|
|
188
|
-
return error instanceof RateLimitError;
|
|
189
|
-
}
|
|
190
|
-
function isRetryableError(error) {
|
|
191
|
-
if (isRateLimitError(error)) {
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
if (isContentfulError(error)) {
|
|
195
|
-
return error.statusCode !== void 0 && (error.statusCode >= 500 || error.statusCode === 429);
|
|
196
|
-
}
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
function getRetryAfter(error) {
|
|
200
|
-
if (isRateLimitError(error)) {
|
|
201
|
-
return error.retryAfter;
|
|
202
|
-
}
|
|
203
|
-
return void 0;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// src/utils/retry.ts
|
|
207
|
-
var DEFAULT_RETRY_CONFIG = {
|
|
208
|
-
maxRetries: 3,
|
|
209
|
-
initialDelay: 1e3,
|
|
210
|
-
// 1 second
|
|
211
|
-
maxDelay: 3e4,
|
|
212
|
-
// 30 seconds
|
|
213
|
-
backoffMultiplier: 2
|
|
214
|
-
};
|
|
215
|
-
function sleep(ms) {
|
|
216
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
217
|
-
}
|
|
218
|
-
function calculateBackoffDelay(attempt, config, retryAfter) {
|
|
219
|
-
if (retryAfter !== void 0) {
|
|
220
|
-
return Math.min(retryAfter * 1e3, config.maxDelay);
|
|
221
|
-
}
|
|
222
|
-
const exponentialDelay = config.initialDelay * config.backoffMultiplier ** attempt;
|
|
223
|
-
const jitter = Math.random() * exponentialDelay;
|
|
224
|
-
return Math.min(exponentialDelay + jitter, config.maxDelay);
|
|
225
|
-
}
|
|
226
|
-
async function withRetry(fn, config) {
|
|
227
|
-
const retryConfig = {
|
|
228
|
-
...DEFAULT_RETRY_CONFIG,
|
|
229
|
-
...config
|
|
230
|
-
};
|
|
231
|
-
let lastError;
|
|
232
|
-
for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
|
|
233
|
-
try {
|
|
234
|
-
return await fn();
|
|
235
|
-
} catch (error) {
|
|
236
|
-
lastError = error;
|
|
237
|
-
if (attempt === retryConfig.maxRetries) {
|
|
238
|
-
break;
|
|
239
|
-
}
|
|
240
|
-
if (!isRetryableError(error)) {
|
|
241
|
-
throw error;
|
|
242
|
-
}
|
|
243
|
-
const retryAfter = getRetryAfter(error);
|
|
244
|
-
const delay3 = calculateBackoffDelay(attempt, retryConfig, retryAfter);
|
|
245
|
-
if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") {
|
|
246
|
-
console.warn(
|
|
247
|
-
`Retry attempt ${attempt + 1}/${retryConfig.maxRetries} after ${delay3}ms`,
|
|
248
|
-
error
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
await sleep(delay3);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
throw lastError;
|
|
255
|
-
}
|
|
256
|
-
var RateLimiter = class {
|
|
257
|
-
/** Minimum interval between requests in milliseconds */
|
|
258
|
-
intervalMs;
|
|
259
|
-
/** Timestamp when the next request can be made */
|
|
260
|
-
nextAvailableTime = 0;
|
|
261
|
-
/** Queue of pending requests */
|
|
262
|
-
queue = [];
|
|
263
|
-
/** Whether the queue processor is running */
|
|
264
|
-
processing = false;
|
|
265
|
-
/** Additional pause time from 429 responses (ms) */
|
|
266
|
-
pauseUntil = 0;
|
|
267
|
-
constructor(requestsPerSecond, _refillRate) {
|
|
268
|
-
this.intervalMs = Math.ceil(1e3 / (requestsPerSecond * 0.8));
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Calculates how long to wait before the next request can be made
|
|
272
|
-
*/
|
|
273
|
-
getWaitTime() {
|
|
274
|
-
const now = Date.now();
|
|
275
|
-
const waitForInterval = Math.max(0, this.nextAvailableTime - now);
|
|
276
|
-
const waitForPause = Math.max(0, this.pauseUntil - now);
|
|
277
|
-
return Math.max(waitForInterval, waitForPause);
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Processes the queue, releasing requests one at a time with proper spacing
|
|
281
|
-
*/
|
|
282
|
-
async processQueue() {
|
|
283
|
-
if (this.processing) {
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
this.processing = true;
|
|
287
|
-
while (this.queue.length > 0) {
|
|
288
|
-
const waitTime = this.getWaitTime();
|
|
289
|
-
if (waitTime > 0) {
|
|
290
|
-
await sleep(waitTime);
|
|
291
|
-
}
|
|
292
|
-
this.nextAvailableTime = Date.now() + this.intervalMs;
|
|
293
|
-
const entry = this.queue.shift();
|
|
294
|
-
entry?.resolve();
|
|
295
|
-
}
|
|
296
|
-
this.processing = false;
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Acquires permission to make a request.
|
|
300
|
-
* Returns a promise that resolves when the request can proceed.
|
|
301
|
-
* Requests are processed in FIFO order with minimum interval spacing.
|
|
302
|
-
*
|
|
303
|
-
* @returns Promise that resolves when rate limit allows the request
|
|
304
|
-
*/
|
|
305
|
-
async acquire() {
|
|
306
|
-
const waitTime = this.getWaitTime();
|
|
307
|
-
if (waitTime === 0 && this.queue.length === 0) {
|
|
308
|
-
this.nextAvailableTime = Date.now() + this.intervalMs;
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
return new Promise((resolve) => {
|
|
312
|
-
this.queue.push({ resolve });
|
|
313
|
-
this.processQueue();
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Gets the number of requests currently waiting in the queue
|
|
318
|
-
*/
|
|
319
|
-
getQueueLength() {
|
|
320
|
-
return this.queue.length;
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Pauses the rate limiter for a specified duration.
|
|
324
|
-
* All requests (current and queued) will wait until pause expires.
|
|
325
|
-
* Does NOT block - just sets the pause time and returns.
|
|
326
|
-
*
|
|
327
|
-
* @param seconds - Duration to pause in seconds
|
|
328
|
-
*/
|
|
329
|
-
pause(seconds) {
|
|
330
|
-
const pauseUntil = Date.now() + seconds * 1e3;
|
|
331
|
-
if (pauseUntil > this.pauseUntil) {
|
|
332
|
-
this.pauseUntil = pauseUntil;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Gets the current interval between requests in milliseconds
|
|
337
|
-
*/
|
|
338
|
-
getIntervalMs() {
|
|
339
|
-
return this.intervalMs;
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
var CONTENTFUL_RATE_LIMITS = {
|
|
343
|
-
/** Content Delivery API: 55 requests/second */
|
|
344
|
-
delivery: 55,
|
|
345
|
-
/** Content Preview API: 14 requests/second */
|
|
346
|
-
preview: 14
|
|
347
|
-
};
|
|
348
|
-
var deliveryRateLimiter = new RateLimiter(CONTENTFUL_RATE_LIMITS.delivery);
|
|
349
|
-
var previewRateLimiter = new RateLimiter(CONTENTFUL_RATE_LIMITS.preview);
|
|
350
|
-
function getRateLimiter(preview) {
|
|
351
|
-
return preview ? previewRateLimiter : deliveryRateLimiter;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// src/client.ts
|
|
355
|
-
function buildQueryString(query) {
|
|
356
|
-
const params = new URLSearchParams();
|
|
357
|
-
Object.entries(query).forEach(([key, value]) => {
|
|
358
|
-
if (value !== void 0 && value !== null) {
|
|
359
|
-
params.append(key, String(value));
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
return params.toString();
|
|
363
|
-
}
|
|
364
|
-
function sleep2(ms) {
|
|
365
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
366
|
-
}
|
|
367
|
-
var RETRY_CONFIG = {
|
|
368
|
-
maxRetries: 10,
|
|
369
|
-
initialDelay: 1e3,
|
|
370
|
-
maxDelay: 15e3,
|
|
371
|
-
backoffMultiplier: 2
|
|
372
|
-
};
|
|
373
|
-
function parseRetryAfter(response) {
|
|
374
|
-
const retryAfterHeader = response.headers.get("X-Contentful-RateLimit-Reset") || response.headers.get("Retry-After");
|
|
375
|
-
if (retryAfterHeader) {
|
|
376
|
-
const parsed = Number.parseInt(retryAfterHeader, 10);
|
|
377
|
-
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
378
|
-
}
|
|
379
|
-
return void 0;
|
|
380
|
-
}
|
|
381
|
-
async function parseErrorResponse(response) {
|
|
382
|
-
const statusCode = response.status;
|
|
383
|
-
let errorData;
|
|
384
|
-
try {
|
|
385
|
-
errorData = await response.json();
|
|
386
|
-
} catch {
|
|
387
|
-
errorData = { message: response.statusText };
|
|
388
|
-
}
|
|
389
|
-
const message = errorData?.message || `Contentful API error: ${statusCode}`;
|
|
390
|
-
switch (statusCode) {
|
|
391
|
-
case 401:
|
|
392
|
-
return new AuthenticationError(message);
|
|
393
|
-
case 404:
|
|
394
|
-
return new EntryNotFoundError(
|
|
395
|
-
errorData?.sys?.id || "unknown",
|
|
396
|
-
errorData?.sys?.contentType?.sys?.id
|
|
397
|
-
);
|
|
398
|
-
case 429: {
|
|
399
|
-
const retryAfterHeader = response.headers.get("X-Contentful-RateLimit-Reset") || response.headers.get("Retry-After");
|
|
400
|
-
const retryAfter = retryAfterHeader ? Number.parseInt(retryAfterHeader, 10) : void 0;
|
|
401
|
-
return new RateLimitError(message, retryAfter, errorData);
|
|
402
|
-
}
|
|
403
|
-
case 400:
|
|
404
|
-
return new ValidationError(message, errorData);
|
|
405
|
-
default:
|
|
406
|
-
return new ContentfulError(message, statusCode, errorData);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
var ContentfulFetchClient = class {
|
|
410
|
-
baseUrl;
|
|
411
|
-
accessToken;
|
|
412
|
-
rateLimiter;
|
|
413
|
-
isPreview;
|
|
414
|
-
constructor(config, preview = false) {
|
|
415
|
-
const host = config.host || (preview ? "preview.contentful.com" : "cdn.contentful.com");
|
|
416
|
-
const environment = config.environment || "master";
|
|
417
|
-
this.baseUrl = `https://${host}/spaces/${config.spaceId}/environments/${environment}`;
|
|
418
|
-
this.accessToken = config.accessToken;
|
|
419
|
-
this.isPreview = preview;
|
|
420
|
-
this.rateLimiter = getRateLimiter(preview);
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Fetches entries from Contentful with proactive rate limiting
|
|
424
|
-
* and automatic retry on rate limit errors.
|
|
425
|
-
*
|
|
426
|
-
* Rate limits are applied proactively:
|
|
427
|
-
* - Preview API: 14 requests/second
|
|
428
|
-
* - Delivery API: 55 requests/second
|
|
429
|
-
*
|
|
430
|
-
* If a 429 is still received (e.g., due to other processes), retries with backoff.
|
|
431
|
-
*/
|
|
432
|
-
async getEntries(query, options) {
|
|
433
|
-
const queryString = buildQueryString(query);
|
|
434
|
-
const url = `${this.baseUrl}/entries?${queryString}`;
|
|
435
|
-
const fetchOptions = {
|
|
436
|
-
headers: {
|
|
437
|
-
Authorization: `Bearer ${this.accessToken}`,
|
|
438
|
-
"Content-Type": "application/json"
|
|
439
|
-
}
|
|
440
|
-
};
|
|
441
|
-
if (options?.next) {
|
|
442
|
-
fetchOptions.next = options.next;
|
|
443
|
-
}
|
|
444
|
-
for (let attempt = 0; attempt <= RETRY_CONFIG.maxRetries; attempt++) {
|
|
445
|
-
try {
|
|
446
|
-
await this.rateLimiter.acquire();
|
|
447
|
-
const response = await fetch(url, fetchOptions);
|
|
448
|
-
if (response.ok) {
|
|
449
|
-
return response.json();
|
|
450
|
-
}
|
|
451
|
-
if (response.status === 429 && attempt < RETRY_CONFIG.maxRetries) {
|
|
452
|
-
const retryAfter = parseRetryAfter(response);
|
|
453
|
-
const delay3 = calculateBackoffDelay(attempt, RETRY_CONFIG, retryAfter);
|
|
454
|
-
console.warn(
|
|
455
|
-
`[Contentful] Rate limited (429), retrying in ${delay3}ms (attempt ${attempt + 1}/${RETRY_CONFIG.maxRetries}). Queue: ${this.rateLimiter.getQueueLength()} waiting. API: ${this.isPreview ? "preview" : "delivery"}`
|
|
456
|
-
);
|
|
457
|
-
const pauseSeconds = Math.max(retryAfter ?? 2, 2);
|
|
458
|
-
this.rateLimiter.pause(pauseSeconds);
|
|
459
|
-
await sleep2(delay3);
|
|
460
|
-
continue;
|
|
461
|
-
}
|
|
462
|
-
throw await parseErrorResponse(response);
|
|
463
|
-
} catch (error) {
|
|
464
|
-
if (error instanceof ContentfulError) {
|
|
465
|
-
throw error;
|
|
466
|
-
}
|
|
467
|
-
console.error("[Contentful] Unexpected error in request", error);
|
|
468
|
-
throw new ContentfulError("Unexpected error in request", 500);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
throw new ContentfulError("Max retries exceeded", 500);
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Fetches a single asset from Contentful by ID with proactive rate limiting
|
|
475
|
-
* and automatic retry on rate limit errors.
|
|
476
|
-
*
|
|
477
|
-
* @param assetId - The Contentful asset ID
|
|
478
|
-
* @param options - Optional fetch options (locale, preview, caching, retry)
|
|
479
|
-
* @returns The asset response or null if not found
|
|
480
|
-
*/
|
|
481
|
-
async getAsset(assetId, options) {
|
|
482
|
-
const url = `${this.baseUrl}/assets/${assetId}`;
|
|
483
|
-
const fetchOptions = {
|
|
484
|
-
headers: {
|
|
485
|
-
Authorization: `Bearer ${this.accessToken}`,
|
|
486
|
-
"Content-Type": "application/json"
|
|
487
|
-
}
|
|
488
|
-
};
|
|
489
|
-
if (options?.next) {
|
|
490
|
-
fetchOptions.next = options.next;
|
|
491
|
-
}
|
|
492
|
-
for (let attempt = 0; attempt <= RETRY_CONFIG.maxRetries; attempt++) {
|
|
493
|
-
try {
|
|
494
|
-
await this.rateLimiter.acquire();
|
|
495
|
-
const response = await fetch(url, fetchOptions);
|
|
496
|
-
if (response.ok) {
|
|
497
|
-
return response.json();
|
|
498
|
-
}
|
|
499
|
-
if (response.status === 404) {
|
|
500
|
-
return null;
|
|
501
|
-
}
|
|
502
|
-
if (response.status === 429 && attempt < RETRY_CONFIG.maxRetries) {
|
|
503
|
-
const retryAfter = parseRetryAfter(response);
|
|
504
|
-
const delay3 = calculateBackoffDelay(attempt, RETRY_CONFIG, retryAfter);
|
|
505
|
-
console.warn(
|
|
506
|
-
`[Contentful] Rate limited (429), retrying in ${delay3}ms (attempt ${attempt + 1}/${RETRY_CONFIG.maxRetries}). Queue: ${this.rateLimiter.getQueueLength()} waiting. API: ${this.isPreview ? "preview" : "delivery"}`
|
|
507
|
-
);
|
|
508
|
-
const pauseSeconds = Math.max(retryAfter ?? 2, 2);
|
|
509
|
-
this.rateLimiter.pause(pauseSeconds);
|
|
510
|
-
await sleep2(delay3);
|
|
511
|
-
continue;
|
|
512
|
-
}
|
|
513
|
-
throw await parseErrorResponse(response);
|
|
514
|
-
} catch (error) {
|
|
515
|
-
if (error instanceof ContentfulError) {
|
|
516
|
-
throw error;
|
|
517
|
-
}
|
|
518
|
-
console.error("[Contentful] Unexpected error in asset request", error);
|
|
519
|
-
throw new ContentfulError("Unexpected error in asset request", 500);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
throw new ContentfulError("Max retries exceeded", 500);
|
|
523
|
-
}
|
|
524
|
-
};
|
|
525
|
-
function createContentfulClient(config) {
|
|
526
|
-
return new ContentfulFetchClient(config, false);
|
|
527
|
-
}
|
|
528
|
-
function createContentfulPreviewClient(config) {
|
|
529
|
-
return new ContentfulFetchClient(config, true);
|
|
530
|
-
}
|
|
531
|
-
function getContentfulClient(config, preview) {
|
|
532
|
-
return preview ? createContentfulPreviewClient(config) : createContentfulClient(config);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// src/converters/helpers.ts
|
|
536
|
-
function resolveBuildYear(value) {
|
|
537
|
-
const buildYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
538
|
-
return value.replaceAll("#buildYear#", buildYear.toString());
|
|
539
|
-
}
|
|
540
|
-
function makeContentfulTitle(title, id, prefix = "Title for ") {
|
|
541
|
-
return title ?? `${prefix}${id}`;
|
|
542
|
-
}
|
|
543
|
-
function stringOrUndefined(value) {
|
|
544
|
-
if (value && value.length > 0) {
|
|
545
|
-
return value;
|
|
546
|
-
}
|
|
547
|
-
return void 0;
|
|
548
|
-
}
|
|
549
|
-
function makeContentfulDescription(description, id) {
|
|
550
|
-
return description ?? `Description for ${id}`;
|
|
551
|
-
}
|
|
552
|
-
function lookupAsset(context, asset) {
|
|
553
|
-
if (!asset) return void 0;
|
|
554
|
-
return context.assets.get(asset.sys.id);
|
|
555
|
-
}
|
|
556
|
-
function lookupIconAsset(context, asset) {
|
|
557
|
-
const visual = lookupAsset(context, asset);
|
|
558
|
-
if (!visual) return void 0;
|
|
559
|
-
if (visual.image?.type === "Svg image") {
|
|
560
|
-
return {
|
|
561
|
-
...visual,
|
|
562
|
-
image: {
|
|
563
|
-
...visual.image,
|
|
564
|
-
isIcon: true
|
|
565
|
-
}
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
|
-
return visual;
|
|
569
|
-
}
|
|
570
|
-
function lookupDownloadAsset(context, asset) {
|
|
571
|
-
if (!asset) return void 0;
|
|
572
|
-
const rawAsset = context.rawAssets.get(asset.sys.id);
|
|
573
|
-
if (!rawAsset?.fields?.file) return void 0;
|
|
574
|
-
const file = rawAsset.fields.file;
|
|
575
|
-
if (!file.url || !file.fileName || !file.contentType) return void 0;
|
|
576
|
-
return {
|
|
577
|
-
assetId: rawAsset.sys.id,
|
|
578
|
-
filename: file.fileName,
|
|
579
|
-
contentType: file.contentType,
|
|
580
|
-
size: file.details?.size,
|
|
581
|
-
url: `https:${file.url}`
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
var DEFAULT_POSITION_FIELDS = {
|
|
585
|
-
index: 0,
|
|
586
|
-
isFirst: false,
|
|
587
|
-
isLast: false,
|
|
588
|
-
indexOfType: 0
|
|
589
|
-
};
|
|
590
|
-
function createInternalLink(id, fields, context, href, internalType, additionalProps) {
|
|
591
|
-
const {
|
|
592
|
-
cmsLabel,
|
|
593
|
-
title,
|
|
594
|
-
name,
|
|
595
|
-
useName,
|
|
596
|
-
featuredImage,
|
|
597
|
-
backgroundColour,
|
|
598
|
-
textColour,
|
|
599
|
-
indexed,
|
|
600
|
-
hidden,
|
|
601
|
-
slug,
|
|
602
|
-
description
|
|
603
|
-
} = fields;
|
|
604
|
-
const text = makeContentfulTitle(title, id);
|
|
605
|
-
return {
|
|
606
|
-
type: "Internal link",
|
|
607
|
-
internalType,
|
|
608
|
-
id,
|
|
609
|
-
name: name ?? cmsLabel ?? "",
|
|
610
|
-
useName,
|
|
611
|
-
text,
|
|
612
|
-
visual: lookupAsset(context, featuredImage),
|
|
613
|
-
backgroundColour,
|
|
614
|
-
textColour,
|
|
615
|
-
indexed,
|
|
616
|
-
hidden,
|
|
617
|
-
slug,
|
|
618
|
-
href,
|
|
619
|
-
title,
|
|
620
|
-
description,
|
|
621
|
-
...additionalProps
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
|
-
function addPositionMetadata(items) {
|
|
625
|
-
if (items.length === 0) return items;
|
|
626
|
-
const typeCount = /* @__PURE__ */ new Map();
|
|
627
|
-
return items.map((item, index) => {
|
|
628
|
-
const currentTypeIndex = typeCount.get(item.type) ?? 0;
|
|
629
|
-
typeCount.set(item.type, currentTypeIndex + 1);
|
|
630
|
-
return {
|
|
631
|
-
...item,
|
|
632
|
-
index,
|
|
633
|
-
isFirst: index === 0,
|
|
634
|
-
isLast: index === items.length - 1,
|
|
635
|
-
indexOfType: currentTypeIndex
|
|
636
|
-
};
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
function calculateContentCount(...contents) {
|
|
640
|
-
return contents.reduce((acc, content) => acc + content.length, 0);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
// src/converters/asset.ts
|
|
644
|
-
function convertAssetToVisual(context, asset, options) {
|
|
645
|
-
if (!asset) return void 0;
|
|
646
|
-
const { fields, sys } = asset;
|
|
647
|
-
if (!fields) return void 0;
|
|
648
|
-
const { id } = sys;
|
|
649
|
-
const { file } = fields;
|
|
650
|
-
if (!file) return void 0;
|
|
651
|
-
const { contentType } = file;
|
|
652
|
-
if (contentType?.startsWith("image/")) {
|
|
653
|
-
const image = convertAssetToImage(file, fields, sys, options);
|
|
654
|
-
if (!image) return void 0;
|
|
655
|
-
return {
|
|
656
|
-
id,
|
|
657
|
-
type: "Visual",
|
|
658
|
-
image
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
if (contentType?.startsWith("video/")) {
|
|
662
|
-
const video = convertAssetToVideo(
|
|
663
|
-
file,
|
|
664
|
-
fields,
|
|
665
|
-
sys,
|
|
666
|
-
context,
|
|
667
|
-
options
|
|
668
|
-
);
|
|
669
|
-
if (!video) return void 0;
|
|
670
|
-
return {
|
|
671
|
-
id,
|
|
672
|
-
type: "Visual",
|
|
673
|
-
video
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
if (contentType === "application/json") {
|
|
677
|
-
const animation = convertAssetToAnimation(
|
|
678
|
-
file,
|
|
679
|
-
fields,
|
|
680
|
-
sys,
|
|
681
|
-
options
|
|
682
|
-
);
|
|
683
|
-
if (!animation) return void 0;
|
|
684
|
-
return {
|
|
685
|
-
id,
|
|
686
|
-
type: "Visual",
|
|
687
|
-
animation
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
return void 0;
|
|
691
|
-
}
|
|
692
|
-
function convertAssetToAnimation(file, fields, sys, options) {
|
|
693
|
-
const { id } = sys;
|
|
694
|
-
const { title, description } = fields;
|
|
695
|
-
const { url } = file;
|
|
696
|
-
if (!url) return void 0;
|
|
697
|
-
return {
|
|
698
|
-
...options,
|
|
699
|
-
id,
|
|
700
|
-
type: "Animation",
|
|
701
|
-
animationSrc: `https:${url}`,
|
|
702
|
-
name: makeContentfulTitle(title, id),
|
|
703
|
-
description: stringOrUndefined(description)
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
function convertAssetToImage(file, fields, sys, options) {
|
|
707
|
-
const { contentType } = file;
|
|
708
|
-
if (contentType === "image/svg+xml") {
|
|
709
|
-
return convertAssetToSvgImage(file, fields, sys, options);
|
|
710
|
-
} else {
|
|
711
|
-
return convertAssetToPicture(file, fields, sys, options);
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
function convertAssetToPicture(file, fields, sys, options) {
|
|
715
|
-
const { id } = sys;
|
|
716
|
-
const { title, description } = fields;
|
|
717
|
-
const { contentType, details, url } = file;
|
|
718
|
-
if (!details) return void 0;
|
|
719
|
-
const { size, image } = details;
|
|
720
|
-
const { width, height } = image || {};
|
|
721
|
-
return {
|
|
722
|
-
...options,
|
|
723
|
-
id,
|
|
724
|
-
type: "Picture",
|
|
725
|
-
src: `https:${url}`,
|
|
726
|
-
mimeType: contentType,
|
|
727
|
-
size,
|
|
728
|
-
width: width || 0,
|
|
729
|
-
height: height || 0,
|
|
730
|
-
name: makeContentfulTitle(title, id),
|
|
731
|
-
description: makeContentfulDescription(description, id)
|
|
732
|
-
};
|
|
733
|
-
}
|
|
734
|
-
function convertAssetToSvgImage(file, fields, sys, options) {
|
|
735
|
-
const { id } = sys;
|
|
736
|
-
const { title, description } = fields;
|
|
737
|
-
const { contentType, details, url } = file;
|
|
738
|
-
if (!details) return void 0;
|
|
739
|
-
const { size, image } = details;
|
|
740
|
-
const { width, height } = image || {};
|
|
741
|
-
return {
|
|
742
|
-
...options,
|
|
743
|
-
id,
|
|
744
|
-
type: "Svg image",
|
|
745
|
-
svgSrc: `https:${url}`,
|
|
746
|
-
mimeType: contentType,
|
|
747
|
-
size,
|
|
748
|
-
width: width || 0,
|
|
749
|
-
height: height || 0,
|
|
750
|
-
name: makeContentfulTitle(title, id),
|
|
751
|
-
description: stringOrUndefined(description)
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
function convertAssetToVideoDetails(file, sys) {
|
|
755
|
-
const { id } = sys;
|
|
756
|
-
const { details, url, contentType, fileName } = file;
|
|
757
|
-
if (!details) return void 0;
|
|
758
|
-
const { size } = details;
|
|
759
|
-
return {
|
|
760
|
-
id,
|
|
761
|
-
videoUrl: `https:${url}`,
|
|
762
|
-
size,
|
|
763
|
-
mimeType: contentType,
|
|
764
|
-
fileName
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
function convertAssetToVideo(file, fields, sys, context, options) {
|
|
768
|
-
const { id } = sys;
|
|
769
|
-
const { title, description } = fields;
|
|
770
|
-
const { details } = file;
|
|
771
|
-
if (!details) {
|
|
772
|
-
return void 0;
|
|
773
|
-
}
|
|
774
|
-
const { image } = details;
|
|
775
|
-
const { width, height } = image || {};
|
|
776
|
-
const videoDetails = convertAssetToVideoDetails(file, sys);
|
|
777
|
-
if (!videoDetails) return void 0;
|
|
778
|
-
return {
|
|
779
|
-
...options,
|
|
780
|
-
id,
|
|
781
|
-
type: "Local video",
|
|
782
|
-
preview: videoDetails,
|
|
783
|
-
videoPrefix: context.videoPrefix,
|
|
784
|
-
width: width || 0,
|
|
785
|
-
height: height || 0,
|
|
786
|
-
name: makeContentfulTitle(title, id),
|
|
787
|
-
description: makeContentfulDescription(description, id)
|
|
788
|
-
};
|
|
789
|
-
}
|
|
790
|
-
function createResponsiveVisual(visual, mobileVisual, customSize) {
|
|
791
|
-
if (!visual) return void 0;
|
|
792
|
-
return {
|
|
793
|
-
visual,
|
|
794
|
-
mobileVisual,
|
|
795
|
-
visualCustomSize: customSize
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
|
-
function convertMediaEntryToVisual(context, entry) {
|
|
799
|
-
const { fields, sys } = entry;
|
|
800
|
-
if (!fields) return void 0;
|
|
801
|
-
const baseVisual = lookupAsset(context, fields.asset);
|
|
802
|
-
if (!baseVisual) return void 0;
|
|
803
|
-
const { name, mask: maskDropdown, maskImage, ...otherFields } = fields;
|
|
804
|
-
let mask;
|
|
805
|
-
if (maskImage) {
|
|
806
|
-
const maskVisual = lookupAsset(context, maskImage);
|
|
807
|
-
if (maskVisual?.image?.type === "Svg image") {
|
|
808
|
-
mask = maskVisual.image.svgSrc;
|
|
809
|
-
} else if (maskVisual?.image?.type === "Picture") {
|
|
810
|
-
mask = maskVisual.image.src;
|
|
811
|
-
}
|
|
812
|
-
} else if (maskDropdown && context.maskResolver) {
|
|
813
|
-
mask = context.maskResolver(maskDropdown);
|
|
814
|
-
}
|
|
815
|
-
const metadata = {
|
|
816
|
-
name: makeContentfulTitle(name, sys.id),
|
|
817
|
-
...otherFields
|
|
818
|
-
};
|
|
819
|
-
const visual = {
|
|
820
|
-
...baseVisual,
|
|
821
|
-
id: sys.id,
|
|
822
|
-
mask
|
|
823
|
-
};
|
|
824
|
-
if (baseVisual.image) {
|
|
825
|
-
visual.image = {
|
|
826
|
-
...baseVisual.image,
|
|
827
|
-
...metadata
|
|
828
|
-
};
|
|
829
|
-
} else if (baseVisual.video) {
|
|
830
|
-
visual.video = {
|
|
831
|
-
...baseVisual.video,
|
|
832
|
-
...metadata
|
|
833
|
-
};
|
|
834
|
-
} else if (baseVisual.animation) {
|
|
835
|
-
visual.animation = {
|
|
836
|
-
...baseVisual.animation,
|
|
837
|
-
...metadata
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
|
-
return visual;
|
|
841
|
-
}
|
|
842
|
-
function convertExternalVideoEntryToVisual(context, entry) {
|
|
843
|
-
const { fields, sys } = entry;
|
|
844
|
-
if (!fields || !fields.url) return void 0;
|
|
845
|
-
const previewVisual = lookupAsset(context, fields.preview);
|
|
846
|
-
const posterVisual = lookupAsset(context, fields.posterImage);
|
|
847
|
-
const preview = previewVisual?.video?.type === "Local video" ? previewVisual.video.preview : void 0;
|
|
848
|
-
const poster = posterVisual?.image?.type === "Picture" ? posterVisual.image.src : void 0;
|
|
849
|
-
const mapHorizontal = (value) => {
|
|
850
|
-
if (!value) return null;
|
|
851
|
-
return value === "Center" ? "Middle" : value;
|
|
852
|
-
};
|
|
853
|
-
const video = {
|
|
854
|
-
id: sys.id,
|
|
855
|
-
type: "External video",
|
|
856
|
-
name: makeContentfulTitle(fields.name, sys.id),
|
|
857
|
-
nameAsCaption: fields.nameAsCaption ?? null,
|
|
858
|
-
external: fields.url,
|
|
859
|
-
preview,
|
|
860
|
-
poster,
|
|
861
|
-
autoPlay: fields.autoPlay ?? null,
|
|
862
|
-
loop: fields.loop ?? null,
|
|
863
|
-
hideControls: fields.hideControls ?? null,
|
|
864
|
-
dontCrop: fields.dontCrop ?? null,
|
|
865
|
-
verticalCropPosition: fields.verticalCropPosition ?? null,
|
|
866
|
-
horizontalCropPosition: mapHorizontal(fields.horizontalCropPosition),
|
|
867
|
-
horizontalPosition: fields.horizontalPosition ?? null,
|
|
868
|
-
widthPercent: fields.width ?? null
|
|
869
|
-
};
|
|
870
|
-
return {
|
|
871
|
-
id: sys.id,
|
|
872
|
-
type: "Visual",
|
|
873
|
-
video
|
|
874
|
-
};
|
|
875
|
-
}
|
|
876
|
-
function lookupMediaEntry(context, link) {
|
|
877
|
-
if (!link) return void 0;
|
|
878
|
-
const id = link.sys.id;
|
|
879
|
-
const possibleEntry = context.includes.get(id);
|
|
880
|
-
if (!possibleEntry) {
|
|
881
|
-
return void 0;
|
|
882
|
-
}
|
|
883
|
-
const { type, entry } = possibleEntry;
|
|
884
|
-
if (type === "media") {
|
|
885
|
-
return convertMediaEntryToVisual(
|
|
886
|
-
context,
|
|
887
|
-
entry
|
|
888
|
-
);
|
|
889
|
-
}
|
|
890
|
-
if (type === "externalVideo") {
|
|
891
|
-
return convertExternalVideoEntryToVisual(
|
|
892
|
-
context,
|
|
893
|
-
entry
|
|
894
|
-
);
|
|
895
|
-
}
|
|
896
|
-
return void 0;
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
// src/converters/svgProcessor.ts
|
|
900
|
-
async function fetchSvgContent(url) {
|
|
901
|
-
const response = await fetch(url);
|
|
902
|
-
if (!response.ok) {
|
|
903
|
-
throw new Error(`Failed to fetch SVG from ${url}: ${response.statusText}`);
|
|
904
|
-
}
|
|
905
|
-
return await response.text();
|
|
906
|
-
}
|
|
907
|
-
function processSvgForSprite(svgContent, iconId) {
|
|
908
|
-
const viewBoxMatch = svgContent.match(/viewBox\s*=\s*["']([^"']+)["']/i);
|
|
909
|
-
const viewBox = viewBoxMatch ? viewBoxMatch[1] : "0 0 24 24";
|
|
910
|
-
const innerContentMatch = svgContent.match(/<svg[^>]*>([\s\S]*)<\/svg>/i);
|
|
911
|
-
if (!innerContentMatch) {
|
|
912
|
-
throw new Error(`Invalid SVG content: could not parse SVG structure`);
|
|
913
|
-
}
|
|
914
|
-
let innerContent = innerContentMatch[1] ?? "";
|
|
915
|
-
innerContent = innerContent.replace(/\s(width|height)=["'][^"']*["']/gi, "");
|
|
916
|
-
const symbolId = `icon-${iconId}`;
|
|
917
|
-
return `<symbol id="${symbolId}" viewBox="${viewBox}">${innerContent}</symbol>`;
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
// src/converters/iconCollector.ts
|
|
921
|
-
function extractIconFromVisual(visual) {
|
|
922
|
-
if (!visual?.image) {
|
|
923
|
-
return void 0;
|
|
924
|
-
}
|
|
925
|
-
if (isSvgImage(visual.image) && visual.image.isIcon) {
|
|
926
|
-
return visual.image;
|
|
927
|
-
}
|
|
928
|
-
return void 0;
|
|
929
|
-
}
|
|
930
|
-
function extractIconsFromResponsiveVisual(visual) {
|
|
931
|
-
const icons = [];
|
|
932
|
-
if (visual?.visual) {
|
|
933
|
-
const svg = extractIconFromVisual(visual.visual);
|
|
934
|
-
if (svg) {
|
|
935
|
-
icons.push(svg);
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
if (visual?.mobileVisual) {
|
|
939
|
-
const svg = extractIconFromVisual(visual.mobileVisual);
|
|
940
|
-
if (svg) {
|
|
941
|
-
icons.push(svg);
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
return icons;
|
|
945
|
-
}
|
|
946
|
-
function collectIconsFromComponent(component) {
|
|
947
|
-
if (!component) {
|
|
948
|
-
return [];
|
|
949
|
-
}
|
|
950
|
-
const icon = extractIconFromVisual(component.icon);
|
|
951
|
-
return icon ? [icon] : [];
|
|
952
|
-
}
|
|
953
|
-
function collectIconsFromCollection(collection) {
|
|
954
|
-
if (!collection) {
|
|
955
|
-
return [];
|
|
956
|
-
}
|
|
957
|
-
const icon = extractIconFromVisual(collection.icon);
|
|
958
|
-
return icon ? [icon] : [];
|
|
959
|
-
}
|
|
960
|
-
function collectIconsFromNavigation(navigation) {
|
|
961
|
-
if (!navigation) {
|
|
962
|
-
return [];
|
|
963
|
-
}
|
|
964
|
-
const icons = [];
|
|
965
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
966
|
-
function addIcon(icon) {
|
|
967
|
-
if (!seenIds.has(icon.id)) {
|
|
968
|
-
seenIds.add(icon.id);
|
|
969
|
-
icons.push(icon);
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
function processNavItem(item) {
|
|
973
|
-
if (item.link?.icon) {
|
|
974
|
-
const linkIcons = extractIconsFromResponsiveVisual(item.link.icon);
|
|
975
|
-
for (const icon of linkIcons) {
|
|
976
|
-
addIcon(icon);
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
if (item.entries) {
|
|
980
|
-
for (const nestedItem of item.entries) {
|
|
981
|
-
processNavItem(nestedItem);
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
for (const item of navigation.entries) {
|
|
986
|
-
processNavItem(item);
|
|
987
|
-
}
|
|
988
|
-
return icons;
|
|
989
|
-
}
|
|
990
|
-
function collectIconsFromRichText(richText, processContent) {
|
|
991
|
-
if (!richText?.json) {
|
|
992
|
-
return;
|
|
993
|
-
}
|
|
994
|
-
const traverseNode = (node) => {
|
|
995
|
-
if (node.data?.target && typeof node.data.target === "object" && "type" in node.data.target && "id" in node.data.target) {
|
|
996
|
-
processContent(node.data.target);
|
|
997
|
-
}
|
|
998
|
-
if ("content" in node && Array.isArray(node.content)) {
|
|
999
|
-
for (const child of node.content) {
|
|
1000
|
-
if (typeof child === "object" && child !== null && "nodeType" in child) {
|
|
1001
|
-
traverseNode(child);
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
};
|
|
1006
|
-
traverseNode(richText.json);
|
|
1007
|
-
}
|
|
1008
|
-
function collectIconsFromContent(contents) {
|
|
1009
|
-
if (!contents) {
|
|
1010
|
-
return [];
|
|
1011
|
-
}
|
|
1012
|
-
const icons = [];
|
|
1013
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
1014
|
-
const processedIds = /* @__PURE__ */ new Set();
|
|
1015
|
-
function addIcon(icon) {
|
|
1016
|
-
if (!seenIds.has(icon.id)) {
|
|
1017
|
-
seenIds.add(icon.id);
|
|
1018
|
-
icons.push(icon);
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
function processContentItem(content) {
|
|
1022
|
-
if (processedIds.has(content.id)) {
|
|
1023
|
-
return;
|
|
1024
|
-
}
|
|
1025
|
-
processedIds.add(content.id);
|
|
1026
|
-
if (content.type === "Component") {
|
|
1027
|
-
const componentIcons = collectIconsFromComponent(content);
|
|
1028
|
-
for (const icon of componentIcons) {
|
|
1029
|
-
addIcon(icon);
|
|
1030
|
-
}
|
|
1031
|
-
const component = content;
|
|
1032
|
-
if (component.contents) {
|
|
1033
|
-
for (const nested of component.contents) {
|
|
1034
|
-
processContentItem(nested);
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
collectIconsFromRichText(component.body, processContentItem);
|
|
1038
|
-
collectIconsFromRichText(component.additionalCopy, processContentItem);
|
|
1039
|
-
}
|
|
1040
|
-
if (content.type === "Collection") {
|
|
1041
|
-
const collectionIcons = collectIconsFromCollection(content);
|
|
1042
|
-
for (const icon of collectionIcons) {
|
|
1043
|
-
addIcon(icon);
|
|
1044
|
-
}
|
|
1045
|
-
const collection = content;
|
|
1046
|
-
if (collection.contents) {
|
|
1047
|
-
for (const nested of collection.contents) {
|
|
1048
|
-
if (typeof nested === "object" && nested !== null && "type" in nested && "id" in nested) {
|
|
1049
|
-
processContentItem(nested);
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
collectIconsFromRichText(collection.body, processContentItem);
|
|
1054
|
-
collectIconsFromRichText(collection.additionalCopy, processContentItem);
|
|
1055
|
-
}
|
|
1056
|
-
if (content.type === "Internal link" || content.type === "External link" || content.type === "Download link" || content.type === "Blank link") {
|
|
1057
|
-
const link = content;
|
|
1058
|
-
if (link.icon) {
|
|
1059
|
-
const linkIcons = extractIconsFromResponsiveVisual(link.icon);
|
|
1060
|
-
for (const icon of linkIcons) {
|
|
1061
|
-
addIcon(icon);
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
for (const content of contents) {
|
|
1067
|
-
processContentItem(content);
|
|
1068
|
-
}
|
|
1069
|
-
return icons;
|
|
1070
|
-
}
|
|
1071
|
-
function deduplicateIcons(...iconArrays) {
|
|
1072
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
1073
|
-
const icons = [];
|
|
1074
|
-
for (const arr of iconArrays) {
|
|
1075
|
-
for (const icon of arr) {
|
|
1076
|
-
if (!seenIds.has(icon.id)) {
|
|
1077
|
-
seenIds.add(icon.id);
|
|
1078
|
-
icons.push(icon);
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
return icons.length > 0 ? icons : void 0;
|
|
1083
|
-
}
|
|
1084
|
-
async function processIconsForSprite(icons) {
|
|
1085
|
-
if (icons.length === 0) {
|
|
1086
|
-
return [];
|
|
1087
|
-
}
|
|
1088
|
-
const processedIcons = await Promise.all(
|
|
1089
|
-
icons.map(async (icon) => {
|
|
1090
|
-
try {
|
|
1091
|
-
const svgContent = await fetchSvgContent(icon.svgSrc);
|
|
1092
|
-
const symbolContent = processSvgForSprite(svgContent, icon.id);
|
|
1093
|
-
return {
|
|
1094
|
-
...icon,
|
|
1095
|
-
symbolContent
|
|
1096
|
-
};
|
|
1097
|
-
} catch (error) {
|
|
1098
|
-
console.error(`Failed to process icon ${icon.id}:`, error);
|
|
1099
|
-
return icon;
|
|
1100
|
-
}
|
|
1101
|
-
})
|
|
1102
|
-
);
|
|
1103
|
-
return processedIcons;
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
// src/api/helpers.ts
|
|
1107
|
-
init_utils();
|
|
1108
|
-
|
|
1109
|
-
// src/utils/arrayUtils.ts
|
|
1110
|
-
function notEmpty(value) {
|
|
1111
|
-
if (value === null || value === void 0) return false;
|
|
1112
|
-
return true;
|
|
1113
|
-
}
|
|
1114
|
-
function arrayOrUndefined(array) {
|
|
1115
|
-
if (array && array.length > 0) {
|
|
1116
|
-
return array;
|
|
1117
|
-
}
|
|
1118
|
-
return void 0;
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
// src/utils/dateUtils.ts
|
|
1122
|
-
function isValidDate(date) {
|
|
1123
|
-
if (date instanceof Date) {
|
|
1124
|
-
return !Number.isNaN(date.getTime());
|
|
1125
|
-
}
|
|
1126
|
-
if (typeof date === "string" || typeof date === "number") {
|
|
1127
|
-
const parsed = new Date(date);
|
|
1128
|
-
return !Number.isNaN(parsed.getTime()) && parsed.getTime() > 0;
|
|
1129
|
-
}
|
|
1130
|
-
return false;
|
|
1131
|
-
}
|
|
1132
|
-
function safeDate(date) {
|
|
1133
|
-
if (!isValidDate(date)) {
|
|
1134
|
-
return null;
|
|
1135
|
-
}
|
|
1136
|
-
if (date instanceof Date) {
|
|
1137
|
-
return date;
|
|
1138
|
-
}
|
|
1139
|
-
return new Date(date);
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
// src/api/helpers.ts
|
|
1143
|
-
function buildFetchOptions(options, defaultDraftOnly = false) {
|
|
1144
|
-
const preview = options?.preview === void 0 ? defaultDraftOnly : options.preview;
|
|
1145
|
-
return {
|
|
1146
|
-
preview,
|
|
1147
|
-
cache: "force-cache",
|
|
1148
|
-
...options
|
|
1149
|
-
};
|
|
1150
|
-
}
|
|
1151
|
-
function getContentfulConfig(preview) {
|
|
1152
|
-
const spaceId = process.env.CONTENTFUL_SPACE_ID;
|
|
1153
|
-
const environment = process.env.CONTENTFUL_ENVIRONMENT || process.env.CONTENTFUL_ENVIRONMENT_NAME;
|
|
1154
|
-
const accessToken = preview ? process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN : process.env.CONTENTFUL_ACCESS_TOKEN;
|
|
1155
|
-
if (!spaceId) {
|
|
1156
|
-
throw new Error("CONTENTFUL_SPACE_ID environment variable is required");
|
|
1157
|
-
}
|
|
1158
|
-
if (!accessToken) {
|
|
1159
|
-
if (preview) {
|
|
1160
|
-
throw new Error("CONTENTFUL_PREVIEW_ACCESS_TOKEN environment variable is required");
|
|
1161
|
-
}
|
|
1162
|
-
throw new Error("CONTENTFUL_ACCESS_TOKEN environment variable is required");
|
|
1163
|
-
}
|
|
1164
|
-
if (!environment) {
|
|
1165
|
-
throw new Error(
|
|
1166
|
-
"CONTENTFUL_ENVIRONMENT or CONTENTFUL_ENVIRONMENT_NAME environment variable is required"
|
|
1167
|
-
);
|
|
1168
|
-
}
|
|
1169
|
-
return {
|
|
1170
|
-
spaceId,
|
|
1171
|
-
accessToken,
|
|
1172
|
-
environment
|
|
1173
|
-
};
|
|
1174
|
-
}
|
|
1175
|
-
var PAGE_LINK_FIELDS = "sys,fields.cmsLabel,fields.title,fields.slug,fields.featuredImage,fields.backgroundColour,fields.textColour,fields.indexed,fields.hidden,fields.tags";
|
|
1176
|
-
var ARTICLE_LINK_FIELDS = "sys,fields.cmsLabel,fields.title,fields.slug,fields.featuredImage,fields.backgroundColour,fields.textColour,fields.indexed,fields.hidden,fields.tags,fields.articleType,fields.date,fields.author,fields.externalLink,fields.download";
|
|
1177
|
-
var ARTICLE_TYPE_LINK_FIELDS = "sys,fields.name,fields.slug,fields.featuredImage,fields.backgroundColour,fields.textColour,fields.indexed,fields.hidden";
|
|
1178
|
-
var TAG_LINK_FIELDS = "sys,fields.cmsLabel,fields.name,fields.slug,fields.featuredImage,fields.backgroundColour,fields.textColour,fields.indexed,fields.hidden";
|
|
1179
|
-
var PERSON_LINK_FIELDS = "sys,fields.name,fields.slug,fields.media,fields.backgroundColour,fields.textColour,fields.indexed,fields.hidden";
|
|
1180
|
-
function convertAllAssets(response, context) {
|
|
1181
|
-
const visuals = /* @__PURE__ */ new Map();
|
|
1182
|
-
const assets = response.includes?.Asset;
|
|
1183
|
-
if (assets && assets.length > 0) {
|
|
1184
|
-
for (const asset of assets) {
|
|
1185
|
-
const visual = convertAssetToVisual(context, asset);
|
|
1186
|
-
if (visual) {
|
|
1187
|
-
visuals.set(visual.id, visual);
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
return visuals;
|
|
1192
|
-
}
|
|
1193
|
-
function convertAllRawAssets(response) {
|
|
1194
|
-
const rawAssets = /* @__PURE__ */ new Map();
|
|
1195
|
-
const assets = response.includes?.Asset;
|
|
1196
|
-
if (assets && assets.length > 0) {
|
|
1197
|
-
for (const asset of assets) {
|
|
1198
|
-
rawAssets.set(asset.sys.id, asset);
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
return rawAssets;
|
|
1202
|
-
}
|
|
1203
|
-
function convertAllIncludes(response) {
|
|
1204
|
-
const includes = /* @__PURE__ */ new Map();
|
|
1205
|
-
const entries = [...response.items, ...response.includes?.Entry || []];
|
|
1206
|
-
if (entries && entries.length > 0) {
|
|
1207
|
-
for (const entry of entries) {
|
|
1208
|
-
if (entry?.sys && entry.fields) {
|
|
1209
|
-
includes.set(entry.sys.id, {
|
|
1210
|
-
id: entry.sys.id,
|
|
1211
|
-
type: entry.sys.contentType.sys.id,
|
|
1212
|
-
entry
|
|
1213
|
-
});
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
return includes;
|
|
1218
|
-
}
|
|
1219
|
-
async function fetchSingleEntity(context, config, fetchConfig, options) {
|
|
1220
|
-
const client = getContentfulClient(config, options.preview);
|
|
1221
|
-
const cacheTags = getCacheTags(
|
|
1222
|
-
fetchConfig.cacheTagType,
|
|
1223
|
-
fetchConfig.cacheTagIdentifier,
|
|
1224
|
-
options?.preview
|
|
1225
|
-
);
|
|
1226
|
-
const requestOptions = {
|
|
1227
|
-
...options,
|
|
1228
|
-
next: {
|
|
1229
|
-
...options?.next,
|
|
1230
|
-
tags: cacheTags
|
|
1231
|
-
}
|
|
1232
|
-
};
|
|
1233
|
-
const fetchFn = async () => {
|
|
1234
|
-
const response = await client.getEntries(
|
|
1235
|
-
{
|
|
1236
|
-
content_type: fetchConfig.contentType,
|
|
1237
|
-
...fetchConfig.query,
|
|
1238
|
-
include: 10,
|
|
1239
|
-
locale: options?.locale,
|
|
1240
|
-
limit: 1
|
|
1241
|
-
},
|
|
1242
|
-
requestOptions
|
|
1243
|
-
);
|
|
1244
|
-
const entry = response.items[0];
|
|
1245
|
-
if (!entry || !entry.fields) {
|
|
1246
|
-
return { data: null, errors: [] };
|
|
1247
|
-
}
|
|
1248
|
-
try {
|
|
1249
|
-
const assets = convertAllAssets(response, context);
|
|
1250
|
-
const rawAssets = convertAllRawAssets(response);
|
|
1251
|
-
const includes = convertAllIncludes(response);
|
|
1252
|
-
const fullContext = {
|
|
1253
|
-
...context,
|
|
1254
|
-
includes,
|
|
1255
|
-
assets,
|
|
1256
|
-
rawAssets,
|
|
1257
|
-
errors: []
|
|
1258
|
-
};
|
|
1259
|
-
const converted = fetchConfig.resolver(fullContext, entry);
|
|
1260
|
-
if (converted && typeof converted === "object" && "icons" in converted) {
|
|
1261
|
-
const icons = converted.icons;
|
|
1262
|
-
if (icons && Array.isArray(icons) && icons.length > 0) {
|
|
1263
|
-
const processedIcons = await processIconsForSprite(icons);
|
|
1264
|
-
converted.icons = processedIcons.length > 0 ? processedIcons : void 0;
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
if (fullContext.errors.length > 0 && typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
|
|
1268
|
-
console.error(`CMS conversion errors for ${fetchConfig.contentType}:`, {
|
|
1269
|
-
entryId: entry.sys.id,
|
|
1270
|
-
...fetchConfig.errorLogContext,
|
|
1271
|
-
errors: fullContext.errors
|
|
1272
|
-
});
|
|
1273
|
-
}
|
|
1274
|
-
return { data: converted, errors: fullContext.errors };
|
|
1275
|
-
} catch (error) {
|
|
1276
|
-
const entryId = entry.sys.id;
|
|
1277
|
-
const entryType = entry.sys.contentType?.sys?.id;
|
|
1278
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown conversion error";
|
|
1279
|
-
const cmsError = {
|
|
1280
|
-
entryId,
|
|
1281
|
-
entryType,
|
|
1282
|
-
message: errorMessage,
|
|
1283
|
-
error
|
|
1284
|
-
};
|
|
1285
|
-
return { data: null, errors: [cmsError] };
|
|
1286
|
-
}
|
|
1287
|
-
};
|
|
1288
|
-
if (options?.retry) {
|
|
1289
|
-
return await withRetry(fetchFn, options.retry);
|
|
1290
|
-
}
|
|
1291
|
-
return await fetchFn();
|
|
1292
|
-
}
|
|
1293
|
-
async function fetchAllLinks(contentType, client, requestOptions, converter, context, pageSize = 100, select) {
|
|
1294
|
-
const allLinks = [];
|
|
1295
|
-
const errors = [];
|
|
1296
|
-
let skip = 0;
|
|
1297
|
-
let hasMore = true;
|
|
1298
|
-
const fetchFn = async () => {
|
|
1299
|
-
while (hasMore) {
|
|
1300
|
-
try {
|
|
1301
|
-
const response = await client.getEntries(
|
|
1302
|
-
{
|
|
1303
|
-
content_type: contentType,
|
|
1304
|
-
include: 2,
|
|
1305
|
-
// Minimal include for link-only fetching
|
|
1306
|
-
locale: requestOptions?.locale,
|
|
1307
|
-
limit: pageSize,
|
|
1308
|
-
skip,
|
|
1309
|
-
...select && { select }
|
|
1310
|
-
},
|
|
1311
|
-
requestOptions
|
|
1312
|
-
);
|
|
1313
|
-
if (response.items.length === 0) {
|
|
1314
|
-
hasMore = false;
|
|
1315
|
-
break;
|
|
1316
|
-
}
|
|
1317
|
-
const includes = convertAllIncludes(response);
|
|
1318
|
-
const assets = convertAllAssets(response, context);
|
|
1319
|
-
const rawAssets = convertAllRawAssets(response);
|
|
1320
|
-
const fullContext = {
|
|
1321
|
-
...context,
|
|
1322
|
-
includes,
|
|
1323
|
-
assets,
|
|
1324
|
-
rawAssets,
|
|
1325
|
-
errors: []
|
|
1326
|
-
};
|
|
1327
|
-
for (const entry of response.items) {
|
|
1328
|
-
if (!entry.fields) continue;
|
|
1329
|
-
try {
|
|
1330
|
-
const converted = converter(
|
|
1331
|
-
fullContext,
|
|
1332
|
-
entry
|
|
1333
|
-
);
|
|
1334
|
-
converted.lastModified = entry.sys.updatedAt ? new Date(entry.sys.updatedAt) : void 0;
|
|
1335
|
-
allLinks.push(converted);
|
|
1336
|
-
} catch (error) {
|
|
1337
|
-
const entryId = entry.sys.id;
|
|
1338
|
-
const entryType = entry.sys.contentType?.sys?.id;
|
|
1339
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown conversion error";
|
|
1340
|
-
errors.push({
|
|
1341
|
-
entryId,
|
|
1342
|
-
entryType,
|
|
1343
|
-
message: errorMessage,
|
|
1344
|
-
error
|
|
1345
|
-
});
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
skip += pageSize;
|
|
1349
|
-
if (skip >= response.total) {
|
|
1350
|
-
hasMore = false;
|
|
1351
|
-
}
|
|
1352
|
-
} catch (error) {
|
|
1353
|
-
console.error("Error fetching links", typeof error, error, JSON.stringify(error, null, 2));
|
|
1354
|
-
throw error;
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
return { data: allLinks, errors };
|
|
1358
|
-
};
|
|
1359
|
-
return await fetchFn();
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
// src/api/article.ts
|
|
1363
|
-
async function contentfulArticleRest(context, config, slug, articleTypeSlug, options) {
|
|
1364
|
-
return fetchSingleEntity(
|
|
1365
|
-
context,
|
|
1366
|
-
config,
|
|
1367
|
-
{
|
|
1368
|
-
contentType: "article",
|
|
1369
|
-
cacheTagType: "article",
|
|
1370
|
-
cacheTagIdentifier: slug,
|
|
1371
|
-
query: {
|
|
1372
|
-
"fields.slug": slug,
|
|
1373
|
-
"fields.articleType.sys.contentType.sys.id": "articleType",
|
|
1374
|
-
"fields.articleType.fields.slug": articleTypeSlug
|
|
1375
|
-
},
|
|
1376
|
-
resolver: (ctx, entry) => ctx.articleResolver(ctx, entry),
|
|
1377
|
-
errorLogContext: { slug, articleTypeSlug }
|
|
1378
|
-
},
|
|
1379
|
-
options
|
|
1380
|
-
);
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
// src/api/article-type.ts
|
|
1384
|
-
init_utils();
|
|
1385
|
-
async function contentfulArticleTypeRest(context, config, slug, options) {
|
|
1386
|
-
const client = getContentfulClient(config, options.preview);
|
|
1387
|
-
const articleTypeCacheTags = getCacheTags("articleType", slug, options?.preview);
|
|
1388
|
-
const customTypeCacheTags = options?.customType ? getCacheTags("customType", options.customType, options?.preview) : [];
|
|
1389
|
-
const requestOptions = {
|
|
1390
|
-
...options,
|
|
1391
|
-
next: {
|
|
1392
|
-
...options?.next,
|
|
1393
|
-
tags: [...articleTypeCacheTags, ...customTypeCacheTags]
|
|
1394
|
-
}
|
|
1395
|
-
};
|
|
1396
|
-
const fetchFn = async () => {
|
|
1397
|
-
const articleTypePromise = client.getEntries(
|
|
1398
|
-
{
|
|
1399
|
-
content_type: "articleType",
|
|
1400
|
-
"fields.slug": slug,
|
|
1401
|
-
include: 10,
|
|
1402
|
-
locale: options?.locale,
|
|
1403
|
-
limit: 1
|
|
1404
|
-
},
|
|
1405
|
-
requestOptions
|
|
1406
|
-
);
|
|
1407
|
-
const customTypePromise = options?.customType ? client.getEntries(
|
|
1408
|
-
{
|
|
1409
|
-
content_type: "customType",
|
|
1410
|
-
"fields.slug": options.customType,
|
|
1411
|
-
include: 10,
|
|
1412
|
-
locale: options?.locale,
|
|
1413
|
-
limit: 1
|
|
1414
|
-
},
|
|
1415
|
-
requestOptions
|
|
1416
|
-
) : Promise.resolve(null);
|
|
1417
|
-
const [articleTypeResponse, customTypeResponse] = await Promise.all([
|
|
1418
|
-
articleTypePromise,
|
|
1419
|
-
customTypePromise
|
|
1420
|
-
]);
|
|
1421
|
-
const articleTypeEntry = articleTypeResponse.items[0];
|
|
1422
|
-
if (!articleTypeEntry || !articleTypeEntry.fields) {
|
|
1423
|
-
return { data: null, errors: [] };
|
|
1424
|
-
}
|
|
1425
|
-
const customTypeEntry = customTypeResponse?.items[0];
|
|
1426
|
-
try {
|
|
1427
|
-
const assets = convertAllAssets(articleTypeResponse, context);
|
|
1428
|
-
const rawAssets = convertAllRawAssets(articleTypeResponse);
|
|
1429
|
-
const includes = convertAllIncludes(articleTypeResponse);
|
|
1430
|
-
if (customTypeResponse) {
|
|
1431
|
-
const customAssets = convertAllAssets(customTypeResponse, context);
|
|
1432
|
-
const customRawAssets = convertAllRawAssets(customTypeResponse);
|
|
1433
|
-
const customIncludes = convertAllIncludes(customTypeResponse);
|
|
1434
|
-
for (const [key, value] of customAssets) assets.set(key, value);
|
|
1435
|
-
for (const [key, value] of customRawAssets) rawAssets.set(key, value);
|
|
1436
|
-
for (const [key, value] of customIncludes) includes.set(key, value);
|
|
1437
|
-
}
|
|
1438
|
-
const fullContext = {
|
|
1439
|
-
...context,
|
|
1440
|
-
includes,
|
|
1441
|
-
assets,
|
|
1442
|
-
rawAssets,
|
|
1443
|
-
errors: []
|
|
1444
|
-
};
|
|
1445
|
-
const converted = context.articleTypeResolver(fullContext, articleTypeEntry, customTypeEntry);
|
|
1446
|
-
if (converted?.icons && converted.icons.length > 0) {
|
|
1447
|
-
const processedIcons = await processIconsForSprite(converted.icons);
|
|
1448
|
-
converted.icons = processedIcons.length > 0 ? processedIcons : void 0;
|
|
1449
|
-
}
|
|
1450
|
-
if (fullContext.errors.length > 0 && typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
|
|
1451
|
-
console.error(`CMS conversion errors for articleType:`, {
|
|
1452
|
-
entryId: articleTypeEntry.sys.id,
|
|
1453
|
-
slug,
|
|
1454
|
-
errors: fullContext.errors
|
|
1455
|
-
});
|
|
1456
|
-
}
|
|
1457
|
-
return { data: converted, errors: fullContext.errors };
|
|
1458
|
-
} catch (error) {
|
|
1459
|
-
const entryId = articleTypeEntry?.sys.id || "unknown";
|
|
1460
|
-
const entryType = articleTypeEntry?.sys.contentType?.sys?.id;
|
|
1461
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown conversion error";
|
|
1462
|
-
const cmsError = {
|
|
1463
|
-
entryId,
|
|
1464
|
-
entryType,
|
|
1465
|
-
message: errorMessage,
|
|
1466
|
-
error
|
|
1467
|
-
};
|
|
1468
|
-
return { data: null, errors: [cmsError] };
|
|
1469
|
-
}
|
|
1470
|
-
};
|
|
1471
|
-
if (options?.retry) {
|
|
1472
|
-
return await withRetry(fetchFn, options.retry);
|
|
1473
|
-
}
|
|
1474
|
-
return await fetchFn();
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
// src/api/asset.ts
|
|
1478
|
-
init_utils();
|
|
1479
|
-
var BROWSER_VIEWABLE_TYPES = [
|
|
1480
|
-
"application/pdf",
|
|
1481
|
-
"text/plain",
|
|
1482
|
-
"text/html",
|
|
1483
|
-
"text/css",
|
|
1484
|
-
"text/javascript",
|
|
1485
|
-
"application/json"
|
|
1486
|
-
];
|
|
1487
|
-
function isBrowserViewable(contentType) {
|
|
1488
|
-
if (contentType.startsWith("image/") || contentType.startsWith("video/")) {
|
|
1489
|
-
return true;
|
|
1490
|
-
}
|
|
1491
|
-
return BROWSER_VIEWABLE_TYPES.includes(contentType);
|
|
1492
|
-
}
|
|
1493
|
-
async function contentfulAssetRest(config, assetId, options) {
|
|
1494
|
-
const client = getContentfulClient(config, options.preview);
|
|
1495
|
-
const cacheTags = getCacheTags("asset", assetId, options?.preview);
|
|
1496
|
-
return client.getAsset(assetId, {
|
|
1497
|
-
...options,
|
|
1498
|
-
next: {
|
|
1499
|
-
revalidate: options?.next?.revalidate ?? 86400,
|
|
1500
|
-
// Default 24 hours
|
|
1501
|
-
tags: [...cacheTags, ...options?.next?.tags ?? []]
|
|
1502
|
-
}
|
|
1503
|
-
});
|
|
1504
|
-
}
|
|
1505
|
-
function createDownloadHandler(config) {
|
|
1506
|
-
const { getConfig, revalidate = 86400, preview = false } = config;
|
|
1507
|
-
return async function GET(_request, { params }) {
|
|
1508
|
-
const { assetId, filename } = await params;
|
|
1509
|
-
const decodedFilename = decodeURIComponent(filename);
|
|
1510
|
-
try {
|
|
1511
|
-
const contentfulConfig = getConfig(preview);
|
|
1512
|
-
const asset = await contentfulAssetRest(contentfulConfig, assetId, {
|
|
1513
|
-
preview,
|
|
1514
|
-
next: { revalidate }
|
|
1515
|
-
});
|
|
1516
|
-
if (!asset?.fields?.file) {
|
|
1517
|
-
return Response.json({ error: "Asset not found" }, { status: 404 });
|
|
1518
|
-
}
|
|
1519
|
-
const { file } = asset.fields;
|
|
1520
|
-
const assetUrl = `https:${file.url}`;
|
|
1521
|
-
const cacheTags = getCacheTags("asset", assetId, preview);
|
|
1522
|
-
const fileFetchOptions = {
|
|
1523
|
-
next: {
|
|
1524
|
-
revalidate,
|
|
1525
|
-
tags: cacheTags
|
|
1526
|
-
}
|
|
1527
|
-
};
|
|
1528
|
-
const fileResponse = await fetch(assetUrl, fileFetchOptions);
|
|
1529
|
-
if (!fileResponse.ok) {
|
|
1530
|
-
return Response.json({ error: "Failed to fetch file" }, { status: 502 });
|
|
1531
|
-
}
|
|
1532
|
-
const headers = new Headers();
|
|
1533
|
-
headers.set("Content-Type", file.contentType);
|
|
1534
|
-
const disposition = isBrowserViewable(file.contentType) ? "inline" : "attachment";
|
|
1535
|
-
headers.set("Content-Disposition", `${disposition}; filename="${decodedFilename}"`);
|
|
1536
|
-
if (file.details?.size) {
|
|
1537
|
-
headers.set("Content-Length", String(file.details.size));
|
|
1538
|
-
}
|
|
1539
|
-
headers.set("Cache-Control", `public, max-age=${revalidate}, s-maxage=${revalidate}`);
|
|
1540
|
-
return new Response(fileResponse.body, {
|
|
1541
|
-
status: 200,
|
|
1542
|
-
headers
|
|
1543
|
-
});
|
|
1544
|
-
} catch (error) {
|
|
1545
|
-
console.error("[Download] Error proxying file:", error);
|
|
1546
|
-
return Response.json({ error: "Internal server error" }, { status: 500 });
|
|
1547
|
-
}
|
|
1548
|
-
};
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
// src/converters/schema.ts
|
|
1552
|
-
function baseSchemaConverter(_context, entry) {
|
|
1553
|
-
const { sys, fields } = entry;
|
|
1554
|
-
const { cmsLabel, markup } = fields;
|
|
1555
|
-
return {
|
|
1556
|
-
id: sys.id,
|
|
1557
|
-
name: cmsLabel,
|
|
1558
|
-
markup: markup ?? "",
|
|
1559
|
-
description: `Schema for ${cmsLabel}`
|
|
1560
|
-
};
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
// src/converters/resolver.ts
|
|
1564
|
-
function resolveHelper(context, fromId, entry, getResolver) {
|
|
1565
|
-
const id = entry.sys.id;
|
|
1566
|
-
const possibleEntry = context.includes.get(id);
|
|
1567
|
-
if (!possibleEntry) {
|
|
1568
|
-
throw new Error(`Cannot find included entry for link from ${fromId} with id ${id}`);
|
|
1569
|
-
}
|
|
1570
|
-
if (!possibleEntry.resolved) {
|
|
1571
|
-
const resolver = getResolver(possibleEntry.type);
|
|
1572
|
-
if (!resolver) {
|
|
1573
|
-
throw new Error(
|
|
1574
|
-
`No resolver found for link type ${possibleEntry.type} (${JSON.stringify(possibleEntry)}) [${JSON.stringify(entry)}]`
|
|
1575
|
-
);
|
|
1576
|
-
}
|
|
1577
|
-
if (typeof resolver !== "function") {
|
|
1578
|
-
console.log("Resolver type", possibleEntry.type, typeof resolver, resolver);
|
|
1579
|
-
}
|
|
1580
|
-
const resolved = resolver(
|
|
1581
|
-
context,
|
|
1582
|
-
possibleEntry.entry
|
|
1583
|
-
);
|
|
1584
|
-
possibleEntry.resolved = resolved;
|
|
1585
|
-
return resolved;
|
|
1586
|
-
}
|
|
1587
|
-
return possibleEntry.resolved;
|
|
1588
|
-
}
|
|
1589
|
-
function resolveSchema(context, fromId, entry) {
|
|
1590
|
-
return resolveHelper(
|
|
1591
|
-
context,
|
|
1592
|
-
fromId,
|
|
1593
|
-
entry,
|
|
1594
|
-
() => baseSchemaConverter
|
|
1595
|
-
);
|
|
1596
|
-
}
|
|
1597
|
-
function resolveLink(context, fromId, entry) {
|
|
1598
|
-
return resolveHelper(
|
|
1599
|
-
context,
|
|
1600
|
-
fromId,
|
|
1601
|
-
entry,
|
|
1602
|
-
(type) => context.linkResolver.get(type)
|
|
1603
|
-
);
|
|
1604
|
-
}
|
|
1605
|
-
function resolveLinks(context, fromId, entries) {
|
|
1606
|
-
return arrayOrUndefined(entries?.map((entry) => resolveLink(context, fromId, entry)));
|
|
1607
|
-
}
|
|
1608
|
-
function resolveContent(context, fromId, entry) {
|
|
1609
|
-
return resolveHelper(context, fromId, entry, (type) => {
|
|
1610
|
-
const resolver = context.contentResolver.get(type);
|
|
1611
|
-
return resolver;
|
|
1612
|
-
});
|
|
1613
|
-
}
|
|
1614
|
-
function resolveNavigationItem(context, fromId, entry) {
|
|
1615
|
-
const result = resolveHelper(
|
|
1616
|
-
context,
|
|
1617
|
-
fromId,
|
|
1618
|
-
entry,
|
|
1619
|
-
() => context.navigationItemResolver
|
|
1620
|
-
);
|
|
1621
|
-
return result;
|
|
1622
|
-
}
|
|
1623
|
-
function resolveCollectionContent(context, fromId, entry) {
|
|
1624
|
-
const id = entry.sys.id;
|
|
1625
|
-
const possibleEntry = context.includes.get(id);
|
|
1626
|
-
if (!possibleEntry) {
|
|
1627
|
-
throw new Error(
|
|
1628
|
-
`Cannot find included entry for collection content from ${fromId} with id ${id}`
|
|
1629
|
-
);
|
|
1630
|
-
}
|
|
1631
|
-
if (possibleEntry.resolved) {
|
|
1632
|
-
return possibleEntry.resolved;
|
|
1633
|
-
}
|
|
1634
|
-
const { type } = possibleEntry;
|
|
1635
|
-
if (type === "media") {
|
|
1636
|
-
const visual = convertMediaEntryToVisual(
|
|
1637
|
-
context,
|
|
1638
|
-
possibleEntry.entry
|
|
1639
|
-
);
|
|
1640
|
-
if (!visual) {
|
|
1641
|
-
throw new Error(`Failed to convert media entry with id ${id}`);
|
|
1642
|
-
}
|
|
1643
|
-
possibleEntry.resolved = visual;
|
|
1644
|
-
return visual;
|
|
1645
|
-
}
|
|
1646
|
-
if (type === "externalVideo") {
|
|
1647
|
-
const visual = convertExternalVideoEntryToVisual(
|
|
1648
|
-
context,
|
|
1649
|
-
possibleEntry.entry
|
|
1650
|
-
);
|
|
1651
|
-
if (!visual) {
|
|
1652
|
-
throw new Error(`Failed to convert externalVideo entry with id ${id}`);
|
|
1653
|
-
}
|
|
1654
|
-
possibleEntry.resolved = visual;
|
|
1655
|
-
return visual;
|
|
1656
|
-
}
|
|
1657
|
-
return resolveHelper(
|
|
1658
|
-
context,
|
|
1659
|
-
fromId,
|
|
1660
|
-
entry,
|
|
1661
|
-
(resolverType) => {
|
|
1662
|
-
const resolver = context.contentResolver.get(resolverType);
|
|
1663
|
-
if (resolver) {
|
|
1664
|
-
return resolver;
|
|
1665
|
-
}
|
|
1666
|
-
const linkResolver = context.linkResolver.get(resolverType);
|
|
1667
|
-
if (linkResolver) {
|
|
1668
|
-
return linkResolver;
|
|
1669
|
-
}
|
|
1670
|
-
return void 0;
|
|
1671
|
-
}
|
|
1672
|
-
);
|
|
1673
|
-
}
|
|
1674
|
-
function resolvePageContent(context, fromId, entry) {
|
|
1675
|
-
const id = entry.sys.id;
|
|
1676
|
-
const possibleEntry = context.includes.get(id);
|
|
1677
|
-
if (!possibleEntry) {
|
|
1678
|
-
const errorMessage = `Cannot find included entry for content from ${fromId} with id ${id}`;
|
|
1679
|
-
const cmsError = {
|
|
1680
|
-
entryId: id,
|
|
1681
|
-
entryType: "unknown",
|
|
1682
|
-
message: errorMessage,
|
|
1683
|
-
error: new Error(errorMessage)
|
|
1684
|
-
};
|
|
1685
|
-
context.errors.push(cmsError);
|
|
1686
|
-
console.error(errorMessage);
|
|
1687
|
-
return null;
|
|
1688
|
-
}
|
|
1689
|
-
const { type } = possibleEntry;
|
|
1690
|
-
if (type === "media") {
|
|
1691
|
-
try {
|
|
1692
|
-
const visual = convertMediaEntryToVisual(
|
|
1693
|
-
context,
|
|
1694
|
-
possibleEntry.entry
|
|
1695
|
-
);
|
|
1696
|
-
if (!visual) {
|
|
1697
|
-
const errorMessage = `Failed to convert media entry with id ${id}`;
|
|
1698
|
-
const cmsError = {
|
|
1699
|
-
entryId: id,
|
|
1700
|
-
entryType: type,
|
|
1701
|
-
message: errorMessage,
|
|
1702
|
-
error: new Error(errorMessage)
|
|
1703
|
-
};
|
|
1704
|
-
context.errors.push(cmsError);
|
|
1705
|
-
console.error(errorMessage);
|
|
1706
|
-
return null;
|
|
1707
|
-
}
|
|
1708
|
-
return visual;
|
|
1709
|
-
} catch (error) {
|
|
1710
|
-
const errorMessage = `Error converting media entry with id ${id}: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
1711
|
-
const cmsError = {
|
|
1712
|
-
entryId: id,
|
|
1713
|
-
entryType: type,
|
|
1714
|
-
message: errorMessage,
|
|
1715
|
-
error
|
|
1716
|
-
};
|
|
1717
|
-
context.errors.push(cmsError);
|
|
1718
|
-
console.error(errorMessage);
|
|
1719
|
-
return null;
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
if (type === "externalVideo") {
|
|
1723
|
-
try {
|
|
1724
|
-
const visual = convertExternalVideoEntryToVisual(
|
|
1725
|
-
context,
|
|
1726
|
-
possibleEntry.entry
|
|
1727
|
-
);
|
|
1728
|
-
if (!visual) {
|
|
1729
|
-
const errorMessage = `Failed to convert externalVideo entry with id ${id}`;
|
|
1730
|
-
const cmsError = {
|
|
1731
|
-
entryId: id,
|
|
1732
|
-
entryType: type,
|
|
1733
|
-
message: errorMessage,
|
|
1734
|
-
error: new Error(errorMessage)
|
|
1735
|
-
};
|
|
1736
|
-
context.errors.push(cmsError);
|
|
1737
|
-
console.error(errorMessage);
|
|
1738
|
-
return null;
|
|
1739
|
-
}
|
|
1740
|
-
return visual;
|
|
1741
|
-
} catch (error) {
|
|
1742
|
-
const errorMessage = `Error converting externalVideo entry with id ${id}: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
1743
|
-
const cmsError = {
|
|
1744
|
-
entryId: id,
|
|
1745
|
-
entryType: type,
|
|
1746
|
-
message: errorMessage,
|
|
1747
|
-
error
|
|
1748
|
-
};
|
|
1749
|
-
context.errors.push(cmsError);
|
|
1750
|
-
console.error(errorMessage);
|
|
1751
|
-
return null;
|
|
1752
|
-
}
|
|
1753
|
-
}
|
|
1754
|
-
try {
|
|
1755
|
-
if (context.contentResolver.has(type)) {
|
|
1756
|
-
return resolveContent(context, fromId, entry);
|
|
1757
|
-
}
|
|
1758
|
-
if (context.linkResolver.has(type)) {
|
|
1759
|
-
return resolveLink(context, fromId, entry);
|
|
1760
|
-
}
|
|
1761
|
-
const errorMessage = `Unknown content type "${type}" for entry with id ${id}`;
|
|
1762
|
-
const cmsError = {
|
|
1763
|
-
entryId: id,
|
|
1764
|
-
entryType: type,
|
|
1765
|
-
message: errorMessage,
|
|
1766
|
-
error: new Error(errorMessage)
|
|
1767
|
-
};
|
|
1768
|
-
context.errors.push(cmsError);
|
|
1769
|
-
console.error(errorMessage);
|
|
1770
|
-
return null;
|
|
1771
|
-
} catch (error) {
|
|
1772
|
-
const errorMessage = `Error resolving content entry with id ${id} of type ${type}: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
1773
|
-
const cmsError = {
|
|
1774
|
-
entryId: id,
|
|
1775
|
-
entryType: type,
|
|
1776
|
-
message: errorMessage,
|
|
1777
|
-
error
|
|
1778
|
-
};
|
|
1779
|
-
context.errors.push(cmsError);
|
|
1780
|
-
console.error(errorMessage);
|
|
1781
|
-
return null;
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
function resolveRichTextDocument(context, fromId, richText) {
|
|
1785
|
-
if (!richText) {
|
|
1786
|
-
return void 0;
|
|
1787
|
-
}
|
|
1788
|
-
const resolvingEntries = /* @__PURE__ */ new Set();
|
|
1789
|
-
const resolvedEntries = /* @__PURE__ */ new Set();
|
|
1790
|
-
const resolveNode = (node) => {
|
|
1791
|
-
if (!("content" in node)) {
|
|
1792
|
-
return node;
|
|
1793
|
-
}
|
|
1794
|
-
const block = node;
|
|
1795
|
-
if (block.data.target && "sys" in block.data.target && block.data.target.sys.linkType === "Entry") {
|
|
1796
|
-
const entryId = block.data.target.sys.id;
|
|
1797
|
-
if (resolvingEntries.has(entryId)) {
|
|
1798
|
-
console.warn(
|
|
1799
|
-
`Circular reference detected in rich text resolution for entry ${entryId}. Skipping to prevent infinite recursion.`
|
|
1800
|
-
);
|
|
1801
|
-
return {
|
|
1802
|
-
...block,
|
|
1803
|
-
content: block.content?.map(resolveNode)
|
|
1804
|
-
};
|
|
1805
|
-
}
|
|
1806
|
-
if (resolvedEntries.has(entryId)) {
|
|
1807
|
-
return {
|
|
1808
|
-
...block,
|
|
1809
|
-
content: block.content?.map(resolveNode)
|
|
1810
|
-
};
|
|
1811
|
-
}
|
|
1812
|
-
try {
|
|
1813
|
-
resolvingEntries.add(entryId);
|
|
1814
|
-
const resolvedEntry = resolvePageContent(
|
|
1815
|
-
context,
|
|
1816
|
-
fromId,
|
|
1817
|
-
block.data.target
|
|
1818
|
-
);
|
|
1819
|
-
resolvingEntries.delete(entryId);
|
|
1820
|
-
resolvedEntries.add(entryId);
|
|
1821
|
-
if (resolvedEntry === null) {
|
|
1822
|
-
return {
|
|
1823
|
-
...block,
|
|
1824
|
-
content: block.content?.map(resolveNode)
|
|
1825
|
-
};
|
|
1826
|
-
}
|
|
1827
|
-
return {
|
|
1828
|
-
...block,
|
|
1829
|
-
data: {
|
|
1830
|
-
...block.data,
|
|
1831
|
-
target: resolvedEntry
|
|
1832
|
-
},
|
|
1833
|
-
content: block.content?.map(resolveNode)
|
|
1834
|
-
};
|
|
1835
|
-
} catch (error) {
|
|
1836
|
-
resolvingEntries.delete(entryId);
|
|
1837
|
-
console.error(`Failed to resolve entry with id ${entryId}:`, error);
|
|
1838
|
-
return {
|
|
1839
|
-
...block,
|
|
1840
|
-
content: block.content?.map(resolveNode)
|
|
1841
|
-
};
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1844
|
-
if (block.data.target && "sys" in block.data.target && block.data.target.sys.linkType === "Asset") {
|
|
1845
|
-
try {
|
|
1846
|
-
const resolvedAsset = lookupAsset(context, block.data.target);
|
|
1847
|
-
if (resolvedAsset) {
|
|
1848
|
-
return {
|
|
1849
|
-
...block,
|
|
1850
|
-
data: {
|
|
1851
|
-
...block.data,
|
|
1852
|
-
target: resolvedAsset
|
|
1853
|
-
},
|
|
1854
|
-
content: block.content?.map(resolveNode)
|
|
1855
|
-
};
|
|
1856
|
-
}
|
|
1857
|
-
} catch (error) {
|
|
1858
|
-
console.error(
|
|
1859
|
-
`Failed to resolve embedded-asset-block with id ${block.data.target.sys.id}:`,
|
|
1860
|
-
error
|
|
1861
|
-
);
|
|
1862
|
-
return {
|
|
1863
|
-
...block,
|
|
1864
|
-
content: block.content?.map(resolveNode)
|
|
1865
|
-
};
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
return {
|
|
1869
|
-
...block,
|
|
1870
|
-
content: block.content?.map(resolveNode)
|
|
1871
|
-
};
|
|
1872
|
-
};
|
|
1873
|
-
const resolved = resolveNode(richText);
|
|
1874
|
-
return { json: resolved };
|
|
1875
|
-
}
|
|
1876
|
-
|
|
1877
|
-
// src/converters/navigationItem.ts
|
|
1878
|
-
function createLink(context, entry) {
|
|
1879
|
-
const {
|
|
1880
|
-
sys: { id },
|
|
1881
|
-
fields
|
|
1882
|
-
} = entry;
|
|
1883
|
-
const {
|
|
1884
|
-
title,
|
|
1885
|
-
link,
|
|
1886
|
-
text,
|
|
1887
|
-
internal,
|
|
1888
|
-
icon: navIcon,
|
|
1889
|
-
mobileIcon: mobileNavIcon,
|
|
1890
|
-
useTitle,
|
|
1891
|
-
...otherFields
|
|
1892
|
-
} = fields;
|
|
1893
|
-
const icon = createResponsiveVisual(
|
|
1894
|
-
lookupIconAsset(context, navIcon),
|
|
1895
|
-
lookupIconAsset(context, mobileNavIcon)
|
|
1896
|
-
);
|
|
1897
|
-
const realText = useTitle ? resolveBuildYear(title) : text ? resolveBuildYear(text) : void 0;
|
|
1898
|
-
if (link) {
|
|
1899
|
-
return {
|
|
1900
|
-
type: "External link",
|
|
1901
|
-
id,
|
|
1902
|
-
href: link,
|
|
1903
|
-
icon,
|
|
1904
|
-
name: makeContentfulTitle(title, id),
|
|
1905
|
-
...otherFields,
|
|
1906
|
-
text: realText
|
|
1907
|
-
};
|
|
1908
|
-
}
|
|
1909
|
-
if (internal) {
|
|
1910
|
-
const resolved = resolveLink(context, id, internal);
|
|
1911
|
-
return { ...resolved, icon, id, text: realText };
|
|
1912
|
-
}
|
|
1913
|
-
const blank = {
|
|
1914
|
-
type: "Blank link",
|
|
1915
|
-
nick: true,
|
|
1916
|
-
id,
|
|
1917
|
-
name: makeContentfulTitle(title, id),
|
|
1918
|
-
...otherFields,
|
|
1919
|
-
icon,
|
|
1920
|
-
text: realText
|
|
1921
|
-
};
|
|
1922
|
-
return blank;
|
|
1923
|
-
}
|
|
1924
|
-
function baseNavigationItemConverter(context, entry) {
|
|
1925
|
-
const {
|
|
1926
|
-
sys: { id },
|
|
1927
|
-
fields
|
|
1928
|
-
} = entry;
|
|
1929
|
-
const { navigationItems } = fields;
|
|
1930
|
-
const link = createLink(context, entry);
|
|
1931
|
-
const resolvedNavigationItems = navigationItems?.map((item) => resolveNavigationItem(context, id, item)).filter((item) => item !== void 0);
|
|
1932
|
-
const result = {
|
|
1933
|
-
id,
|
|
1934
|
-
link,
|
|
1935
|
-
entries: resolvedNavigationItems
|
|
1936
|
-
};
|
|
1937
|
-
return result;
|
|
1938
|
-
}
|
|
1939
|
-
function resolveNavigation(context, link) {
|
|
1940
|
-
const id = link.sys.id;
|
|
1941
|
-
const possibleEntry = context.includes.get(id);
|
|
1942
|
-
if (!possibleEntry || possibleEntry.type !== "navigation") {
|
|
1943
|
-
return void 0;
|
|
1944
|
-
}
|
|
1945
|
-
const entry = possibleEntry.entry;
|
|
1946
|
-
const { fields } = entry;
|
|
1947
|
-
if (!fields) {
|
|
1948
|
-
return void 0;
|
|
1949
|
-
}
|
|
1950
|
-
const { name, entries: fieldEntries, ...rest } = fields;
|
|
1951
|
-
const entries = fieldEntries?.map((item) => resolveNavigationItem(context, id, item)) ?? [];
|
|
1952
|
-
return {
|
|
1953
|
-
id,
|
|
1954
|
-
name,
|
|
1955
|
-
entries,
|
|
1956
|
-
...rest
|
|
1957
|
-
};
|
|
1958
|
-
}
|
|
1959
|
-
|
|
1960
|
-
// src/converters/template.ts
|
|
1961
|
-
function resolveTemplate(context, link) {
|
|
1962
|
-
const id = link.sys.id;
|
|
1963
|
-
const possibleEntry = context.includes.get(id);
|
|
1964
|
-
if (!possibleEntry || possibleEntry.type !== "template") {
|
|
1965
|
-
return null;
|
|
1966
|
-
}
|
|
1967
|
-
const entry = possibleEntry.entry;
|
|
1968
|
-
const { fields } = entry;
|
|
1969
|
-
if (!fields) {
|
|
1970
|
-
return null;
|
|
1971
|
-
}
|
|
1972
|
-
const preContent = fields.preContent?.map((content) => resolvePageContent(context, id, content)).filter((item) => item !== null) ?? [];
|
|
1973
|
-
const postContent = fields.postContent?.map((content) => resolvePageContent(context, id, content)).filter((item) => item !== null) ?? [];
|
|
1974
|
-
const menu = fields.menu ? resolveNavigation(context, fields.menu) : void 0;
|
|
1975
|
-
const footer = fields.footer ? resolveNavigation(context, fields.footer) : void 0;
|
|
1976
|
-
const stickyNav = fields.flags?.includes("Sticky nav") ?? false;
|
|
1977
|
-
const { backgroundColour, textColour } = fields;
|
|
1978
|
-
return {
|
|
1979
|
-
preContent,
|
|
1980
|
-
postContent,
|
|
1981
|
-
menu,
|
|
1982
|
-
footer,
|
|
1983
|
-
backgroundColour,
|
|
1984
|
-
textColour,
|
|
1985
|
-
stickyNav
|
|
1986
|
-
};
|
|
1987
|
-
}
|
|
1988
|
-
|
|
1989
|
-
// src/converters/article.ts
|
|
1990
|
-
function resolveArticleTemplateHierarchy(context, articleTypeLink, directTemplateLink) {
|
|
1991
|
-
if (directTemplateLink) {
|
|
1992
|
-
return resolveTemplate(context, directTemplateLink);
|
|
1993
|
-
}
|
|
1994
|
-
const articleTypeEntry = context.includes.get(articleTypeLink.sys.id);
|
|
1995
|
-
if (!articleTypeEntry) {
|
|
1996
|
-
console.warn(`ArticleType entry not found for id: ${articleTypeLink.sys.id}`);
|
|
1997
|
-
return null;
|
|
1998
|
-
}
|
|
1999
|
-
const articleTypeFields = articleTypeEntry.entry.fields;
|
|
2000
|
-
if (articleTypeFields?.articleTemplate) {
|
|
2001
|
-
return resolveTemplate(context, articleTypeFields.articleTemplate);
|
|
2002
|
-
}
|
|
2003
|
-
return null;
|
|
2004
|
-
}
|
|
2005
|
-
function baseArticleConverter(context, entry) {
|
|
2006
|
-
const { sys, fields } = entry;
|
|
2007
|
-
const { id } = sys;
|
|
2008
|
-
const {
|
|
2009
|
-
slug,
|
|
2010
|
-
title,
|
|
2011
|
-
description,
|
|
2012
|
-
featuredImage,
|
|
2013
|
-
tags,
|
|
2014
|
-
content,
|
|
2015
|
-
template: templateLink,
|
|
2016
|
-
topContent: topContentLinks,
|
|
2017
|
-
bottomContent: bottomContentLinks,
|
|
2018
|
-
articleType,
|
|
2019
|
-
structuredData,
|
|
2020
|
-
...simpleFields
|
|
2021
|
-
} = fields;
|
|
2022
|
-
const articleTypeLink = resolveLink(context, id, articleType);
|
|
2023
|
-
const template = resolveArticleTemplateHierarchy(context, articleType, templateLink);
|
|
2024
|
-
const topContent = topContentLinks?.map((c) => resolvePageContent(context, id, c)).filter((item) => item !== null) ?? [];
|
|
2025
|
-
const articleContent = content?.map((c) => resolvePageContent(context, id, c)).filter((item) => item !== null) ?? [];
|
|
2026
|
-
const bottomContent = bottomContentLinks?.map((c) => resolvePageContent(context, id, c)).filter((item) => item !== null) ?? [];
|
|
2027
|
-
const preContent = template?.preContent ?? [];
|
|
2028
|
-
const postContent = template?.postContent ?? [];
|
|
2029
|
-
const contents = addPositionMetadata([
|
|
2030
|
-
...topContent,
|
|
2031
|
-
...preContent,
|
|
2032
|
-
...articleContent,
|
|
2033
|
-
...postContent,
|
|
2034
|
-
...bottomContent
|
|
2035
|
-
]);
|
|
2036
|
-
const finalMenu = template?.menu;
|
|
2037
|
-
const finalFooter = template?.footer;
|
|
2038
|
-
const icons = deduplicateIcons(
|
|
2039
|
-
collectIconsFromContent(contents),
|
|
2040
|
-
collectIconsFromNavigation(finalMenu),
|
|
2041
|
-
collectIconsFromNavigation(finalFooter)
|
|
2042
|
-
);
|
|
2043
|
-
const articleTitle = makeContentfulTitle(title, sys.id);
|
|
2044
|
-
const article = {
|
|
2045
|
-
type: "Article",
|
|
2046
|
-
id,
|
|
2047
|
-
slug,
|
|
2048
|
-
name: articleTitle,
|
|
2049
|
-
title: articleTitle,
|
|
2050
|
-
description: makeContentfulDescription(description, sys.id),
|
|
2051
|
-
featuredImage: lookupAsset(context, featuredImage),
|
|
2052
|
-
contentCount: calculateContentCount(topContent, articleContent, bottomContent),
|
|
2053
|
-
articleType: articleTypeLink,
|
|
2054
|
-
tags: tags?.map((tag) => resolveLink(context, id, tag)),
|
|
2055
|
-
contents,
|
|
2056
|
-
icons,
|
|
2057
|
-
structuredData: structuredData?.map((link) => resolveSchema(context, id, link)).filter((item) => item !== null),
|
|
2058
|
-
...simpleFields,
|
|
2059
|
-
// Note: summary field exists in Contentful but is not part of IArticle interface
|
|
2060
|
-
// Keeping it in simpleFields for potential future use
|
|
2061
|
-
menu: finalMenu,
|
|
2062
|
-
footer: finalFooter
|
|
2063
|
-
};
|
|
2064
|
-
return article;
|
|
2065
|
-
}
|
|
2066
|
-
function baseArticleLinkConverter(context, entry) {
|
|
2067
|
-
const { sys, fields } = entry;
|
|
2068
|
-
if (sys.contentType.sys.id !== "article") {
|
|
2069
|
-
throw new Error(`Invalid content type: expected "article", got "${sys.contentType.sys.id}"`);
|
|
2070
|
-
}
|
|
2071
|
-
const {
|
|
2072
|
-
articleType: fieldsArticleType,
|
|
2073
|
-
tags: fieldsTags,
|
|
2074
|
-
author: fieldsAuthor,
|
|
2075
|
-
date,
|
|
2076
|
-
externalLink,
|
|
2077
|
-
download: fieldsDownload,
|
|
2078
|
-
...simpleFields
|
|
2079
|
-
} = fields;
|
|
2080
|
-
const articleType = resolveLink(context, sys.id, fieldsArticleType);
|
|
2081
|
-
const tags = fieldsTags?.map((tag) => resolveLink(context, sys.id, tag));
|
|
2082
|
-
const primaryTag = tags?.[0] ?? void 0;
|
|
2083
|
-
const author = fieldsAuthor ? resolveLink(context, sys.id, fieldsAuthor) : void 0;
|
|
2084
|
-
const download = lookupDownloadAsset(context, fieldsDownload);
|
|
2085
|
-
const href = externalLink ? externalLink : context.urlCalculators.article(articleType.slug, fields.slug, primaryTag?.slug);
|
|
2086
|
-
return createInternalLink(
|
|
2087
|
-
sys.id,
|
|
2088
|
-
{ ...simpleFields, name: fields.cmsLabel },
|
|
2089
|
-
context,
|
|
2090
|
-
href,
|
|
2091
|
-
"Article",
|
|
2092
|
-
{
|
|
2093
|
-
tags,
|
|
2094
|
-
primaryTag,
|
|
2095
|
-
articleType,
|
|
2096
|
-
date,
|
|
2097
|
-
author,
|
|
2098
|
-
download
|
|
2099
|
-
}
|
|
2100
|
-
);
|
|
2101
|
-
}
|
|
2102
|
-
function baseArticleTypeLinkConverter(context, entry) {
|
|
2103
|
-
const { sys, fields } = entry;
|
|
2104
|
-
if (sys.contentType.sys.id !== "articleType") {
|
|
2105
|
-
throw new Error(
|
|
2106
|
-
`Invalid content type: expected "articleType", got "${sys.contentType.sys.id}"`
|
|
2107
|
-
);
|
|
2108
|
-
}
|
|
2109
|
-
const { name, cmsLabel, featuredImage, slug, ...simpleFields } = fields;
|
|
2110
|
-
return createInternalLink(
|
|
2111
|
-
sys.id,
|
|
2112
|
-
{
|
|
2113
|
-
cmsLabel,
|
|
2114
|
-
name,
|
|
2115
|
-
title: name,
|
|
2116
|
-
featuredImage,
|
|
2117
|
-
slug,
|
|
2118
|
-
...simpleFields
|
|
2119
|
-
},
|
|
2120
|
-
context,
|
|
2121
|
-
context.urlCalculators.articleType(slug),
|
|
2122
|
-
"ArticleType"
|
|
2123
|
-
);
|
|
2124
|
-
}
|
|
2125
|
-
function baseArticleTypeConverter(context, entry, customTypeEntry) {
|
|
2126
|
-
const { sys, fields } = entry;
|
|
2127
|
-
const { id } = sys;
|
|
2128
|
-
const {
|
|
2129
|
-
name,
|
|
2130
|
-
indexPageDescription,
|
|
2131
|
-
indexPageTitle,
|
|
2132
|
-
featuredImage,
|
|
2133
|
-
menu: menuLink,
|
|
2134
|
-
footer: footerLink,
|
|
2135
|
-
indexPageTemplate: templateLink,
|
|
2136
|
-
indexPageTopContent: topContentLinks,
|
|
2137
|
-
structuredData,
|
|
2138
|
-
indexPageStructuredData,
|
|
2139
|
-
slug,
|
|
2140
|
-
...other
|
|
2141
|
-
} = fields;
|
|
2142
|
-
const customType = customTypeEntry ? context.customTypeResolver(context, customTypeEntry) : void 0;
|
|
2143
|
-
let template = templateLink ? resolveTemplate(context, templateLink) : null;
|
|
2144
|
-
if (!template && customTypeEntry?.fields.template) {
|
|
2145
|
-
template = resolveTemplate(context, customTypeEntry.fields.template);
|
|
2146
|
-
}
|
|
2147
|
-
const menu = menuLink ? resolveNavigation(context, menuLink) : customTypeEntry?.fields.menu ? resolveNavigation(context, customTypeEntry.fields.menu) : template?.menu;
|
|
2148
|
-
const footer = footerLink ? resolveNavigation(context, footerLink) : customTypeEntry?.fields.footer ? resolveNavigation(context, customTypeEntry.fields.footer) : template?.footer;
|
|
2149
|
-
const topContent = topContentLinks?.map((c) => resolvePageContent(context, id, c)).filter((item) => item !== null) ?? [];
|
|
2150
|
-
const preContent = template?.preContent ?? [];
|
|
2151
|
-
const postContent = template?.postContent ?? [];
|
|
2152
|
-
const contents = addPositionMetadata([...topContent, ...preContent, ...postContent]);
|
|
2153
|
-
const finalMenu = menu;
|
|
2154
|
-
const finalFooter = footer;
|
|
2155
|
-
const icons = deduplicateIcons(
|
|
2156
|
-
collectIconsFromContent(contents),
|
|
2157
|
-
collectIconsFromNavigation(finalMenu),
|
|
2158
|
-
collectIconsFromNavigation(finalFooter)
|
|
2159
|
-
);
|
|
2160
|
-
const articleType = {
|
|
2161
|
-
type: "Article type",
|
|
2162
|
-
id,
|
|
2163
|
-
slug,
|
|
2164
|
-
name,
|
|
2165
|
-
title: makeContentfulTitle(indexPageTitle, sys.id),
|
|
2166
|
-
description: makeContentfulDescription(indexPageDescription, sys.id),
|
|
2167
|
-
featuredImage: lookupAsset(context, featuredImage),
|
|
2168
|
-
contentCount: 0,
|
|
2169
|
-
contents,
|
|
2170
|
-
icons,
|
|
2171
|
-
structuredData: [
|
|
2172
|
-
...structuredData?.map((link) => resolveSchema(context, id, link)) ?? [],
|
|
2173
|
-
...indexPageStructuredData?.map((link) => resolveSchema(context, id, link)) ?? []
|
|
2174
|
-
].filter((item) => item !== null),
|
|
2175
|
-
menu: finalMenu,
|
|
2176
|
-
footer: finalFooter,
|
|
2177
|
-
...other
|
|
2178
|
-
};
|
|
2179
|
-
if (customType) {
|
|
2180
|
-
articleType.customType = customType;
|
|
2181
|
-
}
|
|
2182
|
-
return articleType;
|
|
2183
|
-
}
|
|
2184
|
-
|
|
2185
|
-
// src/converters/collection.ts
|
|
2186
|
-
function baseCollectionConverter(context, entry) {
|
|
2187
|
-
const { sys, fields } = entry;
|
|
2188
|
-
const { id } = sys;
|
|
2189
|
-
const {
|
|
2190
|
-
// Fields requiring transformation
|
|
2191
|
-
backgroundVisual: bgVisual,
|
|
2192
|
-
mobileBackgroundVisual: mobileBgVisual,
|
|
2193
|
-
visual: visualField,
|
|
2194
|
-
mobileVisual: mobileVisualField,
|
|
2195
|
-
visualCustomSize,
|
|
2196
|
-
icon: iconField,
|
|
2197
|
-
links: linksField,
|
|
2198
|
-
contents: contentsField,
|
|
2199
|
-
body: bodyField,
|
|
2200
|
-
additionalCopy: additionalCopyField,
|
|
2201
|
-
showHeading,
|
|
2202
|
-
heading,
|
|
2203
|
-
// Already handled elsewhere
|
|
2204
|
-
cmsLabel,
|
|
2205
|
-
...simpleFields
|
|
2206
|
-
// anchor, backgroundColour, textColour, preHeading, heading, postHeading, backgroundOverlayOpacity
|
|
2207
|
-
} = fields;
|
|
2208
|
-
const backgroundVisual = createResponsiveVisual(
|
|
2209
|
-
lookupAsset(context, bgVisual),
|
|
2210
|
-
lookupAsset(context, mobileBgVisual)
|
|
2211
|
-
);
|
|
2212
|
-
const visual = createResponsiveVisual(
|
|
2213
|
-
lookupMediaEntry(context, visualField),
|
|
2214
|
-
lookupMediaEntry(context, mobileVisualField),
|
|
2215
|
-
visualCustomSize
|
|
2216
|
-
);
|
|
2217
|
-
const collection = {
|
|
2218
|
-
type: "Collection",
|
|
2219
|
-
id,
|
|
2220
|
-
name: cmsLabel,
|
|
2221
|
-
cmsLabel,
|
|
2222
|
-
...DEFAULT_POSITION_FIELDS,
|
|
2223
|
-
...simpleFields,
|
|
2224
|
-
heading: showHeading ? heading : void 0,
|
|
2225
|
-
body: resolveRichTextDocument(context, id, bodyField),
|
|
2226
|
-
additionalCopy: resolveRichTextDocument(context, id, additionalCopyField),
|
|
2227
|
-
icon: lookupIconAsset(context, iconField),
|
|
2228
|
-
backgroundVisual,
|
|
2229
|
-
visual,
|
|
2230
|
-
links: linksField?.map((link) => resolveLink(context, id, link)),
|
|
2231
|
-
contents: contentsField?.map((content) => resolveCollectionContent(context, id, content))
|
|
2232
|
-
};
|
|
2233
|
-
return collection;
|
|
2234
|
-
}
|
|
2235
|
-
|
|
2236
|
-
// src/converters/component.ts
|
|
2237
|
-
function baseComponentConverter(context, entry) {
|
|
2238
|
-
const { sys, fields } = entry;
|
|
2239
|
-
const { id } = sys;
|
|
2240
|
-
const {
|
|
2241
|
-
// Fields requiring transformation
|
|
2242
|
-
backgroundVisual: bgVisual,
|
|
2243
|
-
mobileBackgroundVisual: mobileBgVisual,
|
|
2244
|
-
visual: visualField,
|
|
2245
|
-
mobileVisual: mobileVisualField,
|
|
2246
|
-
visualCustomSize,
|
|
2247
|
-
icon: iconField,
|
|
2248
|
-
links: linksField,
|
|
2249
|
-
body: bodyField,
|
|
2250
|
-
additionalCopy: additionalCopyField,
|
|
2251
|
-
// Field name change
|
|
2252
|
-
showHeading,
|
|
2253
|
-
heading,
|
|
2254
|
-
otherMedia: otherMediaField,
|
|
2255
|
-
// Already handled elsewhere
|
|
2256
|
-
cmsLabel,
|
|
2257
|
-
...simpleFields
|
|
2258
|
-
// anchor, backgroundColour, textColour, preHeading, heading, postHeading, backgroundOverlayOpacity
|
|
2259
|
-
} = fields;
|
|
2260
|
-
const backgroundVisual = createResponsiveVisual(
|
|
2261
|
-
lookupAsset(context, bgVisual),
|
|
2262
|
-
lookupAsset(context, mobileBgVisual)
|
|
2263
|
-
);
|
|
2264
|
-
const visual = createResponsiveVisual(
|
|
2265
|
-
lookupMediaEntry(context, visualField),
|
|
2266
|
-
lookupMediaEntry(context, mobileVisualField),
|
|
2267
|
-
visualCustomSize
|
|
2268
|
-
);
|
|
2269
|
-
const otherMedia = otherMediaField?.map((media) => lookupAsset(context, media));
|
|
2270
|
-
const component = {
|
|
2271
|
-
type: "Component",
|
|
2272
|
-
id,
|
|
2273
|
-
name: cmsLabel,
|
|
2274
|
-
cmsLabel,
|
|
2275
|
-
...DEFAULT_POSITION_FIELDS,
|
|
2276
|
-
...simpleFields,
|
|
2277
|
-
heading: showHeading ? heading : void 0,
|
|
2278
|
-
body: resolveRichTextDocument(context, id, bodyField),
|
|
2279
|
-
additionalCopy: resolveRichTextDocument(context, id, additionalCopyField),
|
|
2280
|
-
icon: lookupIconAsset(context, iconField),
|
|
2281
|
-
backgroundVisual,
|
|
2282
|
-
visual,
|
|
2283
|
-
links: resolveLinks(context, id, linksField),
|
|
2284
|
-
otherMedia: arrayOrUndefined(otherMedia?.filter(notEmpty))
|
|
2285
|
-
};
|
|
2286
|
-
return component;
|
|
2287
|
-
}
|
|
2288
|
-
|
|
2289
|
-
// src/converters/customType.ts
|
|
2290
|
-
function baseCustomTypeConverter(context, entry) {
|
|
2291
|
-
const { sys, fields } = entry;
|
|
2292
|
-
const { id } = sys;
|
|
2293
|
-
const {
|
|
2294
|
-
name,
|
|
2295
|
-
slug,
|
|
2296
|
-
indexPageTitle,
|
|
2297
|
-
indexPageDescription,
|
|
2298
|
-
featuredImage,
|
|
2299
|
-
menu: menuLink,
|
|
2300
|
-
footer: footerLink,
|
|
2301
|
-
indexPageTemplate: templateLink,
|
|
2302
|
-
indexPageTopContent: topContentLinks,
|
|
2303
|
-
structuredData,
|
|
2304
|
-
indexPageStructuredData,
|
|
2305
|
-
...other
|
|
2306
|
-
} = fields;
|
|
2307
|
-
const template = templateLink ? resolveTemplate(context, templateLink) : null;
|
|
2308
|
-
const menu = menuLink ? resolveNavigation(context, menuLink) : template?.menu;
|
|
2309
|
-
const footer = footerLink ? resolveNavigation(context, footerLink) : template?.footer;
|
|
2310
|
-
const topContent = topContentLinks?.map((c) => resolvePageContent(context, id, c)).filter((item) => item !== null) ?? [];
|
|
2311
|
-
const preContent = template?.preContent ?? [];
|
|
2312
|
-
const postContent = template?.postContent ?? [];
|
|
2313
|
-
const contents = addPositionMetadata([...topContent, ...preContent, ...postContent]);
|
|
2314
|
-
const icons = deduplicateIcons(
|
|
2315
|
-
collectIconsFromContent(contents),
|
|
2316
|
-
collectIconsFromNavigation(menu),
|
|
2317
|
-
collectIconsFromNavigation(footer)
|
|
2318
|
-
);
|
|
2319
|
-
const title = makeContentfulTitle(indexPageTitle, sys.id);
|
|
2320
|
-
const description = makeContentfulDescription(indexPageDescription, sys.id);
|
|
2321
|
-
const customType = {
|
|
2322
|
-
type: "Custom type",
|
|
2323
|
-
id,
|
|
2324
|
-
slug,
|
|
2325
|
-
name,
|
|
2326
|
-
title,
|
|
2327
|
-
description,
|
|
2328
|
-
featuredImage: lookupAsset(context, featuredImage),
|
|
2329
|
-
contentCount: 0,
|
|
2330
|
-
contents,
|
|
2331
|
-
icons,
|
|
2332
|
-
structuredData: [
|
|
2333
|
-
...structuredData?.map((link) => resolveSchema(context, id, link)) ?? [],
|
|
2334
|
-
...indexPageStructuredData?.map((link) => resolveSchema(context, id, link)) ?? []
|
|
2335
|
-
].filter((item) => item !== null),
|
|
2336
|
-
menu,
|
|
2337
|
-
footer,
|
|
2338
|
-
...other
|
|
2339
|
-
};
|
|
2340
|
-
return customType;
|
|
2341
|
-
}
|
|
2342
|
-
function baseCustomTypeLinkConverter(context, entry) {
|
|
2343
|
-
const {
|
|
2344
|
-
sys: { id, contentType },
|
|
2345
|
-
fields
|
|
2346
|
-
} = entry;
|
|
2347
|
-
if (contentType.sys.id !== "customType") {
|
|
2348
|
-
throw new Error(`Invalid content type: expected "customType", got "${contentType.sys.id}"`);
|
|
2349
|
-
}
|
|
2350
|
-
return createInternalLink(
|
|
2351
|
-
id,
|
|
2352
|
-
fields,
|
|
2353
|
-
context,
|
|
2354
|
-
context.urlCalculators.customType(fields.slug),
|
|
2355
|
-
"CustomType"
|
|
2356
|
-
);
|
|
2357
|
-
}
|
|
2358
|
-
|
|
2359
|
-
// src/converters/externalComponent.ts
|
|
2360
|
-
function baseExternalComponentConverter(context, entry) {
|
|
2361
|
-
const { sys, fields } = entry;
|
|
2362
|
-
const {
|
|
2363
|
-
cmsLabel,
|
|
2364
|
-
backgroundVisual: bgVisual,
|
|
2365
|
-
mobileBackgroundVisual: mobileBgVisual,
|
|
2366
|
-
...simpleFields
|
|
2367
|
-
} = fields;
|
|
2368
|
-
const backgroundVisual = createResponsiveVisual(
|
|
2369
|
-
lookupAsset(context, bgVisual),
|
|
2370
|
-
lookupAsset(context, mobileBgVisual)
|
|
2371
|
-
);
|
|
2372
|
-
const externalComponent = {
|
|
2373
|
-
type: "External component",
|
|
2374
|
-
id: sys.id,
|
|
2375
|
-
name: makeContentfulTitle(cmsLabel, sys.id),
|
|
2376
|
-
cmsLabel,
|
|
2377
|
-
...DEFAULT_POSITION_FIELDS,
|
|
2378
|
-
...simpleFields,
|
|
2379
|
-
backgroundVisual
|
|
2380
|
-
};
|
|
2381
|
-
return externalComponent;
|
|
2382
|
-
}
|
|
2383
|
-
|
|
2384
|
-
// src/converters/link.ts
|
|
2385
|
-
function baseLinkConverter(context, entry) {
|
|
2386
|
-
const { sys, fields } = entry;
|
|
2387
|
-
if (sys.contentType.sys.id !== "link") {
|
|
2388
|
-
throw new Error(`Invalid content type: expected "link", got "${sys.contentType.sys.id}"`);
|
|
2389
|
-
}
|
|
2390
|
-
const id = sys.id;
|
|
2391
|
-
const name = fields.name;
|
|
2392
|
-
const useName = fields.useName;
|
|
2393
|
-
const text = resolveBuildYear(
|
|
2394
|
-
useName ? name : fields.linkText ?? makeContentfulTitle(fields.linkText, id, "Link text for ")
|
|
2395
|
-
);
|
|
2396
|
-
const icon = createResponsiveVisual(
|
|
2397
|
-
lookupIconAsset(context, fields.icon),
|
|
2398
|
-
lookupIconAsset(context, fields.mobileIcon)
|
|
2399
|
-
);
|
|
2400
|
-
const backgroundColour = fields.backgroundColour ?? null;
|
|
2401
|
-
const textColour = fields.textColour ?? null;
|
|
2402
|
-
const variant = fields.variant;
|
|
2403
|
-
const size = fields.size;
|
|
2404
|
-
const baseProps = {
|
|
2405
|
-
id,
|
|
2406
|
-
useName,
|
|
2407
|
-
name,
|
|
2408
|
-
text,
|
|
2409
|
-
icon,
|
|
2410
|
-
backgroundColour,
|
|
2411
|
-
textColour,
|
|
2412
|
-
variant,
|
|
2413
|
-
size
|
|
2414
|
-
};
|
|
2415
|
-
if (fields.internal) {
|
|
2416
|
-
const internalTarget = resolveLink(context, id, fields.internal);
|
|
2417
|
-
return {
|
|
2418
|
-
...baseProps,
|
|
2419
|
-
type: "Internal link",
|
|
2420
|
-
internalType: internalTarget.internalType,
|
|
2421
|
-
href: internalTarget.href,
|
|
2422
|
-
slug: internalTarget.slug,
|
|
2423
|
-
indexed: internalTarget.indexed,
|
|
2424
|
-
hidden: internalTarget.hidden,
|
|
2425
|
-
tags: internalTarget.tags,
|
|
2426
|
-
title: internalTarget.title,
|
|
2427
|
-
description: internalTarget.description
|
|
2428
|
-
};
|
|
2429
|
-
}
|
|
2430
|
-
if (fields.external) {
|
|
2431
|
-
return {
|
|
2432
|
-
...baseProps,
|
|
2433
|
-
type: "External link",
|
|
2434
|
-
href: fields.external
|
|
2435
|
-
};
|
|
2436
|
-
}
|
|
2437
|
-
if (fields.downloadAsset) {
|
|
2438
|
-
const asset = lookupAsset(context, fields.downloadAsset);
|
|
2439
|
-
let href = null;
|
|
2440
|
-
if (asset?.image?.type === "Picture") {
|
|
2441
|
-
href = asset.image.src;
|
|
2442
|
-
} else if (asset?.image?.type === "Svg image") {
|
|
2443
|
-
href = asset.image.svgSrc;
|
|
2444
|
-
} else if (asset?.video) {
|
|
2445
|
-
if (asset.video.type === "Local video") {
|
|
2446
|
-
href = asset.video.preview.videoUrl;
|
|
2447
|
-
} else if (asset.video.type === "Full video") {
|
|
2448
|
-
href = asset.video.full.videoUrl;
|
|
2449
|
-
} else if (asset.video.type === "External video") {
|
|
2450
|
-
href = asset.video.external;
|
|
2451
|
-
}
|
|
2452
|
-
}
|
|
2453
|
-
return {
|
|
2454
|
-
...baseProps,
|
|
2455
|
-
type: "Download link",
|
|
2456
|
-
href,
|
|
2457
|
-
visual: asset
|
|
2458
|
-
};
|
|
2459
|
-
}
|
|
2460
|
-
return {
|
|
2461
|
-
...baseProps,
|
|
2462
|
-
type: "Blank link",
|
|
2463
|
-
href: null
|
|
2464
|
-
};
|
|
2465
|
-
}
|
|
2466
|
-
|
|
2467
|
-
// src/converters/page.ts
|
|
2468
|
-
function basePageConverter(context, entry) {
|
|
2469
|
-
const {
|
|
2470
|
-
sys: { id },
|
|
2471
|
-
fields
|
|
2472
|
-
} = entry;
|
|
2473
|
-
const {
|
|
2474
|
-
slug,
|
|
2475
|
-
title,
|
|
2476
|
-
description,
|
|
2477
|
-
featuredImage,
|
|
2478
|
-
tags,
|
|
2479
|
-
content,
|
|
2480
|
-
menu: pageMenu,
|
|
2481
|
-
footer: pageFooter,
|
|
2482
|
-
template: templateLink,
|
|
2483
|
-
topContent: topContentLinks,
|
|
2484
|
-
bottomContent: bottomContentLinks,
|
|
2485
|
-
structuredData,
|
|
2486
|
-
...simpleFields
|
|
2487
|
-
} = fields;
|
|
2488
|
-
const pageMenuNav = pageMenu ? resolveNavigation(context, pageMenu) : void 0;
|
|
2489
|
-
const pageFooterNav = pageFooter ? resolveNavigation(context, pageFooter) : void 0;
|
|
2490
|
-
const template = templateLink ? resolveTemplate(context, templateLink) : null;
|
|
2491
|
-
const topContent = topContentLinks?.map((c) => resolvePageContent(context, id, c)).filter((item) => item !== null) ?? [];
|
|
2492
|
-
const pageContent = content?.map((c) => resolvePageContent(context, id, c)).filter((item) => item !== null) ?? [];
|
|
2493
|
-
const bottomContent = bottomContentLinks?.map((c) => resolvePageContent(context, id, c)).filter((item) => item !== null) ?? [];
|
|
2494
|
-
const preContent = template?.preContent ?? [];
|
|
2495
|
-
const postContent = template?.postContent ?? [];
|
|
2496
|
-
const contents = addPositionMetadata([
|
|
2497
|
-
...topContent,
|
|
2498
|
-
...preContent,
|
|
2499
|
-
...pageContent,
|
|
2500
|
-
...postContent,
|
|
2501
|
-
...bottomContent
|
|
2502
|
-
]);
|
|
2503
|
-
const finalMenu = pageMenuNav ?? template?.menu;
|
|
2504
|
-
const finalFooter = pageFooterNav ?? template?.footer;
|
|
2505
|
-
const icons = deduplicateIcons(
|
|
2506
|
-
collectIconsFromContent(contents),
|
|
2507
|
-
collectIconsFromNavigation(finalMenu),
|
|
2508
|
-
collectIconsFromNavigation(finalFooter)
|
|
2509
|
-
);
|
|
2510
|
-
const page = {
|
|
2511
|
-
type: "Page",
|
|
2512
|
-
id,
|
|
2513
|
-
isHomePage: slug === "index",
|
|
2514
|
-
slug,
|
|
2515
|
-
name: title,
|
|
2516
|
-
title: makeContentfulTitle(title, id),
|
|
2517
|
-
description: makeContentfulDescription(description, id),
|
|
2518
|
-
featuredImage: lookupAsset(context, featuredImage),
|
|
2519
|
-
tags: tags?.map((tag) => resolveLink(context, id, tag)),
|
|
2520
|
-
contentCount: calculateContentCount(topContent, pageContent, bottomContent),
|
|
2521
|
-
contents,
|
|
2522
|
-
icons,
|
|
2523
|
-
structuredData: structuredData?.map((link) => resolveSchema(context, id, link)).filter((item) => item !== null),
|
|
2524
|
-
...simpleFields,
|
|
2525
|
-
menu: finalMenu,
|
|
2526
|
-
footer: finalFooter
|
|
2527
|
-
};
|
|
2528
|
-
return page;
|
|
2529
|
-
}
|
|
2530
|
-
function calculatePageHref(slug) {
|
|
2531
|
-
if (slug === "index") {
|
|
2532
|
-
return "/";
|
|
2533
|
-
}
|
|
2534
|
-
return `/${slug}/`;
|
|
2535
|
-
}
|
|
2536
|
-
function basePageLinkConverter(context, entry) {
|
|
2537
|
-
const {
|
|
2538
|
-
sys: { id, contentType },
|
|
2539
|
-
fields
|
|
2540
|
-
} = entry;
|
|
2541
|
-
if (contentType.sys.id !== "page") {
|
|
2542
|
-
throw new Error(`Invalid content type: expected "page", got "${contentType.sys.id}"`);
|
|
2543
|
-
}
|
|
2544
|
-
return createInternalLink(
|
|
2545
|
-
id,
|
|
2546
|
-
{
|
|
2547
|
-
...fields,
|
|
2548
|
-
name: fields.cmsLabel
|
|
2549
|
-
},
|
|
2550
|
-
context,
|
|
2551
|
-
context.urlCalculators.page(fields.slug),
|
|
2552
|
-
"Page",
|
|
2553
|
-
{ tags: fields.tags?.map((tag) => resolveLink(context, id, tag)) }
|
|
2554
|
-
);
|
|
2555
|
-
}
|
|
2556
|
-
function calculatePageVariantHref(slug) {
|
|
2557
|
-
return `/${slug}/`;
|
|
2558
|
-
}
|
|
2559
|
-
function basePageVariantLinkConverter(context, entry) {
|
|
2560
|
-
const {
|
|
2561
|
-
sys: { id, contentType },
|
|
2562
|
-
fields
|
|
2563
|
-
} = entry;
|
|
2564
|
-
if (contentType.sys.id !== "pageVariant") {
|
|
2565
|
-
throw new Error(`Invalid content type: expected "pageVariant", got "${contentType.sys.id}"`);
|
|
2566
|
-
}
|
|
2567
|
-
return createInternalLink(
|
|
2568
|
-
id,
|
|
2569
|
-
{
|
|
2570
|
-
...fields,
|
|
2571
|
-
name: fields.cmsLabel
|
|
2572
|
-
},
|
|
2573
|
-
context,
|
|
2574
|
-
context.urlCalculators.pageVariant(fields.slug),
|
|
2575
|
-
"Page",
|
|
2576
|
-
{ tags: fields.tags?.map((tag) => resolveLink(context, id, tag)) }
|
|
2577
|
-
);
|
|
2578
|
-
}
|
|
2579
|
-
|
|
2580
|
-
// src/converters/person.ts
|
|
2581
|
-
function basePersonLinkConverter(context, entry) {
|
|
2582
|
-
const { sys, fields } = entry;
|
|
2583
|
-
const {
|
|
2584
|
-
name,
|
|
2585
|
-
bio: fieldsBio,
|
|
2586
|
-
jobTitle,
|
|
2587
|
-
phoneNumber,
|
|
2588
|
-
emailAddress,
|
|
2589
|
-
linkedIn,
|
|
2590
|
-
location,
|
|
2591
|
-
media,
|
|
2592
|
-
...simpleFields
|
|
2593
|
-
} = fields;
|
|
2594
|
-
if (sys.contentType.sys.id !== "person") {
|
|
2595
|
-
throw new Error(`Invalid content type: expected "person", got "${sys.contentType.sys.id}"`);
|
|
2596
|
-
}
|
|
2597
|
-
const title = makeContentfulTitle(name, sys.id);
|
|
2598
|
-
return createInternalLink(
|
|
2599
|
-
sys.id,
|
|
2600
|
-
{
|
|
2601
|
-
...simpleFields,
|
|
2602
|
-
name,
|
|
2603
|
-
title,
|
|
2604
|
-
featuredImage: media,
|
|
2605
|
-
cmsLabel: title
|
|
2606
|
-
},
|
|
2607
|
-
context,
|
|
2608
|
-
context.urlCalculators.person(fields.slug),
|
|
2609
|
-
"Person",
|
|
2610
|
-
{
|
|
2611
|
-
bio: resolveRichTextDocument(context, sys.id, fieldsBio),
|
|
2612
|
-
jobTitle,
|
|
2613
|
-
phoneNumber,
|
|
2614
|
-
emailAddress,
|
|
2615
|
-
linkedIn,
|
|
2616
|
-
location
|
|
2617
|
-
}
|
|
2618
|
-
);
|
|
2619
|
-
}
|
|
2620
|
-
function basePersonConverter(context, entry, customTypeEntry) {
|
|
2621
|
-
const {
|
|
2622
|
-
sys: { id, contentType },
|
|
2623
|
-
fields
|
|
2624
|
-
} = entry;
|
|
2625
|
-
if (contentType.sys.id !== "person") {
|
|
2626
|
-
throw new Error(`Invalid content type: expected "person", got "${contentType.sys.id}"`);
|
|
2627
|
-
}
|
|
2628
|
-
const {
|
|
2629
|
-
slug,
|
|
2630
|
-
name,
|
|
2631
|
-
description,
|
|
2632
|
-
media,
|
|
2633
|
-
bio: bioField,
|
|
2634
|
-
content: contentLinks,
|
|
2635
|
-
structuredData,
|
|
2636
|
-
...rest
|
|
2637
|
-
} = fields;
|
|
2638
|
-
const customType = customTypeEntry ? context.customTypeResolver(context, customTypeEntry) : void 0;
|
|
2639
|
-
const template = customTypeEntry?.fields.template ? resolveTemplate(context, customTypeEntry.fields.template) : null;
|
|
2640
|
-
const personContent = contentLinks?.map((c) => resolvePageContent(context, id, c)).filter((item) => item !== null) ?? [];
|
|
2641
|
-
const preContent = template?.preContent ?? [];
|
|
2642
|
-
const postContent = template?.postContent ?? [];
|
|
2643
|
-
const contents = addPositionMetadata([...preContent, ...personContent, ...postContent]);
|
|
2644
|
-
const menu = customTypeEntry?.fields.menu ? resolveNavigation(context, customTypeEntry.fields.menu) : template?.menu;
|
|
2645
|
-
const footer = customTypeEntry?.fields.footer ? resolveNavigation(context, customTypeEntry.fields.footer) : template?.footer;
|
|
2646
|
-
const icons = deduplicateIcons(
|
|
2647
|
-
collectIconsFromContent(contents),
|
|
2648
|
-
collectIconsFromNavigation(menu),
|
|
2649
|
-
collectIconsFromNavigation(footer)
|
|
2650
|
-
);
|
|
2651
|
-
return {
|
|
2652
|
-
type: "Person",
|
|
2653
|
-
id,
|
|
2654
|
-
slug,
|
|
2655
|
-
name,
|
|
2656
|
-
title: makeContentfulTitle(name, id),
|
|
2657
|
-
description: description ?? `Description for ${id}`,
|
|
2658
|
-
featuredImage: lookupAsset(context, media),
|
|
2659
|
-
// Person uses 'media' not 'featuredImage'
|
|
2660
|
-
href: context.urlCalculators.person(slug),
|
|
2661
|
-
bio: resolveRichTextDocument(context, id, bioField) ?? null,
|
|
2662
|
-
contentCount: calculateContentCount(personContent),
|
|
2663
|
-
contents,
|
|
2664
|
-
menu,
|
|
2665
|
-
footer,
|
|
2666
|
-
icons,
|
|
2667
|
-
customType,
|
|
2668
|
-
structuredData: structuredData?.map((link) => resolveSchema(context, id, link)).filter((item) => item !== null),
|
|
2669
|
-
...rest
|
|
2670
|
-
};
|
|
2671
|
-
}
|
|
2672
|
-
|
|
2673
|
-
// src/converters/tag.ts
|
|
2674
|
-
function baseTagLinkConverter(context, entry) {
|
|
2675
|
-
const { sys, fields } = entry;
|
|
2676
|
-
const { name, cmsLabel, ...simpleFields } = fields;
|
|
2677
|
-
if (sys.contentType.sys.id !== "tag") {
|
|
2678
|
-
throw new Error(`Invalid content type: expected "tag", got "${sys.contentType.sys.id}"`);
|
|
2679
|
-
}
|
|
2680
|
-
return createInternalLink(
|
|
2681
|
-
sys.id,
|
|
2682
|
-
{
|
|
2683
|
-
...simpleFields,
|
|
2684
|
-
cmsLabel,
|
|
2685
|
-
name,
|
|
2686
|
-
useName: false,
|
|
2687
|
-
title: name
|
|
2688
|
-
},
|
|
2689
|
-
context,
|
|
2690
|
-
context.urlCalculators.tag(fields.slug),
|
|
2691
|
-
"Tag"
|
|
2692
|
-
);
|
|
2693
|
-
}
|
|
2694
|
-
function baseTagConverter(context, entry, customTypeEntry) {
|
|
2695
|
-
const { sys, fields } = entry;
|
|
2696
|
-
const { id } = sys;
|
|
2697
|
-
const {
|
|
2698
|
-
name,
|
|
2699
|
-
slug,
|
|
2700
|
-
description,
|
|
2701
|
-
featuredImage,
|
|
2702
|
-
tagType,
|
|
2703
|
-
menu: menuLink,
|
|
2704
|
-
footer: footerLink,
|
|
2705
|
-
template: templateLink,
|
|
2706
|
-
topContent: topContentLinks,
|
|
2707
|
-
structuredData,
|
|
2708
|
-
...rest
|
|
2709
|
-
} = fields;
|
|
2710
|
-
const customType = customTypeEntry ? context.customTypeResolver(context, customTypeEntry) : void 0;
|
|
2711
|
-
let template = templateLink ? resolveTemplate(context, templateLink) : null;
|
|
2712
|
-
if (!template && customTypeEntry?.fields.template) {
|
|
2713
|
-
template = resolveTemplate(context, customTypeEntry.fields.template);
|
|
2714
|
-
}
|
|
2715
|
-
const menu = menuLink ? resolveNavigation(context, menuLink) : customTypeEntry?.fields.menu ? resolveNavigation(context, customTypeEntry.fields.menu) : template?.menu;
|
|
2716
|
-
const footer = footerLink ? resolveNavigation(context, footerLink) : customTypeEntry?.fields.footer ? resolveNavigation(context, customTypeEntry.fields.footer) : template?.footer;
|
|
2717
|
-
const topContent = topContentLinks?.map((c) => resolvePageContent(context, id, c)).filter((item) => item !== null) ?? [];
|
|
2718
|
-
const preContent = template?.preContent ?? [];
|
|
2719
|
-
const postContent = template?.postContent ?? [];
|
|
2720
|
-
const contents = addPositionMetadata([...topContent, ...preContent, ...postContent]);
|
|
2721
|
-
const icons = deduplicateIcons(
|
|
2722
|
-
collectIconsFromContent(contents),
|
|
2723
|
-
collectIconsFromNavigation(menu),
|
|
2724
|
-
collectIconsFromNavigation(footer)
|
|
2725
|
-
);
|
|
2726
|
-
const tagTypeEntry = tagType ? context.includes.get(tagType.sys.id) : null;
|
|
2727
|
-
const tagTypeName = tagTypeEntry?.entry?.fields?.name;
|
|
2728
|
-
const tag = {
|
|
2729
|
-
type: "Tag",
|
|
2730
|
-
id,
|
|
2731
|
-
slug,
|
|
2732
|
-
name,
|
|
2733
|
-
title: makeContentfulTitle(name, sys.id),
|
|
2734
|
-
description: makeContentfulDescription(description, sys.id),
|
|
2735
|
-
featuredImage: lookupAsset(context, featuredImage),
|
|
2736
|
-
tagType: tagTypeName ?? null,
|
|
2737
|
-
contentCount: calculateContentCount(topContent),
|
|
2738
|
-
contents,
|
|
2739
|
-
icons,
|
|
2740
|
-
structuredData: structuredData?.map((link) => resolveSchema(context, id, link)).filter((item) => item !== null),
|
|
2741
|
-
...rest,
|
|
2742
|
-
menu,
|
|
2743
|
-
footer
|
|
2744
|
-
};
|
|
2745
|
-
if (customType) {
|
|
2746
|
-
tag.customType = customType;
|
|
2747
|
-
}
|
|
2748
|
-
return tag;
|
|
2749
|
-
}
|
|
2750
|
-
|
|
2751
|
-
// src/api/context.ts
|
|
2752
|
-
function createBaseConverterContext(urlCalculators) {
|
|
2753
|
-
const linkResolver = /* @__PURE__ */ new Map();
|
|
2754
|
-
linkResolver.set("page", basePageLinkConverter);
|
|
2755
|
-
linkResolver.set("article", baseArticleLinkConverter);
|
|
2756
|
-
linkResolver.set(
|
|
2757
|
-
"articleType",
|
|
2758
|
-
baseArticleTypeLinkConverter
|
|
2759
|
-
);
|
|
2760
|
-
linkResolver.set("tag", baseTagLinkConverter);
|
|
2761
|
-
linkResolver.set("person", basePersonLinkConverter);
|
|
2762
|
-
linkResolver.set("pageVariant", basePageVariantLinkConverter);
|
|
2763
|
-
linkResolver.set("link", baseLinkConverter);
|
|
2764
|
-
linkResolver.set("customType", baseCustomTypeLinkConverter);
|
|
2765
|
-
const contentResolver = /* @__PURE__ */ new Map();
|
|
2766
|
-
contentResolver.set("collection", baseCollectionConverter);
|
|
2767
|
-
contentResolver.set("component", baseComponentConverter);
|
|
2768
|
-
contentResolver.set(
|
|
2769
|
-
"externalComponent",
|
|
2770
|
-
baseExternalComponentConverter
|
|
2771
|
-
);
|
|
2772
|
-
return {
|
|
2773
|
-
pageResolver: basePageConverter,
|
|
2774
|
-
navigationItemResolver: baseNavigationItemConverter,
|
|
2775
|
-
articleResolver: baseArticleConverter,
|
|
2776
|
-
articleTypeResolver: baseArticleTypeConverter,
|
|
2777
|
-
tagResolver: baseTagConverter,
|
|
2778
|
-
personResolver: basePersonConverter,
|
|
2779
|
-
customTypeResolver: baseCustomTypeConverter,
|
|
2780
|
-
componentResolver: baseComponentConverter,
|
|
2781
|
-
collectionResolver: baseCollectionConverter,
|
|
2782
|
-
linkResolver,
|
|
2783
|
-
contentResolver,
|
|
2784
|
-
urlCalculators,
|
|
2785
|
-
videoPrefix: ""
|
|
2786
|
-
};
|
|
2787
|
-
}
|
|
2788
|
-
|
|
2789
|
-
// src/api/custom-type.ts
|
|
2790
|
-
async function contentfulCustomTypeRest(context, config, slug, options) {
|
|
2791
|
-
return fetchSingleEntity(
|
|
2792
|
-
context,
|
|
2793
|
-
config,
|
|
2794
|
-
{
|
|
2795
|
-
contentType: "customType",
|
|
2796
|
-
cacheTagType: "customType",
|
|
2797
|
-
cacheTagIdentifier: slug,
|
|
2798
|
-
query: { "fields.slug": slug },
|
|
2799
|
-
resolver: (ctx, entry) => ctx.customTypeResolver(ctx, entry),
|
|
2800
|
-
errorLogContext: { slug }
|
|
2801
|
-
},
|
|
2802
|
-
options
|
|
2803
|
-
);
|
|
2804
|
-
}
|
|
2805
|
-
|
|
2806
|
-
// src/api/links.ts
|
|
2807
|
-
init_utils();
|
|
2808
|
-
async function contentfulAllPageLinks(context, config, options) {
|
|
2809
|
-
const client = getContentfulClient(config, options.preview);
|
|
2810
|
-
const cacheTags = getCacheTags("page", void 0, options?.preview);
|
|
2811
|
-
const requestOptions = {
|
|
2812
|
-
...options,
|
|
2813
|
-
next: {
|
|
2814
|
-
...options?.next,
|
|
2815
|
-
tags: cacheTags
|
|
2816
|
-
}
|
|
2817
|
-
};
|
|
2818
|
-
const fetchFn = () => fetchAllLinks(
|
|
2819
|
-
"page",
|
|
2820
|
-
client,
|
|
2821
|
-
requestOptions,
|
|
2822
|
-
basePageLinkConverter,
|
|
2823
|
-
context,
|
|
2824
|
-
100,
|
|
2825
|
-
PAGE_LINK_FIELDS
|
|
2826
|
-
);
|
|
2827
|
-
if (options?.retry) {
|
|
2828
|
-
return await withRetry(fetchFn, options.retry);
|
|
2829
|
-
}
|
|
2830
|
-
return await fetchFn();
|
|
2831
|
-
}
|
|
2832
|
-
async function contentfulAllArticleLinks(context, config, options) {
|
|
2833
|
-
const client = getContentfulClient(config, options.preview);
|
|
2834
|
-
const cacheTags = getCacheTags("article", void 0, options?.preview);
|
|
2835
|
-
const requestOptions = {
|
|
2836
|
-
...options,
|
|
2837
|
-
next: {
|
|
2838
|
-
...options?.next,
|
|
2839
|
-
tags: cacheTags
|
|
2840
|
-
}
|
|
2841
|
-
};
|
|
2842
|
-
const fetchFn = () => fetchAllLinks(
|
|
2843
|
-
"article",
|
|
2844
|
-
client,
|
|
2845
|
-
requestOptions,
|
|
2846
|
-
baseArticleLinkConverter,
|
|
2847
|
-
context,
|
|
2848
|
-
100,
|
|
2849
|
-
ARTICLE_LINK_FIELDS
|
|
2850
|
-
);
|
|
2851
|
-
if (options?.retry) {
|
|
2852
|
-
return await withRetry(fetchFn, options.retry);
|
|
2853
|
-
}
|
|
2854
|
-
return await fetchFn();
|
|
2855
|
-
}
|
|
2856
|
-
async function contentfulAllTagLinks(context, config, options) {
|
|
2857
|
-
const client = getContentfulClient(config, options.preview);
|
|
2858
|
-
const cacheTags = getCacheTags("tag", void 0, options?.preview);
|
|
2859
|
-
const requestOptions = {
|
|
2860
|
-
...options,
|
|
2861
|
-
next: {
|
|
2862
|
-
...options?.next,
|
|
2863
|
-
tags: cacheTags
|
|
2864
|
-
}
|
|
2865
|
-
};
|
|
2866
|
-
const fetchFn = () => fetchAllLinks(
|
|
2867
|
-
"tag",
|
|
2868
|
-
client,
|
|
2869
|
-
requestOptions,
|
|
2870
|
-
baseTagLinkConverter,
|
|
2871
|
-
context,
|
|
2872
|
-
100,
|
|
2873
|
-
TAG_LINK_FIELDS
|
|
2874
|
-
);
|
|
2875
|
-
if (options?.retry) {
|
|
2876
|
-
return await withRetry(fetchFn, options.retry);
|
|
2877
|
-
}
|
|
2878
|
-
return await fetchFn();
|
|
2879
|
-
}
|
|
2880
|
-
async function contentfulAllPersonLinks(context, config, options) {
|
|
2881
|
-
const client = getContentfulClient(config, options.preview);
|
|
2882
|
-
const cacheTags = getCacheTags("person", void 0, options?.preview);
|
|
2883
|
-
const requestOptions = {
|
|
2884
|
-
...options,
|
|
2885
|
-
next: {
|
|
2886
|
-
...options?.next,
|
|
2887
|
-
tags: cacheTags
|
|
2888
|
-
}
|
|
2889
|
-
};
|
|
2890
|
-
const fetchFn = () => fetchAllLinks(
|
|
2891
|
-
"person",
|
|
2892
|
-
client,
|
|
2893
|
-
requestOptions,
|
|
2894
|
-
basePersonLinkConverter,
|
|
2895
|
-
context,
|
|
2896
|
-
100,
|
|
2897
|
-
PERSON_LINK_FIELDS
|
|
2898
|
-
);
|
|
2899
|
-
if (options?.retry) {
|
|
2900
|
-
return await withRetry(fetchFn, options.retry);
|
|
2901
|
-
}
|
|
2902
|
-
return await fetchFn();
|
|
2903
|
-
}
|
|
2904
|
-
async function contentfulAllArticleTypeLinks(context, config, options) {
|
|
2905
|
-
const client = getContentfulClient(config, options.preview);
|
|
2906
|
-
const cacheTags = getCacheTags("articleType", void 0, options?.preview);
|
|
2907
|
-
const requestOptions = {
|
|
2908
|
-
...options,
|
|
2909
|
-
next: {
|
|
2910
|
-
...options?.next,
|
|
2911
|
-
tags: cacheTags
|
|
2912
|
-
}
|
|
2913
|
-
};
|
|
2914
|
-
const fetchFn = () => fetchAllLinks(
|
|
2915
|
-
"articleType",
|
|
2916
|
-
client,
|
|
2917
|
-
requestOptions,
|
|
2918
|
-
baseArticleTypeLinkConverter,
|
|
2919
|
-
context,
|
|
2920
|
-
100,
|
|
2921
|
-
ARTICLE_TYPE_LINK_FIELDS
|
|
2922
|
-
);
|
|
2923
|
-
if (options?.retry) {
|
|
2924
|
-
return await withRetry(fetchFn, options.retry);
|
|
2925
|
-
}
|
|
2926
|
-
return await fetchFn();
|
|
2927
|
-
}
|
|
2928
|
-
|
|
2929
|
-
// src/api/page.ts
|
|
2930
|
-
async function contentfulPageRest(context, config, slug, options) {
|
|
2931
|
-
return fetchSingleEntity(
|
|
2932
|
-
context,
|
|
2933
|
-
config,
|
|
2934
|
-
{
|
|
2935
|
-
contentType: "page",
|
|
2936
|
-
cacheTagType: "page",
|
|
2937
|
-
cacheTagIdentifier: slug,
|
|
2938
|
-
query: { "fields.slug": slug },
|
|
2939
|
-
resolver: (ctx, entry) => ctx.pageResolver(ctx, entry),
|
|
2940
|
-
errorLogContext: { slug }
|
|
2941
|
-
},
|
|
2942
|
-
options
|
|
2943
|
-
);
|
|
2944
|
-
}
|
|
2945
|
-
|
|
2946
|
-
// src/api/person.ts
|
|
2947
|
-
init_utils();
|
|
2948
|
-
async function contentfulPersonRest(context, config, slug, options) {
|
|
2949
|
-
const client = getContentfulClient(config, options.preview);
|
|
2950
|
-
const personCacheTags = getCacheTags("person", slug, options?.preview);
|
|
2951
|
-
const customTypeCacheTags = options?.customType ? getCacheTags("customType", options.customType, options?.preview) : [];
|
|
2952
|
-
const requestOptions = {
|
|
2953
|
-
...options,
|
|
2954
|
-
next: {
|
|
2955
|
-
...options?.next,
|
|
2956
|
-
tags: [...personCacheTags, ...customTypeCacheTags]
|
|
2957
|
-
}
|
|
2958
|
-
};
|
|
2959
|
-
const fetchFn = async () => {
|
|
2960
|
-
const personPromise = client.getEntries(
|
|
2961
|
-
{
|
|
2962
|
-
content_type: "person",
|
|
2963
|
-
"fields.slug": slug,
|
|
2964
|
-
include: 10,
|
|
2965
|
-
locale: options?.locale,
|
|
2966
|
-
limit: 1
|
|
2967
|
-
},
|
|
2968
|
-
requestOptions
|
|
2969
|
-
);
|
|
2970
|
-
const customTypePromise = options?.customType ? client.getEntries(
|
|
2971
|
-
{
|
|
2972
|
-
content_type: "customType",
|
|
2973
|
-
"fields.slug": options.customType,
|
|
2974
|
-
include: 10,
|
|
2975
|
-
locale: options?.locale,
|
|
2976
|
-
limit: 1
|
|
2977
|
-
},
|
|
2978
|
-
requestOptions
|
|
2979
|
-
) : Promise.resolve(null);
|
|
2980
|
-
const [personResponse, customTypeResponse] = await Promise.all([
|
|
2981
|
-
personPromise,
|
|
2982
|
-
customTypePromise
|
|
2983
|
-
]);
|
|
2984
|
-
const personEntry = personResponse.items[0];
|
|
2985
|
-
if (!personEntry || !personEntry.fields) {
|
|
2986
|
-
return { data: null, errors: [] };
|
|
2987
|
-
}
|
|
2988
|
-
const customTypeEntry = customTypeResponse?.items[0];
|
|
2989
|
-
try {
|
|
2990
|
-
const assets = convertAllAssets(personResponse, context);
|
|
2991
|
-
const rawAssets = convertAllRawAssets(personResponse);
|
|
2992
|
-
const includes = convertAllIncludes(personResponse);
|
|
2993
|
-
if (customTypeResponse) {
|
|
2994
|
-
const customAssets = convertAllAssets(customTypeResponse, context);
|
|
2995
|
-
const customRawAssets = convertAllRawAssets(customTypeResponse);
|
|
2996
|
-
const customIncludes = convertAllIncludes(customTypeResponse);
|
|
2997
|
-
for (const [key, value] of customAssets) assets.set(key, value);
|
|
2998
|
-
for (const [key, value] of customRawAssets) rawAssets.set(key, value);
|
|
2999
|
-
for (const [key, value] of customIncludes) includes.set(key, value);
|
|
3000
|
-
}
|
|
3001
|
-
const fullContext = {
|
|
3002
|
-
...context,
|
|
3003
|
-
includes,
|
|
3004
|
-
assets,
|
|
3005
|
-
rawAssets,
|
|
3006
|
-
errors: []
|
|
3007
|
-
};
|
|
3008
|
-
const converted = context.personResolver(fullContext, personEntry, customTypeEntry);
|
|
3009
|
-
if (converted?.icons && converted.icons.length > 0) {
|
|
3010
|
-
const processedIcons = await processIconsForSprite(converted.icons);
|
|
3011
|
-
converted.icons = processedIcons.length > 0 ? processedIcons : void 0;
|
|
3012
|
-
}
|
|
3013
|
-
if (fullContext.errors.length > 0 && typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
|
|
3014
|
-
console.error(`CMS conversion errors for person:`, {
|
|
3015
|
-
entryId: personEntry.sys.id,
|
|
3016
|
-
slug,
|
|
3017
|
-
errors: fullContext.errors
|
|
3018
|
-
});
|
|
3019
|
-
}
|
|
3020
|
-
return { data: converted, errors: fullContext.errors };
|
|
3021
|
-
} catch (error) {
|
|
3022
|
-
const entryId = personEntry?.sys.id || "unknown";
|
|
3023
|
-
const entryType = personEntry?.sys.contentType?.sys?.id;
|
|
3024
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown conversion error";
|
|
3025
|
-
const cmsError = {
|
|
3026
|
-
entryId,
|
|
3027
|
-
entryType,
|
|
3028
|
-
message: errorMessage,
|
|
3029
|
-
error
|
|
3030
|
-
};
|
|
3031
|
-
return { data: null, errors: [cmsError] };
|
|
3032
|
-
}
|
|
3033
|
-
};
|
|
3034
|
-
if (options?.retry) {
|
|
3035
|
-
return await withRetry(fetchFn, options.retry);
|
|
3036
|
-
}
|
|
3037
|
-
return await fetchFn();
|
|
3038
|
-
}
|
|
3039
|
-
|
|
3040
|
-
// src/api/preview.ts
|
|
3041
|
-
async function getPreviewEntryInfo(context, config, entryId, options) {
|
|
3042
|
-
const client = getContentfulClient(config, true);
|
|
3043
|
-
const response = await client.getEntries(
|
|
3044
|
-
{
|
|
3045
|
-
"sys.id": entryId,
|
|
3046
|
-
include: 1
|
|
3047
|
-
// Need 1 level for Article → ArticleType slug
|
|
3048
|
-
},
|
|
3049
|
-
{
|
|
3050
|
-
...options,
|
|
3051
|
-
preview: true
|
|
3052
|
-
}
|
|
3053
|
-
);
|
|
3054
|
-
const entry = response.items[0];
|
|
3055
|
-
if (!entry) {
|
|
3056
|
-
return null;
|
|
3057
|
-
}
|
|
3058
|
-
const contentTypeId = entry.sys.contentType.sys.id;
|
|
3059
|
-
const { fields } = entry;
|
|
3060
|
-
switch (contentTypeId) {
|
|
3061
|
-
case "page": {
|
|
3062
|
-
const slug = fields.slug;
|
|
3063
|
-
if (!slug) return null;
|
|
3064
|
-
return {
|
|
3065
|
-
contentType: "page",
|
|
3066
|
-
entryId,
|
|
3067
|
-
slug,
|
|
3068
|
-
href: context.urlCalculators.page(slug)
|
|
3069
|
-
};
|
|
3070
|
-
}
|
|
3071
|
-
case "pageVariant": {
|
|
3072
|
-
const slug = fields.slug;
|
|
3073
|
-
if (!slug) return null;
|
|
3074
|
-
return {
|
|
3075
|
-
contentType: "pageVariant",
|
|
3076
|
-
entryId,
|
|
3077
|
-
slug,
|
|
3078
|
-
href: context.urlCalculators.pageVariant(slug)
|
|
3079
|
-
};
|
|
3080
|
-
}
|
|
3081
|
-
case "article": {
|
|
3082
|
-
const slug = fields.slug;
|
|
3083
|
-
const articleTypeSlug = getArticleTypeSlug(entry, response.includes?.Entry);
|
|
3084
|
-
if (!slug || !articleTypeSlug) return null;
|
|
3085
|
-
return {
|
|
3086
|
-
contentType: "article",
|
|
3087
|
-
entryId,
|
|
3088
|
-
slug,
|
|
3089
|
-
articleTypeSlug,
|
|
3090
|
-
href: context.urlCalculators.article(articleTypeSlug, slug)
|
|
3091
|
-
};
|
|
3092
|
-
}
|
|
3093
|
-
case "articleType": {
|
|
3094
|
-
const slug = fields.slug;
|
|
3095
|
-
if (!slug) return null;
|
|
3096
|
-
return {
|
|
3097
|
-
contentType: "articleType",
|
|
3098
|
-
entryId,
|
|
3099
|
-
slug,
|
|
3100
|
-
href: context.urlCalculators.articleType(slug)
|
|
3101
|
-
};
|
|
3102
|
-
}
|
|
3103
|
-
case "tag": {
|
|
3104
|
-
const slug = fields.slug;
|
|
3105
|
-
if (!slug) return null;
|
|
3106
|
-
return {
|
|
3107
|
-
contentType: "tag",
|
|
3108
|
-
entryId,
|
|
3109
|
-
slug,
|
|
3110
|
-
href: context.urlCalculators.tag(slug)
|
|
3111
|
-
};
|
|
3112
|
-
}
|
|
3113
|
-
case "person": {
|
|
3114
|
-
const slug = fields.slug;
|
|
3115
|
-
if (!slug) return null;
|
|
3116
|
-
return {
|
|
3117
|
-
contentType: "person",
|
|
3118
|
-
entryId,
|
|
3119
|
-
slug,
|
|
3120
|
-
href: context.urlCalculators.person(slug)
|
|
3121
|
-
};
|
|
3122
|
-
}
|
|
3123
|
-
case "customType": {
|
|
3124
|
-
const slug = fields.slug;
|
|
3125
|
-
if (!slug) return null;
|
|
3126
|
-
return {
|
|
3127
|
-
contentType: "customType",
|
|
3128
|
-
entryId,
|
|
3129
|
-
slug,
|
|
3130
|
-
href: context.urlCalculators.customType(slug)
|
|
3131
|
-
};
|
|
3132
|
-
}
|
|
3133
|
-
default:
|
|
3134
|
-
return null;
|
|
3135
|
-
}
|
|
3136
|
-
}
|
|
3137
|
-
function getArticleTypeSlug(entry, includes) {
|
|
3138
|
-
const articleTypeField = entry.fields.articleType;
|
|
3139
|
-
if (!articleTypeField) {
|
|
3140
|
-
return void 0;
|
|
3141
|
-
}
|
|
3142
|
-
if (articleTypeField.fields?.slug) {
|
|
3143
|
-
return articleTypeField.fields.slug;
|
|
3144
|
-
}
|
|
3145
|
-
const articleTypeId = articleTypeField.sys?.id;
|
|
3146
|
-
if (!articleTypeId || !includes) {
|
|
3147
|
-
return void 0;
|
|
3148
|
-
}
|
|
3149
|
-
const articleTypeEntry = includes.find((inc) => inc.sys.id === articleTypeId);
|
|
3150
|
-
return articleTypeEntry?.fields?.slug;
|
|
3151
|
-
}
|
|
3152
|
-
|
|
3153
|
-
// src/api/related-articles.ts
|
|
3154
|
-
function filterRelatedArticles(articles, options) {
|
|
3155
|
-
const {
|
|
3156
|
-
excludeArticleIds,
|
|
3157
|
-
articleTypeIds: rawArticleTypeIds,
|
|
3158
|
-
tagIds: rawTagIds,
|
|
3159
|
-
authorIds: rawAuthorIds,
|
|
3160
|
-
before,
|
|
3161
|
-
after,
|
|
3162
|
-
count
|
|
3163
|
-
} = options;
|
|
3164
|
-
const excludeIds = excludeArticleIds ? [...new Set(excludeArticleIds)] : [];
|
|
3165
|
-
const articleTypeIds = rawArticleTypeIds ? [...new Set(rawArticleTypeIds)] : [];
|
|
3166
|
-
const tagIds = rawTagIds ? [...new Set(rawTagIds)] : [];
|
|
3167
|
-
const authorIds = rawAuthorIds ? [...new Set(rawAuthorIds)] : [];
|
|
3168
|
-
const scoredArticles = articles.map((article) => {
|
|
3169
|
-
let score = 0;
|
|
3170
|
-
if (excludeIds.length > 0 && excludeIds.includes(article.id)) {
|
|
3171
|
-
return null;
|
|
3172
|
-
}
|
|
3173
|
-
if (articleTypeIds.length > 0) {
|
|
3174
|
-
if (!article.articleType?.id || !articleTypeIds.includes(article.articleType.id)) {
|
|
3175
|
-
return null;
|
|
3176
|
-
}
|
|
3177
|
-
}
|
|
3178
|
-
if (article.date) {
|
|
3179
|
-
const articleDate = new Date(article.date);
|
|
3180
|
-
if (before && articleDate > before) {
|
|
3181
|
-
return null;
|
|
3182
|
-
}
|
|
3183
|
-
if (after && articleDate < after) {
|
|
3184
|
-
return null;
|
|
3185
|
-
}
|
|
3186
|
-
}
|
|
3187
|
-
if (tagIds.length > 0 && article.tags) {
|
|
3188
|
-
const articleTagIds = article.tags.map((tag) => tag.id);
|
|
3189
|
-
const matchingTags = tagIds.filter((tagId) => articleTagIds.includes(tagId));
|
|
3190
|
-
score += matchingTags.length * 10;
|
|
3191
|
-
}
|
|
3192
|
-
if (authorIds.length > 0 && article.author?.id) {
|
|
3193
|
-
if (authorIds.includes(article.author.id)) {
|
|
3194
|
-
score += 5;
|
|
3195
|
-
}
|
|
3196
|
-
}
|
|
3197
|
-
return { article, score };
|
|
3198
|
-
}).filter((item) => item !== null);
|
|
3199
|
-
scoredArticles.sort((a, b) => {
|
|
3200
|
-
if (b.score !== a.score) {
|
|
3201
|
-
return b.score - a.score;
|
|
3202
|
-
}
|
|
3203
|
-
if (a.article.date && b.article.date) {
|
|
3204
|
-
const articleDateA = new Date(a.article.date);
|
|
3205
|
-
const articleDateB = new Date(b.article.date);
|
|
3206
|
-
return articleDateB.getTime() - articleDateA.getTime();
|
|
3207
|
-
}
|
|
3208
|
-
return 0;
|
|
3209
|
-
});
|
|
3210
|
-
const topArticles = count !== void 0 ? scoredArticles.slice(0, count) : scoredArticles;
|
|
3211
|
-
return topArticles.map(({ article }) => article);
|
|
3212
|
-
}
|
|
3213
|
-
|
|
3214
|
-
// src/api/sitemap.ts
|
|
3215
|
-
function linksToSitemapEntries(links, sitemapConfig) {
|
|
3216
|
-
return links.filter((link) => link.indexed !== false && link.hidden !== true && link.href).map((link) => ({
|
|
3217
|
-
url: link.href,
|
|
3218
|
-
lastModified: link.lastModified,
|
|
3219
|
-
changeFrequency: sitemapConfig?.changeFrequency,
|
|
3220
|
-
priority: sitemapConfig?.priority
|
|
3221
|
-
}));
|
|
3222
|
-
}
|
|
3223
|
-
async function contentfulPageSitemapEntries(context, config, sitemapConfig, options) {
|
|
3224
|
-
const response = await contentfulAllPageLinks(context, config, options);
|
|
3225
|
-
return {
|
|
3226
|
-
data: linksToSitemapEntries(response.data, sitemapConfig),
|
|
3227
|
-
errors: response.errors
|
|
3228
|
-
};
|
|
3229
|
-
}
|
|
3230
|
-
async function contentfulArticleSitemapEntries(context, config, sitemapConfig, options) {
|
|
3231
|
-
const response = await contentfulAllArticleLinks(context, config, options);
|
|
3232
|
-
return {
|
|
3233
|
-
data: linksToSitemapEntries(response.data, sitemapConfig),
|
|
3234
|
-
errors: response.errors
|
|
3235
|
-
};
|
|
3236
|
-
}
|
|
3237
|
-
async function contentfulArticleTypeSitemapEntries(context, config, sitemapConfig, options) {
|
|
3238
|
-
const response = await contentfulAllArticleTypeLinks(context, config, options);
|
|
3239
|
-
return {
|
|
3240
|
-
data: linksToSitemapEntries(response.data, sitemapConfig),
|
|
3241
|
-
errors: response.errors
|
|
3242
|
-
};
|
|
3243
|
-
}
|
|
3244
|
-
async function contentfulTagSitemapEntries(context, config, sitemapConfig, options) {
|
|
3245
|
-
const response = await contentfulAllTagLinks(context, config, options);
|
|
3246
|
-
return {
|
|
3247
|
-
data: linksToSitemapEntries(response.data, sitemapConfig),
|
|
3248
|
-
errors: response.errors
|
|
3249
|
-
};
|
|
3250
|
-
}
|
|
3251
|
-
async function contentfulPersonSitemapEntries(context, config, sitemapConfig, options) {
|
|
3252
|
-
const response = await contentfulAllPersonLinks(context, config, options);
|
|
3253
|
-
return {
|
|
3254
|
-
data: linksToSitemapEntries(response.data, sitemapConfig),
|
|
3255
|
-
errors: response.errors
|
|
3256
|
-
};
|
|
3257
|
-
}
|
|
3258
|
-
async function getAllSitemapEntries(context, config, sitemapConfig, options) {
|
|
3259
|
-
const allEntries = [];
|
|
3260
|
-
const allErrors = [];
|
|
3261
|
-
const results = await Promise.all(
|
|
3262
|
-
sitemapConfig.providers.map((provider) => provider(context, config, options))
|
|
3263
|
-
);
|
|
3264
|
-
for (const result of results) {
|
|
3265
|
-
allEntries.push(...result.data);
|
|
3266
|
-
allErrors.push(...result.errors);
|
|
3267
|
-
}
|
|
3268
|
-
return { data: allEntries, errors: allErrors };
|
|
3269
|
-
}
|
|
3270
|
-
function createSitemapProvider(fetcher, sitemapConfig) {
|
|
3271
|
-
return (context, config, options) => fetcher(context, config, sitemapConfig, options);
|
|
3272
|
-
}
|
|
3273
|
-
|
|
3274
|
-
// src/api/tag.ts
|
|
3275
|
-
init_utils();
|
|
3276
|
-
async function contentfulTagRest(context, config, slug, options) {
|
|
3277
|
-
const client = getContentfulClient(config, options.preview);
|
|
3278
|
-
const tagCacheTags = getCacheTags("tag", slug, options?.preview);
|
|
3279
|
-
const customTypeCacheTags = options?.customType ? getCacheTags("customType", options.customType, options?.preview) : [];
|
|
3280
|
-
const requestOptions = {
|
|
3281
|
-
...options,
|
|
3282
|
-
next: {
|
|
3283
|
-
...options?.next,
|
|
3284
|
-
tags: [...tagCacheTags, ...customTypeCacheTags]
|
|
3285
|
-
}
|
|
3286
|
-
};
|
|
3287
|
-
const fetchFn = async () => {
|
|
3288
|
-
const tagPromise = client.getEntries(
|
|
3289
|
-
{
|
|
3290
|
-
content_type: "tag",
|
|
3291
|
-
"fields.slug": slug,
|
|
3292
|
-
include: 10,
|
|
3293
|
-
locale: options?.locale,
|
|
3294
|
-
limit: 1
|
|
3295
|
-
},
|
|
3296
|
-
requestOptions
|
|
3297
|
-
);
|
|
3298
|
-
const customTypePromise = options?.customType ? client.getEntries(
|
|
3299
|
-
{
|
|
3300
|
-
content_type: "customType",
|
|
3301
|
-
"fields.slug": options.customType,
|
|
3302
|
-
include: 10,
|
|
3303
|
-
locale: options?.locale,
|
|
3304
|
-
limit: 1
|
|
3305
|
-
},
|
|
3306
|
-
requestOptions
|
|
3307
|
-
) : Promise.resolve(null);
|
|
3308
|
-
const [tagResponse, customTypeResponse] = await Promise.all([tagPromise, customTypePromise]);
|
|
3309
|
-
const tagEntry = tagResponse.items[0];
|
|
3310
|
-
if (!tagEntry || !tagEntry.fields) {
|
|
3311
|
-
return { data: null, errors: [] };
|
|
3312
|
-
}
|
|
3313
|
-
const customTypeEntry = customTypeResponse?.items[0];
|
|
3314
|
-
try {
|
|
3315
|
-
const assets = convertAllAssets(tagResponse, context);
|
|
3316
|
-
const rawAssets = convertAllRawAssets(tagResponse);
|
|
3317
|
-
const includes = convertAllIncludes(tagResponse);
|
|
3318
|
-
if (customTypeResponse) {
|
|
3319
|
-
const customAssets = convertAllAssets(customTypeResponse, context);
|
|
3320
|
-
const customRawAssets = convertAllRawAssets(customTypeResponse);
|
|
3321
|
-
const customIncludes = convertAllIncludes(customTypeResponse);
|
|
3322
|
-
for (const [key, value] of customAssets) assets.set(key, value);
|
|
3323
|
-
for (const [key, value] of customRawAssets) rawAssets.set(key, value);
|
|
3324
|
-
for (const [key, value] of customIncludes) includes.set(key, value);
|
|
3325
|
-
}
|
|
3326
|
-
const fullContext = {
|
|
3327
|
-
...context,
|
|
3328
|
-
includes,
|
|
3329
|
-
assets,
|
|
3330
|
-
rawAssets,
|
|
3331
|
-
errors: []
|
|
3332
|
-
};
|
|
3333
|
-
const converted = context.tagResolver(fullContext, tagEntry, customTypeEntry);
|
|
3334
|
-
if (converted?.icons && converted.icons.length > 0) {
|
|
3335
|
-
const processedIcons = await processIconsForSprite(converted.icons);
|
|
3336
|
-
converted.icons = processedIcons.length > 0 ? processedIcons : void 0;
|
|
3337
|
-
}
|
|
3338
|
-
if (fullContext.errors.length > 0 && typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
|
|
3339
|
-
console.error(`CMS conversion errors for tag:`, {
|
|
3340
|
-
entryId: tagEntry.sys.id,
|
|
3341
|
-
slug,
|
|
3342
|
-
errors: fullContext.errors
|
|
3343
|
-
});
|
|
3344
|
-
}
|
|
3345
|
-
return { data: converted, errors: fullContext.errors };
|
|
3346
|
-
} catch (error) {
|
|
3347
|
-
const entryId = tagEntry?.sys.id || "unknown";
|
|
3348
|
-
const entryType = tagEntry?.sys.contentType?.sys?.id;
|
|
3349
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown conversion error";
|
|
3350
|
-
const cmsError = {
|
|
3351
|
-
entryId,
|
|
3352
|
-
entryType,
|
|
3353
|
-
message: errorMessage,
|
|
3354
|
-
error
|
|
3355
|
-
};
|
|
3356
|
-
return { data: null, errors: [cmsError] };
|
|
3357
|
-
}
|
|
3358
|
-
};
|
|
3359
|
-
if (options?.retry) {
|
|
3360
|
-
return await withRetry(fetchFn, options.retry);
|
|
3361
|
-
}
|
|
3362
|
-
return await fetchFn();
|
|
3363
|
-
}
|
|
3364
|
-
|
|
3365
|
-
// src/api/template.ts
|
|
3366
|
-
function templateConverter(context, entry) {
|
|
3367
|
-
const { fields, sys } = entry;
|
|
3368
|
-
const id = sys.id;
|
|
3369
|
-
const preContent = fields.preContent?.map((content) => resolvePageContent(context, id, content)).filter((item) => item !== null) ?? [];
|
|
3370
|
-
const postContent = fields.postContent?.map((content) => resolvePageContent(context, id, content)).filter((item) => item !== null) ?? [];
|
|
3371
|
-
const menu = fields.menu ? resolveNavigation(context, fields.menu) : void 0;
|
|
3372
|
-
const footer = fields.footer ? resolveNavigation(context, fields.footer) : void 0;
|
|
3373
|
-
const stickyNav = fields.flags?.includes("Sticky nav") ?? false;
|
|
3374
|
-
const contentIcons = collectIconsFromContent([...preContent, ...postContent]);
|
|
3375
|
-
const menuIcons = collectIconsFromNavigation(menu);
|
|
3376
|
-
const footerIcons = collectIconsFromNavigation(footer);
|
|
3377
|
-
const allIcons = [...contentIcons, ...menuIcons, ...footerIcons];
|
|
3378
|
-
const seenIconIds = /* @__PURE__ */ new Set();
|
|
3379
|
-
const icons = allIcons.filter((icon) => {
|
|
3380
|
-
if (seenIconIds.has(icon.id)) {
|
|
3381
|
-
return false;
|
|
3382
|
-
}
|
|
3383
|
-
seenIconIds.add(icon.id);
|
|
3384
|
-
return true;
|
|
3385
|
-
});
|
|
3386
|
-
return {
|
|
3387
|
-
id,
|
|
3388
|
-
cmsLabel: fields.cmsLabel,
|
|
3389
|
-
preContent,
|
|
3390
|
-
postContent,
|
|
3391
|
-
menu,
|
|
3392
|
-
footer,
|
|
3393
|
-
backgroundColour: fields.backgroundColour,
|
|
3394
|
-
textColour: fields.textColour,
|
|
3395
|
-
stickyNav,
|
|
3396
|
-
icons: icons.length > 0 ? icons : void 0
|
|
3397
|
-
};
|
|
3398
|
-
}
|
|
3399
|
-
async function contentfulTemplateRest(context, config, cmsLabel, options) {
|
|
3400
|
-
return fetchSingleEntity(
|
|
3401
|
-
context,
|
|
3402
|
-
config,
|
|
3403
|
-
{
|
|
3404
|
-
contentType: "template",
|
|
3405
|
-
cacheTagType: "template",
|
|
3406
|
-
cacheTagIdentifier: cmsLabel,
|
|
3407
|
-
query: { "fields.cmsLabel": cmsLabel },
|
|
3408
|
-
resolver: (ctx, entry) => templateConverter(ctx, entry),
|
|
3409
|
-
errorLogContext: { cmsLabel }
|
|
3410
|
-
},
|
|
3411
|
-
options
|
|
3412
|
-
);
|
|
3413
|
-
}
|
|
3414
|
-
|
|
3415
|
-
// src/revalidation/handlers.ts
|
|
3416
|
-
init_tags();
|
|
3417
|
-
init_utils();
|
|
3418
|
-
var defaultLocale = "en-US";
|
|
3419
|
-
var articleTypeHandler = {
|
|
3420
|
-
extract: (data) => ({
|
|
3421
|
-
slug: data.fields?.slug?.[defaultLocale]
|
|
3422
|
-
}),
|
|
3423
|
-
makeTags: (extracted) => [extracted.slug ? articleTypeIndexTag(extracted.slug) : void 0],
|
|
3424
|
-
getGlobalTags: () => [ArticleTypeTag, ArticleTypeIndexTag]
|
|
3425
|
-
};
|
|
3426
|
-
var articleHandler = {
|
|
3427
|
-
extract: (data) => ({
|
|
3428
|
-
slug: data.fields?.slug?.[defaultLocale]
|
|
3429
|
-
}),
|
|
3430
|
-
makeTags: (extracted) => [extracted?.slug ? articleTag(extracted.slug) : void 0],
|
|
3431
|
-
getGlobalTags: () => [ArticleTag],
|
|
3432
|
-
rebuildSearch: true
|
|
3433
|
-
};
|
|
3434
|
-
var pageVariantHandler = {
|
|
3435
|
-
extract: (data) => {
|
|
3436
|
-
const pageVariantSlug = data.fields?.slug?.[defaultLocale];
|
|
3437
|
-
return pageVariantSlug;
|
|
3438
|
-
},
|
|
3439
|
-
makeTags: (extracted) => [extracted ? pageTag(extracted) : void 0],
|
|
3440
|
-
getGlobalTags: () => [PageTag]
|
|
3441
|
-
};
|
|
3442
|
-
var templateHandler = {
|
|
3443
|
-
extract: (data) => data.fields?.cmsLabel?.[defaultLocale],
|
|
3444
|
-
makeTags: (extracted) => [extracted ? templateTag(extracted) : void 0],
|
|
3445
|
-
getGlobalTags: () => [TemplateTag]
|
|
3446
|
-
};
|
|
3447
|
-
var contentTypeHandlers = {
|
|
3448
|
-
page: {
|
|
3449
|
-
extract: (data) => data.fields?.slug?.[defaultLocale],
|
|
3450
|
-
makeTags: (extracted) => [extracted ? pageTag(extracted) : void 0],
|
|
3451
|
-
getGlobalTags: () => [PageTag]
|
|
3452
|
-
},
|
|
3453
|
-
pageVariant: pageVariantHandler,
|
|
3454
|
-
// biome-ignore lint/suspicious/noExplicitAny: Any is ok for handlers with different types
|
|
3455
|
-
article: articleHandler,
|
|
3456
|
-
// biome-ignore lint/suspicious/noExplicitAny: Any is ok for handlers with different types
|
|
3457
|
-
articleType: articleTypeHandler,
|
|
3458
|
-
person: {
|
|
3459
|
-
extract: (data) => data.fields?.slug?.[defaultLocale],
|
|
3460
|
-
makeTags: (extracted) => [extracted ? personTag(extracted) : void 0],
|
|
3461
|
-
getGlobalTags: () => [PersonTag]
|
|
3462
|
-
},
|
|
3463
|
-
tag: {
|
|
3464
|
-
extract: (data) => data.fields?.slug?.[defaultLocale],
|
|
3465
|
-
makeTags: (extracted) => [extracted ? tagTag(extracted) : void 0],
|
|
3466
|
-
getGlobalTags: () => [TagTag]
|
|
3467
|
-
},
|
|
3468
|
-
// biome-ignore lint/suspicious/noExplicitAny: Any is ok for handlers with different types
|
|
3469
|
-
template: templateHandler,
|
|
3470
|
-
customType: {
|
|
3471
|
-
extract: () => void 0,
|
|
3472
|
-
makeTags: () => void 0,
|
|
3473
|
-
getGlobalTags: () => [CustomTypeTag]
|
|
3474
|
-
},
|
|
3475
|
-
navigation: {
|
|
3476
|
-
extract: () => void 0,
|
|
3477
|
-
makeTags: () => void 0,
|
|
3478
|
-
getGlobalTags: () => [NavigationTag]
|
|
3479
|
-
},
|
|
3480
|
-
banner: {
|
|
3481
|
-
extract: () => void 0,
|
|
3482
|
-
makeTags: () => void 0,
|
|
3483
|
-
getGlobalTags: () => [BannerTag]
|
|
3484
|
-
},
|
|
3485
|
-
location: {
|
|
3486
|
-
extract: (data) => data.fields?.slug?.[defaultLocale],
|
|
3487
|
-
makeTags: (extracted) => [extracted ? locationTag(extracted) : void 0],
|
|
3488
|
-
getGlobalTags: () => [LocationTag]
|
|
3489
|
-
}
|
|
3490
|
-
};
|
|
3491
|
-
async function revalidateAsset(content, message, preview = false) {
|
|
3492
|
-
const id = content.sys.id;
|
|
3493
|
-
const fileName = content.fields?.file?.[defaultLocale]?.fileName;
|
|
3494
|
-
const tags = [assetTag(id), fileName ? assetTag(fileName) : void 0, AssetTag].filter(
|
|
3495
|
-
(tag) => tag !== void 0
|
|
3496
|
-
);
|
|
3497
|
-
if (preview) {
|
|
3498
|
-
await revalidateSingleTag(GlobalTag, message);
|
|
3499
|
-
} else {
|
|
3500
|
-
await revalidateTags(tags, message);
|
|
3501
|
-
}
|
|
3502
|
-
return true;
|
|
3503
|
-
}
|
|
3504
|
-
async function revalidateEntry(handler, content, contentType, preview = false) {
|
|
3505
|
-
const extracted = await Promise.resolve(handler.extract(content));
|
|
3506
|
-
const tags = handler.makeTags(extracted);
|
|
3507
|
-
const validTags = tags?.filter((tag) => tag !== void 0) ?? [];
|
|
3508
|
-
const globalTags = handler.getGlobalTags();
|
|
3509
|
-
const tagsToRevalidate = [...validTags, ...globalTags];
|
|
3510
|
-
if (preview) {
|
|
3511
|
-
await revalidateSingleTag(GlobalTag, `publish ${contentType} (preview mode)`);
|
|
3512
|
-
} else {
|
|
3513
|
-
await revalidateTags(tagsToRevalidate, `publish ${contentType}`);
|
|
3514
|
-
}
|
|
3515
|
-
return true;
|
|
3516
|
-
}
|
|
3517
|
-
async function revalidateDeletedEntry(contentType, preview = false) {
|
|
3518
|
-
const { getCacheTagsForProduction: getCacheTagsForProduction2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
|
|
3519
|
-
const tags = getCacheTagsForProduction2(contentType);
|
|
3520
|
-
if (preview) {
|
|
3521
|
-
await revalidateSingleTag(GlobalTag, `delete ${contentType} (preview mode)`);
|
|
3522
|
-
} else {
|
|
3523
|
-
await revalidateTags(tags, `delete ${contentType}`);
|
|
3524
|
-
}
|
|
3525
|
-
return true;
|
|
3526
|
-
}
|
|
3527
|
-
async function revalidateDeletedAsset(assetId, preview = false) {
|
|
3528
|
-
const tags = [assetTag(assetId), AssetTag];
|
|
3529
|
-
if (preview) {
|
|
3530
|
-
await revalidateSingleTag(GlobalTag, `delete asset ${assetId} (preview mode)`);
|
|
3531
|
-
} else {
|
|
3532
|
-
await revalidateTags(tags, `delete asset ${assetId}`);
|
|
3533
|
-
}
|
|
3534
|
-
return true;
|
|
3535
|
-
}
|
|
3536
|
-
init_tags();
|
|
3537
|
-
init_utils();
|
|
3538
|
-
async function publishAsset(content, preview) {
|
|
3539
|
-
return await revalidateAsset(content, "publish asset", preview);
|
|
3540
|
-
}
|
|
3541
|
-
async function deleteAsset(content, preview) {
|
|
3542
|
-
return await revalidateDeletedAsset(content.sys.id, preview);
|
|
3543
|
-
}
|
|
3544
|
-
async function publishEntry(content, preview) {
|
|
3545
|
-
const contentType = content.sys.contentType.sys.id;
|
|
3546
|
-
const handler = contentTypeHandlers[contentType];
|
|
3547
|
-
if (!handler) {
|
|
3548
|
-
console.warn(`Unknown content type: ${contentType}`);
|
|
3549
|
-
return false;
|
|
3550
|
-
}
|
|
3551
|
-
return await revalidateEntry(handler, content, contentType, preview);
|
|
3552
|
-
}
|
|
3553
|
-
async function deleteEntry(content, preview) {
|
|
3554
|
-
console.log(`Delete entry: ${content.sys.id}`);
|
|
3555
|
-
const contentType = content.sys.contentType.sys.id;
|
|
3556
|
-
const success = await revalidateDeletedEntry(contentType, preview);
|
|
3557
|
-
return success;
|
|
3558
|
-
}
|
|
3559
|
-
async function revalidateAll(preview) {
|
|
3560
|
-
console.log("Revalidating all tags");
|
|
3561
|
-
await delay2(500);
|
|
3562
|
-
if (preview) {
|
|
3563
|
-
await revalidateSingleTag(GlobalTag, "global revalidation");
|
|
3564
|
-
} else {
|
|
3565
|
-
for (const tag of AllTags) {
|
|
3566
|
-
await revalidateSingleTag(tag, "global revalidation");
|
|
3567
|
-
}
|
|
3568
|
-
}
|
|
3569
|
-
return true;
|
|
3570
|
-
}
|
|
3571
|
-
function delay2(ms) {
|
|
3572
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3573
|
-
}
|
|
3574
|
-
async function handleRevalidation(request, preview = false) {
|
|
3575
|
-
try {
|
|
3576
|
-
const all = request.headers.get("REVALIDATE_ALL");
|
|
3577
|
-
const single = request.headers.get("REVALIDATION_TAG");
|
|
3578
|
-
if (all) {
|
|
3579
|
-
const success2 = await revalidateAll(preview);
|
|
3580
|
-
return NextResponse.json({ revalidated: success2, now: Date.now() }, { status: 200 });
|
|
3581
|
-
}
|
|
3582
|
-
if (single) {
|
|
3583
|
-
const success2 = await revalidateSingleTag(single, "single tag revalidation");
|
|
3584
|
-
return NextResponse.json({ revalidated: success2, now: Date.now() }, { status: 200 });
|
|
3585
|
-
}
|
|
3586
|
-
const content = await request.json();
|
|
3587
|
-
let success = false;
|
|
3588
|
-
switch (content.sys.type) {
|
|
3589
|
-
case "DeletedAsset":
|
|
3590
|
-
success = await deleteAsset(content, preview);
|
|
3591
|
-
break;
|
|
3592
|
-
case "DeletedEntry":
|
|
3593
|
-
success = await deleteEntry(content, preview);
|
|
3594
|
-
break;
|
|
3595
|
-
case "Asset":
|
|
3596
|
-
success = await publishAsset(content, preview);
|
|
3597
|
-
break;
|
|
3598
|
-
case "Entry":
|
|
3599
|
-
success = await publishEntry(content, preview);
|
|
3600
|
-
break;
|
|
3601
|
-
default:
|
|
3602
|
-
console.warn(`Unknown message type: ${content.sys.type}`);
|
|
3603
|
-
return NextResponse.json({ message: "Unknown message type" }, { status: 400 });
|
|
3604
|
-
}
|
|
3605
|
-
if (!success) {
|
|
3606
|
-
return NextResponse.json({ message: "Unknown content type" }, { status: 400 });
|
|
3607
|
-
}
|
|
3608
|
-
return NextResponse.json({ revalidated: success, now: Date.now() }, { status: 200 });
|
|
3609
|
-
} catch (error) {
|
|
3610
|
-
console.error("Error in revalidation:", error);
|
|
3611
|
-
return NextResponse.json({ message: "Error in revalidation" }, { status: 500 });
|
|
3612
|
-
}
|
|
3613
|
-
}
|
|
3614
|
-
|
|
3615
|
-
// src/revalidation/nextjs-route.ts
|
|
3616
|
-
function createRevalidationHandler(config = {}) {
|
|
3617
|
-
return async function POST(request) {
|
|
3618
|
-
const { secret, validateSecret = true, preview = false } = config;
|
|
3619
|
-
if (validateSecret && secret) {
|
|
3620
|
-
const requestSecret = request.headers.get("REVALIDATION_SECRET");
|
|
3621
|
-
if (requestSecret !== secret) {
|
|
3622
|
-
return NextResponse.json({ message: "Invalid secret" }, { status: 401 });
|
|
3623
|
-
}
|
|
3624
|
-
}
|
|
3625
|
-
return await handleRevalidation(request, preview);
|
|
3626
|
-
};
|
|
3627
|
-
}
|
|
3628
|
-
|
|
3629
|
-
// src/revalidation/index.ts
|
|
3630
|
-
init_tags();
|
|
3631
|
-
init_utils();
|
|
3632
|
-
|
|
3633
|
-
export { AllTags, ArticleTag, ArticleTypeTag, AssetTag, AuthenticationError, BannerTag, ContentfulError, CustomTypeTag, EntryNotFoundError, GlobalTag, LocationTag, NavigationTag, PageTag, PersonTag, RateLimitError, RateLimiter, TagTag, TemplateTag, ValidationError, arrayOrUndefined, articleTag, articleTypeIndexTag, articleTypeTag, assetTag, basePageConverter, buildFetchOptions, calculateBackoffDelay, calculatePageHref, calculatePageVariantHref, contentfulAllArticleLinks, contentfulAllArticleTypeLinks, contentfulAllPageLinks, contentfulAllPersonLinks, contentfulAllTagLinks, contentfulArticleRest, contentfulArticleSitemapEntries, contentfulArticleTypeRest, contentfulArticleTypeSitemapEntries, contentfulAssetRest, contentfulCustomTypeRest, contentfulPageRest, contentfulPageSitemapEntries, contentfulPersonRest, contentfulPersonSitemapEntries, contentfulTagRest, contentfulTagSitemapEntries, contentfulTemplateRest, createBaseConverterContext, createContentfulClient, createContentfulPreviewClient, createDownloadHandler, createResponsiveVisual, createRevalidationHandler, createSitemapProvider, customTypeTag, filterRelatedArticles, getAllSitemapEntries, getCacheTags, getCacheTagsForPreview, getCacheTagsForProduction, getContentfulClient, getContentfulConfig, getPreviewEntryInfo, getRetryAfter, isBrowserViewable, isContentfulError, isRateLimitError, isRetryableError, isValidDate, locationTag, lookupAsset, notEmpty, pageTag, personTag, resolveLink, resolveLinks, resolveRichTextDocument, revalidateSingleTag, revalidateTags, safeDate, tagTag, templateTag, withRetry };
|
|
3634
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
/**
|
|
2
|
+
* @se-studio/contentful-rest-api
|
|
3
|
+
*
|
|
4
|
+
* Type-safe Contentful REST API client with caching and rate limiting
|
|
5
|
+
* for Next.js applications.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { contentfulPageRest, createContentfulClient } from '@se-studio/contentful-rest-api';
|
|
10
|
+
*
|
|
11
|
+
* const page = await contentfulPageRest(
|
|
12
|
+
* {
|
|
13
|
+
* spaceId: process.env.CONTENTFUL_SPACE_ID!,
|
|
14
|
+
* accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!,
|
|
15
|
+
* },
|
|
16
|
+
* 'home'
|
|
17
|
+
* );
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
// API functions
|
|
21
|
+
export { buildFetchOptions, contentfulAllArticleLinks, contentfulAllArticleTypeLinks, contentfulAllPageLinks, contentfulAllPersonLinks, contentfulAllTagLinks, contentfulArticleRest, contentfulArticleSitemapEntries, contentfulArticleTypeRest, contentfulArticleTypeSitemapEntries,
|
|
22
|
+
// Asset/download functions
|
|
23
|
+
contentfulAssetRest, contentfulCustomTypeRest, contentfulPageRest, contentfulPageSitemapEntries, contentfulPersonRest, contentfulPersonSitemapEntries, contentfulTagRest, contentfulTagSitemapEntries, contentfulTemplateRest, createBaseConverterContext, createSitemapProvider, filterRelatedArticles, getAllSitemapEntries, getContentfulConfig,
|
|
24
|
+
// Preview
|
|
25
|
+
getPreviewEntryInfo, isBrowserViewable, } from './api';
|
|
26
|
+
// Client exports
|
|
27
|
+
export { createContentfulClient, createContentfulPreviewClient, getContentfulClient, } from './client';
|
|
28
|
+
// Converter exports
|
|
29
|
+
export { basePageConverter, calculatePageHref, calculatePageVariantHref, createResponsiveVisual, lookupAsset, resolveLink, resolveLinks, resolveRichTextDocument, } from './converters';
|
|
30
|
+
// Revalidation utilities
|
|
31
|
+
export { AllTags, ArticleTag, ArticleTypeTag, AssetTag, articleTag, articleTypeIndexTag, articleTypeTag, assetTag, BannerTag, CustomTypeTag, customTypeTag, GlobalTag, getCacheTags,
|
|
32
|
+
// Utility functions
|
|
33
|
+
getCacheTagsForPreview, getCacheTagsForProduction, LocationTag, locationTag, NavigationTag,
|
|
34
|
+
// Tag constants and functions
|
|
35
|
+
PageTag, PersonTag, pageTag, personTag, TagTag, TemplateTag, tagTag, templateTag, } from './revalidation';
|
|
36
|
+
// Error handling and retry utilities
|
|
37
|
+
export { AuthenticationError, arrayOrUndefined, ContentfulError, calculateBackoffDelay, EntryNotFoundError, getRetryAfter, isContentfulError, isRateLimitError, isRetryableError, isValidDate, notEmpty, RateLimitError, RateLimiter, safeDate, ValidationError, withRetry, } from './utils';
|
|
3635
38
|
//# sourceMappingURL=index.js.map
|