@fourtwelvelabs/fetch-contentful 0.4.0 → 1.0.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/CHANGELOG.md +177 -0
- package/README.md +192 -57
- package/dist/cli/index.mjs +82 -77
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +309 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +254 -26
- package/dist/index.d.ts +254 -26
- package/dist/index.mjs +305 -140
- package/dist/index.mjs.map +1 -1
- package/docs/tada.md +57 -32
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2,11 +2,112 @@ import { Kind, parse, print, visit } from 'graphql';
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
|
|
5
|
+
// src/annotate.ts
|
|
6
|
+
var ESC = String.fromCharCode(27);
|
|
7
|
+
var ANSI = {
|
|
8
|
+
reset: `${ESC}[0m`,
|
|
9
|
+
bold: `${ESC}[1m`,
|
|
10
|
+
dim: `${ESC}[2m`,
|
|
11
|
+
red: `${ESC}[31m`
|
|
12
|
+
};
|
|
13
|
+
var DEFAULT_CONTEXT_LINES = 3;
|
|
14
|
+
function supportsColor() {
|
|
15
|
+
if (typeof process === "undefined" || !process.env) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const forced = process.env.FORCE_COLOR;
|
|
19
|
+
const disabled = process.env.NO_COLOR;
|
|
20
|
+
if (forced) {
|
|
21
|
+
return forced !== "0" && forced !== "false";
|
|
22
|
+
}
|
|
23
|
+
if (disabled) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return Boolean(process.stderr?.isTTY);
|
|
27
|
+
}
|
|
28
|
+
function makePalette(enabled) {
|
|
29
|
+
const paint = (code) => (text) => enabled ? `${code}${text}${ANSI.reset}` : text;
|
|
30
|
+
return {
|
|
31
|
+
dim: paint(ANSI.dim),
|
|
32
|
+
red: paint(ANSI.red),
|
|
33
|
+
bold: paint(ANSI.bold)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function collectMarks(errors, lineCount) {
|
|
37
|
+
const marks = [];
|
|
38
|
+
const seen = /* @__PURE__ */ new Set();
|
|
39
|
+
for (const error of errors) {
|
|
40
|
+
for (const location of error.locations ?? []) {
|
|
41
|
+
const line = Math.trunc(location.line);
|
|
42
|
+
if (!(line >= 1 && line <= lineCount)) continue;
|
|
43
|
+
const reported = Math.trunc(location.column);
|
|
44
|
+
const column = reported >= 1 ? reported : 1;
|
|
45
|
+
const key = `${line}:${column}:${error.message}`;
|
|
46
|
+
if (seen.has(key)) continue;
|
|
47
|
+
seen.add(key);
|
|
48
|
+
marks.push({ line, column, message: error.message });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return marks.sort((a, b) => a.line - b.line || a.column - b.column);
|
|
52
|
+
}
|
|
53
|
+
function excerptsFor(marks, lineCount, contextLines) {
|
|
54
|
+
const excerpts = [];
|
|
55
|
+
for (const mark of marks) {
|
|
56
|
+
const start = Math.max(1, mark.line - contextLines);
|
|
57
|
+
const end = Math.min(lineCount, mark.line + contextLines);
|
|
58
|
+
const previous = excerpts[excerpts.length - 1];
|
|
59
|
+
if (previous && start <= previous.end + 1) {
|
|
60
|
+
previous.end = Math.max(previous.end, end);
|
|
61
|
+
} else {
|
|
62
|
+
excerpts.push({ start, end });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return excerpts;
|
|
66
|
+
}
|
|
67
|
+
function annotateQuery(query, errors, options = {}) {
|
|
68
|
+
const lines = query.split("\n");
|
|
69
|
+
const marks = collectMarks(errors, lines.length);
|
|
70
|
+
if (marks.length === 0) return "";
|
|
71
|
+
const color = makePalette(options.color ?? supportsColor());
|
|
72
|
+
const excerpts = excerptsFor(marks, lines.length, options.contextLines ?? DEFAULT_CONTEXT_LINES);
|
|
73
|
+
const gutter = String(
|
|
74
|
+
excerpts.reduce((widest, excerpt) => Math.max(widest, excerpt.end), 0)
|
|
75
|
+
).length;
|
|
76
|
+
const blank = " ".repeat(gutter);
|
|
77
|
+
const out = [];
|
|
78
|
+
for (const [index, excerpt] of excerpts.entries()) {
|
|
79
|
+
if (index > 0) out.push(color.dim(` ${blank} ...`));
|
|
80
|
+
const window = lines.slice(excerpt.start - 1, excerpt.end);
|
|
81
|
+
for (const [offset, source] of window.entries()) {
|
|
82
|
+
const line = excerpt.start + offset;
|
|
83
|
+
const number = String(line).padStart(gutter, " ");
|
|
84
|
+
const here = marks.filter((mark) => mark.line === line);
|
|
85
|
+
if (here.length === 0) {
|
|
86
|
+
out.push(`${color.dim(` ${number} |`)} ${source}`);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
out.push(`${color.red(`> ${number} |`)} ${color.bold(source)}`);
|
|
90
|
+
for (const mark of here) {
|
|
91
|
+
const lead = " ".repeat(mark.column - 1);
|
|
92
|
+
out.push(`${color.dim(` ${blank} |`)} ${lead}${color.red(`^ ${mark.message}`)}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return out.join("\n");
|
|
97
|
+
}
|
|
98
|
+
|
|
5
99
|
// src/errors.ts
|
|
6
100
|
var FetchContentfulError = class extends Error {
|
|
7
101
|
code;
|
|
8
102
|
status;
|
|
9
103
|
errors;
|
|
104
|
+
/**
|
|
105
|
+
* The GraphQL query exactly as it was sent to Contentful — fragments
|
|
106
|
+
* inlined, arguments injected, and (for a subquery) generated. This is
|
|
107
|
+
* the text the `line`/`column` in {@link FetchContentfulError.errors}
|
|
108
|
+
* point into, so it is the text to annotate; see `annotateQuery`.
|
|
109
|
+
*/
|
|
110
|
+
query;
|
|
10
111
|
/** Internal: whether a retry may succeed. */
|
|
11
112
|
retryable;
|
|
12
113
|
/** Internal: server-requested retry delay (from Retry-After), in ms. */
|
|
@@ -17,6 +118,7 @@ var FetchContentfulError = class extends Error {
|
|
|
17
118
|
this.code = options.code;
|
|
18
119
|
this.status = options.status;
|
|
19
120
|
this.errors = options.errors;
|
|
121
|
+
this.query = options.query;
|
|
20
122
|
this.retryable = options.retryable ?? false;
|
|
21
123
|
this.retryAfterMs = options.retryAfterMs;
|
|
22
124
|
}
|
|
@@ -132,16 +234,14 @@ function getOperation(document) {
|
|
|
132
234
|
);
|
|
133
235
|
const operation = operations[0];
|
|
134
236
|
if (!operation || operations.length > 1) {
|
|
135
|
-
throw new FetchContentfulError(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
);
|
|
237
|
+
throw new FetchContentfulError("fetch-contentful expects exactly one operation per document.", {
|
|
238
|
+
code: "CONFIG"
|
|
239
|
+
});
|
|
139
240
|
}
|
|
140
241
|
if (operation.operation !== "query") {
|
|
141
|
-
throw new FetchContentfulError(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
);
|
|
242
|
+
throw new FetchContentfulError("fetch-contentful only supports query operations.", {
|
|
243
|
+
code: "CONFIG"
|
|
244
|
+
});
|
|
145
245
|
}
|
|
146
246
|
return operation;
|
|
147
247
|
}
|
|
@@ -153,41 +253,34 @@ function inlineFragments(document) {
|
|
|
153
253
|
}
|
|
154
254
|
}
|
|
155
255
|
function inlineSelectionSet(selectionSet, stack) {
|
|
156
|
-
const selections = selectionSet.selections.map(
|
|
157
|
-
(selection)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
{ code: "CONFIG" }
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
if (stack.includes(name)) {
|
|
168
|
-
throw new FetchContentfulError(
|
|
169
|
-
`Fragment cycle detected involving "${name}".`,
|
|
170
|
-
{ code: "CONFIG" }
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
return {
|
|
174
|
-
kind: Kind.INLINE_FRAGMENT,
|
|
175
|
-
typeCondition: fragment.typeCondition,
|
|
176
|
-
selectionSet: inlineSelectionSet(fragment.selectionSet, [
|
|
177
|
-
...stack,
|
|
178
|
-
name
|
|
179
|
-
])
|
|
180
|
-
};
|
|
256
|
+
const selections = selectionSet.selections.map((selection) => {
|
|
257
|
+
if (selection.kind === Kind.FRAGMENT_SPREAD) {
|
|
258
|
+
const name = selection.name.value;
|
|
259
|
+
const fragment = fragments.get(name);
|
|
260
|
+
if (!fragment) {
|
|
261
|
+
throw new FetchContentfulError(`Unknown fragment "${name}" referenced in query.`, {
|
|
262
|
+
code: "CONFIG"
|
|
263
|
+
});
|
|
181
264
|
}
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
};
|
|
265
|
+
if (stack.includes(name)) {
|
|
266
|
+
throw new FetchContentfulError(`Fragment cycle detected involving "${name}".`, {
|
|
267
|
+
code: "CONFIG"
|
|
268
|
+
});
|
|
187
269
|
}
|
|
188
|
-
return
|
|
270
|
+
return {
|
|
271
|
+
kind: Kind.INLINE_FRAGMENT,
|
|
272
|
+
typeCondition: fragment.typeCondition,
|
|
273
|
+
selectionSet: inlineSelectionSet(fragment.selectionSet, [...stack, name])
|
|
274
|
+
};
|
|
189
275
|
}
|
|
190
|
-
|
|
276
|
+
if (selection.selectionSet) {
|
|
277
|
+
return {
|
|
278
|
+
...selection,
|
|
279
|
+
selectionSet: inlineSelectionSet(selection.selectionSet, stack)
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
return selection;
|
|
283
|
+
});
|
|
191
284
|
return { ...selectionSet, selections };
|
|
192
285
|
}
|
|
193
286
|
const definitions = document.definitions.filter((definition) => definition.kind !== Kind.FRAGMENT_DEFINITION).map(
|
|
@@ -222,10 +315,9 @@ function planSplits(document, options) {
|
|
|
222
315
|
continue;
|
|
223
316
|
}
|
|
224
317
|
if (selection.kind !== Kind.FIELD) {
|
|
225
|
-
throw new FetchContentfulError(
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
);
|
|
318
|
+
throw new FetchContentfulError("Fragment spreads must be inlined before split planning.", {
|
|
319
|
+
code: "CONFIG"
|
|
320
|
+
});
|
|
229
321
|
}
|
|
230
322
|
const field = selection;
|
|
231
323
|
const isExplicitSplit = hasDirective(field, SPLIT_DIRECTIVE);
|
|
@@ -238,10 +330,7 @@ function planSplits(document, options) {
|
|
|
238
330
|
}
|
|
239
331
|
const shouldSplit = depth > 0 && field.selectionSet !== void 0 && !hasDirective(field, NO_SPLIT_DIRECTIVE) && (isExplicitSplit || isAutoSplit);
|
|
240
332
|
if (shouldSplit) {
|
|
241
|
-
const planned = withDirective(
|
|
242
|
-
withoutDirective(field, SPLIT_DIRECTIVE),
|
|
243
|
-
NO_SPLIT_DIRECTIVE
|
|
244
|
-
);
|
|
333
|
+
const planned = withDirective(withoutDirective(field, SPLIT_DIRECTIVE), NO_SPLIT_DIRECTIVE);
|
|
245
334
|
plans.push({
|
|
246
335
|
path: [...path],
|
|
247
336
|
responseKey: responseKeyOf(field),
|
|
@@ -340,10 +429,7 @@ function whereIdInArgument() {
|
|
|
340
429
|
}
|
|
341
430
|
function buildSubquery(plan, typename, batchSize, context) {
|
|
342
431
|
const rootKey = `${lowerFirst(typename)}${COLLECTION_SUFFIX}`;
|
|
343
|
-
const args = [
|
|
344
|
-
whereIdInArgument(),
|
|
345
|
-
intArgument("limit", batchSize)
|
|
346
|
-
];
|
|
432
|
+
const args = [whereIdInArgument(), intArgument("limit", batchSize)];
|
|
347
433
|
if (context.preview) {
|
|
348
434
|
args.push(booleanArgument("preview", true));
|
|
349
435
|
}
|
|
@@ -352,10 +438,7 @@ function buildSubquery(plan, typename, batchSize, context) {
|
|
|
352
438
|
}
|
|
353
439
|
const rootField = {
|
|
354
440
|
...namedField(rootKey, void 0, [
|
|
355
|
-
namedField("items", void 0, [
|
|
356
|
-
namedField("sys", void 0, [namedField("id")]),
|
|
357
|
-
plan.field
|
|
358
|
-
])
|
|
441
|
+
namedField("items", void 0, [namedField("sys", void 0, [namedField("id")]), plan.field])
|
|
359
442
|
]),
|
|
360
443
|
arguments: args
|
|
361
444
|
};
|
|
@@ -405,6 +488,58 @@ function stripInternalDirectives(document) {
|
|
|
405
488
|
});
|
|
406
489
|
}
|
|
407
490
|
|
|
491
|
+
// src/unresolvable.ts
|
|
492
|
+
var UNRESOLVABLE_LINK_CODE = "UNRESOLVABLE_LINK";
|
|
493
|
+
var COLLECTION_SUFFIX2 = "Collection";
|
|
494
|
+
function isRecord(value) {
|
|
495
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
496
|
+
}
|
|
497
|
+
function isCollectionValue(value) {
|
|
498
|
+
return isRecord(value) && Array.isArray(value["items"]);
|
|
499
|
+
}
|
|
500
|
+
function isCollectionKey(key) {
|
|
501
|
+
return key.endsWith(COLLECTION_SUFFIX2) && key !== COLLECTION_SUFFIX2;
|
|
502
|
+
}
|
|
503
|
+
function isUnresolvableLinkError(error) {
|
|
504
|
+
const contentful = error.extensions?.["contentful"];
|
|
505
|
+
return isRecord(contentful) && contentful["code"] === UNRESOLVABLE_LINK_CODE;
|
|
506
|
+
}
|
|
507
|
+
function partitionUnresolvableLinks(errors) {
|
|
508
|
+
const unresolvable = [];
|
|
509
|
+
const fatal = [];
|
|
510
|
+
for (const error of errors) {
|
|
511
|
+
if (isUnresolvableLinkError(error)) {
|
|
512
|
+
unresolvable.push(error);
|
|
513
|
+
} else {
|
|
514
|
+
fatal.push(error);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return { unresolvable, fatal };
|
|
518
|
+
}
|
|
519
|
+
function walk(value) {
|
|
520
|
+
if (Array.isArray(value)) {
|
|
521
|
+
return value.map(walk);
|
|
522
|
+
}
|
|
523
|
+
if (isRecord(value)) {
|
|
524
|
+
const result = {};
|
|
525
|
+
for (const [key, child] of Object.entries(value)) {
|
|
526
|
+
result[key] = isCollectionKey(key) && isCollectionValue(child) ? stripCollection(child) : walk(child);
|
|
527
|
+
}
|
|
528
|
+
return result;
|
|
529
|
+
}
|
|
530
|
+
return value;
|
|
531
|
+
}
|
|
532
|
+
function stripCollection(collection) {
|
|
533
|
+
const result = {};
|
|
534
|
+
for (const [key, value] of Object.entries(collection)) {
|
|
535
|
+
result[key] = key === "items" ? value.filter((item) => item !== null && item !== void 0).map(walk) : walk(value);
|
|
536
|
+
}
|
|
537
|
+
return result;
|
|
538
|
+
}
|
|
539
|
+
function omitUnresolvedLinks(data) {
|
|
540
|
+
return walk(data);
|
|
541
|
+
}
|
|
542
|
+
|
|
408
543
|
// src/client.ts
|
|
409
544
|
function graphqlEndpoint(space, environment) {
|
|
410
545
|
return `https://graphql.contentful.com/content/v1/spaces/${encodeURIComponent(
|
|
@@ -440,6 +575,35 @@ function pickDeclaredVariables(document, variables) {
|
|
|
440
575
|
function isRetryableStatus(status) {
|
|
441
576
|
return status === 408 || status === 429 || status >= 500;
|
|
442
577
|
}
|
|
578
|
+
function isRecord2(value) {
|
|
579
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
580
|
+
}
|
|
581
|
+
function reportUnresolvableLinks(errors, context) {
|
|
582
|
+
if (!context.onUnresolvableLink) return;
|
|
583
|
+
try {
|
|
584
|
+
context.onUnresolvableLink(errors);
|
|
585
|
+
} catch {
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
async function readGraphQLErrors(response) {
|
|
589
|
+
try {
|
|
590
|
+
const payload = await response.json();
|
|
591
|
+
const errors = payload?.errors;
|
|
592
|
+
return Array.isArray(errors) && errors.length > 0 ? errors : void 0;
|
|
593
|
+
} catch {
|
|
594
|
+
return void 0;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function describeFailure(summary, errors, query, context) {
|
|
598
|
+
if (!errors || errors.length === 0) return summary;
|
|
599
|
+
const message = `${summary} ${errors.map((error) => error.message).join("; ")}`;
|
|
600
|
+
if (context.annotateQueryOnError === false) return message;
|
|
601
|
+
const annotated = annotateQuery(query, errors);
|
|
602
|
+
return annotated ? `${message}
|
|
603
|
+
|
|
604
|
+
${annotated}
|
|
605
|
+
` : message;
|
|
606
|
+
}
|
|
443
607
|
async function rawRequest(document, variables, context) {
|
|
444
608
|
const query = print(stripInternalDirectives(document));
|
|
445
609
|
const body = JSON.stringify({
|
|
@@ -465,16 +629,24 @@ async function rawRequest(document, variables, context) {
|
|
|
465
629
|
} catch (cause) {
|
|
466
630
|
throw new FetchContentfulError(
|
|
467
631
|
`Network error while contacting Contentful: ${String(cause)}`,
|
|
468
|
-
{ code: "NETWORK", retryable: true, cause }
|
|
632
|
+
{ code: "NETWORK", query, retryable: true, cause }
|
|
469
633
|
);
|
|
470
634
|
}
|
|
471
635
|
if (!response.ok) {
|
|
472
636
|
const retryable = isRetryableStatus(response.status);
|
|
637
|
+
const errors = retryable ? void 0 : await readGraphQLErrors(response);
|
|
473
638
|
throw new FetchContentfulError(
|
|
474
|
-
|
|
639
|
+
describeFailure(
|
|
640
|
+
`Contentful responded with HTTP ${response.status}.`,
|
|
641
|
+
errors,
|
|
642
|
+
query,
|
|
643
|
+
context
|
|
644
|
+
),
|
|
475
645
|
{
|
|
476
646
|
code: "HTTP",
|
|
477
647
|
status: response.status,
|
|
648
|
+
errors,
|
|
649
|
+
query,
|
|
478
650
|
retryable,
|
|
479
651
|
retryAfterMs: retryable ? parseRetryAfter(response.headers.get("Retry-After")) : void 0
|
|
480
652
|
}
|
|
@@ -484,22 +656,29 @@ async function rawRequest(document, variables, context) {
|
|
|
484
656
|
try {
|
|
485
657
|
payload = await response.json();
|
|
486
658
|
} catch (cause) {
|
|
487
|
-
throw new FetchContentfulError(
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
659
|
+
throw new FetchContentfulError("Contentful returned an unreadable response body.", {
|
|
660
|
+
code: "NETWORK",
|
|
661
|
+
query,
|
|
662
|
+
retryable: true,
|
|
663
|
+
cause
|
|
664
|
+
});
|
|
491
665
|
}
|
|
492
666
|
if (payload.errors && payload.errors.length > 0) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
667
|
+
const { unresolvable, fatal } = partitionUnresolvableLinks(payload.errors);
|
|
668
|
+
const tolerable = context.unresolvableLinks !== "error" && fatal.length === 0 && unresolvable.length > 0 && isRecord2(payload.data);
|
|
669
|
+
if (!tolerable) {
|
|
670
|
+
throw new FetchContentfulError(
|
|
671
|
+
describeFailure("Contentful returned GraphQL errors:", payload.errors, query, context),
|
|
672
|
+
{ code: "GRAPHQL", errors: payload.errors, query }
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
reportUnresolvableLinks(unresolvable, context);
|
|
497
676
|
}
|
|
498
677
|
if (!payload.data || typeof payload.data !== "object") {
|
|
499
|
-
throw new FetchContentfulError(
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
);
|
|
678
|
+
throw new FetchContentfulError("Contentful returned no data and no errors.", {
|
|
679
|
+
code: "GRAPHQL",
|
|
680
|
+
query
|
|
681
|
+
});
|
|
503
682
|
}
|
|
504
683
|
return payload.data;
|
|
505
684
|
}, context.retry);
|
|
@@ -515,7 +694,6 @@ function readEnvSettings() {
|
|
|
515
694
|
space: void 0,
|
|
516
695
|
environment: void 0,
|
|
517
696
|
deliveryToken: void 0,
|
|
518
|
-
token: void 0,
|
|
519
697
|
previewToken: void 0
|
|
520
698
|
};
|
|
521
699
|
}
|
|
@@ -530,7 +708,6 @@ function readEnvSettings() {
|
|
|
530
708
|
process.env.CONTENTFUL_ENVIRONMENT || process.env.NEXT_PUBLIC_CONTENTFUL_ENVIRONMENT
|
|
531
709
|
),
|
|
532
710
|
deliveryToken,
|
|
533
|
-
token: deliveryToken,
|
|
534
711
|
// No `NEXT_PUBLIC_` fallback: see the note at the top of this file.
|
|
535
712
|
previewToken: orUndefined(process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN)
|
|
536
713
|
};
|
|
@@ -572,20 +749,18 @@ function injectIntoField(field, args) {
|
|
|
572
749
|
return { ...field, arguments: [...argumentsOf(field), ...additions] };
|
|
573
750
|
}
|
|
574
751
|
function injectIntoSelectionSet(selectionSet, args) {
|
|
575
|
-
const selections = selectionSet.selections.map(
|
|
576
|
-
(selection)
|
|
577
|
-
|
|
578
|
-
return injectIntoField(selection, args);
|
|
579
|
-
}
|
|
580
|
-
if (selection.kind === Kind.INLINE_FRAGMENT) {
|
|
581
|
-
return {
|
|
582
|
-
...selection,
|
|
583
|
-
selectionSet: injectIntoSelectionSet(selection.selectionSet, args)
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
return selection;
|
|
752
|
+
const selections = selectionSet.selections.map((selection) => {
|
|
753
|
+
if (selection.kind === Kind.FIELD) {
|
|
754
|
+
return injectIntoField(selection, args);
|
|
587
755
|
}
|
|
588
|
-
|
|
756
|
+
if (selection.kind === Kind.INLINE_FRAGMENT) {
|
|
757
|
+
return {
|
|
758
|
+
...selection,
|
|
759
|
+
selectionSet: injectIntoSelectionSet(selection.selectionSet, args)
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
return selection;
|
|
763
|
+
});
|
|
589
764
|
return { ...selectionSet, selections };
|
|
590
765
|
}
|
|
591
766
|
function injectRootArgs(document, args) {
|
|
@@ -626,27 +801,24 @@ async function fetchLocales(context) {
|
|
|
626
801
|
if (context.signal) init.signal = context.signal;
|
|
627
802
|
response = await context.fetch(localesEndpoint(context), init);
|
|
628
803
|
} catch (cause) {
|
|
629
|
-
throw new FetchContentfulError(
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
804
|
+
throw new FetchContentfulError(`Network error while fetching locales: ${String(cause)}`, {
|
|
805
|
+
code: "NETWORK",
|
|
806
|
+
retryable: true,
|
|
807
|
+
cause
|
|
808
|
+
});
|
|
633
809
|
}
|
|
634
810
|
if (!response.ok) {
|
|
635
|
-
throw new FetchContentfulError(
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
retryable: response.status === 429 || response.status >= 500
|
|
641
|
-
}
|
|
642
|
-
);
|
|
811
|
+
throw new FetchContentfulError(`Locale request failed with HTTP ${response.status}.`, {
|
|
812
|
+
code: "HTTP",
|
|
813
|
+
status: response.status,
|
|
814
|
+
retryable: response.status === 429 || response.status >= 500
|
|
815
|
+
});
|
|
643
816
|
}
|
|
644
817
|
const payload = await response.json();
|
|
645
818
|
if (!Array.isArray(payload.items)) {
|
|
646
|
-
throw new FetchContentfulError(
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
);
|
|
819
|
+
throw new FetchContentfulError("Locale response did not include an items array.", {
|
|
820
|
+
code: "NETWORK"
|
|
821
|
+
});
|
|
650
822
|
}
|
|
651
823
|
return payload.items.map((item) => ({
|
|
652
824
|
code: item.code,
|
|
@@ -674,24 +846,22 @@ function clearLocaleCache() {
|
|
|
674
846
|
}
|
|
675
847
|
|
|
676
848
|
// src/shape.ts
|
|
677
|
-
var
|
|
678
|
-
function
|
|
849
|
+
var COLLECTION_SUFFIX3 = "Collection";
|
|
850
|
+
function isRecord3(value) {
|
|
679
851
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
680
852
|
}
|
|
681
|
-
function
|
|
682
|
-
return
|
|
853
|
+
function isCollectionValue2(value) {
|
|
854
|
+
return isRecord3(value) && Array.isArray(value["items"]);
|
|
683
855
|
}
|
|
684
856
|
function shapeData(data) {
|
|
685
857
|
if (Array.isArray(data)) {
|
|
686
858
|
return data.map((item) => shapeData(item));
|
|
687
859
|
}
|
|
688
|
-
if (
|
|
860
|
+
if (isRecord3(data)) {
|
|
689
861
|
const shaped = {};
|
|
690
862
|
for (const [key, value] of Object.entries(data)) {
|
|
691
|
-
if (key.endsWith(
|
|
692
|
-
shaped[key.slice(0, -
|
|
693
|
-
value.items
|
|
694
|
-
);
|
|
863
|
+
if (key.endsWith(COLLECTION_SUFFIX3) && key !== COLLECTION_SUFFIX3 && isCollectionValue2(value)) {
|
|
864
|
+
shaped[key.slice(0, -COLLECTION_SUFFIX3.length)] = shapeData(value.items);
|
|
695
865
|
} else {
|
|
696
866
|
shaped[key] = shapeData(value);
|
|
697
867
|
}
|
|
@@ -701,7 +871,7 @@ function shapeData(data) {
|
|
|
701
871
|
return data;
|
|
702
872
|
}
|
|
703
873
|
function unwrapSingleRoot(data) {
|
|
704
|
-
if (
|
|
874
|
+
if (isRecord3(data)) {
|
|
705
875
|
const keys = Object.keys(data);
|
|
706
876
|
if (keys.length === 1) {
|
|
707
877
|
return data[keys[0]];
|
|
@@ -716,7 +886,7 @@ var DEFAULT_RETRIES = 5;
|
|
|
716
886
|
var DEFAULT_RETRY_DELAY_MS = 250;
|
|
717
887
|
var DEFAULT_MAX_RETRY_DELAY_MS = 8e3;
|
|
718
888
|
var DEFAULT_SPLIT_BATCH_SIZE = 50;
|
|
719
|
-
function
|
|
889
|
+
function isRecord4(value) {
|
|
720
890
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
721
891
|
}
|
|
722
892
|
function collectAtPath(data, path) {
|
|
@@ -724,7 +894,7 @@ function collectAtPath(data, path) {
|
|
|
724
894
|
for (const segment of path) {
|
|
725
895
|
const next = [];
|
|
726
896
|
for (const value of current) {
|
|
727
|
-
if (
|
|
897
|
+
if (isRecord4(value)) {
|
|
728
898
|
const child = value[segment];
|
|
729
899
|
if (Array.isArray(child)) {
|
|
730
900
|
next.push(...child);
|
|
@@ -735,14 +905,14 @@ function collectAtPath(data, path) {
|
|
|
735
905
|
}
|
|
736
906
|
current = next;
|
|
737
907
|
}
|
|
738
|
-
return current.filter(
|
|
908
|
+
return current.filter(isRecord4);
|
|
739
909
|
}
|
|
740
910
|
function resolveContext(options) {
|
|
741
911
|
const env = readEnvSettings();
|
|
742
912
|
const space = options.space ?? env.space;
|
|
743
913
|
const environment = options.environment ?? env.environment ?? "master";
|
|
744
914
|
const preview = options.preview ?? false;
|
|
745
|
-
const token = preview ? options.previewToken ??
|
|
915
|
+
const token = preview ? options.previewToken ?? env.previewToken : options.deliveryToken ?? env.deliveryToken;
|
|
746
916
|
if (!space || !token) {
|
|
747
917
|
const missing = [];
|
|
748
918
|
if (!space) {
|
|
@@ -776,8 +946,13 @@ function resolveContext(options) {
|
|
|
776
946
|
fetch: fetchImpl,
|
|
777
947
|
retry,
|
|
778
948
|
autoSplitNestedCollections: options.autoSplitNestedCollections ?? true,
|
|
779
|
-
splitBatchSize: options.splitBatchSize ?? DEFAULT_SPLIT_BATCH_SIZE
|
|
949
|
+
splitBatchSize: options.splitBatchSize ?? DEFAULT_SPLIT_BATCH_SIZE,
|
|
950
|
+
annotateQueryOnError: options.annotateQueryOnError ?? true,
|
|
951
|
+
unresolvableLinks: options.unresolvableLinks ?? "omit"
|
|
780
952
|
};
|
|
953
|
+
if (options.onUnresolvableLink) {
|
|
954
|
+
context.onUnresolvableLink = options.onUnresolvableLink;
|
|
955
|
+
}
|
|
781
956
|
if (options.signal) context.signal = options.signal;
|
|
782
957
|
if (options.next) context.next = options.next;
|
|
783
958
|
if (options.cache) context.cache = options.cache;
|
|
@@ -795,7 +970,7 @@ async function resolvePlan(plan, data, variables, context) {
|
|
|
795
970
|
for (const parent of relevant) {
|
|
796
971
|
const typename = parent[TYPENAME_ALIAS];
|
|
797
972
|
const sys = parent[SYS_ID_ALIAS];
|
|
798
|
-
if (!
|
|
973
|
+
if (!isRecord4(sys) || typeof sys["id"] !== "string") {
|
|
799
974
|
throw new FetchContentfulError(
|
|
800
975
|
`Cannot split "${plan.responseKey}": a parent entry is missing its sys id.`,
|
|
801
976
|
{ code: "STITCH" }
|
|
@@ -818,19 +993,14 @@ async function resolvePlan(plan, data, variables, context) {
|
|
|
818
993
|
];
|
|
819
994
|
await Promise.all(
|
|
820
995
|
chunk(ids, context.splitBatchSize).map(async (idBatch) => {
|
|
821
|
-
const { document, rootKey } = buildSubquery(
|
|
822
|
-
plan,
|
|
823
|
-
typename,
|
|
824
|
-
idBatch.length,
|
|
825
|
-
context
|
|
826
|
-
);
|
|
996
|
+
const { document, rootKey } = buildSubquery(plan, typename, idBatch.length, context);
|
|
827
997
|
const subData = await executeDocument(
|
|
828
998
|
document,
|
|
829
999
|
{ ...variables, _splitIds: idBatch },
|
|
830
1000
|
context
|
|
831
1001
|
);
|
|
832
1002
|
const collection = subData[rootKey];
|
|
833
|
-
const items =
|
|
1003
|
+
const items = isRecord4(collection) && Array.isArray(collection["items"]) ? collection["items"] : void 0;
|
|
834
1004
|
if (!items) {
|
|
835
1005
|
throw new FetchContentfulError(
|
|
836
1006
|
`Split subquery for "${typename}" returned no "${rootKey}.items". Check that the content type follows Contentful naming conventions.`,
|
|
@@ -838,9 +1008,9 @@ async function resolvePlan(plan, data, variables, context) {
|
|
|
838
1008
|
);
|
|
839
1009
|
}
|
|
840
1010
|
for (const item of items) {
|
|
841
|
-
if (
|
|
1011
|
+
if (isRecord4(item)) {
|
|
842
1012
|
const sys = item["sys"];
|
|
843
|
-
if (
|
|
1013
|
+
if (isRecord4(sys) && typeof sys["id"] === "string") {
|
|
844
1014
|
resolved.set(`${typename}:${sys["id"]}`, item[plan.responseKey]);
|
|
845
1015
|
}
|
|
846
1016
|
}
|
|
@@ -873,9 +1043,7 @@ function cleanupMarkers(data, plans) {
|
|
|
873
1043
|
async function executeDocument(document, variables, context) {
|
|
874
1044
|
const { document: outer, plans } = planSplits(document, context);
|
|
875
1045
|
const data = await rawRequest(outer, variables, context);
|
|
876
|
-
await Promise.all(
|
|
877
|
-
plans.map((plan) => resolvePlan(plan, data, variables, context))
|
|
878
|
-
);
|
|
1046
|
+
await Promise.all(plans.map((plan) => resolvePlan(plan, data, variables, context)));
|
|
879
1047
|
cleanupMarkers(data, plans);
|
|
880
1048
|
return data;
|
|
881
1049
|
}
|
|
@@ -889,10 +1057,10 @@ function toDocument(query) {
|
|
|
889
1057
|
try {
|
|
890
1058
|
return parse(typeof query === "string" ? query : String(query));
|
|
891
1059
|
} catch (cause) {
|
|
892
|
-
throw new FetchContentfulError(
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
);
|
|
1060
|
+
throw new FetchContentfulError(`Failed to parse GraphQL query: ${String(cause)}`, {
|
|
1061
|
+
code: "CONFIG",
|
|
1062
|
+
cause
|
|
1063
|
+
});
|
|
896
1064
|
}
|
|
897
1065
|
}
|
|
898
1066
|
function withAutoVariables(document, variables, context) {
|
|
@@ -928,13 +1096,10 @@ async function runFetchContentful(query, options) {
|
|
|
928
1096
|
);
|
|
929
1097
|
}
|
|
930
1098
|
}
|
|
931
|
-
const variables = withAutoVariables(
|
|
932
|
-
document,
|
|
933
|
-
options.variables ?? {},
|
|
934
|
-
context
|
|
935
|
-
);
|
|
1099
|
+
const variables = withAutoVariables(document, options.variables ?? {}, context);
|
|
936
1100
|
const data = await executeDocument(document, variables, context);
|
|
937
|
-
const
|
|
1101
|
+
const resolved = context.unresolvableLinks === "null" ? data : omitUnresolvedLinks(data);
|
|
1102
|
+
const result = options.shapeResponseData === false ? resolved : shapeData(resolved);
|
|
938
1103
|
if (options.unwrapRootField === false) {
|
|
939
1104
|
return result;
|
|
940
1105
|
}
|
|
@@ -954,6 +1119,6 @@ function createFetchContentful(defaults = {}) {
|
|
|
954
1119
|
}
|
|
955
1120
|
var index_default = fetchContentful;
|
|
956
1121
|
|
|
957
|
-
export { FetchContentfulError, clearLocaleCache, collectAtPath, createFetchContentful, index_default as default, fetchContentful, getLocales, injectRootArgs, inlineFragments, isFetchContentfulError, readEnvSettings, shapeData, unwrapSingleRoot };
|
|
1122
|
+
export { FetchContentfulError, UNRESOLVABLE_LINK_CODE, annotateQuery, clearLocaleCache, collectAtPath, createFetchContentful, index_default as default, fetchContentful, getLocales, injectRootArgs, inlineFragments, isFetchContentfulError, isUnresolvableLinkError, omitUnresolvedLinks, partitionUnresolvableLinks, readEnvSettings, shapeData, unwrapSingleRoot };
|
|
958
1123
|
//# sourceMappingURL=index.mjs.map
|
|
959
1124
|
//# sourceMappingURL=index.mjs.map
|