@se-studio/contentful-rest-api 1.0.32 → 1.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +50 -7
- package/dist/index.js +182 -18
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IBaseArticle, IBaseArticleType, BaseCollectionContent, IBaseCollection, IBaseComponent, IBaseCustomType, INavigationItem, IBasePage, IBaseTag, IInternalLink, IVisual, IBasePerson, INavigation, IArticleLink, IArticleTypeLink, ITyped, ISvgImage, IResponsiveVisual } from '@se-studio/core-data-types';
|
|
2
2
|
import * as contentful from 'contentful';
|
|
3
|
-
import { EntrySkeletonType, EntryFieldTypes, Entry, UnresolvedLink } from 'contentful';
|
|
3
|
+
import { EntrySkeletonType, EntryFieldTypes, Entry, Asset, UnresolvedLink } from 'contentful';
|
|
4
4
|
import { Document } from '@contentful/rich-text-types';
|
|
5
5
|
import { NextRequest, NextResponse } from 'next/server';
|
|
6
6
|
|
|
@@ -185,6 +185,7 @@ interface BaseArticleFields {
|
|
|
185
185
|
redirectTo?: EntryFieldTypes.Symbol;
|
|
186
186
|
structuredData?: EntryFieldTypes.Array<EntryFieldTypes.EntryLink<BaseSchemaSkeleton>>;
|
|
187
187
|
externalLink?: EntryFieldTypes.Symbol;
|
|
188
|
+
download?: EntryFieldTypes.AssetLink;
|
|
188
189
|
flags?: EntryFieldTypes.Array<EntryFieldTypes.Symbol<'Sticky nav'>>;
|
|
189
190
|
}
|
|
190
191
|
type BaseArticleSkeleton = EntrySkeletonType<BaseArticleFields, 'article'>;
|
|
@@ -406,7 +407,7 @@ type LinkResolverMap = Map<string, LinkResolverFunction>;
|
|
|
406
407
|
type UrlCalculators = {
|
|
407
408
|
page: (slug: string) => string;
|
|
408
409
|
pageVariant: (slug: string) => string;
|
|
409
|
-
article: (articleTypeSlug: string, slug: string) => string;
|
|
410
|
+
article: (articleTypeSlug: string, slug: string, primaryTagSlug?: string) => string;
|
|
410
411
|
articleType: (slug: string) => string;
|
|
411
412
|
tag: (slug: string) => string;
|
|
412
413
|
person: (slug: string) => string;
|
|
@@ -430,6 +431,7 @@ type BaseConverterContext = {
|
|
|
430
431
|
type ConverterContext = BaseConverterContext & {
|
|
431
432
|
includes: Map<string, PossibleResolvedEntry>;
|
|
432
433
|
assets: Map<string, IVisual>;
|
|
434
|
+
rawAssets: Map<string, Asset>;
|
|
433
435
|
errors: CmsError[];
|
|
434
436
|
};
|
|
435
437
|
|
|
@@ -437,10 +439,6 @@ declare function contentfulArticleRest(context: BaseConverterContext, config: Co
|
|
|
437
439
|
|
|
438
440
|
declare function contentfulArticleTypeRest(context: BaseConverterContext, config: ContentfulConfig, indexPageSlug: string, options?: FetchOptions): Promise<CmsResponse<IBaseArticleType | null>>;
|
|
439
441
|
|
|
440
|
-
declare function createBaseConverterContext(): BaseConverterContext;
|
|
441
|
-
|
|
442
|
-
declare function contentfulCustomTypeRest(context: BaseConverterContext, config: ContentfulConfig, indexPageSlug: string, options?: FetchOptions): Promise<CmsResponse<IBaseCustomType | null>>;
|
|
443
|
-
|
|
444
442
|
interface ContentfulResponse<T = any> {
|
|
445
443
|
sys: {
|
|
446
444
|
type: 'Array';
|
|
@@ -454,6 +452,31 @@ interface ContentfulResponse<T = any> {
|
|
|
454
452
|
Asset?: any[];
|
|
455
453
|
};
|
|
456
454
|
}
|
|
455
|
+
interface ContentfulAssetFields {
|
|
456
|
+
title?: string;
|
|
457
|
+
description?: string;
|
|
458
|
+
file?: {
|
|
459
|
+
url: string;
|
|
460
|
+
fileName: string;
|
|
461
|
+
contentType: string;
|
|
462
|
+
details?: {
|
|
463
|
+
size?: number;
|
|
464
|
+
image?: {
|
|
465
|
+
width: number;
|
|
466
|
+
height: number;
|
|
467
|
+
};
|
|
468
|
+
};
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
interface ContentfulAssetResponse {
|
|
472
|
+
sys: {
|
|
473
|
+
id: string;
|
|
474
|
+
type: 'Asset';
|
|
475
|
+
createdAt: string;
|
|
476
|
+
updatedAt: string;
|
|
477
|
+
};
|
|
478
|
+
fields: ContentfulAssetFields;
|
|
479
|
+
}
|
|
457
480
|
interface ContentfulQuery {
|
|
458
481
|
content_type?: string;
|
|
459
482
|
locale?: string;
|
|
@@ -469,11 +492,31 @@ declare class ContentfulFetchClient {
|
|
|
469
492
|
private readonly isPreview;
|
|
470
493
|
constructor(config: ContentfulConfig, preview?: boolean);
|
|
471
494
|
getEntries<T = any>(query: ContentfulQuery, options?: FetchOptions): Promise<ContentfulResponse<T>>;
|
|
495
|
+
getAsset(assetId: string, options?: FetchOptions): Promise<ContentfulAssetResponse | null>;
|
|
472
496
|
}
|
|
473
497
|
declare function createContentfulClient(config: ContentfulConfig): ContentfulFetchClient;
|
|
474
498
|
declare function createContentfulPreviewClient(config: ContentfulConfig): ContentfulFetchClient;
|
|
475
499
|
declare function getContentfulClient(config: ContentfulConfig, preview?: boolean): ContentfulFetchClient;
|
|
476
500
|
|
|
501
|
+
declare function isBrowserViewable(contentType: string): boolean;
|
|
502
|
+
declare function contentfulAssetRest(config: ContentfulConfig, assetId: string, options?: FetchOptions): Promise<ContentfulAssetResponse | null>;
|
|
503
|
+
interface DownloadHandlerConfig {
|
|
504
|
+
getConfig: (preview?: boolean) => ContentfulConfig;
|
|
505
|
+
revalidate?: number;
|
|
506
|
+
preview?: boolean;
|
|
507
|
+
}
|
|
508
|
+
interface DownloadRouteParams {
|
|
509
|
+
params: Promise<{
|
|
510
|
+
assetId: string;
|
|
511
|
+
filename: string;
|
|
512
|
+
}>;
|
|
513
|
+
}
|
|
514
|
+
declare function createDownloadHandler(config: DownloadHandlerConfig): (_request: NextRequest, { params }: DownloadRouteParams) => Promise<Response>;
|
|
515
|
+
|
|
516
|
+
declare function createBaseConverterContext(): BaseConverterContext;
|
|
517
|
+
|
|
518
|
+
declare function contentfulCustomTypeRest(context: BaseConverterContext, config: ContentfulConfig, indexPageSlug: string, options?: FetchOptions): Promise<CmsResponse<IBaseCustomType | null>>;
|
|
519
|
+
|
|
477
520
|
type IContentfulPerson = IBasePerson & {
|
|
478
521
|
bio?: IContentfulRichText | null;
|
|
479
522
|
};
|
|
@@ -653,4 +696,4 @@ declare class RateLimiter {
|
|
|
653
696
|
getIntervalMs(): number;
|
|
654
697
|
}
|
|
655
698
|
|
|
656
|
-
export { AllTags, ArticleTag, ArticleTypeTag, AssetTag, AuthenticationError, BannerTag, type BaseConverterContext, type CmsError, type CmsResponse, type ContentResolverFunction, ContentfulFetchClient as ContentfulClient, type ContentfulConfig, ContentfulError, ContentfulFetchClient, type ConverterContext, CustomTypeTag, type DefaultChainModifier, EntryNotFoundError, type FetchOptions, GlobalTag, type IContentfulCollection, type IContentfulComponent, type IContentfulPerson, type IContentfulRichText, type IFetchedTemplate, type ISitemapEntry, LocationTag, NavigationTag, PageTag, PersonTag, type PreviewContentType, type PreviewEntryInfo, RateLimitError, RateLimiter, type RelatedArticlesOptions, type RetryConfig, type RevalidationConfig, type SitemapChangeFrequency, type SitemapConfig, type SitemapContentTypeConfig, type SitemapEntryProvider, TagTag, TemplateTag, type UrlCalculators, ValidationError, arrayOrUndefined, articleTag, articleTypeIndexTag, articleTypeTag, assetTag, basePageConverter, calculateBackoffDelay, contentfulAllArticleLinks, contentfulAllArticleTypeLinks, contentfulAllPageLinks, contentfulAllPersonLinks, contentfulAllTagLinks, contentfulArticleRest, contentfulArticleSitemapEntries, contentfulArticleTypeRest, contentfulArticleTypeSitemapEntries, contentfulCustomTypeRest, contentfulPageRest, contentfulPageSitemapEntries, contentfulPersonSitemapEntries, contentfulTagRest, contentfulTagSitemapEntries, contentfulTemplateRest, createBaseConverterContext, createContentfulClient, createContentfulPreviewClient, createResponsiveVisual, createRevalidationHandler, createSitemapProvider, customTypeTag, filterRelatedArticles, getAllSitemapEntries, getCacheTags, getCacheTagsForPreview, getCacheTagsForProduction, getContentfulClient, getPreviewEntryInfo, getRetryAfter, isContentfulError, isRateLimitError, isRetryableError, isValidDate, locationTag, lookupAsset, notEmpty, pageTag, personTag, resolveLink, resolveLinks, resolveRichTextDocument, revalidateSingleTag, revalidateTags, safeDate, tagTag, templateTag, withRetry };
|
|
699
|
+
export { AllTags, ArticleTag, ArticleTypeTag, AssetTag, AuthenticationError, BannerTag, type BaseConverterContext, type CmsError, type CmsResponse, type ContentResolverFunction, ContentfulFetchClient as ContentfulClient, type ContentfulConfig, ContentfulError, ContentfulFetchClient, type ConverterContext, CustomTypeTag, type DefaultChainModifier, type DownloadHandlerConfig, type DownloadRouteParams, EntryNotFoundError, type FetchOptions, GlobalTag, type IContentfulCollection, type IContentfulComponent, type IContentfulPerson, type IContentfulRichText, type IFetchedTemplate, type ISitemapEntry, LocationTag, NavigationTag, PageTag, PersonTag, type PreviewContentType, type PreviewEntryInfo, RateLimitError, RateLimiter, type RelatedArticlesOptions, type RetryConfig, type RevalidationConfig, type SitemapChangeFrequency, type SitemapConfig, type SitemapContentTypeConfig, type SitemapEntryProvider, TagTag, TemplateTag, type UrlCalculators, ValidationError, arrayOrUndefined, articleTag, articleTypeIndexTag, articleTypeTag, assetTag, basePageConverter, calculateBackoffDelay, contentfulAllArticleLinks, contentfulAllArticleTypeLinks, contentfulAllPageLinks, contentfulAllPersonLinks, contentfulAllTagLinks, contentfulArticleRest, contentfulArticleSitemapEntries, contentfulArticleTypeRest, contentfulArticleTypeSitemapEntries, contentfulAssetRest, contentfulCustomTypeRest, contentfulPageRest, contentfulPageSitemapEntries, contentfulPersonSitemapEntries, contentfulTagRest, contentfulTagSitemapEntries, contentfulTemplateRest, createBaseConverterContext, createContentfulClient, createContentfulPreviewClient, createDownloadHandler, createResponsiveVisual, createRevalidationHandler, createSitemapProvider, customTypeTag, filterRelatedArticles, getAllSitemapEntries, getCacheTags, getCacheTagsForPreview, getCacheTagsForProduction, getContentfulClient, getPreviewEntryInfo, getRetryAfter, isBrowserViewable, isContentfulError, isRateLimitError, isRetryableError, isValidDate, locationTag, lookupAsset, notEmpty, pageTag, personTag, resolveLink, resolveLinks, resolveRichTextDocument, revalidateSingleTag, revalidateTags, safeDate, tagTag, templateTag, withRetry };
|
package/dist/index.js
CHANGED
|
@@ -470,6 +470,57 @@ var ContentfulFetchClient = class {
|
|
|
470
470
|
}
|
|
471
471
|
throw new ContentfulError("Max retries exceeded", 500);
|
|
472
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
|
+
}
|
|
473
524
|
};
|
|
474
525
|
function createContentfulClient(config) {
|
|
475
526
|
return new ContentfulFetchClient(config, false);
|
|
@@ -516,6 +567,20 @@ function lookupIconAsset(context, asset) {
|
|
|
516
567
|
}
|
|
517
568
|
return visual;
|
|
518
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
|
+
}
|
|
519
584
|
var DEFAULT_POSITION_FIELDS = {
|
|
520
585
|
index: 0,
|
|
521
586
|
isFirst: false,
|
|
@@ -582,6 +647,7 @@ function convertAssetToVisual(context, asset, options) {
|
|
|
582
647
|
const { contentType } = file;
|
|
583
648
|
if (contentType?.startsWith("image/")) {
|
|
584
649
|
const image = convertAssetToImage(file, fields, sys, options);
|
|
650
|
+
if (!image) return void 0;
|
|
585
651
|
return {
|
|
586
652
|
id,
|
|
587
653
|
type: "Visual",
|
|
@@ -596,6 +662,7 @@ function convertAssetToVisual(context, asset, options) {
|
|
|
596
662
|
context,
|
|
597
663
|
options
|
|
598
664
|
);
|
|
665
|
+
if (!video) return void 0;
|
|
599
666
|
return {
|
|
600
667
|
id,
|
|
601
668
|
type: "Visual",
|
|
@@ -609,6 +676,7 @@ function convertAssetToVisual(context, asset, options) {
|
|
|
609
676
|
sys,
|
|
610
677
|
options
|
|
611
678
|
);
|
|
679
|
+
if (!animation) return void 0;
|
|
612
680
|
return {
|
|
613
681
|
id,
|
|
614
682
|
type: "Visual",
|
|
@@ -621,6 +689,7 @@ function convertAssetToAnimation(file, fields, sys, options) {
|
|
|
621
689
|
const { id } = sys;
|
|
622
690
|
const { title, description } = fields;
|
|
623
691
|
const { url } = file;
|
|
692
|
+
if (!url) return void 0;
|
|
624
693
|
return {
|
|
625
694
|
...options,
|
|
626
695
|
id,
|
|
@@ -642,6 +711,7 @@ function convertAssetToPicture(file, fields, sys, options) {
|
|
|
642
711
|
const { id } = sys;
|
|
643
712
|
const { title, description } = fields;
|
|
644
713
|
const { contentType, details, url } = file;
|
|
714
|
+
if (!details) return void 0;
|
|
645
715
|
const { size, image } = details;
|
|
646
716
|
const { width, height } = image || {};
|
|
647
717
|
return {
|
|
@@ -661,6 +731,7 @@ function convertAssetToSvgImage(file, fields, sys, options) {
|
|
|
661
731
|
const { id } = sys;
|
|
662
732
|
const { title, description } = fields;
|
|
663
733
|
const { contentType, details, url } = file;
|
|
734
|
+
if (!details) return void 0;
|
|
664
735
|
const { size, image } = details;
|
|
665
736
|
const { width, height } = image || {};
|
|
666
737
|
return {
|
|
@@ -679,6 +750,7 @@ function convertAssetToSvgImage(file, fields, sys, options) {
|
|
|
679
750
|
function convertAssetToVideoDetails(file, sys) {
|
|
680
751
|
const { id } = sys;
|
|
681
752
|
const { details, url, contentType, fileName } = file;
|
|
753
|
+
if (!details) return void 0;
|
|
682
754
|
const { size } = details;
|
|
683
755
|
return {
|
|
684
756
|
id,
|
|
@@ -692,9 +764,13 @@ function convertAssetToVideo(file, fields, sys, context, options) {
|
|
|
692
764
|
const { id } = sys;
|
|
693
765
|
const { title, description } = fields;
|
|
694
766
|
const { details } = file;
|
|
767
|
+
if (!details) {
|
|
768
|
+
return void 0;
|
|
769
|
+
}
|
|
695
770
|
const { image } = details;
|
|
696
771
|
const { width, height } = image || {};
|
|
697
772
|
const videoDetails = convertAssetToVideoDetails(file, sys);
|
|
773
|
+
if (!videoDetails) return void 0;
|
|
698
774
|
return {
|
|
699
775
|
...options,
|
|
700
776
|
id,
|
|
@@ -1031,7 +1107,7 @@ function safeDate(date) {
|
|
|
1031
1107
|
|
|
1032
1108
|
// src/api/helpers.ts
|
|
1033
1109
|
var PAGE_LINK_FIELDS = "sys,fields.cmsLabel,fields.title,fields.slug,fields.featuredImage,fields.backgroundColour,fields.textColour,fields.indexed,fields.hidden,fields.tags";
|
|
1034
|
-
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";
|
|
1110
|
+
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";
|
|
1035
1111
|
var ARTICLE_TYPE_LINK_FIELDS = "sys,fields.name,fields.slug,fields.featuredImage,fields.backgroundColour,fields.textColour,fields.indexed,fields.hidden";
|
|
1036
1112
|
var TAG_LINK_FIELDS = "sys,fields.cmsLabel,fields.name,fields.slug,fields.featuredImage,fields.backgroundColour,fields.textColour,fields.indexed,fields.hidden";
|
|
1037
1113
|
var PERSON_LINK_FIELDS = "sys,fields.name,fields.slug,fields.media,fields.backgroundColour,fields.textColour,fields.indexed,fields.hidden";
|
|
@@ -1048,6 +1124,16 @@ function convertAllAssets(response, context) {
|
|
|
1048
1124
|
}
|
|
1049
1125
|
return visuals;
|
|
1050
1126
|
}
|
|
1127
|
+
function convertAllRawAssets(response) {
|
|
1128
|
+
const rawAssets = /* @__PURE__ */ new Map();
|
|
1129
|
+
const assets = response.includes?.Asset;
|
|
1130
|
+
if (assets && assets.length > 0) {
|
|
1131
|
+
for (const asset of assets) {
|
|
1132
|
+
rawAssets.set(asset.sys.id, asset);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
return rawAssets;
|
|
1136
|
+
}
|
|
1051
1137
|
function convertAllIncludes(response) {
|
|
1052
1138
|
const includes = /* @__PURE__ */ new Map();
|
|
1053
1139
|
const entries = [...response.items, ...response.includes?.Entry || []];
|
|
@@ -1095,11 +1181,13 @@ async function fetchSingleEntity(context, config, fetchConfig, options) {
|
|
|
1095
1181
|
}
|
|
1096
1182
|
try {
|
|
1097
1183
|
const assets = convertAllAssets(response, context);
|
|
1184
|
+
const rawAssets = convertAllRawAssets(response);
|
|
1098
1185
|
const includes = convertAllIncludes(response);
|
|
1099
1186
|
const fullContext = {
|
|
1100
1187
|
...context,
|
|
1101
1188
|
includes,
|
|
1102
1189
|
assets,
|
|
1190
|
+
rawAssets,
|
|
1103
1191
|
errors: []
|
|
1104
1192
|
};
|
|
1105
1193
|
const converted = fetchConfig.resolver(fullContext, entry);
|
|
@@ -1162,10 +1250,12 @@ async function fetchAllLinks(contentType, client, requestOptions, converter, con
|
|
|
1162
1250
|
}
|
|
1163
1251
|
const includes = convertAllIncludes(response);
|
|
1164
1252
|
const assets = convertAllAssets(response, context);
|
|
1253
|
+
const rawAssets = convertAllRawAssets(response);
|
|
1165
1254
|
const fullContext = {
|
|
1166
1255
|
...context,
|
|
1167
1256
|
includes,
|
|
1168
1257
|
assets,
|
|
1258
|
+
rawAssets,
|
|
1169
1259
|
errors: []
|
|
1170
1260
|
};
|
|
1171
1261
|
for (const entry of response.items) {
|
|
@@ -1241,6 +1331,80 @@ async function contentfulArticleTypeRest(context, config, indexPageSlug, options
|
|
|
1241
1331
|
);
|
|
1242
1332
|
}
|
|
1243
1333
|
|
|
1334
|
+
// src/api/asset.ts
|
|
1335
|
+
init_utils();
|
|
1336
|
+
var BROWSER_VIEWABLE_TYPES = [
|
|
1337
|
+
"application/pdf",
|
|
1338
|
+
"text/plain",
|
|
1339
|
+
"text/html",
|
|
1340
|
+
"text/css",
|
|
1341
|
+
"text/javascript",
|
|
1342
|
+
"application/json"
|
|
1343
|
+
];
|
|
1344
|
+
function isBrowserViewable(contentType) {
|
|
1345
|
+
if (contentType.startsWith("image/") || contentType.startsWith("video/")) {
|
|
1346
|
+
return true;
|
|
1347
|
+
}
|
|
1348
|
+
return BROWSER_VIEWABLE_TYPES.includes(contentType);
|
|
1349
|
+
}
|
|
1350
|
+
async function contentfulAssetRest(config, assetId, options) {
|
|
1351
|
+
const client = getContentfulClient(config, options?.preview);
|
|
1352
|
+
const cacheTags = getCacheTags("asset", assetId, options?.preview);
|
|
1353
|
+
return client.getAsset(assetId, {
|
|
1354
|
+
...options,
|
|
1355
|
+
next: {
|
|
1356
|
+
revalidate: options?.next?.revalidate ?? 86400,
|
|
1357
|
+
// Default 24 hours
|
|
1358
|
+
tags: [...cacheTags, ...options?.next?.tags ?? []]
|
|
1359
|
+
}
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
function createDownloadHandler(config) {
|
|
1363
|
+
const { getConfig, revalidate = 86400, preview = false } = config;
|
|
1364
|
+
return async function GET(_request, { params }) {
|
|
1365
|
+
const { assetId, filename } = await params;
|
|
1366
|
+
const decodedFilename = decodeURIComponent(filename);
|
|
1367
|
+
try {
|
|
1368
|
+
const contentfulConfig = getConfig(preview);
|
|
1369
|
+
const asset = await contentfulAssetRest(contentfulConfig, assetId, {
|
|
1370
|
+
preview,
|
|
1371
|
+
next: { revalidate }
|
|
1372
|
+
});
|
|
1373
|
+
if (!asset?.fields?.file) {
|
|
1374
|
+
return Response.json({ error: "Asset not found" }, { status: 404 });
|
|
1375
|
+
}
|
|
1376
|
+
const { file } = asset.fields;
|
|
1377
|
+
const assetUrl = `https:${file.url}`;
|
|
1378
|
+
const cacheTags = getCacheTags("asset", assetId, preview);
|
|
1379
|
+
const fileFetchOptions = {
|
|
1380
|
+
next: {
|
|
1381
|
+
revalidate,
|
|
1382
|
+
tags: cacheTags
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
const fileResponse = await fetch(assetUrl, fileFetchOptions);
|
|
1386
|
+
if (!fileResponse.ok) {
|
|
1387
|
+
return Response.json({ error: "Failed to fetch file" }, { status: 502 });
|
|
1388
|
+
}
|
|
1389
|
+
const headers = new Headers();
|
|
1390
|
+
headers.set("Content-Type", file.contentType);
|
|
1391
|
+
const disposition = isBrowserViewable(file.contentType) ? "inline" : "attachment";
|
|
1392
|
+
headers.set("Content-Disposition", `${disposition}; filename="${decodedFilename}"`);
|
|
1393
|
+
if (file.details?.size) {
|
|
1394
|
+
headers.set("Content-Length", String(file.details.size));
|
|
1395
|
+
}
|
|
1396
|
+
headers.set("Cache-Control", `public, max-age=${revalidate}, s-maxage=${revalidate}`);
|
|
1397
|
+
return new Response(fileResponse.body, {
|
|
1398
|
+
status: 200,
|
|
1399
|
+
headers
|
|
1400
|
+
});
|
|
1401
|
+
} catch (error) {
|
|
1402
|
+
console.error("[Download] Error proxying file:", error);
|
|
1403
|
+
return Response.json({ error: "Internal server error" }, { status: 500 });
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1244
1408
|
// src/converters/resolver.ts
|
|
1245
1409
|
function resolveHelper(context, fromId, entry, getResolver) {
|
|
1246
1410
|
const id = entry.sys.id;
|
|
@@ -1734,7 +1898,7 @@ function baseArticleConverter(context, entry) {
|
|
|
1734
1898
|
function calculateArticleTypeHref(slug) {
|
|
1735
1899
|
return `/${slug}/`;
|
|
1736
1900
|
}
|
|
1737
|
-
function calculateArticleHref(articleTypeSlug, slug) {
|
|
1901
|
+
function calculateArticleHref(articleTypeSlug, slug, _primaryTagSlug) {
|
|
1738
1902
|
return `${calculateArticleTypeHref(articleTypeSlug)}${slug}/`;
|
|
1739
1903
|
}
|
|
1740
1904
|
function baseArticleLinkConverter(context, entry) {
|
|
@@ -1747,26 +1911,24 @@ function baseArticleLinkConverter(context, entry) {
|
|
|
1747
1911
|
tags: fieldsTags,
|
|
1748
1912
|
author: fieldsAuthor,
|
|
1749
1913
|
date,
|
|
1914
|
+
externalLink,
|
|
1915
|
+
download: fieldsDownload,
|
|
1750
1916
|
...simpleFields
|
|
1751
1917
|
} = fields;
|
|
1752
1918
|
const articleType = resolveLink(context, sys.id, fieldsArticleType);
|
|
1753
1919
|
const tags = fieldsTags?.map((tag) => resolveLink(context, sys.id, tag));
|
|
1754
1920
|
const primaryTag = tags?.[0] ?? void 0;
|
|
1755
1921
|
const author = fieldsAuthor ? resolveLink(context, sys.id, fieldsAuthor) : void 0;
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
date,
|
|
1767
|
-
author
|
|
1768
|
-
}
|
|
1769
|
-
);
|
|
1922
|
+
const download = lookupDownloadAsset(context, fieldsDownload);
|
|
1923
|
+
const href = externalLink ? externalLink : context.urlCalculators.article(articleType.slug, fields.slug, primaryTag?.slug);
|
|
1924
|
+
return createInternalLink(sys.id, simpleFields, context, href, "Article", {
|
|
1925
|
+
tags,
|
|
1926
|
+
primaryTag,
|
|
1927
|
+
articleType,
|
|
1928
|
+
date,
|
|
1929
|
+
author,
|
|
1930
|
+
download
|
|
1931
|
+
});
|
|
1770
1932
|
}
|
|
1771
1933
|
function baseArticleTypeLinkConverter(context, entry) {
|
|
1772
1934
|
const { sys, fields } = entry;
|
|
@@ -2291,13 +2453,15 @@ function calculateTagHref(slug) {
|
|
|
2291
2453
|
}
|
|
2292
2454
|
function baseTagLinkConverter(context, entry) {
|
|
2293
2455
|
const { sys, fields } = entry;
|
|
2456
|
+
const { name, ...simpleFields } = fields;
|
|
2294
2457
|
if (sys.contentType.sys.id !== "tag") {
|
|
2295
2458
|
throw new Error(`Invalid content type: expected "tag", got "${sys.contentType.sys.id}"`);
|
|
2296
2459
|
}
|
|
2297
2460
|
return createInternalLink(
|
|
2298
2461
|
sys.id,
|
|
2299
2462
|
{
|
|
2300
|
-
...
|
|
2463
|
+
...simpleFields,
|
|
2464
|
+
title: name
|
|
2301
2465
|
},
|
|
2302
2466
|
context,
|
|
2303
2467
|
context.urlCalculators.tag(fields.slug),
|
|
@@ -3076,6 +3240,6 @@ function createRevalidationHandler(config = {}) {
|
|
|
3076
3240
|
init_tags();
|
|
3077
3241
|
init_utils();
|
|
3078
3242
|
|
|
3079
|
-
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, calculateBackoffDelay, contentfulAllArticleLinks, contentfulAllArticleTypeLinks, contentfulAllPageLinks, contentfulAllPersonLinks, contentfulAllTagLinks, contentfulArticleRest, contentfulArticleSitemapEntries, contentfulArticleTypeRest, contentfulArticleTypeSitemapEntries, contentfulCustomTypeRest, contentfulPageRest, contentfulPageSitemapEntries, contentfulPersonSitemapEntries, contentfulTagRest, contentfulTagSitemapEntries, contentfulTemplateRest, createBaseConverterContext, createContentfulClient, createContentfulPreviewClient, createResponsiveVisual, createRevalidationHandler, createSitemapProvider, customTypeTag, filterRelatedArticles, getAllSitemapEntries, getCacheTags, getCacheTagsForPreview, getCacheTagsForProduction, getContentfulClient, getPreviewEntryInfo, getRetryAfter, isContentfulError, isRateLimitError, isRetryableError, isValidDate, locationTag, lookupAsset, notEmpty, pageTag, personTag, resolveLink, resolveLinks, resolveRichTextDocument, revalidateSingleTag, revalidateTags, safeDate, tagTag, templateTag, withRetry };
|
|
3243
|
+
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, calculateBackoffDelay, contentfulAllArticleLinks, contentfulAllArticleTypeLinks, contentfulAllPageLinks, contentfulAllPersonLinks, contentfulAllTagLinks, contentfulArticleRest, contentfulArticleSitemapEntries, contentfulArticleTypeRest, contentfulArticleTypeSitemapEntries, contentfulAssetRest, contentfulCustomTypeRest, contentfulPageRest, contentfulPageSitemapEntries, contentfulPersonSitemapEntries, contentfulTagRest, contentfulTagSitemapEntries, contentfulTemplateRest, createBaseConverterContext, createContentfulClient, createContentfulPreviewClient, createDownloadHandler, createResponsiveVisual, createRevalidationHandler, createSitemapProvider, customTypeTag, filterRelatedArticles, getAllSitemapEntries, getCacheTags, getCacheTagsForPreview, getCacheTagsForProduction, getContentfulClient, getPreviewEntryInfo, getRetryAfter, isBrowserViewable, isContentfulError, isRateLimitError, isRetryableError, isValidDate, locationTag, lookupAsset, notEmpty, pageTag, personTag, resolveLink, resolveLinks, resolveRichTextDocument, revalidateSingleTag, revalidateTags, safeDate, tagTag, templateTag, withRetry };
|
|
3080
3244
|
//# sourceMappingURL=index.js.map
|
|
3081
3245
|
//# sourceMappingURL=index.js.map
|