@se-studio/contentful-rest-api 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +467 -0
- package/dist/index.d.ts +462 -0
- package/dist/index.js +1435 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1435 @@
|
|
|
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;
|
|
7
|
+
};
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
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})`;
|
|
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
|
+
}
|
|
42
|
+
}
|
|
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
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return lines.join("\n");
|
|
59
|
+
}
|
|
60
|
+
function logTimingResult(result) {
|
|
61
|
+
if (!shouldLog) return;
|
|
62
|
+
console.log(`
|
|
63
|
+
[Timing] ${formatTimingResult(result)}`);
|
|
64
|
+
}
|
|
65
|
+
function createTimer(label) {
|
|
66
|
+
return new Timer(label);
|
|
67
|
+
}
|
|
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 };
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
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
|
+
});
|
|
179
|
+
}
|
|
180
|
+
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
|
+
});
|
|
188
|
+
}
|
|
189
|
+
function getContentfulClient(config, preview = false) {
|
|
190
|
+
return (preview ? createContentfulPreviewClient(config) : createContentfulClient(config)).withoutLinkResolution;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/converters/helpers.ts
|
|
194
|
+
function makeContentfulTitle(title, id, prefix = "Title for ") {
|
|
195
|
+
return title ?? `${prefix}${id}`;
|
|
196
|
+
}
|
|
197
|
+
function makeContentfulDescription(description, id) {
|
|
198
|
+
return description ?? `Description for ${id}`;
|
|
199
|
+
}
|
|
200
|
+
function lookupAsset(context, asset) {
|
|
201
|
+
if (!asset) return void 0;
|
|
202
|
+
return context.assets.get(asset.sys.id);
|
|
203
|
+
}
|
|
204
|
+
var DEFAULT_POSITION_FIELDS = {
|
|
205
|
+
index: 0,
|
|
206
|
+
isFirst: false,
|
|
207
|
+
isLast: false,
|
|
208
|
+
indexOfType: 0
|
|
209
|
+
};
|
|
210
|
+
function createInternalLink(id, fields, context, href, additionalProps) {
|
|
211
|
+
const { cmsLabel, title, featuredImage, backgroundColour, textColour, indexed, hidden, slug } = fields;
|
|
212
|
+
return {
|
|
213
|
+
type: "Internal link",
|
|
214
|
+
id,
|
|
215
|
+
name: cmsLabel,
|
|
216
|
+
useName: true,
|
|
217
|
+
text: makeContentfulTitle(title, id),
|
|
218
|
+
visual: lookupAsset(context, featuredImage),
|
|
219
|
+
backgroundColour,
|
|
220
|
+
textColour,
|
|
221
|
+
indexed,
|
|
222
|
+
hidden,
|
|
223
|
+
slug,
|
|
224
|
+
href,
|
|
225
|
+
...additionalProps
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function addPositionMetadata(items) {
|
|
229
|
+
if (items.length === 0) return items;
|
|
230
|
+
const typeCount = /* @__PURE__ */ new Map();
|
|
231
|
+
return items.map((item, index) => {
|
|
232
|
+
const currentTypeIndex = typeCount.get(item.type) ?? 0;
|
|
233
|
+
typeCount.set(item.type, currentTypeIndex + 1);
|
|
234
|
+
return {
|
|
235
|
+
...item,
|
|
236
|
+
index,
|
|
237
|
+
isFirst: index === 0,
|
|
238
|
+
isLast: index === items.length - 1,
|
|
239
|
+
indexOfType: currentTypeIndex
|
|
240
|
+
};
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// src/converters/asset.ts
|
|
245
|
+
function convertAssetToVisual(asset, options) {
|
|
246
|
+
if (!asset) return void 0;
|
|
247
|
+
const { fields, sys } = asset;
|
|
248
|
+
if (!fields) return void 0;
|
|
249
|
+
const { id } = sys;
|
|
250
|
+
const { file } = fields;
|
|
251
|
+
if (!file) return void 0;
|
|
252
|
+
const { contentType } = file;
|
|
253
|
+
if (contentType?.startsWith("image/")) {
|
|
254
|
+
const image = convertAssetToImage(file, fields, sys, options);
|
|
255
|
+
return {
|
|
256
|
+
id,
|
|
257
|
+
type: "Visual",
|
|
258
|
+
image
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (contentType?.startsWith("video/")) {
|
|
262
|
+
const video = convertAssetToVideo(file, fields, sys, options);
|
|
263
|
+
return {
|
|
264
|
+
id,
|
|
265
|
+
type: "Visual",
|
|
266
|
+
video
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return void 0;
|
|
270
|
+
}
|
|
271
|
+
function convertAssetToImage(file, fields, sys, options) {
|
|
272
|
+
const { id } = sys;
|
|
273
|
+
const { title, description } = fields;
|
|
274
|
+
const { contentType, details, url } = file;
|
|
275
|
+
const { size, image } = details;
|
|
276
|
+
const { width, height } = image || {};
|
|
277
|
+
return {
|
|
278
|
+
...options,
|
|
279
|
+
id,
|
|
280
|
+
type: "Picture",
|
|
281
|
+
src: `https:${url}`,
|
|
282
|
+
mimeType: contentType,
|
|
283
|
+
size,
|
|
284
|
+
width: width || 0,
|
|
285
|
+
height: height || 0,
|
|
286
|
+
name: makeContentfulTitle(title, id),
|
|
287
|
+
description: makeContentfulDescription(description, id)
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function convertAssetToVideoDetails(file, sys) {
|
|
291
|
+
const { id } = sys;
|
|
292
|
+
const { details, url, contentType, fileName } = file;
|
|
293
|
+
const { size } = details;
|
|
294
|
+
return {
|
|
295
|
+
id,
|
|
296
|
+
videoUrl: `https:${url}`,
|
|
297
|
+
size,
|
|
298
|
+
mimeType: contentType,
|
|
299
|
+
fileName
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function convertAssetToVideo(file, fields, sys, options) {
|
|
303
|
+
const { id } = sys;
|
|
304
|
+
const { title, description } = fields;
|
|
305
|
+
const { details } = file;
|
|
306
|
+
const { image } = details;
|
|
307
|
+
const { width, height } = image || {};
|
|
308
|
+
const videoDetails = convertAssetToVideoDetails(file, sys);
|
|
309
|
+
return {
|
|
310
|
+
...options,
|
|
311
|
+
id,
|
|
312
|
+
type: "Local video",
|
|
313
|
+
preview: videoDetails,
|
|
314
|
+
width: width || 0,
|
|
315
|
+
height: height || 0,
|
|
316
|
+
name: makeContentfulTitle(title, id),
|
|
317
|
+
description: makeContentfulDescription(description, id)
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function createResponsiveVisual(visual, mobileVisual, customSize) {
|
|
321
|
+
if (!visual) return void 0;
|
|
322
|
+
return {
|
|
323
|
+
visual,
|
|
324
|
+
mobileVisual,
|
|
325
|
+
visualCustomSize: customSize
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
function convertMediaEntryToVisual(context, entry) {
|
|
329
|
+
const { fields, sys } = entry;
|
|
330
|
+
if (!fields) return void 0;
|
|
331
|
+
const baseVisual = lookupAsset(context, fields.asset);
|
|
332
|
+
if (!baseVisual) return void 0;
|
|
333
|
+
const metadata = {
|
|
334
|
+
name: makeContentfulTitle(fields.name, sys.id),
|
|
335
|
+
nameAsCaption: fields.nameAsCaption ?? null,
|
|
336
|
+
dontCrop: fields.dontCrop ?? null,
|
|
337
|
+
verticalCropPosition: fields.verticalCropPosition ?? null,
|
|
338
|
+
horizontalCropPosition: fields.horizontalCropPosition ?? null,
|
|
339
|
+
horizontalPosition: fields.horizontalPosition ?? null,
|
|
340
|
+
widthPercent: fields.width ?? null
|
|
341
|
+
};
|
|
342
|
+
const visual = {
|
|
343
|
+
...baseVisual,
|
|
344
|
+
id: sys.id
|
|
345
|
+
};
|
|
346
|
+
if (baseVisual.image) {
|
|
347
|
+
visual.image = {
|
|
348
|
+
...baseVisual.image,
|
|
349
|
+
...metadata
|
|
350
|
+
};
|
|
351
|
+
} else if (baseVisual.video) {
|
|
352
|
+
visual.video = {
|
|
353
|
+
...baseVisual.video,
|
|
354
|
+
...metadata,
|
|
355
|
+
autoPlay: fields.autoplay ?? null,
|
|
356
|
+
loop: fields.loop ?? null,
|
|
357
|
+
hideControls: fields.hideControls ?? null
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
return visual;
|
|
361
|
+
}
|
|
362
|
+
function convertExternalVideoEntryToVisual(context, entry) {
|
|
363
|
+
const { fields, sys } = entry;
|
|
364
|
+
if (!fields || !fields.url) return void 0;
|
|
365
|
+
const previewVisual = lookupAsset(context, fields.preview);
|
|
366
|
+
const posterVisual = lookupAsset(context, fields.posterImage);
|
|
367
|
+
const preview = previewVisual?.video?.type === "Local video" ? previewVisual.video.preview : void 0;
|
|
368
|
+
const poster = posterVisual?.image?.type === "Picture" ? posterVisual.image.src : void 0;
|
|
369
|
+
const mapHorizontal = (value) => {
|
|
370
|
+
if (!value) return null;
|
|
371
|
+
return value === "Center" ? "Middle" : value;
|
|
372
|
+
};
|
|
373
|
+
const video = {
|
|
374
|
+
id: sys.id,
|
|
375
|
+
type: "External video",
|
|
376
|
+
name: makeContentfulTitle(fields.name, sys.id),
|
|
377
|
+
nameAsCaption: fields.nameAsCaption ?? null,
|
|
378
|
+
external: fields.url,
|
|
379
|
+
preview,
|
|
380
|
+
poster,
|
|
381
|
+
autoPlay: fields.autoPlay ?? null,
|
|
382
|
+
loop: fields.loop ?? null,
|
|
383
|
+
hideControls: fields.hideControls ?? null,
|
|
384
|
+
dontCrop: fields.dontCrop ?? null,
|
|
385
|
+
verticalCropPosition: fields.verticalCropPosition ?? null,
|
|
386
|
+
horizontalCropPosition: mapHorizontal(fields.horizontalCropPosition),
|
|
387
|
+
horizontalPosition: fields.horizontalPosition ?? null,
|
|
388
|
+
widthPercent: fields.width ?? null
|
|
389
|
+
};
|
|
390
|
+
return {
|
|
391
|
+
id: sys.id,
|
|
392
|
+
type: "Visual",
|
|
393
|
+
video
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
function lookupMediaEntry(context, link) {
|
|
397
|
+
if (!link) return void 0;
|
|
398
|
+
const id = link.sys.id;
|
|
399
|
+
const possibleEntry = context.includes.get(id);
|
|
400
|
+
if (!possibleEntry) {
|
|
401
|
+
return void 0;
|
|
402
|
+
}
|
|
403
|
+
const { type, entry } = possibleEntry;
|
|
404
|
+
if (type === "media") {
|
|
405
|
+
return convertMediaEntryToVisual(
|
|
406
|
+
context,
|
|
407
|
+
entry
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
if (type === "externalVideo") {
|
|
411
|
+
return convertExternalVideoEntryToVisual(
|
|
412
|
+
context,
|
|
413
|
+
entry
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
return void 0;
|
|
417
|
+
}
|
|
418
|
+
|
|
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
|
+
// src/converters/resolver.ts
|
|
446
|
+
function resolveHelper(context, entry, getResolver) {
|
|
447
|
+
const id = entry.sys.id;
|
|
448
|
+
const possibleEntry = context.includes.get(id);
|
|
449
|
+
if (!possibleEntry) {
|
|
450
|
+
throw new Error(`Cannot find included entry for link with id ${id}`);
|
|
451
|
+
}
|
|
452
|
+
if (!possibleEntry.resolved) {
|
|
453
|
+
const resolver = getResolver(possibleEntry.type);
|
|
454
|
+
if (!resolver) {
|
|
455
|
+
throw new Error(
|
|
456
|
+
`No resolver found for link type ${possibleEntry.type} (${JSON.stringify(possibleEntry)}) [${JSON.stringify(entry)}]`
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
const resolved = resolver(
|
|
460
|
+
context,
|
|
461
|
+
possibleEntry.entry
|
|
462
|
+
);
|
|
463
|
+
possibleEntry.resolved = resolved;
|
|
464
|
+
return resolved;
|
|
465
|
+
}
|
|
466
|
+
return possibleEntry.resolved;
|
|
467
|
+
}
|
|
468
|
+
function resolveLink(context, entry) {
|
|
469
|
+
return resolveHelper(
|
|
470
|
+
context,
|
|
471
|
+
entry,
|
|
472
|
+
(type) => context.linkResolver.get(type)
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
function resolveNavigationItem(context, entry) {
|
|
476
|
+
return resolveHelper(
|
|
477
|
+
context,
|
|
478
|
+
entry,
|
|
479
|
+
() => context.navigationItemResolver
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
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;
|
|
489
|
+
}
|
|
490
|
+
return void 0;
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
function resolvePageContent(context, entry) {
|
|
494
|
+
const id = entry.sys.id;
|
|
495
|
+
const possibleEntry = context.includes.get(id);
|
|
496
|
+
if (!possibleEntry) {
|
|
497
|
+
throw new Error(`Cannot find included entry for content with id ${id}`);
|
|
498
|
+
}
|
|
499
|
+
const { type } = possibleEntry;
|
|
500
|
+
if (type === "component" || type === "collection") {
|
|
501
|
+
return resolveCollectionContent(context, entry);
|
|
502
|
+
}
|
|
503
|
+
if (type === "media") {
|
|
504
|
+
const visual = convertMediaEntryToVisual(
|
|
505
|
+
context,
|
|
506
|
+
possibleEntry.entry
|
|
507
|
+
);
|
|
508
|
+
if (!visual) {
|
|
509
|
+
throw new Error(`Failed to convert media entry with id ${id}`);
|
|
510
|
+
}
|
|
511
|
+
return visual;
|
|
512
|
+
}
|
|
513
|
+
if (type === "externalVideo") {
|
|
514
|
+
const visual = convertExternalVideoEntryToVisual(
|
|
515
|
+
context,
|
|
516
|
+
possibleEntry.entry
|
|
517
|
+
);
|
|
518
|
+
if (!visual) {
|
|
519
|
+
throw new Error(`Failed to convert externalVideo entry with id ${id}`);
|
|
520
|
+
}
|
|
521
|
+
return visual;
|
|
522
|
+
}
|
|
523
|
+
if (type === "externalComponent") {
|
|
524
|
+
return resolveHelper(context, entry, () => baseExternalComponentConverter);
|
|
525
|
+
}
|
|
526
|
+
if (context.linkResolver.has(type)) {
|
|
527
|
+
return resolveLink(context, entry);
|
|
528
|
+
}
|
|
529
|
+
throw new Error(`Unknown content type "${type}" for entry with id ${id}`);
|
|
530
|
+
}
|
|
531
|
+
function resolveRichTextDocument(context, richText) {
|
|
532
|
+
if (!richText) {
|
|
533
|
+
return void 0;
|
|
534
|
+
}
|
|
535
|
+
const resolvingEntries = /* @__PURE__ */ new Set();
|
|
536
|
+
const resolvedEntries = /* @__PURE__ */ new Set();
|
|
537
|
+
const resolveNode = (node) => {
|
|
538
|
+
if (!("content" in node)) {
|
|
539
|
+
return node;
|
|
540
|
+
}
|
|
541
|
+
const block = node;
|
|
542
|
+
if (block.data.target && "sys" in block.data.target && block.data.target.sys.linkType === "Entry") {
|
|
543
|
+
const entryId = block.data.target.sys.id;
|
|
544
|
+
if (resolvingEntries.has(entryId)) {
|
|
545
|
+
console.warn(
|
|
546
|
+
`Circular reference detected in rich text resolution for entry ${entryId}. Skipping to prevent infinite recursion.`
|
|
547
|
+
);
|
|
548
|
+
return {
|
|
549
|
+
...block,
|
|
550
|
+
content: block.content?.map(resolveNode)
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
if (resolvedEntries.has(entryId)) {
|
|
554
|
+
return {
|
|
555
|
+
...block,
|
|
556
|
+
content: block.content?.map(resolveNode)
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
try {
|
|
560
|
+
resolvingEntries.add(entryId);
|
|
561
|
+
const resolvedEntry = resolvePageContent(
|
|
562
|
+
context,
|
|
563
|
+
block.data.target
|
|
564
|
+
);
|
|
565
|
+
resolvingEntries.delete(entryId);
|
|
566
|
+
resolvedEntries.add(entryId);
|
|
567
|
+
return {
|
|
568
|
+
...block,
|
|
569
|
+
data: {
|
|
570
|
+
...block.data,
|
|
571
|
+
target: resolvedEntry
|
|
572
|
+
},
|
|
573
|
+
content: block.content?.map(resolveNode)
|
|
574
|
+
};
|
|
575
|
+
} catch (error) {
|
|
576
|
+
resolvingEntries.delete(entryId);
|
|
577
|
+
console.error(`Failed to resolve entry with id ${entryId}:`, error);
|
|
578
|
+
return {
|
|
579
|
+
...block,
|
|
580
|
+
content: block.content?.map(resolveNode)
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
if (block.data.target && "sys" in block.data.target && block.data.target.sys.linkType === "Asset") {
|
|
585
|
+
try {
|
|
586
|
+
const resolvedAsset = lookupAsset(context, block.data.target);
|
|
587
|
+
if (resolvedAsset) {
|
|
588
|
+
return {
|
|
589
|
+
...block,
|
|
590
|
+
data: {
|
|
591
|
+
...block.data,
|
|
592
|
+
target: resolvedAsset
|
|
593
|
+
},
|
|
594
|
+
content: block.content?.map(resolveNode)
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
} catch (error) {
|
|
598
|
+
console.error(
|
|
599
|
+
`Failed to resolve embedded-asset-block with id ${block.data.target.sys.id}:`,
|
|
600
|
+
error
|
|
601
|
+
);
|
|
602
|
+
return {
|
|
603
|
+
...block,
|
|
604
|
+
content: block.content?.map(resolveNode)
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return {
|
|
609
|
+
...block,
|
|
610
|
+
content: block.content?.map(resolveNode)
|
|
611
|
+
};
|
|
612
|
+
};
|
|
613
|
+
const resolved = resolveNode(richText);
|
|
614
|
+
return { json: resolved };
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// src/converters/collection.ts
|
|
618
|
+
function baseCollectionConverter(context, entry) {
|
|
619
|
+
const { sys, fields } = entry;
|
|
620
|
+
const {
|
|
621
|
+
// Fields requiring transformation
|
|
622
|
+
backgroundVisual: bgVisual,
|
|
623
|
+
mobileBackgroundVisual: mobileBgVisual,
|
|
624
|
+
visual: visualField,
|
|
625
|
+
mobileVisual: mobileVisualField,
|
|
626
|
+
visualCustomSize,
|
|
627
|
+
icon: iconField,
|
|
628
|
+
links: linksField,
|
|
629
|
+
contents: contentsField,
|
|
630
|
+
body: bodyField,
|
|
631
|
+
additionalCopy: additionalCopyField,
|
|
632
|
+
// Field name change
|
|
633
|
+
collectionType,
|
|
634
|
+
// CMS-only fields (must exclude)
|
|
635
|
+
textSize: _textSize,
|
|
636
|
+
showHeading: _showHeading,
|
|
637
|
+
// Already handled elsewhere
|
|
638
|
+
cmsLabel,
|
|
639
|
+
...simpleFields
|
|
640
|
+
// anchor, backgroundColour, textColour, preHeading, heading, postHeading, backgroundOverlayOpacity
|
|
641
|
+
} = fields;
|
|
642
|
+
const backgroundVisual = createResponsiveVisual(
|
|
643
|
+
lookupAsset(context, bgVisual),
|
|
644
|
+
lookupAsset(context, mobileBgVisual)
|
|
645
|
+
);
|
|
646
|
+
const visual = createResponsiveVisual(
|
|
647
|
+
lookupMediaEntry(context, visualField),
|
|
648
|
+
lookupMediaEntry(context, mobileVisualField),
|
|
649
|
+
visualCustomSize
|
|
650
|
+
);
|
|
651
|
+
return {
|
|
652
|
+
type: "Collection",
|
|
653
|
+
id: sys.id,
|
|
654
|
+
name: cmsLabel,
|
|
655
|
+
cmsLabel,
|
|
656
|
+
collectionType,
|
|
657
|
+
...DEFAULT_POSITION_FIELDS,
|
|
658
|
+
...simpleFields,
|
|
659
|
+
body: resolveRichTextDocument(context, bodyField),
|
|
660
|
+
additionalCopy: resolveRichTextDocument(context, additionalCopyField),
|
|
661
|
+
icon: lookupAsset(context, iconField),
|
|
662
|
+
backgroundVisual,
|
|
663
|
+
visual,
|
|
664
|
+
links: linksField?.map((link) => resolveLink(context, link)),
|
|
665
|
+
contents: contentsField?.map((content) => resolveCollectionContent(context, content))
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// src/converters/component.ts
|
|
670
|
+
function baseComponentConverter(context, entry) {
|
|
671
|
+
const { sys, fields } = entry;
|
|
672
|
+
const {
|
|
673
|
+
// Fields requiring transformation
|
|
674
|
+
backgroundVisual: bgVisual,
|
|
675
|
+
mobileBackgroundVisual: mobileBgVisual,
|
|
676
|
+
visual: visualField,
|
|
677
|
+
mobileVisual: mobileVisualField,
|
|
678
|
+
visualCustomSize,
|
|
679
|
+
icon: iconField,
|
|
680
|
+
links: linksField,
|
|
681
|
+
body: bodyField,
|
|
682
|
+
additionalCopy: additionalCopyField,
|
|
683
|
+
// Field name change
|
|
684
|
+
componentType,
|
|
685
|
+
// CMS-only fields (must exclude)
|
|
686
|
+
textSize: _textSize,
|
|
687
|
+
showHeading: _showHeading,
|
|
688
|
+
otherMedia: _otherMedia,
|
|
689
|
+
otherVisuals: _otherVisuals,
|
|
690
|
+
// Already handled elsewhere
|
|
691
|
+
cmsLabel,
|
|
692
|
+
...simpleFields
|
|
693
|
+
// anchor, backgroundColour, textColour, preHeading, heading, postHeading, backgroundOverlayOpacity
|
|
694
|
+
} = fields;
|
|
695
|
+
const backgroundVisual = createResponsiveVisual(
|
|
696
|
+
lookupAsset(context, bgVisual),
|
|
697
|
+
lookupAsset(context, mobileBgVisual)
|
|
698
|
+
);
|
|
699
|
+
const visual = createResponsiveVisual(
|
|
700
|
+
lookupMediaEntry(context, visualField),
|
|
701
|
+
lookupMediaEntry(context, mobileVisualField),
|
|
702
|
+
visualCustomSize
|
|
703
|
+
);
|
|
704
|
+
return {
|
|
705
|
+
type: "Component",
|
|
706
|
+
id: sys.id,
|
|
707
|
+
name: cmsLabel,
|
|
708
|
+
cmsLabel,
|
|
709
|
+
componentType,
|
|
710
|
+
...DEFAULT_POSITION_FIELDS,
|
|
711
|
+
...simpleFields,
|
|
712
|
+
body: resolveRichTextDocument(context, bodyField),
|
|
713
|
+
additionalCopy: resolveRichTextDocument(context, additionalCopyField),
|
|
714
|
+
icon: lookupAsset(context, iconField),
|
|
715
|
+
backgroundVisual,
|
|
716
|
+
visual,
|
|
717
|
+
links: linksField?.map((link) => resolveLink(context, link))
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// src/converters/link.ts
|
|
722
|
+
function baseLinkConverter(context, entry) {
|
|
723
|
+
const { sys, fields } = entry;
|
|
724
|
+
if (sys.contentType.sys.id !== "link") {
|
|
725
|
+
throw new Error(`Invalid content type: expected "link", got "${sys.contentType.sys.id}"`);
|
|
726
|
+
}
|
|
727
|
+
const id = sys.id;
|
|
728
|
+
const name = fields.name;
|
|
729
|
+
const useName = fields.useName;
|
|
730
|
+
const text = useName ? name : fields.linkText ?? makeContentfulTitle(fields.linkText, id, "Link text for ");
|
|
731
|
+
const icon = lookupAsset(context, fields.icon);
|
|
732
|
+
const backgroundColour = fields.backgroundColour ?? null;
|
|
733
|
+
const textColour = fields.textColour ?? null;
|
|
734
|
+
const variant = fields.variant;
|
|
735
|
+
const size = fields.size;
|
|
736
|
+
const baseProps = {
|
|
737
|
+
id,
|
|
738
|
+
useName,
|
|
739
|
+
name,
|
|
740
|
+
text,
|
|
741
|
+
icon,
|
|
742
|
+
backgroundColour,
|
|
743
|
+
textColour,
|
|
744
|
+
variant,
|
|
745
|
+
size
|
|
746
|
+
};
|
|
747
|
+
if (fields.internal) {
|
|
748
|
+
const internalTarget = resolveLink(context, fields.internal);
|
|
749
|
+
return {
|
|
750
|
+
...baseProps,
|
|
751
|
+
type: "Internal link",
|
|
752
|
+
href: internalTarget.href,
|
|
753
|
+
slug: internalTarget.slug,
|
|
754
|
+
indexed: internalTarget.indexed,
|
|
755
|
+
hidden: internalTarget.hidden,
|
|
756
|
+
tags: internalTarget.tags
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
if (fields.external) {
|
|
760
|
+
return {
|
|
761
|
+
...baseProps,
|
|
762
|
+
type: "External link",
|
|
763
|
+
href: fields.external
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
if (fields.downloadAsset) {
|
|
767
|
+
const asset = lookupAsset(context, fields.downloadAsset);
|
|
768
|
+
let href = null;
|
|
769
|
+
if (asset?.image?.type === "Picture") {
|
|
770
|
+
href = asset.image.src;
|
|
771
|
+
} else if (asset?.image?.type === "Svg image") {
|
|
772
|
+
href = asset.image.svgSrc;
|
|
773
|
+
} else if (asset?.video) {
|
|
774
|
+
if (asset.video.type === "Local video") {
|
|
775
|
+
href = asset.video.preview.videoUrl;
|
|
776
|
+
} else if (asset.video.type === "Full video") {
|
|
777
|
+
href = asset.video.full.videoUrl;
|
|
778
|
+
} else if (asset.video.type === "External video") {
|
|
779
|
+
href = asset.video.external;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return {
|
|
783
|
+
...baseProps,
|
|
784
|
+
type: "Download link",
|
|
785
|
+
href,
|
|
786
|
+
visual: asset
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
return {
|
|
790
|
+
...baseProps,
|
|
791
|
+
type: "Blank link",
|
|
792
|
+
href: null
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// src/converters/template.ts
|
|
797
|
+
function resolveTemplate(context, link) {
|
|
798
|
+
const id = link.sys.id;
|
|
799
|
+
const possibleEntry = context.includes.get(id);
|
|
800
|
+
if (!possibleEntry || possibleEntry.type !== "template") {
|
|
801
|
+
return null;
|
|
802
|
+
}
|
|
803
|
+
const entry = possibleEntry.entry;
|
|
804
|
+
const { fields } = entry;
|
|
805
|
+
if (!fields) {
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
const preContent = fields.preContent?.map((content) => resolvePageContent(context, content)) ?? [];
|
|
809
|
+
const postContent = fields.postContent?.map((content) => resolvePageContent(context, content)) ?? [];
|
|
810
|
+
const menu = fields.menu ? resolveNavigation(context, fields.menu) : void 0;
|
|
811
|
+
const footer = fields.footer ? resolveNavigation(context, fields.footer) : void 0;
|
|
812
|
+
const stickyNav = fields.flags?.includes("Sticky nav") ?? false;
|
|
813
|
+
const { backgroundColour, textColour } = fields;
|
|
814
|
+
return {
|
|
815
|
+
preContent,
|
|
816
|
+
postContent,
|
|
817
|
+
menu,
|
|
818
|
+
footer,
|
|
819
|
+
backgroundColour,
|
|
820
|
+
textColour,
|
|
821
|
+
stickyNav
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
function resolveNavigation(context, link) {
|
|
825
|
+
const id = link.sys.id;
|
|
826
|
+
const possibleEntry = context.includes.get(id);
|
|
827
|
+
if (!possibleEntry || possibleEntry.type !== "navigation") {
|
|
828
|
+
return void 0;
|
|
829
|
+
}
|
|
830
|
+
const entry = possibleEntry.entry;
|
|
831
|
+
const { sys, fields } = entry;
|
|
832
|
+
if (!fields) {
|
|
833
|
+
return void 0;
|
|
834
|
+
}
|
|
835
|
+
const entries = fields.entries?.map((item) => resolveNavigationItem(context, item)) ?? [];
|
|
836
|
+
const { name, textColour, backgroundColour } = fields;
|
|
837
|
+
return {
|
|
838
|
+
id: sys.id,
|
|
839
|
+
name,
|
|
840
|
+
entries,
|
|
841
|
+
textColour,
|
|
842
|
+
backgroundColour
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// src/converters/page.ts
|
|
847
|
+
function basePageConverter(context, entry) {
|
|
848
|
+
const { sys, fields } = entry;
|
|
849
|
+
const {
|
|
850
|
+
slug,
|
|
851
|
+
title,
|
|
852
|
+
description,
|
|
853
|
+
featuredImage,
|
|
854
|
+
tags,
|
|
855
|
+
content,
|
|
856
|
+
template: templateLink,
|
|
857
|
+
topContent: topContentLinks,
|
|
858
|
+
bottomContent: bottomContentLinks,
|
|
859
|
+
...simpleFields
|
|
860
|
+
} = fields;
|
|
861
|
+
const template = templateLink ? resolveTemplate(context, templateLink) : null;
|
|
862
|
+
const topContent = topContentLinks?.map((c) => resolvePageContent(context, c)) ?? [];
|
|
863
|
+
const pageContent = content?.map((c) => resolvePageContent(context, c)) ?? [];
|
|
864
|
+
const bottomContent = bottomContentLinks?.map((c) => resolvePageContent(context, c)) ?? [];
|
|
865
|
+
const preContent = template?.preContent ?? [];
|
|
866
|
+
const postContent = template?.postContent ?? [];
|
|
867
|
+
const contents = addPositionMetadata([
|
|
868
|
+
...topContent,
|
|
869
|
+
...preContent,
|
|
870
|
+
...pageContent,
|
|
871
|
+
...postContent,
|
|
872
|
+
...bottomContent
|
|
873
|
+
]);
|
|
874
|
+
return {
|
|
875
|
+
type: "Page",
|
|
876
|
+
id: sys.id,
|
|
877
|
+
isHomePage: slug === "index",
|
|
878
|
+
slug,
|
|
879
|
+
title: makeContentfulTitle(title, sys.id),
|
|
880
|
+
description: makeContentfulDescription(description, sys.id),
|
|
881
|
+
featuredImage: lookupAsset(context, featuredImage),
|
|
882
|
+
tags: tags?.map((tag) => resolveLink(context, tag)),
|
|
883
|
+
contents,
|
|
884
|
+
...simpleFields,
|
|
885
|
+
menu: template?.menu,
|
|
886
|
+
footer: template?.footer
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
function calculatePageHref(slug) {
|
|
890
|
+
if (slug === "index") {
|
|
891
|
+
return "/";
|
|
892
|
+
}
|
|
893
|
+
return `/${slug}/`;
|
|
894
|
+
}
|
|
895
|
+
function basePageLinkConverter(context, entry) {
|
|
896
|
+
const { sys, fields } = entry;
|
|
897
|
+
if (sys.contentType.sys.id !== "page") {
|
|
898
|
+
throw new Error(`Invalid content type: expected "page", got "${sys.contentType.sys.id}"`);
|
|
899
|
+
}
|
|
900
|
+
return createInternalLink(
|
|
901
|
+
sys.id,
|
|
902
|
+
{
|
|
903
|
+
cmsLabel: fields.cmsLabel,
|
|
904
|
+
title: fields.title,
|
|
905
|
+
featuredImage: fields.featuredImage,
|
|
906
|
+
backgroundColour: fields.backgroundColour,
|
|
907
|
+
textColour: fields.textColour,
|
|
908
|
+
indexed: fields.indexed,
|
|
909
|
+
hidden: fields.hidden,
|
|
910
|
+
slug: fields.slug
|
|
911
|
+
},
|
|
912
|
+
context,
|
|
913
|
+
calculatePageHref(fields.slug),
|
|
914
|
+
{ tags: fields.tags?.map((tag) => resolveLink(context, tag)) }
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
function calculatePageVariantHref(slug) {
|
|
918
|
+
return `/${slug}/`;
|
|
919
|
+
}
|
|
920
|
+
function basePageVariantLinkConverter(context, entry) {
|
|
921
|
+
const { sys, fields } = entry;
|
|
922
|
+
if (sys.contentType.sys.id !== "pageVariant") {
|
|
923
|
+
throw new Error(
|
|
924
|
+
`Invalid content type: expected "pageVariant", got "${sys.contentType.sys.id}"`
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
return createInternalLink(
|
|
928
|
+
sys.id,
|
|
929
|
+
{
|
|
930
|
+
cmsLabel: fields.cmsLabel,
|
|
931
|
+
title: fields.title,
|
|
932
|
+
featuredImage: fields.featuredImage,
|
|
933
|
+
backgroundColour: fields.backgroundColour,
|
|
934
|
+
textColour: fields.textColour,
|
|
935
|
+
indexed: fields.indexed,
|
|
936
|
+
hidden: fields.hidden,
|
|
937
|
+
slug: fields.slug
|
|
938
|
+
},
|
|
939
|
+
context,
|
|
940
|
+
calculatePageVariantHref(fields.slug),
|
|
941
|
+
{ tags: fields.tags?.map((tag) => resolveLink(context, tag)) }
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// src/converters/person.ts
|
|
946
|
+
function calculatePersonHref(slug) {
|
|
947
|
+
return `/people/${slug}/`;
|
|
948
|
+
}
|
|
949
|
+
function basePersonLinkConverter(context, entry) {
|
|
950
|
+
const { sys, fields } = entry;
|
|
951
|
+
if (sys.contentType.sys.id !== "person") {
|
|
952
|
+
throw new Error(`Invalid content type: expected "person", got "${sys.contentType.sys.id}"`);
|
|
953
|
+
}
|
|
954
|
+
return createInternalLink(
|
|
955
|
+
sys.id,
|
|
956
|
+
{
|
|
957
|
+
cmsLabel: fields.name,
|
|
958
|
+
// Person has no cmsLabel, use name field
|
|
959
|
+
title: fields.name,
|
|
960
|
+
featuredImage: fields.media,
|
|
961
|
+
// Person uses 'media' not 'featuredImage'
|
|
962
|
+
backgroundColour: fields.backgroundColour,
|
|
963
|
+
textColour: fields.textColour,
|
|
964
|
+
indexed: fields.indexed,
|
|
965
|
+
hidden: fields.hidden,
|
|
966
|
+
slug: fields.slug
|
|
967
|
+
},
|
|
968
|
+
context,
|
|
969
|
+
calculatePersonHref(fields.slug)
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// src/converters/article.ts
|
|
974
|
+
function baseArticleConverter(context, entry) {
|
|
975
|
+
const { sys, fields } = entry;
|
|
976
|
+
const {
|
|
977
|
+
slug,
|
|
978
|
+
title,
|
|
979
|
+
description,
|
|
980
|
+
featuredImage,
|
|
981
|
+
tags,
|
|
982
|
+
content,
|
|
983
|
+
template: templateLink,
|
|
984
|
+
topContent: topContentLinks,
|
|
985
|
+
bottomContent: bottomContentLinks,
|
|
986
|
+
articleType,
|
|
987
|
+
...simpleFields
|
|
988
|
+
} = fields;
|
|
989
|
+
const articleTypeLink = resolveLink(context, articleType);
|
|
990
|
+
const template = templateLink ? resolveTemplate(context, templateLink) : null;
|
|
991
|
+
const topContent = topContentLinks?.map((c) => resolvePageContent(context, c)) ?? [];
|
|
992
|
+
const articleContent = content?.map((c) => resolvePageContent(context, c)) ?? [];
|
|
993
|
+
const bottomContent = bottomContentLinks?.map((c) => resolvePageContent(context, c)) ?? [];
|
|
994
|
+
const preContent = template?.preContent ?? [];
|
|
995
|
+
const postContent = template?.postContent ?? [];
|
|
996
|
+
const contents = addPositionMetadata([
|
|
997
|
+
...topContent,
|
|
998
|
+
...preContent,
|
|
999
|
+
...articleContent,
|
|
1000
|
+
...postContent,
|
|
1001
|
+
...bottomContent
|
|
1002
|
+
]);
|
|
1003
|
+
return {
|
|
1004
|
+
type: "Article",
|
|
1005
|
+
id: sys.id,
|
|
1006
|
+
slug,
|
|
1007
|
+
title: makeContentfulTitle(title, sys.id),
|
|
1008
|
+
description: makeContentfulDescription(description, sys.id),
|
|
1009
|
+
featuredImage: lookupAsset(context, featuredImage),
|
|
1010
|
+
articleType: articleTypeLink,
|
|
1011
|
+
tags: tags?.map((tag) => resolveLink(context, tag)),
|
|
1012
|
+
contents,
|
|
1013
|
+
...simpleFields,
|
|
1014
|
+
// Note: summary field exists in Contentful but is not part of IArticle interface
|
|
1015
|
+
// Keeping it in simpleFields for potential future use
|
|
1016
|
+
menu: template?.menu,
|
|
1017
|
+
footer: template?.footer
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
function calculateArticleTypeHref(slug) {
|
|
1021
|
+
return `/${slug}/`;
|
|
1022
|
+
}
|
|
1023
|
+
function calculateArticleHref(articleTypeSlug, slug) {
|
|
1024
|
+
return `${calculateArticleTypeHref(articleTypeSlug)}${slug}/`;
|
|
1025
|
+
}
|
|
1026
|
+
function baseArticleLinkConverter(context, entry) {
|
|
1027
|
+
const { sys, fields } = entry;
|
|
1028
|
+
if (sys.contentType.sys.id !== "article") {
|
|
1029
|
+
throw new Error(`Invalid content type: expected "article", got "${sys.contentType.sys.id}"`);
|
|
1030
|
+
}
|
|
1031
|
+
const articleTypeLink = resolveLink(context, fields.articleType);
|
|
1032
|
+
return createInternalLink(
|
|
1033
|
+
sys.id,
|
|
1034
|
+
{
|
|
1035
|
+
cmsLabel: fields.cmsLabel,
|
|
1036
|
+
title: fields.title,
|
|
1037
|
+
featuredImage: fields.featuredImage,
|
|
1038
|
+
backgroundColour: fields.backgroundColour,
|
|
1039
|
+
textColour: fields.textColour,
|
|
1040
|
+
indexed: fields.indexed,
|
|
1041
|
+
hidden: fields.hidden,
|
|
1042
|
+
slug: fields.slug
|
|
1043
|
+
},
|
|
1044
|
+
context,
|
|
1045
|
+
calculateArticleHref(articleTypeLink.slug, fields.slug),
|
|
1046
|
+
{ tags: fields.tags?.map((tag) => resolveLink(context, tag)) }
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
function baseArticleTypeLinkConverter(context, entry) {
|
|
1050
|
+
const { sys, fields } = entry;
|
|
1051
|
+
if (sys.contentType.sys.id !== "articleType") {
|
|
1052
|
+
throw new Error(
|
|
1053
|
+
`Invalid content type: expected "articleType", got "${sys.contentType.sys.id}"`
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
return createInternalLink(
|
|
1057
|
+
sys.id,
|
|
1058
|
+
{
|
|
1059
|
+
cmsLabel: fields.name,
|
|
1060
|
+
title: fields.name,
|
|
1061
|
+
featuredImage: fields.featuredImage,
|
|
1062
|
+
backgroundColour: fields.backgroundColour,
|
|
1063
|
+
textColour: fields.textColour,
|
|
1064
|
+
indexed: fields.indexed,
|
|
1065
|
+
hidden: fields.hidden,
|
|
1066
|
+
slug: fields.slug
|
|
1067
|
+
},
|
|
1068
|
+
context,
|
|
1069
|
+
calculateArticleTypeHref(fields.slug)
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// src/converters/navigationItem.ts
|
|
1074
|
+
function createLink(context, entry) {
|
|
1075
|
+
const {
|
|
1076
|
+
sys: { id },
|
|
1077
|
+
fields
|
|
1078
|
+
} = entry;
|
|
1079
|
+
const { title, link, internal, icon, useTitle, ...otherFields } = fields;
|
|
1080
|
+
if (link) {
|
|
1081
|
+
return {
|
|
1082
|
+
type: "External link",
|
|
1083
|
+
id,
|
|
1084
|
+
href: link,
|
|
1085
|
+
icon: lookupAsset(context, icon),
|
|
1086
|
+
name: makeContentfulTitle(title, id),
|
|
1087
|
+
...useTitle && { text: title },
|
|
1088
|
+
...otherFields
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
if (internal) {
|
|
1092
|
+
const resolved = resolveLink(context, internal);
|
|
1093
|
+
return resolved;
|
|
1094
|
+
}
|
|
1095
|
+
return void 0;
|
|
1096
|
+
}
|
|
1097
|
+
function baseNavigationItemConverter(context, entry) {
|
|
1098
|
+
const { sys, fields } = entry;
|
|
1099
|
+
const { longText, navigationItems } = fields;
|
|
1100
|
+
const link = createLink(context, entry);
|
|
1101
|
+
const resolvedNavigationItems = navigationItems?.map((item) => resolveNavigationItem(context, item)).filter((item) => item !== void 0);
|
|
1102
|
+
return {
|
|
1103
|
+
id: sys.id,
|
|
1104
|
+
longText,
|
|
1105
|
+
link,
|
|
1106
|
+
entries: resolvedNavigationItems
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// src/converters/tag.ts
|
|
1111
|
+
function calculateTagHref(slug) {
|
|
1112
|
+
return `/tag/${slug}/`;
|
|
1113
|
+
}
|
|
1114
|
+
function baseTagLinkConverter(context, entry) {
|
|
1115
|
+
const { sys, fields } = entry;
|
|
1116
|
+
if (sys.contentType.sys.id !== "tag") {
|
|
1117
|
+
throw new Error(`Invalid content type: expected "tag", got "${sys.contentType.sys.id}"`);
|
|
1118
|
+
}
|
|
1119
|
+
return createInternalLink(
|
|
1120
|
+
sys.id,
|
|
1121
|
+
{
|
|
1122
|
+
cmsLabel: fields.cmsLabel,
|
|
1123
|
+
title: fields.name,
|
|
1124
|
+
featuredImage: fields.featuredImage,
|
|
1125
|
+
backgroundColour: fields.backgroundColour,
|
|
1126
|
+
textColour: fields.textColour,
|
|
1127
|
+
indexed: fields.indexed,
|
|
1128
|
+
hidden: fields.hidden,
|
|
1129
|
+
slug: fields.slug
|
|
1130
|
+
},
|
|
1131
|
+
context,
|
|
1132
|
+
calculateTagHref(fields.slug)
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
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
|
+
// src/utils/retry.ts
|
|
1201
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
1202
|
+
maxRetries: 3,
|
|
1203
|
+
initialDelay: 1e3,
|
|
1204
|
+
// 1 second
|
|
1205
|
+
maxDelay: 3e4,
|
|
1206
|
+
// 30 seconds
|
|
1207
|
+
backoffMultiplier: 2
|
|
1208
|
+
};
|
|
1209
|
+
function sleep(ms) {
|
|
1210
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1211
|
+
}
|
|
1212
|
+
function calculateBackoffDelay(attempt, config, retryAfter) {
|
|
1213
|
+
if (retryAfter !== void 0) {
|
|
1214
|
+
return Math.min(retryAfter * 1e3, config.maxDelay);
|
|
1215
|
+
}
|
|
1216
|
+
const exponentialDelay = config.initialDelay * config.backoffMultiplier ** attempt;
|
|
1217
|
+
const jitter = Math.random() * exponentialDelay;
|
|
1218
|
+
return Math.min(exponentialDelay + jitter, config.maxDelay);
|
|
1219
|
+
}
|
|
1220
|
+
async function withRetry(fn, config) {
|
|
1221
|
+
const retryConfig = {
|
|
1222
|
+
...DEFAULT_RETRY_CONFIG,
|
|
1223
|
+
...config
|
|
1224
|
+
};
|
|
1225
|
+
let lastError;
|
|
1226
|
+
for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
|
|
1227
|
+
try {
|
|
1228
|
+
return await fn();
|
|
1229
|
+
} catch (error) {
|
|
1230
|
+
lastError = error;
|
|
1231
|
+
if (attempt === retryConfig.maxRetries) {
|
|
1232
|
+
break;
|
|
1233
|
+
}
|
|
1234
|
+
if (!isRetryableError(error)) {
|
|
1235
|
+
throw error;
|
|
1236
|
+
}
|
|
1237
|
+
const retryAfter = getRetryAfter(error);
|
|
1238
|
+
const delay = calculateBackoffDelay(attempt, retryConfig, retryAfter);
|
|
1239
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") {
|
|
1240
|
+
console.warn(
|
|
1241
|
+
`Retry attempt ${attempt + 1}/${retryConfig.maxRetries} after ${delay}ms`,
|
|
1242
|
+
error
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
await sleep(delay);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
throw lastError;
|
|
1249
|
+
}
|
|
1250
|
+
var RateLimiter = class {
|
|
1251
|
+
constructor(maxTokens, refillRate) {
|
|
1252
|
+
this.maxTokens = maxTokens;
|
|
1253
|
+
this.refillRate = refillRate;
|
|
1254
|
+
this.tokens = maxTokens;
|
|
1255
|
+
this.lastRefill = Date.now();
|
|
1256
|
+
}
|
|
1257
|
+
tokens;
|
|
1258
|
+
lastRefill;
|
|
1259
|
+
/**
|
|
1260
|
+
* Refills tokens based on time elapsed
|
|
1261
|
+
*/
|
|
1262
|
+
refill() {
|
|
1263
|
+
const now = Date.now();
|
|
1264
|
+
const timePassed = (now - this.lastRefill) / 1e3;
|
|
1265
|
+
const tokensToAdd = timePassed * this.refillRate;
|
|
1266
|
+
this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
|
|
1267
|
+
this.lastRefill = now;
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Attempts to consume a token
|
|
1271
|
+
* @returns true if token was consumed, false if rate limited
|
|
1272
|
+
*/
|
|
1273
|
+
tryConsume() {
|
|
1274
|
+
this.refill();
|
|
1275
|
+
if (this.tokens >= 1) {
|
|
1276
|
+
this.tokens -= 1;
|
|
1277
|
+
return true;
|
|
1278
|
+
}
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Waits until a token is available and consumes it
|
|
1283
|
+
*/
|
|
1284
|
+
async consume() {
|
|
1285
|
+
while (!this.tryConsume()) {
|
|
1286
|
+
const waitTime = 1 / this.refillRate * 1e3;
|
|
1287
|
+
await sleep(waitTime);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* Gets current number of available tokens
|
|
1292
|
+
*/
|
|
1293
|
+
getAvailableTokens() {
|
|
1294
|
+
this.refill();
|
|
1295
|
+
return Math.floor(this.tokens);
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
|
|
1299
|
+
// src/utils/index.ts
|
|
1300
|
+
init_timing();
|
|
1301
|
+
|
|
1302
|
+
// src/api.ts
|
|
1303
|
+
function convertAllAssets(response) {
|
|
1304
|
+
const visuals = /* @__PURE__ */ new Map();
|
|
1305
|
+
const assets = response.includes?.Asset;
|
|
1306
|
+
if (assets && assets.length > 0) {
|
|
1307
|
+
for (const asset of assets) {
|
|
1308
|
+
const visual = convertAssetToVisual(asset);
|
|
1309
|
+
if (visual) {
|
|
1310
|
+
visuals.set(visual.id, visual);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
return visuals;
|
|
1315
|
+
}
|
|
1316
|
+
function convertAllIncludes(response) {
|
|
1317
|
+
const includes = /* @__PURE__ */ new Map();
|
|
1318
|
+
const entries = [...response.items, ...response.includes?.Entry || []];
|
|
1319
|
+
if (entries && entries.length > 0) {
|
|
1320
|
+
for (const entry of entries) {
|
|
1321
|
+
if (entry?.sys && entry.fields) {
|
|
1322
|
+
includes.set(entry.sys.id, {
|
|
1323
|
+
id: entry.sys.id,
|
|
1324
|
+
type: entry.sys.contentType.sys.id,
|
|
1325
|
+
entry
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
return includes;
|
|
1331
|
+
}
|
|
1332
|
+
async function contentfulPageRest(context, config, slug, options) {
|
|
1333
|
+
const totalTimer = new Timer(`contentfulPageRest(${slug})`);
|
|
1334
|
+
const client = getContentfulClient(config, options?.preview);
|
|
1335
|
+
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();
|
|
1345
|
+
const pageEntry = response.items[0];
|
|
1346
|
+
if (!pageEntry || !pageEntry.fields) {
|
|
1347
|
+
totalTimer.endAndLog();
|
|
1348
|
+
return null;
|
|
1349
|
+
}
|
|
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");
|
|
1355
|
+
const includes = convertAllIncludes(response);
|
|
1356
|
+
const includesTiming = includesTimer.end();
|
|
1357
|
+
includesTiming.metadata = { count: includes.size };
|
|
1358
|
+
const fullContext = {
|
|
1359
|
+
...context,
|
|
1360
|
+
includes,
|
|
1361
|
+
assets
|
|
1362
|
+
};
|
|
1363
|
+
const conversionTimer = new Timer("Page conversion");
|
|
1364
|
+
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
|
+
return converted;
|
|
1371
|
+
};
|
|
1372
|
+
if (options?.retry) {
|
|
1373
|
+
return await withRetry(fetchFn, options.retry);
|
|
1374
|
+
}
|
|
1375
|
+
return await fetchFn();
|
|
1376
|
+
}
|
|
1377
|
+
function createBaseConverterContext() {
|
|
1378
|
+
const linkResolver = /* @__PURE__ */ new Map();
|
|
1379
|
+
linkResolver.set("page", basePageLinkConverter);
|
|
1380
|
+
linkResolver.set("article", baseArticleLinkConverter);
|
|
1381
|
+
linkResolver.set(
|
|
1382
|
+
"articleType",
|
|
1383
|
+
baseArticleTypeLinkConverter
|
|
1384
|
+
);
|
|
1385
|
+
linkResolver.set("tag", baseTagLinkConverter);
|
|
1386
|
+
linkResolver.set("person", basePersonLinkConverter);
|
|
1387
|
+
linkResolver.set(
|
|
1388
|
+
"pageVariant",
|
|
1389
|
+
basePageVariantLinkConverter
|
|
1390
|
+
);
|
|
1391
|
+
linkResolver.set("link", baseLinkConverter);
|
|
1392
|
+
return {
|
|
1393
|
+
pageResolver: basePageConverter,
|
|
1394
|
+
navigationItemResolver: baseNavigationItemConverter,
|
|
1395
|
+
articleResolver: baseArticleConverter,
|
|
1396
|
+
componentResolver: baseComponentConverter,
|
|
1397
|
+
collectionResolver: baseCollectionConverter,
|
|
1398
|
+
linkResolver
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
async function contentfulArticleRest(context, config, slug, articleTypeSlug, options) {
|
|
1402
|
+
const client = getContentfulClient(config, options?.preview);
|
|
1403
|
+
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
|
+
});
|
|
1413
|
+
const articleEntry = response.items[0];
|
|
1414
|
+
if (!articleEntry || !articleEntry.fields) {
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1417
|
+
const assets = convertAllAssets(response);
|
|
1418
|
+
const includes = convertAllIncludes(response);
|
|
1419
|
+
const fullContext = {
|
|
1420
|
+
...context,
|
|
1421
|
+
includes,
|
|
1422
|
+
assets
|
|
1423
|
+
};
|
|
1424
|
+
const converted = fullContext.articleResolver(fullContext, articleEntry);
|
|
1425
|
+
return converted;
|
|
1426
|
+
};
|
|
1427
|
+
if (options?.retry) {
|
|
1428
|
+
return await withRetry(fetchFn, options.retry);
|
|
1429
|
+
}
|
|
1430
|
+
return await fetchFn();
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
export { AuthenticationError, ContentfulError, EntryNotFoundError, RateLimitError, RateLimiter, Timer, ValidationError, basePageConverter, calculateBackoffDelay, contentfulArticleRest, contentfulPageRest, createBaseConverterContext, createContentfulClient, createContentfulPreviewClient, createTimer, getContentfulClient, getRetryAfter, isContentfulError, isRateLimitError, isRetryableError, logTimingResult, withRetry };
|
|
1434
|
+
//# sourceMappingURL=index.js.map
|
|
1435
|
+
//# sourceMappingURL=index.js.map
|