@fourtwelvelabs/fetch-contentful 0.4.1 → 1.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/CHANGELOG.md +165 -0
- package/README.md +228 -57
- package/dist/cli/index.mjs +21 -72
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +452 -149
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +307 -26
- package/dist/index.d.ts +307 -26
- package/dist/index.mjs +448 -151
- package/dist/index.mjs.map +1 -1
- package/docs/tada.md +31 -29
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -6,11 +6,112 @@ var graphql = require('graphql');
|
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
8
|
|
|
9
|
+
// src/annotate.ts
|
|
10
|
+
var ESC = String.fromCharCode(27);
|
|
11
|
+
var ANSI = {
|
|
12
|
+
reset: `${ESC}[0m`,
|
|
13
|
+
bold: `${ESC}[1m`,
|
|
14
|
+
dim: `${ESC}[2m`,
|
|
15
|
+
red: `${ESC}[31m`
|
|
16
|
+
};
|
|
17
|
+
var DEFAULT_CONTEXT_LINES = 3;
|
|
18
|
+
function supportsColor() {
|
|
19
|
+
if (typeof process === "undefined" || !process.env) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const forced = process.env.FORCE_COLOR;
|
|
23
|
+
const disabled = process.env.NO_COLOR;
|
|
24
|
+
if (forced) {
|
|
25
|
+
return forced !== "0" && forced !== "false";
|
|
26
|
+
}
|
|
27
|
+
if (disabled) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return Boolean(process.stderr?.isTTY);
|
|
31
|
+
}
|
|
32
|
+
function makePalette(enabled) {
|
|
33
|
+
const paint = (code) => (text) => enabled ? `${code}${text}${ANSI.reset}` : text;
|
|
34
|
+
return {
|
|
35
|
+
dim: paint(ANSI.dim),
|
|
36
|
+
red: paint(ANSI.red),
|
|
37
|
+
bold: paint(ANSI.bold)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function collectMarks(errors, lineCount) {
|
|
41
|
+
const marks = [];
|
|
42
|
+
const seen = /* @__PURE__ */ new Set();
|
|
43
|
+
for (const error of errors) {
|
|
44
|
+
for (const location of error.locations ?? []) {
|
|
45
|
+
const line = Math.trunc(location.line);
|
|
46
|
+
if (!(line >= 1 && line <= lineCount)) continue;
|
|
47
|
+
const reported = Math.trunc(location.column);
|
|
48
|
+
const column = reported >= 1 ? reported : 1;
|
|
49
|
+
const key = `${line}:${column}:${error.message}`;
|
|
50
|
+
if (seen.has(key)) continue;
|
|
51
|
+
seen.add(key);
|
|
52
|
+
marks.push({ line, column, message: error.message });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return marks.sort((a, b) => a.line - b.line || a.column - b.column);
|
|
56
|
+
}
|
|
57
|
+
function excerptsFor(marks, lineCount, contextLines) {
|
|
58
|
+
const excerpts = [];
|
|
59
|
+
for (const mark of marks) {
|
|
60
|
+
const start = Math.max(1, mark.line - contextLines);
|
|
61
|
+
const end = Math.min(lineCount, mark.line + contextLines);
|
|
62
|
+
const previous = excerpts[excerpts.length - 1];
|
|
63
|
+
if (previous && start <= previous.end + 1) {
|
|
64
|
+
previous.end = Math.max(previous.end, end);
|
|
65
|
+
} else {
|
|
66
|
+
excerpts.push({ start, end });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return excerpts;
|
|
70
|
+
}
|
|
71
|
+
function annotateQuery(query, errors, options = {}) {
|
|
72
|
+
const lines = query.split("\n");
|
|
73
|
+
const marks = collectMarks(errors, lines.length);
|
|
74
|
+
if (marks.length === 0) return "";
|
|
75
|
+
const color = makePalette(options.color ?? supportsColor());
|
|
76
|
+
const excerpts = excerptsFor(marks, lines.length, options.contextLines ?? DEFAULT_CONTEXT_LINES);
|
|
77
|
+
const gutter = String(
|
|
78
|
+
excerpts.reduce((widest, excerpt) => Math.max(widest, excerpt.end), 0)
|
|
79
|
+
).length;
|
|
80
|
+
const blank = " ".repeat(gutter);
|
|
81
|
+
const out = [];
|
|
82
|
+
for (const [index, excerpt] of excerpts.entries()) {
|
|
83
|
+
if (index > 0) out.push(color.dim(` ${blank} ...`));
|
|
84
|
+
const window = lines.slice(excerpt.start - 1, excerpt.end);
|
|
85
|
+
for (const [offset, source] of window.entries()) {
|
|
86
|
+
const line = excerpt.start + offset;
|
|
87
|
+
const number = String(line).padStart(gutter, " ");
|
|
88
|
+
const here = marks.filter((mark) => mark.line === line);
|
|
89
|
+
if (here.length === 0) {
|
|
90
|
+
out.push(`${color.dim(` ${number} |`)} ${source}`);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
out.push(`${color.red(`> ${number} |`)} ${color.bold(source)}`);
|
|
94
|
+
for (const mark of here) {
|
|
95
|
+
const lead = " ".repeat(mark.column - 1);
|
|
96
|
+
out.push(`${color.dim(` ${blank} |`)} ${lead}${color.red(`^ ${mark.message}`)}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return out.join("\n");
|
|
101
|
+
}
|
|
102
|
+
|
|
9
103
|
// src/errors.ts
|
|
10
104
|
var FetchContentfulError = class extends Error {
|
|
11
105
|
code;
|
|
12
106
|
status;
|
|
13
107
|
errors;
|
|
108
|
+
/**
|
|
109
|
+
* The GraphQL query exactly as it was sent to Contentful — fragments
|
|
110
|
+
* inlined, arguments injected, and (for a subquery) generated. This is
|
|
111
|
+
* the text the `line`/`column` in {@link FetchContentfulError.errors}
|
|
112
|
+
* point into, so it is the text to annotate; see `annotateQuery`.
|
|
113
|
+
*/
|
|
114
|
+
query;
|
|
14
115
|
/** Internal: whether a retry may succeed. */
|
|
15
116
|
retryable;
|
|
16
117
|
/** Internal: server-requested retry delay (from Retry-After), in ms. */
|
|
@@ -21,6 +122,7 @@ var FetchContentfulError = class extends Error {
|
|
|
21
122
|
this.code = options.code;
|
|
22
123
|
this.status = options.status;
|
|
23
124
|
this.errors = options.errors;
|
|
125
|
+
this.query = options.query;
|
|
24
126
|
this.retryable = options.retryable ?? false;
|
|
25
127
|
this.retryAfterMs = options.retryAfterMs;
|
|
26
128
|
}
|
|
@@ -28,6 +130,48 @@ var FetchContentfulError = class extends Error {
|
|
|
28
130
|
function isFetchContentfulError(value) {
|
|
29
131
|
return value instanceof FetchContentfulError;
|
|
30
132
|
}
|
|
133
|
+
var WORD_LIKE = /* @__PURE__ */ new Set([graphql.TokenKind.NAME, graphql.TokenKind.INT, graphql.TokenKind.FLOAT]);
|
|
134
|
+
function minifyQuery(query) {
|
|
135
|
+
const lexer = new graphql.Lexer(new graphql.Source(query));
|
|
136
|
+
let previousKind;
|
|
137
|
+
let out = "";
|
|
138
|
+
for (let token = lexer.advance(); token.kind !== graphql.TokenKind.EOF; token = lexer.advance()) {
|
|
139
|
+
if (previousKind && WORD_LIKE.has(previousKind) && WORD_LIKE.has(token.kind)) {
|
|
140
|
+
out += " ";
|
|
141
|
+
}
|
|
142
|
+
out += query.slice(token.start, token.end);
|
|
143
|
+
previousKind = token.kind;
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/persisted-query.ts
|
|
149
|
+
var PERSISTED_QUERY_VERSION = 1;
|
|
150
|
+
function toHex(bytes) {
|
|
151
|
+
let hex = "";
|
|
152
|
+
for (const byte of bytes) {
|
|
153
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
154
|
+
}
|
|
155
|
+
return hex;
|
|
156
|
+
}
|
|
157
|
+
async function sha256Hex(query) {
|
|
158
|
+
const data = new TextEncoder().encode(query);
|
|
159
|
+
const subtle = globalThis.crypto?.subtle;
|
|
160
|
+
if (subtle) {
|
|
161
|
+
const digest = await subtle.digest("SHA-256", data);
|
|
162
|
+
return toHex(new Uint8Array(digest));
|
|
163
|
+
}
|
|
164
|
+
const { createHash } = await import('crypto');
|
|
165
|
+
return createHash("sha256").update(data).digest("hex");
|
|
166
|
+
}
|
|
167
|
+
function persistedQueryExtensions(sha256Hash) {
|
|
168
|
+
return { persistedQuery: { version: PERSISTED_QUERY_VERSION, sha256Hash } };
|
|
169
|
+
}
|
|
170
|
+
function isPersistedQueryNotFoundError(error) {
|
|
171
|
+
if (error.message === "PersistedQueryNotFound") return true;
|
|
172
|
+
const extensions = error.extensions;
|
|
173
|
+
return extensions?.code === "PERSISTED_QUERY_NOT_FOUND";
|
|
174
|
+
}
|
|
31
175
|
|
|
32
176
|
// src/retry.ts
|
|
33
177
|
function defaultSleep(ms) {
|
|
@@ -94,6 +238,9 @@ function withDirective(field, name) {
|
|
|
94
238
|
function responseKeyOf(field) {
|
|
95
239
|
return field.alias?.value ?? field.name.value;
|
|
96
240
|
}
|
|
241
|
+
function hasResponseKey(selection, key) {
|
|
242
|
+
return selection.kind === graphql.Kind.FIELD && responseKeyOf(selection) === key;
|
|
243
|
+
}
|
|
97
244
|
function isCollectionField(field) {
|
|
98
245
|
if (!field.name.value.endsWith(COLLECTION_SUFFIX) || field.name.value === COLLECTION_SUFFIX || !field.selectionSet) {
|
|
99
246
|
return false;
|
|
@@ -136,16 +283,14 @@ function getOperation(document) {
|
|
|
136
283
|
);
|
|
137
284
|
const operation = operations[0];
|
|
138
285
|
if (!operation || operations.length > 1) {
|
|
139
|
-
throw new FetchContentfulError(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
);
|
|
286
|
+
throw new FetchContentfulError("fetch-contentful expects exactly one operation per document.", {
|
|
287
|
+
code: "CONFIG"
|
|
288
|
+
});
|
|
143
289
|
}
|
|
144
290
|
if (operation.operation !== "query") {
|
|
145
|
-
throw new FetchContentfulError(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
);
|
|
291
|
+
throw new FetchContentfulError("fetch-contentful only supports query operations.", {
|
|
292
|
+
code: "CONFIG"
|
|
293
|
+
});
|
|
149
294
|
}
|
|
150
295
|
return operation;
|
|
151
296
|
}
|
|
@@ -157,41 +302,34 @@ function inlineFragments(document) {
|
|
|
157
302
|
}
|
|
158
303
|
}
|
|
159
304
|
function inlineSelectionSet(selectionSet, stack) {
|
|
160
|
-
const selections = selectionSet.selections.map(
|
|
161
|
-
(selection)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
{ code: "CONFIG" }
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
if (stack.includes(name)) {
|
|
172
|
-
throw new FetchContentfulError(
|
|
173
|
-
`Fragment cycle detected involving "${name}".`,
|
|
174
|
-
{ code: "CONFIG" }
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
kind: graphql.Kind.INLINE_FRAGMENT,
|
|
179
|
-
typeCondition: fragment.typeCondition,
|
|
180
|
-
selectionSet: inlineSelectionSet(fragment.selectionSet, [
|
|
181
|
-
...stack,
|
|
182
|
-
name
|
|
183
|
-
])
|
|
184
|
-
};
|
|
305
|
+
const selections = selectionSet.selections.map((selection) => {
|
|
306
|
+
if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
|
|
307
|
+
const name = selection.name.value;
|
|
308
|
+
const fragment = fragments.get(name);
|
|
309
|
+
if (!fragment) {
|
|
310
|
+
throw new FetchContentfulError(`Unknown fragment "${name}" referenced in query.`, {
|
|
311
|
+
code: "CONFIG"
|
|
312
|
+
});
|
|
185
313
|
}
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
};
|
|
314
|
+
if (stack.includes(name)) {
|
|
315
|
+
throw new FetchContentfulError(`Fragment cycle detected involving "${name}".`, {
|
|
316
|
+
code: "CONFIG"
|
|
317
|
+
});
|
|
191
318
|
}
|
|
192
|
-
return
|
|
319
|
+
return {
|
|
320
|
+
kind: graphql.Kind.INLINE_FRAGMENT,
|
|
321
|
+
typeCondition: fragment.typeCondition,
|
|
322
|
+
selectionSet: inlineSelectionSet(fragment.selectionSet, [...stack, name])
|
|
323
|
+
};
|
|
193
324
|
}
|
|
194
|
-
|
|
325
|
+
if (selection.selectionSet) {
|
|
326
|
+
return {
|
|
327
|
+
...selection,
|
|
328
|
+
selectionSet: inlineSelectionSet(selection.selectionSet, stack)
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
return selection;
|
|
332
|
+
});
|
|
195
333
|
return { ...selectionSet, selections };
|
|
196
334
|
}
|
|
197
335
|
const definitions = document.definitions.filter((definition) => definition.kind !== graphql.Kind.FRAGMENT_DEFINITION).map(
|
|
@@ -226,26 +364,23 @@ function planSplits(document, options) {
|
|
|
226
364
|
continue;
|
|
227
365
|
}
|
|
228
366
|
if (selection.kind !== graphql.Kind.FIELD) {
|
|
229
|
-
throw new FetchContentfulError(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
);
|
|
367
|
+
throw new FetchContentfulError("Fragment spreads must be inlined before split planning.", {
|
|
368
|
+
code: "CONFIG"
|
|
369
|
+
});
|
|
233
370
|
}
|
|
234
371
|
const field = selection;
|
|
235
372
|
const isExplicitSplit = hasDirective(field, SPLIT_DIRECTIVE);
|
|
236
373
|
const isAutoSplit = options.autoSplitNestedCollections && isCollectionField(field);
|
|
374
|
+
const isForcedSplit = options.forceSplit?.(field) ?? false;
|
|
237
375
|
if (isExplicitSplit && depth === 0) {
|
|
238
376
|
throw new FetchContentfulError(
|
|
239
377
|
`Cannot split root field "${responseKeyOf(field)}": @split needs a parent entry to stitch the result back onto, and root fields have none. Move the directive to a nested field, or page through this field with its own \`limit\` and \`skip\` arguments.`,
|
|
240
378
|
{ code: "CONFIG" }
|
|
241
379
|
);
|
|
242
380
|
}
|
|
243
|
-
const shouldSplit = depth > 0 && field.selectionSet !== void 0 && !hasDirective(field, NO_SPLIT_DIRECTIVE) && (isExplicitSplit || isAutoSplit);
|
|
381
|
+
const shouldSplit = depth > 0 && field.selectionSet !== void 0 && !hasDirective(field, NO_SPLIT_DIRECTIVE) && (isExplicitSplit || isAutoSplit || isForcedSplit);
|
|
244
382
|
if (shouldSplit) {
|
|
245
|
-
const planned = withDirective(
|
|
246
|
-
withoutDirective(field, SPLIT_DIRECTIVE),
|
|
247
|
-
NO_SPLIT_DIRECTIVE
|
|
248
|
-
);
|
|
383
|
+
const planned = withDirective(withoutDirective(field, SPLIT_DIRECTIVE), NO_SPLIT_DIRECTIVE);
|
|
249
384
|
plans.push({
|
|
250
385
|
path: [...path],
|
|
251
386
|
responseKey: responseKeyOf(field),
|
|
@@ -274,8 +409,11 @@ function planSplits(document, options) {
|
|
|
274
409
|
selections.push(field);
|
|
275
410
|
}
|
|
276
411
|
}
|
|
277
|
-
if (needsMarkers) {
|
|
278
|
-
selections.push(sysIdMarker()
|
|
412
|
+
if (needsMarkers && !selections.some((s) => hasResponseKey(s, SYS_ID_ALIAS))) {
|
|
413
|
+
selections.push(sysIdMarker());
|
|
414
|
+
}
|
|
415
|
+
if (needsMarkers && !selections.some((s) => hasResponseKey(s, TYPENAME_ALIAS))) {
|
|
416
|
+
selections.push(typenameMarker());
|
|
279
417
|
}
|
|
280
418
|
return { ...selectionSet, selections };
|
|
281
419
|
}
|
|
@@ -293,6 +431,49 @@ function planSplits(document, options) {
|
|
|
293
431
|
plans
|
|
294
432
|
};
|
|
295
433
|
}
|
|
434
|
+
function findSplitCandidates(document) {
|
|
435
|
+
const operation = getOperation(document);
|
|
436
|
+
const candidates = [];
|
|
437
|
+
function walk2(selectionSet, depth) {
|
|
438
|
+
for (const selection of selectionSet.selections) {
|
|
439
|
+
if (selection.kind === graphql.Kind.INLINE_FRAGMENT) {
|
|
440
|
+
walk2(selection.selectionSet, depth);
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (selection.kind !== graphql.Kind.FIELD || !selection.selectionSet) continue;
|
|
444
|
+
const isMarker = hasResponseKey(selection, SYS_ID_ALIAS) || hasResponseKey(selection, TYPENAME_ALIAS);
|
|
445
|
+
if (depth > 0 && selection.name.value !== "items" && !isMarker) {
|
|
446
|
+
candidates.push({ field: selection, size: graphql.print(selection).length });
|
|
447
|
+
}
|
|
448
|
+
walk2(selection.selectionSet, depth + 1);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
walk2(operation.selectionSet, 0);
|
|
452
|
+
return candidates;
|
|
453
|
+
}
|
|
454
|
+
function planSplitsWithSizeBudget(document, options) {
|
|
455
|
+
const first = planSplits(document, {
|
|
456
|
+
autoSplitNestedCollections: options.autoSplitNestedCollections
|
|
457
|
+
});
|
|
458
|
+
if (!options.autoSplitOnSize) return first;
|
|
459
|
+
let outer = first.document;
|
|
460
|
+
const plans = [...first.plans];
|
|
461
|
+
let remainingIterations = findSplitCandidates(outer).length;
|
|
462
|
+
while (options.measure(outer) > options.maxQuerySize && remainingIterations-- > 0) {
|
|
463
|
+
const candidates = findSplitCandidates(outer);
|
|
464
|
+
if (candidates.length === 0) break;
|
|
465
|
+
const target = candidates.reduce(
|
|
466
|
+
(largest, candidate) => candidate.size > largest.size ? candidate : largest
|
|
467
|
+
);
|
|
468
|
+
const next = planSplits(outer, {
|
|
469
|
+
autoSplitNestedCollections: false,
|
|
470
|
+
forceSplit: (field) => field === target.field
|
|
471
|
+
});
|
|
472
|
+
outer = next.document;
|
|
473
|
+
plans.push(...next.plans);
|
|
474
|
+
}
|
|
475
|
+
return { document: outer, plans };
|
|
476
|
+
}
|
|
296
477
|
function stringArgument(name, value) {
|
|
297
478
|
return {
|
|
298
479
|
kind: graphql.Kind.ARGUMENT,
|
|
@@ -344,10 +525,7 @@ function whereIdInArgument() {
|
|
|
344
525
|
}
|
|
345
526
|
function buildSubquery(plan, typename, batchSize, context) {
|
|
346
527
|
const rootKey = `${lowerFirst(typename)}${COLLECTION_SUFFIX}`;
|
|
347
|
-
const args = [
|
|
348
|
-
whereIdInArgument(),
|
|
349
|
-
intArgument("limit", batchSize)
|
|
350
|
-
];
|
|
528
|
+
const args = [whereIdInArgument(), intArgument("limit", batchSize)];
|
|
351
529
|
if (context.preview) {
|
|
352
530
|
args.push(booleanArgument("preview", true));
|
|
353
531
|
}
|
|
@@ -356,10 +534,7 @@ function buildSubquery(plan, typename, batchSize, context) {
|
|
|
356
534
|
}
|
|
357
535
|
const rootField = {
|
|
358
536
|
...namedField(rootKey, void 0, [
|
|
359
|
-
namedField("items", void 0, [
|
|
360
|
-
namedField("sys", void 0, [namedField("id")]),
|
|
361
|
-
plan.field
|
|
362
|
-
])
|
|
537
|
+
namedField("items", void 0, [namedField("sys", void 0, [namedField("id")]), plan.field])
|
|
363
538
|
]),
|
|
364
539
|
arguments: args
|
|
365
540
|
};
|
|
@@ -409,12 +584,68 @@ function stripInternalDirectives(document) {
|
|
|
409
584
|
});
|
|
410
585
|
}
|
|
411
586
|
|
|
587
|
+
// src/unresolvable.ts
|
|
588
|
+
var UNRESOLVABLE_LINK_CODE = "UNRESOLVABLE_LINK";
|
|
589
|
+
var COLLECTION_SUFFIX2 = "Collection";
|
|
590
|
+
function isRecord(value) {
|
|
591
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
592
|
+
}
|
|
593
|
+
function isCollectionValue(value) {
|
|
594
|
+
return isRecord(value) && Array.isArray(value["items"]);
|
|
595
|
+
}
|
|
596
|
+
function isCollectionKey(key) {
|
|
597
|
+
return key.endsWith(COLLECTION_SUFFIX2) && key !== COLLECTION_SUFFIX2;
|
|
598
|
+
}
|
|
599
|
+
function isUnresolvableLinkError(error) {
|
|
600
|
+
const contentful = error.extensions?.["contentful"];
|
|
601
|
+
return isRecord(contentful) && contentful["code"] === UNRESOLVABLE_LINK_CODE;
|
|
602
|
+
}
|
|
603
|
+
function partitionUnresolvableLinks(errors) {
|
|
604
|
+
const unresolvable = [];
|
|
605
|
+
const fatal = [];
|
|
606
|
+
for (const error of errors) {
|
|
607
|
+
if (isUnresolvableLinkError(error)) {
|
|
608
|
+
unresolvable.push(error);
|
|
609
|
+
} else {
|
|
610
|
+
fatal.push(error);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return { unresolvable, fatal };
|
|
614
|
+
}
|
|
615
|
+
function walk(value) {
|
|
616
|
+
if (Array.isArray(value)) {
|
|
617
|
+
return value.map(walk);
|
|
618
|
+
}
|
|
619
|
+
if (isRecord(value)) {
|
|
620
|
+
const result = {};
|
|
621
|
+
for (const [key, child] of Object.entries(value)) {
|
|
622
|
+
result[key] = isCollectionKey(key) && isCollectionValue(child) ? stripCollection(child) : walk(child);
|
|
623
|
+
}
|
|
624
|
+
return result;
|
|
625
|
+
}
|
|
626
|
+
return value;
|
|
627
|
+
}
|
|
628
|
+
function stripCollection(collection) {
|
|
629
|
+
const result = {};
|
|
630
|
+
for (const [key, value] of Object.entries(collection)) {
|
|
631
|
+
result[key] = key === "items" ? value.filter((item) => item !== null && item !== void 0).map(walk) : walk(value);
|
|
632
|
+
}
|
|
633
|
+
return result;
|
|
634
|
+
}
|
|
635
|
+
function omitUnresolvedLinks(data) {
|
|
636
|
+
return walk(data);
|
|
637
|
+
}
|
|
638
|
+
|
|
412
639
|
// src/client.ts
|
|
413
640
|
function graphqlEndpoint(space, environment) {
|
|
414
641
|
return `https://graphql.contentful.com/content/v1/spaces/${encodeURIComponent(
|
|
415
642
|
space
|
|
416
643
|
)}/environments/${encodeURIComponent(environment)}`;
|
|
417
644
|
}
|
|
645
|
+
function printOutgoingQuery(document, context) {
|
|
646
|
+
const printed = graphql.print(stripInternalDirectives(document));
|
|
647
|
+
return context.minifyQuery === false ? printed : minifyQuery(printed);
|
|
648
|
+
}
|
|
418
649
|
function parseRetryAfter(header) {
|
|
419
650
|
if (!header) return void 0;
|
|
420
651
|
const seconds = Number(header);
|
|
@@ -444,14 +675,47 @@ function pickDeclaredVariables(document, variables) {
|
|
|
444
675
|
function isRetryableStatus(status) {
|
|
445
676
|
return status === 408 || status === 429 || status >= 500;
|
|
446
677
|
}
|
|
678
|
+
function isRecord2(value) {
|
|
679
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
680
|
+
}
|
|
681
|
+
function reportUnresolvableLinks(errors, context) {
|
|
682
|
+
if (!context.onUnresolvableLink) return;
|
|
683
|
+
try {
|
|
684
|
+
context.onUnresolvableLink(errors);
|
|
685
|
+
} catch {
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
async function readGraphQLErrors(response) {
|
|
689
|
+
try {
|
|
690
|
+
const payload = await response.json();
|
|
691
|
+
const errors = payload?.errors;
|
|
692
|
+
return Array.isArray(errors) && errors.length > 0 ? errors : void 0;
|
|
693
|
+
} catch {
|
|
694
|
+
return void 0;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
function describeFailure(summary, errors, query, context) {
|
|
698
|
+
if (!errors || errors.length === 0) return summary;
|
|
699
|
+
const message = `${summary} ${errors.map((error) => error.message).join("; ")}`;
|
|
700
|
+
if (context.annotateQueryOnError === false) return message;
|
|
701
|
+
const annotated = annotateQuery(query, errors);
|
|
702
|
+
return annotated ? `${message}
|
|
703
|
+
|
|
704
|
+
${annotated}
|
|
705
|
+
` : message;
|
|
706
|
+
}
|
|
447
707
|
async function rawRequest(document, variables, context) {
|
|
448
|
-
const query =
|
|
449
|
-
const
|
|
450
|
-
query,
|
|
451
|
-
variables: pickDeclaredVariables(document, variables)
|
|
452
|
-
});
|
|
708
|
+
const query = printOutgoingQuery(document, context);
|
|
709
|
+
const pickedVariables = pickDeclaredVariables(document, variables);
|
|
453
710
|
const url = graphqlEndpoint(context.space, context.environment);
|
|
454
|
-
|
|
711
|
+
const usePersistedQuery = context.automaticPersistedQueries === true;
|
|
712
|
+
const sha256Hash = usePersistedQuery ? await sha256Hex(query) : void 0;
|
|
713
|
+
async function send(includeQuery) {
|
|
714
|
+
const body = JSON.stringify({
|
|
715
|
+
...includeQuery ? { query } : {},
|
|
716
|
+
variables: pickedVariables,
|
|
717
|
+
...sha256Hash ? { extensions: persistedQueryExtensions(sha256Hash) } : {}
|
|
718
|
+
});
|
|
455
719
|
let response;
|
|
456
720
|
try {
|
|
457
721
|
const init = {
|
|
@@ -469,16 +733,24 @@ async function rawRequest(document, variables, context) {
|
|
|
469
733
|
} catch (cause) {
|
|
470
734
|
throw new FetchContentfulError(
|
|
471
735
|
`Network error while contacting Contentful: ${String(cause)}`,
|
|
472
|
-
{ code: "NETWORK", retryable: true, cause }
|
|
736
|
+
{ code: "NETWORK", query, retryable: true, cause }
|
|
473
737
|
);
|
|
474
738
|
}
|
|
475
739
|
if (!response.ok) {
|
|
476
740
|
const retryable = isRetryableStatus(response.status);
|
|
741
|
+
const errors = retryable ? void 0 : await readGraphQLErrors(response);
|
|
477
742
|
throw new FetchContentfulError(
|
|
478
|
-
|
|
743
|
+
describeFailure(
|
|
744
|
+
`Contentful responded with HTTP ${response.status}.`,
|
|
745
|
+
errors,
|
|
746
|
+
query,
|
|
747
|
+
context
|
|
748
|
+
),
|
|
479
749
|
{
|
|
480
750
|
code: "HTTP",
|
|
481
751
|
status: response.status,
|
|
752
|
+
errors,
|
|
753
|
+
query,
|
|
482
754
|
retryable,
|
|
483
755
|
retryAfterMs: retryable ? parseRetryAfter(response.headers.get("Retry-After")) : void 0
|
|
484
756
|
}
|
|
@@ -488,24 +760,46 @@ async function rawRequest(document, variables, context) {
|
|
|
488
760
|
try {
|
|
489
761
|
payload = await response.json();
|
|
490
762
|
} catch (cause) {
|
|
491
|
-
throw new FetchContentfulError(
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
763
|
+
throw new FetchContentfulError("Contentful returned an unreadable response body.", {
|
|
764
|
+
code: "NETWORK",
|
|
765
|
+
query,
|
|
766
|
+
retryable: true,
|
|
767
|
+
cause
|
|
768
|
+
});
|
|
495
769
|
}
|
|
496
770
|
if (payload.errors && payload.errors.length > 0) {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
771
|
+
const { unresolvable, fatal } = partitionUnresolvableLinks(payload.errors);
|
|
772
|
+
const tolerable = context.unresolvableLinks !== "error" && fatal.length === 0 && unresolvable.length > 0 && isRecord2(payload.data);
|
|
773
|
+
if (!tolerable) {
|
|
774
|
+
throw new FetchContentfulError(
|
|
775
|
+
describeFailure("Contentful returned GraphQL errors:", payload.errors, query, context),
|
|
776
|
+
{ code: "GRAPHQL", errors: payload.errors, query }
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
reportUnresolvableLinks(unresolvable, context);
|
|
501
780
|
}
|
|
502
781
|
if (!payload.data || typeof payload.data !== "object") {
|
|
503
|
-
throw new FetchContentfulError(
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
);
|
|
782
|
+
throw new FetchContentfulError("Contentful returned no data and no errors.", {
|
|
783
|
+
code: "GRAPHQL",
|
|
784
|
+
query
|
|
785
|
+
});
|
|
507
786
|
}
|
|
508
787
|
return payload.data;
|
|
788
|
+
}
|
|
789
|
+
let mustIncludeQuery = !usePersistedQuery;
|
|
790
|
+
return withRetries(async () => {
|
|
791
|
+
if (mustIncludeQuery) {
|
|
792
|
+
return send(true);
|
|
793
|
+
}
|
|
794
|
+
try {
|
|
795
|
+
return await send(false);
|
|
796
|
+
} catch (error) {
|
|
797
|
+
if (isFetchContentfulError(error) && error.code === "GRAPHQL" && error.errors?.some(isPersistedQueryNotFoundError)) {
|
|
798
|
+
mustIncludeQuery = true;
|
|
799
|
+
return await send(true);
|
|
800
|
+
}
|
|
801
|
+
throw error;
|
|
802
|
+
}
|
|
509
803
|
}, context.retry);
|
|
510
804
|
}
|
|
511
805
|
|
|
@@ -519,7 +813,6 @@ function readEnvSettings() {
|
|
|
519
813
|
space: void 0,
|
|
520
814
|
environment: void 0,
|
|
521
815
|
deliveryToken: void 0,
|
|
522
|
-
token: void 0,
|
|
523
816
|
previewToken: void 0
|
|
524
817
|
};
|
|
525
818
|
}
|
|
@@ -534,7 +827,6 @@ function readEnvSettings() {
|
|
|
534
827
|
process.env.CONTENTFUL_ENVIRONMENT || process.env.NEXT_PUBLIC_CONTENTFUL_ENVIRONMENT
|
|
535
828
|
),
|
|
536
829
|
deliveryToken,
|
|
537
|
-
token: deliveryToken,
|
|
538
830
|
// No `NEXT_PUBLIC_` fallback: see the note at the top of this file.
|
|
539
831
|
previewToken: orUndefined(process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN)
|
|
540
832
|
};
|
|
@@ -576,20 +868,18 @@ function injectIntoField(field, args) {
|
|
|
576
868
|
return { ...field, arguments: [...argumentsOf(field), ...additions] };
|
|
577
869
|
}
|
|
578
870
|
function injectIntoSelectionSet(selectionSet, args) {
|
|
579
|
-
const selections = selectionSet.selections.map(
|
|
580
|
-
(selection)
|
|
581
|
-
|
|
582
|
-
return injectIntoField(selection, args);
|
|
583
|
-
}
|
|
584
|
-
if (selection.kind === graphql.Kind.INLINE_FRAGMENT) {
|
|
585
|
-
return {
|
|
586
|
-
...selection,
|
|
587
|
-
selectionSet: injectIntoSelectionSet(selection.selectionSet, args)
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
return selection;
|
|
871
|
+
const selections = selectionSet.selections.map((selection) => {
|
|
872
|
+
if (selection.kind === graphql.Kind.FIELD) {
|
|
873
|
+
return injectIntoField(selection, args);
|
|
591
874
|
}
|
|
592
|
-
|
|
875
|
+
if (selection.kind === graphql.Kind.INLINE_FRAGMENT) {
|
|
876
|
+
return {
|
|
877
|
+
...selection,
|
|
878
|
+
selectionSet: injectIntoSelectionSet(selection.selectionSet, args)
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
return selection;
|
|
882
|
+
});
|
|
593
883
|
return { ...selectionSet, selections };
|
|
594
884
|
}
|
|
595
885
|
function injectRootArgs(document, args) {
|
|
@@ -630,27 +920,24 @@ async function fetchLocales(context) {
|
|
|
630
920
|
if (context.signal) init.signal = context.signal;
|
|
631
921
|
response = await context.fetch(localesEndpoint(context), init);
|
|
632
922
|
} catch (cause) {
|
|
633
|
-
throw new FetchContentfulError(
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
923
|
+
throw new FetchContentfulError(`Network error while fetching locales: ${String(cause)}`, {
|
|
924
|
+
code: "NETWORK",
|
|
925
|
+
retryable: true,
|
|
926
|
+
cause
|
|
927
|
+
});
|
|
637
928
|
}
|
|
638
929
|
if (!response.ok) {
|
|
639
|
-
throw new FetchContentfulError(
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
retryable: response.status === 429 || response.status >= 500
|
|
645
|
-
}
|
|
646
|
-
);
|
|
930
|
+
throw new FetchContentfulError(`Locale request failed with HTTP ${response.status}.`, {
|
|
931
|
+
code: "HTTP",
|
|
932
|
+
status: response.status,
|
|
933
|
+
retryable: response.status === 429 || response.status >= 500
|
|
934
|
+
});
|
|
647
935
|
}
|
|
648
936
|
const payload = await response.json();
|
|
649
937
|
if (!Array.isArray(payload.items)) {
|
|
650
|
-
throw new FetchContentfulError(
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
);
|
|
938
|
+
throw new FetchContentfulError("Locale response did not include an items array.", {
|
|
939
|
+
code: "NETWORK"
|
|
940
|
+
});
|
|
654
941
|
}
|
|
655
942
|
return payload.items.map((item) => ({
|
|
656
943
|
code: item.code,
|
|
@@ -678,24 +965,22 @@ function clearLocaleCache() {
|
|
|
678
965
|
}
|
|
679
966
|
|
|
680
967
|
// src/shape.ts
|
|
681
|
-
var
|
|
682
|
-
function
|
|
968
|
+
var COLLECTION_SUFFIX3 = "Collection";
|
|
969
|
+
function isRecord3(value) {
|
|
683
970
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
684
971
|
}
|
|
685
|
-
function
|
|
686
|
-
return
|
|
972
|
+
function isCollectionValue2(value) {
|
|
973
|
+
return isRecord3(value) && Array.isArray(value["items"]);
|
|
687
974
|
}
|
|
688
975
|
function shapeData(data) {
|
|
689
976
|
if (Array.isArray(data)) {
|
|
690
977
|
return data.map((item) => shapeData(item));
|
|
691
978
|
}
|
|
692
|
-
if (
|
|
979
|
+
if (isRecord3(data)) {
|
|
693
980
|
const shaped = {};
|
|
694
981
|
for (const [key, value] of Object.entries(data)) {
|
|
695
|
-
if (key.endsWith(
|
|
696
|
-
shaped[key.slice(0, -
|
|
697
|
-
value.items
|
|
698
|
-
);
|
|
982
|
+
if (key.endsWith(COLLECTION_SUFFIX3) && key !== COLLECTION_SUFFIX3 && isCollectionValue2(value)) {
|
|
983
|
+
shaped[key.slice(0, -COLLECTION_SUFFIX3.length)] = shapeData(value.items);
|
|
699
984
|
} else {
|
|
700
985
|
shaped[key] = shapeData(value);
|
|
701
986
|
}
|
|
@@ -705,7 +990,7 @@ function shapeData(data) {
|
|
|
705
990
|
return data;
|
|
706
991
|
}
|
|
707
992
|
function unwrapSingleRoot(data) {
|
|
708
|
-
if (
|
|
993
|
+
if (isRecord3(data)) {
|
|
709
994
|
const keys = Object.keys(data);
|
|
710
995
|
if (keys.length === 1) {
|
|
711
996
|
return data[keys[0]];
|
|
@@ -720,7 +1005,9 @@ var DEFAULT_RETRIES = 5;
|
|
|
720
1005
|
var DEFAULT_RETRY_DELAY_MS = 250;
|
|
721
1006
|
var DEFAULT_MAX_RETRY_DELAY_MS = 8e3;
|
|
722
1007
|
var DEFAULT_SPLIT_BATCH_SIZE = 50;
|
|
723
|
-
|
|
1008
|
+
var DEFAULT_MAX_QUERY_SIZE = 7500;
|
|
1009
|
+
var DEFAULT_MAX_QUERY_SIZE_WITH_APQ = 15500;
|
|
1010
|
+
function isRecord4(value) {
|
|
724
1011
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
725
1012
|
}
|
|
726
1013
|
function collectAtPath(data, path) {
|
|
@@ -728,7 +1015,7 @@ function collectAtPath(data, path) {
|
|
|
728
1015
|
for (const segment of path) {
|
|
729
1016
|
const next = [];
|
|
730
1017
|
for (const value of current) {
|
|
731
|
-
if (
|
|
1018
|
+
if (isRecord4(value)) {
|
|
732
1019
|
const child = value[segment];
|
|
733
1020
|
if (Array.isArray(child)) {
|
|
734
1021
|
next.push(...child);
|
|
@@ -739,14 +1026,14 @@ function collectAtPath(data, path) {
|
|
|
739
1026
|
}
|
|
740
1027
|
current = next;
|
|
741
1028
|
}
|
|
742
|
-
return current.filter(
|
|
1029
|
+
return current.filter(isRecord4);
|
|
743
1030
|
}
|
|
744
1031
|
function resolveContext(options) {
|
|
745
1032
|
const env = readEnvSettings();
|
|
746
1033
|
const space = options.space ?? env.space;
|
|
747
1034
|
const environment = options.environment ?? env.environment ?? "master";
|
|
748
1035
|
const preview = options.preview ?? false;
|
|
749
|
-
const token = preview ? options.previewToken ??
|
|
1036
|
+
const token = preview ? options.previewToken ?? env.previewToken : options.deliveryToken ?? env.deliveryToken;
|
|
750
1037
|
if (!space || !token) {
|
|
751
1038
|
const missing = [];
|
|
752
1039
|
if (!space) {
|
|
@@ -770,6 +1057,7 @@ function resolveContext(options) {
|
|
|
770
1057
|
maxDelayMs: options.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS
|
|
771
1058
|
};
|
|
772
1059
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1060
|
+
const automaticPersistedQueries = options.automaticPersistedQueries ?? false;
|
|
773
1061
|
const context = {
|
|
774
1062
|
space,
|
|
775
1063
|
environment,
|
|
@@ -780,8 +1068,19 @@ function resolveContext(options) {
|
|
|
780
1068
|
fetch: fetchImpl,
|
|
781
1069
|
retry,
|
|
782
1070
|
autoSplitNestedCollections: options.autoSplitNestedCollections ?? true,
|
|
783
|
-
|
|
1071
|
+
autoSplitOnSize: options.autoSplitOnSize ?? true,
|
|
1072
|
+
// APQ lifts Contentful's size ceiling from 8 KB to 16 KB, so the budget
|
|
1073
|
+
// that triggers a size-driven split follows suit unless overridden.
|
|
1074
|
+
maxQuerySize: options.maxQuerySize ?? (automaticPersistedQueries ? DEFAULT_MAX_QUERY_SIZE_WITH_APQ : DEFAULT_MAX_QUERY_SIZE),
|
|
1075
|
+
splitBatchSize: options.splitBatchSize ?? DEFAULT_SPLIT_BATCH_SIZE,
|
|
1076
|
+
annotateQueryOnError: options.annotateQueryOnError ?? true,
|
|
1077
|
+
unresolvableLinks: options.unresolvableLinks ?? "omit",
|
|
1078
|
+
minifyQuery: options.minifyQuery ?? true,
|
|
1079
|
+
automaticPersistedQueries
|
|
784
1080
|
};
|
|
1081
|
+
if (options.onUnresolvableLink) {
|
|
1082
|
+
context.onUnresolvableLink = options.onUnresolvableLink;
|
|
1083
|
+
}
|
|
785
1084
|
if (options.signal) context.signal = options.signal;
|
|
786
1085
|
if (options.next) context.next = options.next;
|
|
787
1086
|
if (options.cache) context.cache = options.cache;
|
|
@@ -799,7 +1098,7 @@ async function resolvePlan(plan, data, variables, context) {
|
|
|
799
1098
|
for (const parent of relevant) {
|
|
800
1099
|
const typename = parent[TYPENAME_ALIAS];
|
|
801
1100
|
const sys = parent[SYS_ID_ALIAS];
|
|
802
|
-
if (!
|
|
1101
|
+
if (!isRecord4(sys) || typeof sys["id"] !== "string") {
|
|
803
1102
|
throw new FetchContentfulError(
|
|
804
1103
|
`Cannot split "${plan.responseKey}": a parent entry is missing its sys id.`,
|
|
805
1104
|
{ code: "STITCH" }
|
|
@@ -822,19 +1121,14 @@ async function resolvePlan(plan, data, variables, context) {
|
|
|
822
1121
|
];
|
|
823
1122
|
await Promise.all(
|
|
824
1123
|
chunk(ids, context.splitBatchSize).map(async (idBatch) => {
|
|
825
|
-
const { document, rootKey } = buildSubquery(
|
|
826
|
-
plan,
|
|
827
|
-
typename,
|
|
828
|
-
idBatch.length,
|
|
829
|
-
context
|
|
830
|
-
);
|
|
1124
|
+
const { document, rootKey } = buildSubquery(plan, typename, idBatch.length, context);
|
|
831
1125
|
const subData = await executeDocument(
|
|
832
1126
|
document,
|
|
833
1127
|
{ ...variables, _splitIds: idBatch },
|
|
834
1128
|
context
|
|
835
1129
|
);
|
|
836
1130
|
const collection = subData[rootKey];
|
|
837
|
-
const items =
|
|
1131
|
+
const items = isRecord4(collection) && Array.isArray(collection["items"]) ? collection["items"] : void 0;
|
|
838
1132
|
if (!items) {
|
|
839
1133
|
throw new FetchContentfulError(
|
|
840
1134
|
`Split subquery for "${typename}" returned no "${rootKey}.items". Check that the content type follows Contentful naming conventions.`,
|
|
@@ -842,9 +1136,9 @@ async function resolvePlan(plan, data, variables, context) {
|
|
|
842
1136
|
);
|
|
843
1137
|
}
|
|
844
1138
|
for (const item of items) {
|
|
845
|
-
if (
|
|
1139
|
+
if (isRecord4(item)) {
|
|
846
1140
|
const sys = item["sys"];
|
|
847
|
-
if (
|
|
1141
|
+
if (isRecord4(sys) && typeof sys["id"] === "string") {
|
|
848
1142
|
resolved.set(`${typename}:${sys["id"]}`, item[plan.responseKey]);
|
|
849
1143
|
}
|
|
850
1144
|
}
|
|
@@ -874,12 +1168,18 @@ function cleanupMarkers(data, plans) {
|
|
|
874
1168
|
}
|
|
875
1169
|
}
|
|
876
1170
|
}
|
|
1171
|
+
function byteLength(text) {
|
|
1172
|
+
return new TextEncoder().encode(text).length;
|
|
1173
|
+
}
|
|
877
1174
|
async function executeDocument(document, variables, context) {
|
|
878
|
-
const { document: outer, plans } =
|
|
1175
|
+
const { document: outer, plans } = planSplitsWithSizeBudget(document, {
|
|
1176
|
+
autoSplitNestedCollections: context.autoSplitNestedCollections,
|
|
1177
|
+
autoSplitOnSize: context.autoSplitOnSize,
|
|
1178
|
+
maxQuerySize: context.maxQuerySize,
|
|
1179
|
+
measure: (doc) => byteLength(printOutgoingQuery(doc, context))
|
|
1180
|
+
});
|
|
879
1181
|
const data = await rawRequest(outer, variables, context);
|
|
880
|
-
await Promise.all(
|
|
881
|
-
plans.map((plan) => resolvePlan(plan, data, variables, context))
|
|
882
|
-
);
|
|
1182
|
+
await Promise.all(plans.map((plan) => resolvePlan(plan, data, variables, context)));
|
|
883
1183
|
cleanupMarkers(data, plans);
|
|
884
1184
|
return data;
|
|
885
1185
|
}
|
|
@@ -893,10 +1193,10 @@ function toDocument(query) {
|
|
|
893
1193
|
try {
|
|
894
1194
|
return graphql.parse(typeof query === "string" ? query : String(query));
|
|
895
1195
|
} catch (cause) {
|
|
896
|
-
throw new FetchContentfulError(
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
);
|
|
1196
|
+
throw new FetchContentfulError(`Failed to parse GraphQL query: ${String(cause)}`, {
|
|
1197
|
+
code: "CONFIG",
|
|
1198
|
+
cause
|
|
1199
|
+
});
|
|
900
1200
|
}
|
|
901
1201
|
}
|
|
902
1202
|
function withAutoVariables(document, variables, context) {
|
|
@@ -932,13 +1232,10 @@ async function runFetchContentful(query, options) {
|
|
|
932
1232
|
);
|
|
933
1233
|
}
|
|
934
1234
|
}
|
|
935
|
-
const variables = withAutoVariables(
|
|
936
|
-
document,
|
|
937
|
-
options.variables ?? {},
|
|
938
|
-
context
|
|
939
|
-
);
|
|
1235
|
+
const variables = withAutoVariables(document, options.variables ?? {}, context);
|
|
940
1236
|
const data = await executeDocument(document, variables, context);
|
|
941
|
-
const
|
|
1237
|
+
const resolved = context.unresolvableLinks === "null" ? data : omitUnresolvedLinks(data);
|
|
1238
|
+
const result = options.shapeResponseData === false ? resolved : shapeData(resolved);
|
|
942
1239
|
if (options.unwrapRootField === false) {
|
|
943
1240
|
return result;
|
|
944
1241
|
}
|
|
@@ -959,6 +1256,8 @@ function createFetchContentful(defaults = {}) {
|
|
|
959
1256
|
var index_default = fetchContentful;
|
|
960
1257
|
|
|
961
1258
|
exports.FetchContentfulError = FetchContentfulError;
|
|
1259
|
+
exports.UNRESOLVABLE_LINK_CODE = UNRESOLVABLE_LINK_CODE;
|
|
1260
|
+
exports.annotateQuery = annotateQuery;
|
|
962
1261
|
exports.clearLocaleCache = clearLocaleCache;
|
|
963
1262
|
exports.collectAtPath = collectAtPath;
|
|
964
1263
|
exports.createFetchContentful = createFetchContentful;
|
|
@@ -968,6 +1267,10 @@ exports.getLocales = getLocales;
|
|
|
968
1267
|
exports.injectRootArgs = injectRootArgs;
|
|
969
1268
|
exports.inlineFragments = inlineFragments;
|
|
970
1269
|
exports.isFetchContentfulError = isFetchContentfulError;
|
|
1270
|
+
exports.isUnresolvableLinkError = isUnresolvableLinkError;
|
|
1271
|
+
exports.minifyQuery = minifyQuery;
|
|
1272
|
+
exports.omitUnresolvedLinks = omitUnresolvedLinks;
|
|
1273
|
+
exports.partitionUnresolvableLinks = partitionUnresolvableLinks;
|
|
971
1274
|
exports.readEnvSettings = readEnvSettings;
|
|
972
1275
|
exports.shapeData = shapeData;
|
|
973
1276
|
exports.unwrapSingleRoot = unwrapSingleRoot;
|