@se-studio/contentful-rest-api 0.1.0 → 1.0.0-alpha-20251110080939

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.js CHANGED
@@ -1,193 +1,144 @@
1
- import { createClient } from 'contentful';
2
-
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __esm = (fn, res) => function __init() {
6
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
1
+ // src/utils/errors.ts
2
+ var ContentfulError = class _ContentfulError extends Error {
3
+ constructor(message, statusCode, details) {
4
+ super(message);
5
+ this.statusCode = statusCode;
6
+ this.details = details;
7
+ this.name = "ContentfulError";
8
+ Object.setPrototypeOf(this, _ContentfulError.prototype);
9
+ }
7
10
  };
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
+ var RateLimitError = class _RateLimitError extends ContentfulError {
12
+ constructor(message, retryAfter, details) {
13
+ super(message, 429, details);
14
+ this.retryAfter = retryAfter;
15
+ this.name = "RateLimitError";
16
+ Object.setPrototypeOf(this, _RateLimitError.prototype);
17
+ }
11
18
  };
12
-
13
- // src/utils/timing.ts
14
- var timing_exports = {};
15
- __export(timing_exports, {
16
- Timer: () => Timer,
17
- createTimer: () => createTimer,
18
- logTimingResult: () => logTimingResult
19
- });
20
- function formatTimingResult(result, indent = "") {
21
- const lines = [];
22
- let output = `${result.label}: ${result.duration}ms`;
23
- if (result.metadata) {
24
- const metadataStr = Object.entries(result.metadata).map(([k, v]) => `${k}: ${v}`).join(", ");
25
- output += ` (${metadataStr})`;
19
+ var EntryNotFoundError = class _EntryNotFoundError extends ContentfulError {
20
+ constructor(entryId, contentType) {
21
+ super(`Entry not found: ${entryId}${contentType ? ` (${contentType})` : ""}`, 404);
22
+ this.entryId = entryId;
23
+ this.contentType = contentType;
24
+ this.name = "EntryNotFoundError";
25
+ Object.setPrototypeOf(this, _EntryNotFoundError.prototype);
26
26
  }
27
- if (result.memoryDelta) {
28
- const mem = result.memoryDelta;
29
- const parts = [];
30
- if (Math.abs(mem.heapUsedMB) > 0.01) {
31
- parts.push(`heap: ${mem.heapUsedMB >= 0 ? "+" : ""}${mem.heapUsedMB}MB`);
32
- }
33
- if (Math.abs(mem.rssMB) > 0.01) {
34
- parts.push(`rss: ${mem.rssMB >= 0 ? "+" : ""}${mem.rssMB}MB`);
35
- }
36
- if (Math.abs(mem.externalMB) > 0.01) {
37
- parts.push(`ext: ${mem.externalMB >= 0 ? "+" : ""}${mem.externalMB}MB`);
38
- }
39
- if (parts.length > 0) {
40
- output += ` [${parts.join(", ")}]`;
41
- }
27
+ };
28
+ var AuthenticationError = class _AuthenticationError extends ContentfulError {
29
+ constructor(message = "Authentication failed") {
30
+ super(message, 401);
31
+ this.name = "AuthenticationError";
32
+ Object.setPrototypeOf(this, _AuthenticationError.prototype);
42
33
  }
43
- lines.push(`${indent}${output}`);
44
- if (result.children && result.children.length > 0) {
45
- for (let i = 0; i < result.children.length; i++) {
46
- const child = result.children[i];
47
- if (!child) continue;
48
- const isLast = i === result.children.length - 1;
49
- const childIndent = indent + (isLast ? " \u2514\u2500 " : " \u251C\u2500 ");
50
- const grandchildIndent = indent + (isLast ? " " : " \u2502 ");
51
- const childLines = formatTimingResult(child, "").split("\n");
52
- lines.push(childIndent + childLines[0]);
53
- for (let j = 1; j < childLines.length; j++) {
54
- lines.push(grandchildIndent + childLines[j]);
55
- }
56
- }
34
+ };
35
+ var ValidationError = class _ValidationError extends ContentfulError {
36
+ constructor(message, validationErrors) {
37
+ super(message, 400, validationErrors);
38
+ this.validationErrors = validationErrors;
39
+ this.name = "ValidationError";
40
+ Object.setPrototypeOf(this, _ValidationError.prototype);
41
+ }
42
+ };
43
+ function isContentfulError(error) {
44
+ return error instanceof ContentfulError;
45
+ }
46
+ function isRateLimitError(error) {
47
+ return error instanceof RateLimitError;
48
+ }
49
+ function isRetryableError(error) {
50
+ if (isRateLimitError(error)) {
51
+ return true;
52
+ }
53
+ if (isContentfulError(error)) {
54
+ return error.statusCode !== void 0 && (error.statusCode >= 500 || error.statusCode === 429);
57
55
  }
58
- return lines.join("\n");
56
+ return false;
59
57
  }
60
- function logTimingResult(result) {
61
- if (!shouldLog) return;
62
- console.log(`
63
- [Timing] ${formatTimingResult(result)}`);
58
+ function getRetryAfter(error) {
59
+ if (isRateLimitError(error)) {
60
+ return error.retryAfter;
61
+ }
62
+ return void 0;
64
63
  }
65
- function createTimer(label) {
66
- return new Timer(label);
64
+
65
+ // src/client.ts
66
+ function buildQueryString(query) {
67
+ const params = new URLSearchParams();
68
+ Object.entries(query).forEach(([key, value]) => {
69
+ if (value !== void 0 && value !== null) {
70
+ params.append(key, String(value));
71
+ }
72
+ });
73
+ return params.toString();
67
74
  }
68
- var isDevelopment, isDebugTimingEnabled, shouldLog, Timer;
69
- var init_timing = __esm({
70
- "src/utils/timing.ts"() {
71
- isDevelopment = process.env.NODE_ENV === "development";
72
- isDebugTimingEnabled = process.env.DEBUG_TIMING === "true";
73
- shouldLog = isDevelopment || isDebugTimingEnabled;
74
- Timer = class _Timer {
75
- startTime;
76
- memoryStart;
77
- label;
78
- children = [];
79
- metadata = {};
80
- trackMemory;
81
- constructor(label, trackMemory = true) {
82
- this.label = label;
83
- this.startTime = performance.now();
84
- this.trackMemory = trackMemory && shouldLog;
85
- if (this.trackMemory && typeof process !== "undefined" && process.memoryUsage) {
86
- this.memoryStart = process.memoryUsage();
87
- }
88
- }
89
- /**
90
- * Add metadata to the timing result
91
- */
92
- addMetadata(key, value) {
93
- this.metadata[key] = value;
94
- }
95
- /**
96
- * Create and return a child timer
97
- */
98
- child(label) {
99
- return new _Timer(`${this.label} > ${label}`);
100
- }
101
- /**
102
- * End the timer and return the result
103
- */
104
- end() {
105
- const duration = performance.now() - this.startTime;
106
- const result = {
107
- label: this.label,
108
- duration: Math.round(duration * 100) / 100,
109
- // Round to 2 decimal places
110
- children: this.children.length > 0 ? this.children : void 0,
111
- metadata: Object.keys(this.metadata).length > 0 ? this.metadata : void 0
112
- };
113
- if (this.memoryStart && typeof process !== "undefined" && process.memoryUsage) {
114
- const memoryEnd = process.memoryUsage();
115
- result.memoryDelta = {
116
- heapUsedMB: Number(
117
- ((memoryEnd.heapUsed - this.memoryStart.heapUsed) / 1024 / 1024).toFixed(2)
118
- ),
119
- heapTotalMB: Number(
120
- ((memoryEnd.heapTotal - this.memoryStart.heapTotal) / 1024 / 1024).toFixed(2)
121
- ),
122
- rssMB: Number(((memoryEnd.rss - this.memoryStart.rss) / 1024 / 1024).toFixed(2)),
123
- externalMB: Number(
124
- ((memoryEnd.external - this.memoryStart.external) / 1024 / 1024).toFixed(2)
125
- )
126
- };
127
- }
128
- return result;
129
- }
130
- /**
131
- * End timer and log the result
132
- */
133
- endAndLog() {
134
- const result = this.end();
135
- if (shouldLog) {
136
- logTimingResult(result);
137
- }
138
- return result;
139
- }
140
- /**
141
- * Execute a function and time it
142
- */
143
- static async time(label, fn, metadata) {
144
- const timer = new _Timer(label);
145
- if (metadata) {
146
- for (const [key, value] of Object.entries(metadata)) {
147
- timer.addMetadata(key, value);
148
- }
149
- }
150
- const result = await fn();
151
- const timing = timer.end();
152
- return { result, timing };
153
- }
154
- /**
155
- * Execute a synchronous function and time it
156
- */
157
- static timeSync(label, fn, metadata) {
158
- const timer = new _Timer(label);
159
- if (metadata) {
160
- for (const [key, value] of Object.entries(metadata)) {
161
- timer.addMetadata(key, value);
162
- }
163
- }
164
- const result = fn();
165
- const timing = timer.end();
166
- return { result, timing };
75
+ async function parseErrorResponse(response) {
76
+ const statusCode = response.status;
77
+ let errorData;
78
+ try {
79
+ errorData = await response.json();
80
+ } catch {
81
+ errorData = { message: response.statusText };
82
+ }
83
+ const message = errorData?.message || `Contentful API error: ${statusCode}`;
84
+ switch (statusCode) {
85
+ case 401:
86
+ return new AuthenticationError(message);
87
+ case 404:
88
+ return new EntryNotFoundError(
89
+ errorData?.sys?.id || "unknown",
90
+ errorData?.sys?.contentType?.sys?.id
91
+ );
92
+ case 429: {
93
+ const retryAfterHeader = response.headers.get("X-Contentful-RateLimit-Reset") || response.headers.get("Retry-After");
94
+ const retryAfter = retryAfterHeader ? Number.parseInt(retryAfterHeader, 10) : void 0;
95
+ return new RateLimitError(message, retryAfter, errorData);
96
+ }
97
+ case 400:
98
+ return new ValidationError(message, errorData);
99
+ default:
100
+ return new ContentfulError(message, statusCode, errorData);
101
+ }
102
+ }
103
+ var ContentfulFetchClient = class {
104
+ baseUrl;
105
+ accessToken;
106
+ constructor(config, preview = false) {
107
+ const host = config.host || (preview ? "preview.contentful.com" : "cdn.contentful.com");
108
+ const environment = config.environment || "master";
109
+ this.baseUrl = `https://${host}/spaces/${config.spaceId}/environments/${environment}`;
110
+ this.accessToken = config.accessToken;
111
+ }
112
+ /**
113
+ * Fetches entries from Contentful
114
+ */
115
+ async getEntries(query, options) {
116
+ const queryString = buildQueryString(query);
117
+ const url = `${this.baseUrl}/entries?${queryString}`;
118
+ const fetchOptions = {
119
+ headers: {
120
+ Authorization: `Bearer ${this.accessToken}`,
121
+ "Content-Type": "application/json"
167
122
  }
168
123
  };
124
+ if (options?.next) {
125
+ fetchOptions.next = options.next;
126
+ }
127
+ const response = await fetch(url, fetchOptions);
128
+ if (!response.ok) {
129
+ throw await parseErrorResponse(response);
130
+ }
131
+ return response.json();
169
132
  }
170
- });
133
+ };
171
134
  function createContentfulClient(config) {
172
- return createClient({
173
- space: config.spaceId,
174
- accessToken: config.accessToken,
175
- environment: config.environment || "master",
176
- host: config.host,
177
- ...config.options
178
- });
135
+ return new ContentfulFetchClient(config, false);
179
136
  }
180
137
  function createContentfulPreviewClient(config) {
181
- return createClient({
182
- space: config.spaceId,
183
- accessToken: config.accessToken,
184
- environment: config.environment || "master",
185
- host: "preview.contentful.com",
186
- ...config.options
187
- });
138
+ return new ContentfulFetchClient(config, true);
188
139
  }
189
140
  function getContentfulClient(config, preview = false) {
190
- return (preview ? createContentfulPreviewClient(config) : createContentfulClient(config)).withoutLinkResolution;
141
+ return preview ? createContentfulPreviewClient(config) : createContentfulClient(config);
191
142
  }
192
143
 
193
144
  // src/converters/helpers.ts
@@ -212,7 +163,7 @@ function createInternalLink(id, fields, context, href, additionalProps) {
212
163
  return {
213
164
  type: "Internal link",
214
165
  id,
215
- name: cmsLabel,
166
+ name: cmsLabel ?? "",
216
167
  useName: true,
217
168
  text: makeContentfulTitle(title, id),
218
169
  visual: lookupAsset(context, featuredImage),
@@ -242,7 +193,7 @@ function addPositionMetadata(items) {
242
193
  }
243
194
 
244
195
  // src/converters/asset.ts
245
- function convertAssetToVisual(asset, options) {
196
+ function convertAssetToVisual(context, asset, options) {
246
197
  if (!asset) return void 0;
247
198
  const { fields, sys } = asset;
248
199
  if (!fields) return void 0;
@@ -259,7 +210,13 @@ function convertAssetToVisual(asset, options) {
259
210
  };
260
211
  }
261
212
  if (contentType?.startsWith("video/")) {
262
- const video = convertAssetToVideo(file, fields, sys, options);
213
+ const video = convertAssetToVideo(
214
+ file,
215
+ fields,
216
+ sys,
217
+ context,
218
+ options
219
+ );
263
220
  return {
264
221
  id,
265
222
  type: "Visual",
@@ -269,6 +226,14 @@ function convertAssetToVisual(asset, options) {
269
226
  return void 0;
270
227
  }
271
228
  function convertAssetToImage(file, fields, sys, options) {
229
+ const { contentType } = file;
230
+ if (contentType === "image/svg+xml") {
231
+ return convertAssetToSvgImage(file, fields, sys, options);
232
+ } else {
233
+ return convertAssetToPicture(file, fields, sys, options);
234
+ }
235
+ }
236
+ function convertAssetToPicture(file, fields, sys, options) {
272
237
  const { id } = sys;
273
238
  const { title, description } = fields;
274
239
  const { contentType, details, url } = file;
@@ -287,6 +252,25 @@ function convertAssetToImage(file, fields, sys, options) {
287
252
  description: makeContentfulDescription(description, id)
288
253
  };
289
254
  }
255
+ function convertAssetToSvgImage(file, fields, sys, options) {
256
+ const { id } = sys;
257
+ const { title, description } = fields;
258
+ const { contentType, details, url } = file;
259
+ const { size, image } = details;
260
+ const { width, height } = image || {};
261
+ return {
262
+ ...options,
263
+ id,
264
+ type: "Svg image",
265
+ svgSrc: `https:${url}`,
266
+ mimeType: contentType,
267
+ size,
268
+ width: width || 0,
269
+ height: height || 0,
270
+ name: makeContentfulTitle(title, id),
271
+ description: makeContentfulDescription(description, id)
272
+ };
273
+ }
290
274
  function convertAssetToVideoDetails(file, sys) {
291
275
  const { id } = sys;
292
276
  const { details, url, contentType, fileName } = file;
@@ -299,7 +283,7 @@ function convertAssetToVideoDetails(file, sys) {
299
283
  fileName
300
284
  };
301
285
  }
302
- function convertAssetToVideo(file, fields, sys, options) {
286
+ function convertAssetToVideo(file, fields, sys, context, options) {
303
287
  const { id } = sys;
304
288
  const { title, description } = fields;
305
289
  const { details } = file;
@@ -311,6 +295,7 @@ function convertAssetToVideo(file, fields, sys, options) {
311
295
  id,
312
296
  type: "Local video",
313
297
  preview: videoDetails,
298
+ videoPrefix: context.videoPrefix,
314
299
  width: width || 0,
315
300
  height: height || 0,
316
301
  name: makeContentfulTitle(title, id),
@@ -416,32 +401,6 @@ function lookupMediaEntry(context, link) {
416
401
  return void 0;
417
402
  }
418
403
 
419
- // src/converters/externalComponent.ts
420
- function baseExternalComponentConverter(_context, entry) {
421
- const { sys, fields } = entry;
422
- const {
423
- externalComponentType,
424
- cmsLabel,
425
- data,
426
- heading: _heading,
427
- // Exclude: not in target interface
428
- ...simpleFields
429
- // backgroundColour, textColour
430
- } = fields;
431
- return {
432
- type: "External component",
433
- id: sys.id,
434
- name: cmsLabel ?? `External component ${sys.id}`,
435
- cmsLabel: cmsLabel ?? null,
436
- externalType: externalComponentType,
437
- data: data ?? null,
438
- ...DEFAULT_POSITION_FIELDS,
439
- ...simpleFields,
440
- backgroundOverlayOpacity: null,
441
- backgroundVisual: void 0
442
- };
443
- }
444
-
445
404
  // src/converters/resolver.ts
446
405
  function resolveHelper(context, entry, getResolver) {
447
406
  const id = entry.sys.id;
@@ -456,6 +415,9 @@ function resolveHelper(context, entry, getResolver) {
456
415
  `No resolver found for link type ${possibleEntry.type} (${JSON.stringify(possibleEntry)}) [${JSON.stringify(entry)}]`
457
416
  );
458
417
  }
418
+ if (typeof resolver !== "function") {
419
+ console.log("Resolver type", possibleEntry.type, typeof resolver, resolver);
420
+ }
459
421
  const resolved = resolver(
460
422
  context,
461
423
  possibleEntry.entry
@@ -472,6 +434,15 @@ function resolveLink(context, entry) {
472
434
  (type) => context.linkResolver.get(type)
473
435
  );
474
436
  }
437
+ function resolveLinks(context, entries) {
438
+ return entries?.map((entry) => resolveLink(context, entry)) || [];
439
+ }
440
+ function resolveContent(context, entry) {
441
+ return resolveHelper(context, entry, (type) => {
442
+ const resolver = context.contentResolver.get(type);
443
+ return resolver;
444
+ });
445
+ }
475
446
  function resolveNavigationItem(context, entry) {
476
447
  return resolveHelper(
477
448
  context,
@@ -480,15 +451,14 @@ function resolveNavigationItem(context, entry) {
480
451
  );
481
452
  }
482
453
  function resolveCollectionContent(context, entry) {
483
- return resolveHelper(context, entry, (type) => {
484
- if (type === "component") {
485
- return context.componentResolver;
486
- }
487
- if (type === "collection") {
488
- return context.collectionResolver;
454
+ return resolveHelper(
455
+ context,
456
+ entry,
457
+ (type) => {
458
+ const resolver = context.contentResolver.get(type);
459
+ return resolver;
489
460
  }
490
- return void 0;
491
- });
461
+ );
492
462
  }
493
463
  function resolvePageContent(context, entry) {
494
464
  const id = entry.sys.id;
@@ -497,9 +467,6 @@ function resolvePageContent(context, entry) {
497
467
  throw new Error(`Cannot find included entry for content with id ${id}`);
498
468
  }
499
469
  const { type } = possibleEntry;
500
- if (type === "component" || type === "collection") {
501
- return resolveCollectionContent(context, entry);
502
- }
503
470
  if (type === "media") {
504
471
  const visual = convertMediaEntryToVisual(
505
472
  context,
@@ -520,8 +487,8 @@ function resolvePageContent(context, entry) {
520
487
  }
521
488
  return visual;
522
489
  }
523
- if (type === "externalComponent") {
524
- return resolveHelper(context, entry, () => baseExternalComponentConverter);
490
+ if (context.contentResolver.has(type)) {
491
+ return resolveContent(context, entry);
525
492
  }
526
493
  if (context.linkResolver.has(type)) {
527
494
  return resolveLink(context, entry);
@@ -631,9 +598,8 @@ function baseCollectionConverter(context, entry) {
631
598
  additionalCopy: additionalCopyField,
632
599
  // Field name change
633
600
  collectionType,
634
- // CMS-only fields (must exclude)
635
- textSize: _textSize,
636
- showHeading: _showHeading,
601
+ showHeading,
602
+ heading,
637
603
  // Already handled elsewhere
638
604
  cmsLabel,
639
605
  ...simpleFields
@@ -648,7 +614,7 @@ function baseCollectionConverter(context, entry) {
648
614
  lookupMediaEntry(context, mobileVisualField),
649
615
  visualCustomSize
650
616
  );
651
- return {
617
+ const collection = {
652
618
  type: "Collection",
653
619
  id: sys.id,
654
620
  name: cmsLabel,
@@ -656,6 +622,7 @@ function baseCollectionConverter(context, entry) {
656
622
  collectionType,
657
623
  ...DEFAULT_POSITION_FIELDS,
658
624
  ...simpleFields,
625
+ heading: showHeading ? heading : void 0,
659
626
  body: resolveRichTextDocument(context, bodyField),
660
627
  additionalCopy: resolveRichTextDocument(context, additionalCopyField),
661
628
  icon: lookupAsset(context, iconField),
@@ -664,6 +631,7 @@ function baseCollectionConverter(context, entry) {
664
631
  links: linksField?.map((link) => resolveLink(context, link)),
665
632
  contents: contentsField?.map((content) => resolveCollectionContent(context, content))
666
633
  };
634
+ return collection;
667
635
  }
668
636
 
669
637
  // src/converters/component.ts
@@ -682,9 +650,8 @@ function baseComponentConverter(context, entry) {
682
650
  additionalCopy: additionalCopyField,
683
651
  // Field name change
684
652
  componentType,
685
- // CMS-only fields (must exclude)
686
- textSize: _textSize,
687
- showHeading: _showHeading,
653
+ showHeading,
654
+ heading,
688
655
  otherMedia: _otherMedia,
689
656
  otherVisuals: _otherVisuals,
690
657
  // Already handled elsewhere
@@ -701,7 +668,7 @@ function baseComponentConverter(context, entry) {
701
668
  lookupMediaEntry(context, mobileVisualField),
702
669
  visualCustomSize
703
670
  );
704
- return {
671
+ const component = {
705
672
  type: "Component",
706
673
  id: sys.id,
707
674
  name: cmsLabel,
@@ -709,13 +676,15 @@ function baseComponentConverter(context, entry) {
709
676
  componentType,
710
677
  ...DEFAULT_POSITION_FIELDS,
711
678
  ...simpleFields,
679
+ heading: showHeading ? heading : void 0,
712
680
  body: resolveRichTextDocument(context, bodyField),
713
681
  additionalCopy: resolveRichTextDocument(context, additionalCopyField),
714
682
  icon: lookupAsset(context, iconField),
715
683
  backgroundVisual,
716
684
  visual,
717
- links: linksField?.map((link) => resolveLink(context, link))
685
+ links: resolveLinks(context, linksField)
718
686
  };
687
+ return component;
719
688
  }
720
689
 
721
690
  // src/converters/link.ts
@@ -871,7 +840,7 @@ function basePageConverter(context, entry) {
871
840
  ...postContent,
872
841
  ...bottomContent
873
842
  ]);
874
- return {
843
+ const page = {
875
844
  type: "Page",
876
845
  id: sys.id,
877
846
  isHomePage: slug === "index",
@@ -885,6 +854,7 @@ function basePageConverter(context, entry) {
885
854
  menu: template?.menu,
886
855
  footer: template?.footer
887
856
  };
857
+ return page;
888
858
  }
889
859
  function calculatePageHref(slug) {
890
860
  if (slug === "index") {
@@ -1000,7 +970,7 @@ function baseArticleConverter(context, entry) {
1000
970
  ...postContent,
1001
971
  ...bottomContent
1002
972
  ]);
1003
- return {
973
+ const article = {
1004
974
  type: "Article",
1005
975
  id: sys.id,
1006
976
  slug,
@@ -1016,6 +986,7 @@ function baseArticleConverter(context, entry) {
1016
986
  menu: template?.menu,
1017
987
  footer: template?.footer
1018
988
  };
989
+ return article;
1019
990
  }
1020
991
  function calculateArticleTypeHref(slug) {
1021
992
  return `/${slug}/`;
@@ -1096,12 +1067,11 @@ function createLink(context, entry) {
1096
1067
  }
1097
1068
  function baseNavigationItemConverter(context, entry) {
1098
1069
  const { sys, fields } = entry;
1099
- const { longText, navigationItems } = fields;
1070
+ const { navigationItems } = fields;
1100
1071
  const link = createLink(context, entry);
1101
1072
  const resolvedNavigationItems = navigationItems?.map((item) => resolveNavigationItem(context, item)).filter((item) => item !== void 0);
1102
1073
  return {
1103
1074
  id: sys.id,
1104
- longText,
1105
1075
  link,
1106
1076
  entries: resolvedNavigationItems
1107
1077
  };
@@ -1133,70 +1103,6 @@ function baseTagLinkConverter(context, entry) {
1133
1103
  );
1134
1104
  }
1135
1105
 
1136
- // src/utils/errors.ts
1137
- var ContentfulError = class _ContentfulError extends Error {
1138
- constructor(message, statusCode, details) {
1139
- super(message);
1140
- this.statusCode = statusCode;
1141
- this.details = details;
1142
- this.name = "ContentfulError";
1143
- Object.setPrototypeOf(this, _ContentfulError.prototype);
1144
- }
1145
- };
1146
- var RateLimitError = class _RateLimitError extends ContentfulError {
1147
- constructor(message, retryAfter, details) {
1148
- super(message, 429, details);
1149
- this.retryAfter = retryAfter;
1150
- this.name = "RateLimitError";
1151
- Object.setPrototypeOf(this, _RateLimitError.prototype);
1152
- }
1153
- };
1154
- var EntryNotFoundError = class _EntryNotFoundError extends ContentfulError {
1155
- constructor(entryId, contentType) {
1156
- super(`Entry not found: ${entryId}${contentType ? ` (${contentType})` : ""}`, 404);
1157
- this.entryId = entryId;
1158
- this.contentType = contentType;
1159
- this.name = "EntryNotFoundError";
1160
- Object.setPrototypeOf(this, _EntryNotFoundError.prototype);
1161
- }
1162
- };
1163
- var AuthenticationError = class _AuthenticationError extends ContentfulError {
1164
- constructor(message = "Authentication failed") {
1165
- super(message, 401);
1166
- this.name = "AuthenticationError";
1167
- Object.setPrototypeOf(this, _AuthenticationError.prototype);
1168
- }
1169
- };
1170
- var ValidationError = class _ValidationError extends ContentfulError {
1171
- constructor(message, validationErrors) {
1172
- super(message, 400, validationErrors);
1173
- this.validationErrors = validationErrors;
1174
- this.name = "ValidationError";
1175
- Object.setPrototypeOf(this, _ValidationError.prototype);
1176
- }
1177
- };
1178
- function isContentfulError(error) {
1179
- return error instanceof ContentfulError;
1180
- }
1181
- function isRateLimitError(error) {
1182
- return error instanceof RateLimitError;
1183
- }
1184
- function isRetryableError(error) {
1185
- if (isRateLimitError(error)) {
1186
- return true;
1187
- }
1188
- if (isContentfulError(error)) {
1189
- return error.statusCode !== void 0 && (error.statusCode >= 500 || error.statusCode === 429);
1190
- }
1191
- return false;
1192
- }
1193
- function getRetryAfter(error) {
1194
- if (isRateLimitError(error)) {
1195
- return error.retryAfter;
1196
- }
1197
- return void 0;
1198
- }
1199
-
1200
1106
  // src/utils/retry.ts
1201
1107
  var DEFAULT_RETRY_CONFIG = {
1202
1108
  maxRetries: 3,
@@ -1296,16 +1202,13 @@ var RateLimiter = class {
1296
1202
  }
1297
1203
  };
1298
1204
 
1299
- // src/utils/index.ts
1300
- init_timing();
1301
-
1302
1205
  // src/api.ts
1303
- function convertAllAssets(response) {
1206
+ function convertAllAssets(response, context) {
1304
1207
  const visuals = /* @__PURE__ */ new Map();
1305
1208
  const assets = response.includes?.Asset;
1306
1209
  if (assets && assets.length > 0) {
1307
1210
  for (const asset of assets) {
1308
- const visual = convertAssetToVisual(asset);
1211
+ const visual = convertAssetToVisual(context, asset);
1309
1212
  if (visual) {
1310
1213
  visuals.set(visual.id, visual);
1311
1214
  }
@@ -1330,43 +1233,30 @@ function convertAllIncludes(response) {
1330
1233
  return includes;
1331
1234
  }
1332
1235
  async function contentfulPageRest(context, config, slug, options) {
1333
- const totalTimer = new Timer(`contentfulPageRest(${slug})`);
1334
1236
  const client = getContentfulClient(config, options?.preview);
1335
1237
  const fetchFn = async () => {
1336
- const apiFetchTimer = new Timer("API fetch");
1337
- const response = await client.getEntries({
1338
- content_type: "page",
1339
- "fields.slug": slug,
1340
- include: 10,
1341
- locale: options?.locale,
1342
- limit: 1
1343
- });
1344
- const apiFetchTiming = apiFetchTimer.end();
1238
+ const response = await client.getEntries(
1239
+ {
1240
+ content_type: "page",
1241
+ "fields.slug": slug,
1242
+ include: 10,
1243
+ locale: options?.locale,
1244
+ limit: 1
1245
+ },
1246
+ options
1247
+ );
1345
1248
  const pageEntry = response.items[0];
1346
1249
  if (!pageEntry || !pageEntry.fields) {
1347
- totalTimer.endAndLog();
1348
1250
  return null;
1349
1251
  }
1350
- const assetTimer = new Timer("Asset conversion");
1351
- const assets = convertAllAssets(response);
1352
- const assetTiming = assetTimer.end();
1353
- assetTiming.metadata = { count: assets.size };
1354
- const includesTimer = new Timer("Includes indexing");
1252
+ const assets = convertAllAssets(response, context);
1355
1253
  const includes = convertAllIncludes(response);
1356
- const includesTiming = includesTimer.end();
1357
- includesTiming.metadata = { count: includes.size };
1358
1254
  const fullContext = {
1359
1255
  ...context,
1360
1256
  includes,
1361
1257
  assets
1362
1258
  };
1363
- const conversionTimer = new Timer("Page conversion");
1364
1259
  const converted = fullContext.pageResolver(fullContext, pageEntry);
1365
- const conversionTiming = conversionTimer.end();
1366
- const totalTiming = totalTimer.end();
1367
- totalTiming.children = [apiFetchTiming, assetTiming, includesTiming, conversionTiming];
1368
- const { logTimingResult: logTimingResult2 } = await Promise.resolve().then(() => (init_timing(), timing_exports));
1369
- logTimingResult2(totalTiming);
1370
1260
  return converted;
1371
1261
  };
1372
1262
  if (options?.retry) {
@@ -1378,43 +1268,49 @@ function createBaseConverterContext() {
1378
1268
  const linkResolver = /* @__PURE__ */ new Map();
1379
1269
  linkResolver.set("page", basePageLinkConverter);
1380
1270
  linkResolver.set("article", baseArticleLinkConverter);
1381
- linkResolver.set(
1382
- "articleType",
1383
- baseArticleTypeLinkConverter
1384
- );
1271
+ linkResolver.set("articleType", baseArticleTypeLinkConverter);
1385
1272
  linkResolver.set("tag", baseTagLinkConverter);
1386
1273
  linkResolver.set("person", basePersonLinkConverter);
1387
- linkResolver.set(
1388
- "pageVariant",
1389
- basePageVariantLinkConverter
1390
- );
1274
+ linkResolver.set("pageVariant", basePageVariantLinkConverter);
1391
1275
  linkResolver.set("link", baseLinkConverter);
1276
+ const contentResolver = /* @__PURE__ */ new Map();
1277
+ contentResolver.set("collection", baseCollectionConverter);
1278
+ contentResolver.set("component", baseComponentConverter);
1279
+ contentResolver.set(
1280
+ "externalComponent",
1281
+ baseComponentConverter
1282
+ );
1392
1283
  return {
1393
1284
  pageResolver: basePageConverter,
1394
1285
  navigationItemResolver: baseNavigationItemConverter,
1395
1286
  articleResolver: baseArticleConverter,
1396
1287
  componentResolver: baseComponentConverter,
1397
1288
  collectionResolver: baseCollectionConverter,
1398
- linkResolver
1289
+ linkResolver,
1290
+ contentResolver,
1291
+ videoPrefix: ""
1399
1292
  };
1400
1293
  }
1401
1294
  async function contentfulArticleRest(context, config, slug, articleTypeSlug, options) {
1402
1295
  const client = getContentfulClient(config, options?.preview);
1403
1296
  const fetchFn = async () => {
1404
- const response = await client.getEntries({
1405
- content_type: "article",
1406
- "fields.slug": slug,
1407
- "fields.articleType.sys.contentType.sys.id": "articleType",
1408
- "fields.articleType.fields.slug": articleTypeSlug,
1409
- include: 10,
1410
- locale: options?.locale,
1411
- limit: 1
1412
- });
1297
+ const response = await client.getEntries(
1298
+ {
1299
+ content_type: "article",
1300
+ "fields.slug": slug,
1301
+ "fields.articleType.sys.contentType.sys.id": "articleType",
1302
+ "fields.articleType.fields.slug": articleTypeSlug,
1303
+ include: 10,
1304
+ locale: options?.locale,
1305
+ limit: 1
1306
+ },
1307
+ options
1308
+ );
1413
1309
  const articleEntry = response.items[0];
1414
1310
  if (!articleEntry || !articleEntry.fields) {
1415
1311
  return null;
1416
1312
  }
1417
- const assets = convertAllAssets(response);
1313
+ const assets = convertAllAssets(response, context);
1418
1314
  const includes = convertAllIncludes(response);
1419
1315
  const fullContext = {
1420
1316
  ...context,
@@ -1430,6 +1326,6 @@ async function contentfulArticleRest(context, config, slug, articleTypeSlug, opt
1430
1326
  return await fetchFn();
1431
1327
  }
1432
1328
 
1433
- export { AuthenticationError, ContentfulError, EntryNotFoundError, RateLimitError, RateLimiter, Timer, ValidationError, basePageConverter, calculateBackoffDelay, contentfulArticleRest, contentfulPageRest, createBaseConverterContext, createContentfulClient, createContentfulPreviewClient, createTimer, getContentfulClient, getRetryAfter, isContentfulError, isRateLimitError, isRetryableError, logTimingResult, withRetry };
1329
+ export { AuthenticationError, ContentfulError, EntryNotFoundError, RateLimitError, RateLimiter, ValidationError, basePageConverter, calculateBackoffDelay, contentfulArticleRest, contentfulPageRest, createBaseConverterContext, createContentfulClient, createContentfulPreviewClient, createResponsiveVisual, getContentfulClient, getRetryAfter, isContentfulError, isRateLimitError, isRetryableError, lookupAsset, resolveLink, resolveLinks, resolveRichTextDocument, withRetry };
1434
1330
  //# sourceMappingURL=index.js.map
1435
1331
  //# sourceMappingURL=index.js.map