@fourtwelvelabs/fetch-contentful 1.0.0 → 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 +12 -0
- package/README.md +37 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +144 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -1
- package/dist/index.d.ts +54 -1
- package/dist/index.mjs +145 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -130,6 +130,29 @@ interface FetchContentfulOptions<TVariables extends GraphQLVariables = GraphQLVa
|
|
|
130
130
|
* entry to stitch the result back onto.
|
|
131
131
|
*/
|
|
132
132
|
autoSplitNestedCollections?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* When the outer query — after minification, and after the splitting
|
|
135
|
+
* above — still exceeds {@link FetchContentfulOptions.maxQuerySize}, keep
|
|
136
|
+
* peeling off the single largest remaining field (any field with a
|
|
137
|
+
* selection set, one-to-one references included, not just `*Collection`
|
|
138
|
+
* fields) into its own subquery until it fits or nothing splittable is
|
|
139
|
+
* left. Defaults to `true`.
|
|
140
|
+
*
|
|
141
|
+
* Independent of `autoSplitNestedCollections`: that option controls which
|
|
142
|
+
* fields split *by shape*, this one adds more splits *by measured size* as
|
|
143
|
+
* a last resort. A query that still doesn't fit after every splittable
|
|
144
|
+
* field has been removed is sent as-is — at that point there is nothing
|
|
145
|
+
* left for this library to do, and Contentful's own `QUERY_TOO_BIG` is the
|
|
146
|
+
* accurate error to see.
|
|
147
|
+
*/
|
|
148
|
+
autoSplitOnSize?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Byte budget that triggers {@link FetchContentfulOptions.autoSplitOnSize}.
|
|
151
|
+
* Defaults to `7500` — comfortably under Contentful's published 8 KB
|
|
152
|
+
* query size limit — or `15500` when `automaticPersistedQueries` is
|
|
153
|
+
* enabled, which raises Contentful's own ceiling to 16 KB.
|
|
154
|
+
*/
|
|
155
|
+
maxQuerySize?: number;
|
|
133
156
|
/** How many parent entry ids to resolve per subquery request. Defaults to `50`. */
|
|
134
157
|
splitBatchSize?: number;
|
|
135
158
|
/**
|
|
@@ -195,6 +218,29 @@ interface FetchContentfulOptions<TVariables extends GraphQLVariables = GraphQLVa
|
|
|
195
218
|
* on demand.
|
|
196
219
|
*/
|
|
197
220
|
annotateQueryOnError?: boolean;
|
|
221
|
+
/**
|
|
222
|
+
* Strip insignificant whitespace from every outgoing query before it's
|
|
223
|
+
* sent — the same effect a tool like GQLMin has, applied automatically.
|
|
224
|
+
* Defaults to `true`; the only reason to disable it is to read a raw
|
|
225
|
+
* request off the wire while debugging, since {@link
|
|
226
|
+
* FetchContentfulError.query} and the annotated caret display always
|
|
227
|
+
* reflect whatever text was actually transmitted.
|
|
228
|
+
*/
|
|
229
|
+
minifyQuery?: boolean;
|
|
230
|
+
/**
|
|
231
|
+
* Send every request as an Automatic Persisted Query: the optimistic first
|
|
232
|
+
* attempt sends only a hash of the query, and the query text itself is
|
|
233
|
+
* sent (once) only the first time Contentful reports that hash unknown.
|
|
234
|
+
* This is what actually lifts Contentful's query size ceiling from 8 KB to
|
|
235
|
+
* 16 KB, and every request after a query's first use no longer transmits
|
|
236
|
+
* the query text at all.
|
|
237
|
+
*
|
|
238
|
+
* **Requires the Premium plan or above** — see
|
|
239
|
+
* https://www.contentful.com/developers/docs/references/graphql/automatic-persisted-queries/.
|
|
240
|
+
* On any other plan this doubles every request's round trips for no
|
|
241
|
+
* benefit, which is why it defaults to `false`.
|
|
242
|
+
*/
|
|
243
|
+
automaticPersistedQueries?: boolean;
|
|
198
244
|
/** Custom fetch implementation (useful for tests). Defaults to `globalThis.fetch`. */
|
|
199
245
|
fetch?: typeof fetch;
|
|
200
246
|
/** AbortSignal forwarded to every underlying fetch call. */
|
|
@@ -468,6 +514,13 @@ declare function getLocales(context: LocalesContext): Promise<ContentfulLocale[]
|
|
|
468
514
|
/** Clears the in-module locale cache (mainly useful in tests). */
|
|
469
515
|
declare function clearLocaleCache(): void;
|
|
470
516
|
|
|
517
|
+
/**
|
|
518
|
+
* Minifies an already-valid GraphQL document string to the shortest text
|
|
519
|
+
* that still lexes to the same tokens. Safe to call on `print()` output or
|
|
520
|
+
* on a hand-written query; comments are dropped either way.
|
|
521
|
+
*/
|
|
522
|
+
declare function minifyQuery(query: string): string;
|
|
523
|
+
|
|
471
524
|
/**
|
|
472
525
|
* Recursively rewrites Contentful collection wrappers:
|
|
473
526
|
* `{ fooCollection: { items: [...] } }` becomes `{ foo: [...] }`.
|
|
@@ -676,4 +729,4 @@ declare function createFetchContentful<TDefaultVariables extends GraphQLVariable
|
|
|
676
729
|
<TData = Record<string, unknown>, TVariables extends GraphQLVariables = GraphQLVariables>(query: string, options?: FetchContentfulOptions<TVariables>): Promise<UnwrapSingleRoot<ShapeCollections<OmitUnresolvedLinks<TData>>>>;
|
|
677
730
|
};
|
|
678
731
|
|
|
679
|
-
export { type AnnotateQueryOptions, type AutoInjectedVariable, type CallerVariables, type ContentfulGraphQLError, type ContentfulLocale, type DocumentResult, FetchContentfulError, type FetchContentfulErrorCode, type FetchContentfulOptions, type GraphQLVariables, type NextFetchOptions, type OmitUnresolvedLinks, type ShapeCollections, type TypedFetchContentfulOptions, UNRESOLVABLE_LINK_CODE, type UnresolvableLinkMode, type UnwrapSingleRoot, type VariablesOption, annotateQuery, clearLocaleCache, collectAtPath, createFetchContentful, fetchContentful as default, fetchContentful, getLocales, injectRootArgs, inlineFragments, isFetchContentfulError, isUnresolvableLinkError, omitUnresolvedLinks, partitionUnresolvableLinks, readEnvSettings, shapeData, unwrapSingleRoot };
|
|
732
|
+
export { type AnnotateQueryOptions, type AutoInjectedVariable, type CallerVariables, type ContentfulGraphQLError, type ContentfulLocale, type DocumentResult, FetchContentfulError, type FetchContentfulErrorCode, type FetchContentfulOptions, type GraphQLVariables, type NextFetchOptions, type OmitUnresolvedLinks, type ShapeCollections, type TypedFetchContentfulOptions, UNRESOLVABLE_LINK_CODE, type UnresolvableLinkMode, type UnwrapSingleRoot, type VariablesOption, annotateQuery, clearLocaleCache, collectAtPath, createFetchContentful, fetchContentful as default, fetchContentful, getLocales, injectRootArgs, inlineFragments, isFetchContentfulError, isUnresolvableLinkError, minifyQuery, omitUnresolvedLinks, partitionUnresolvableLinks, readEnvSettings, shapeData, unwrapSingleRoot };
|
package/dist/index.d.ts
CHANGED
|
@@ -130,6 +130,29 @@ interface FetchContentfulOptions<TVariables extends GraphQLVariables = GraphQLVa
|
|
|
130
130
|
* entry to stitch the result back onto.
|
|
131
131
|
*/
|
|
132
132
|
autoSplitNestedCollections?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* When the outer query — after minification, and after the splitting
|
|
135
|
+
* above — still exceeds {@link FetchContentfulOptions.maxQuerySize}, keep
|
|
136
|
+
* peeling off the single largest remaining field (any field with a
|
|
137
|
+
* selection set, one-to-one references included, not just `*Collection`
|
|
138
|
+
* fields) into its own subquery until it fits or nothing splittable is
|
|
139
|
+
* left. Defaults to `true`.
|
|
140
|
+
*
|
|
141
|
+
* Independent of `autoSplitNestedCollections`: that option controls which
|
|
142
|
+
* fields split *by shape*, this one adds more splits *by measured size* as
|
|
143
|
+
* a last resort. A query that still doesn't fit after every splittable
|
|
144
|
+
* field has been removed is sent as-is — at that point there is nothing
|
|
145
|
+
* left for this library to do, and Contentful's own `QUERY_TOO_BIG` is the
|
|
146
|
+
* accurate error to see.
|
|
147
|
+
*/
|
|
148
|
+
autoSplitOnSize?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Byte budget that triggers {@link FetchContentfulOptions.autoSplitOnSize}.
|
|
151
|
+
* Defaults to `7500` — comfortably under Contentful's published 8 KB
|
|
152
|
+
* query size limit — or `15500` when `automaticPersistedQueries` is
|
|
153
|
+
* enabled, which raises Contentful's own ceiling to 16 KB.
|
|
154
|
+
*/
|
|
155
|
+
maxQuerySize?: number;
|
|
133
156
|
/** How many parent entry ids to resolve per subquery request. Defaults to `50`. */
|
|
134
157
|
splitBatchSize?: number;
|
|
135
158
|
/**
|
|
@@ -195,6 +218,29 @@ interface FetchContentfulOptions<TVariables extends GraphQLVariables = GraphQLVa
|
|
|
195
218
|
* on demand.
|
|
196
219
|
*/
|
|
197
220
|
annotateQueryOnError?: boolean;
|
|
221
|
+
/**
|
|
222
|
+
* Strip insignificant whitespace from every outgoing query before it's
|
|
223
|
+
* sent — the same effect a tool like GQLMin has, applied automatically.
|
|
224
|
+
* Defaults to `true`; the only reason to disable it is to read a raw
|
|
225
|
+
* request off the wire while debugging, since {@link
|
|
226
|
+
* FetchContentfulError.query} and the annotated caret display always
|
|
227
|
+
* reflect whatever text was actually transmitted.
|
|
228
|
+
*/
|
|
229
|
+
minifyQuery?: boolean;
|
|
230
|
+
/**
|
|
231
|
+
* Send every request as an Automatic Persisted Query: the optimistic first
|
|
232
|
+
* attempt sends only a hash of the query, and the query text itself is
|
|
233
|
+
* sent (once) only the first time Contentful reports that hash unknown.
|
|
234
|
+
* This is what actually lifts Contentful's query size ceiling from 8 KB to
|
|
235
|
+
* 16 KB, and every request after a query's first use no longer transmits
|
|
236
|
+
* the query text at all.
|
|
237
|
+
*
|
|
238
|
+
* **Requires the Premium plan or above** — see
|
|
239
|
+
* https://www.contentful.com/developers/docs/references/graphql/automatic-persisted-queries/.
|
|
240
|
+
* On any other plan this doubles every request's round trips for no
|
|
241
|
+
* benefit, which is why it defaults to `false`.
|
|
242
|
+
*/
|
|
243
|
+
automaticPersistedQueries?: boolean;
|
|
198
244
|
/** Custom fetch implementation (useful for tests). Defaults to `globalThis.fetch`. */
|
|
199
245
|
fetch?: typeof fetch;
|
|
200
246
|
/** AbortSignal forwarded to every underlying fetch call. */
|
|
@@ -468,6 +514,13 @@ declare function getLocales(context: LocalesContext): Promise<ContentfulLocale[]
|
|
|
468
514
|
/** Clears the in-module locale cache (mainly useful in tests). */
|
|
469
515
|
declare function clearLocaleCache(): void;
|
|
470
516
|
|
|
517
|
+
/**
|
|
518
|
+
* Minifies an already-valid GraphQL document string to the shortest text
|
|
519
|
+
* that still lexes to the same tokens. Safe to call on `print()` output or
|
|
520
|
+
* on a hand-written query; comments are dropped either way.
|
|
521
|
+
*/
|
|
522
|
+
declare function minifyQuery(query: string): string;
|
|
523
|
+
|
|
471
524
|
/**
|
|
472
525
|
* Recursively rewrites Contentful collection wrappers:
|
|
473
526
|
* `{ fooCollection: { items: [...] } }` becomes `{ foo: [...] }`.
|
|
@@ -676,4 +729,4 @@ declare function createFetchContentful<TDefaultVariables extends GraphQLVariable
|
|
|
676
729
|
<TData = Record<string, unknown>, TVariables extends GraphQLVariables = GraphQLVariables>(query: string, options?: FetchContentfulOptions<TVariables>): Promise<UnwrapSingleRoot<ShapeCollections<OmitUnresolvedLinks<TData>>>>;
|
|
677
730
|
};
|
|
678
731
|
|
|
679
|
-
export { type AnnotateQueryOptions, type AutoInjectedVariable, type CallerVariables, type ContentfulGraphQLError, type ContentfulLocale, type DocumentResult, FetchContentfulError, type FetchContentfulErrorCode, type FetchContentfulOptions, type GraphQLVariables, type NextFetchOptions, type OmitUnresolvedLinks, type ShapeCollections, type TypedFetchContentfulOptions, UNRESOLVABLE_LINK_CODE, type UnresolvableLinkMode, type UnwrapSingleRoot, type VariablesOption, annotateQuery, clearLocaleCache, collectAtPath, createFetchContentful, fetchContentful as default, fetchContentful, getLocales, injectRootArgs, inlineFragments, isFetchContentfulError, isUnresolvableLinkError, omitUnresolvedLinks, partitionUnresolvableLinks, readEnvSettings, shapeData, unwrapSingleRoot };
|
|
732
|
+
export { type AnnotateQueryOptions, type AutoInjectedVariable, type CallerVariables, type ContentfulGraphQLError, type ContentfulLocale, type DocumentResult, FetchContentfulError, type FetchContentfulErrorCode, type FetchContentfulOptions, type GraphQLVariables, type NextFetchOptions, type OmitUnresolvedLinks, type ShapeCollections, type TypedFetchContentfulOptions, UNRESOLVABLE_LINK_CODE, type UnresolvableLinkMode, type UnwrapSingleRoot, type VariablesOption, annotateQuery, clearLocaleCache, collectAtPath, createFetchContentful, fetchContentful as default, fetchContentful, getLocales, injectRootArgs, inlineFragments, isFetchContentfulError, isUnresolvableLinkError, minifyQuery, omitUnresolvedLinks, partitionUnresolvableLinks, readEnvSettings, shapeData, unwrapSingleRoot };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Kind, parse, print, visit } from 'graphql';
|
|
1
|
+
import { TokenKind, Lexer, Source, Kind, parse, print, visit } from 'graphql';
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
|
|
@@ -126,6 +126,48 @@ var FetchContentfulError = class extends Error {
|
|
|
126
126
|
function isFetchContentfulError(value) {
|
|
127
127
|
return value instanceof FetchContentfulError;
|
|
128
128
|
}
|
|
129
|
+
var WORD_LIKE = /* @__PURE__ */ new Set([TokenKind.NAME, TokenKind.INT, TokenKind.FLOAT]);
|
|
130
|
+
function minifyQuery(query) {
|
|
131
|
+
const lexer = new Lexer(new Source(query));
|
|
132
|
+
let previousKind;
|
|
133
|
+
let out = "";
|
|
134
|
+
for (let token = lexer.advance(); token.kind !== TokenKind.EOF; token = lexer.advance()) {
|
|
135
|
+
if (previousKind && WORD_LIKE.has(previousKind) && WORD_LIKE.has(token.kind)) {
|
|
136
|
+
out += " ";
|
|
137
|
+
}
|
|
138
|
+
out += query.slice(token.start, token.end);
|
|
139
|
+
previousKind = token.kind;
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/persisted-query.ts
|
|
145
|
+
var PERSISTED_QUERY_VERSION = 1;
|
|
146
|
+
function toHex(bytes) {
|
|
147
|
+
let hex = "";
|
|
148
|
+
for (const byte of bytes) {
|
|
149
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
150
|
+
}
|
|
151
|
+
return hex;
|
|
152
|
+
}
|
|
153
|
+
async function sha256Hex(query) {
|
|
154
|
+
const data = new TextEncoder().encode(query);
|
|
155
|
+
const subtle = globalThis.crypto?.subtle;
|
|
156
|
+
if (subtle) {
|
|
157
|
+
const digest = await subtle.digest("SHA-256", data);
|
|
158
|
+
return toHex(new Uint8Array(digest));
|
|
159
|
+
}
|
|
160
|
+
const { createHash } = await import('crypto');
|
|
161
|
+
return createHash("sha256").update(data).digest("hex");
|
|
162
|
+
}
|
|
163
|
+
function persistedQueryExtensions(sha256Hash) {
|
|
164
|
+
return { persistedQuery: { version: PERSISTED_QUERY_VERSION, sha256Hash } };
|
|
165
|
+
}
|
|
166
|
+
function isPersistedQueryNotFoundError(error) {
|
|
167
|
+
if (error.message === "PersistedQueryNotFound") return true;
|
|
168
|
+
const extensions = error.extensions;
|
|
169
|
+
return extensions?.code === "PERSISTED_QUERY_NOT_FOUND";
|
|
170
|
+
}
|
|
129
171
|
|
|
130
172
|
// src/retry.ts
|
|
131
173
|
function defaultSleep(ms) {
|
|
@@ -192,6 +234,9 @@ function withDirective(field, name) {
|
|
|
192
234
|
function responseKeyOf(field) {
|
|
193
235
|
return field.alias?.value ?? field.name.value;
|
|
194
236
|
}
|
|
237
|
+
function hasResponseKey(selection, key) {
|
|
238
|
+
return selection.kind === Kind.FIELD && responseKeyOf(selection) === key;
|
|
239
|
+
}
|
|
195
240
|
function isCollectionField(field) {
|
|
196
241
|
if (!field.name.value.endsWith(COLLECTION_SUFFIX) || field.name.value === COLLECTION_SUFFIX || !field.selectionSet) {
|
|
197
242
|
return false;
|
|
@@ -322,13 +367,14 @@ function planSplits(document, options) {
|
|
|
322
367
|
const field = selection;
|
|
323
368
|
const isExplicitSplit = hasDirective(field, SPLIT_DIRECTIVE);
|
|
324
369
|
const isAutoSplit = options.autoSplitNestedCollections && isCollectionField(field);
|
|
370
|
+
const isForcedSplit = options.forceSplit?.(field) ?? false;
|
|
325
371
|
if (isExplicitSplit && depth === 0) {
|
|
326
372
|
throw new FetchContentfulError(
|
|
327
373
|
`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.`,
|
|
328
374
|
{ code: "CONFIG" }
|
|
329
375
|
);
|
|
330
376
|
}
|
|
331
|
-
const shouldSplit = depth > 0 && field.selectionSet !== void 0 && !hasDirective(field, NO_SPLIT_DIRECTIVE) && (isExplicitSplit || isAutoSplit);
|
|
377
|
+
const shouldSplit = depth > 0 && field.selectionSet !== void 0 && !hasDirective(field, NO_SPLIT_DIRECTIVE) && (isExplicitSplit || isAutoSplit || isForcedSplit);
|
|
332
378
|
if (shouldSplit) {
|
|
333
379
|
const planned = withDirective(withoutDirective(field, SPLIT_DIRECTIVE), NO_SPLIT_DIRECTIVE);
|
|
334
380
|
plans.push({
|
|
@@ -359,8 +405,11 @@ function planSplits(document, options) {
|
|
|
359
405
|
selections.push(field);
|
|
360
406
|
}
|
|
361
407
|
}
|
|
362
|
-
if (needsMarkers) {
|
|
363
|
-
selections.push(sysIdMarker()
|
|
408
|
+
if (needsMarkers && !selections.some((s) => hasResponseKey(s, SYS_ID_ALIAS))) {
|
|
409
|
+
selections.push(sysIdMarker());
|
|
410
|
+
}
|
|
411
|
+
if (needsMarkers && !selections.some((s) => hasResponseKey(s, TYPENAME_ALIAS))) {
|
|
412
|
+
selections.push(typenameMarker());
|
|
364
413
|
}
|
|
365
414
|
return { ...selectionSet, selections };
|
|
366
415
|
}
|
|
@@ -378,6 +427,49 @@ function planSplits(document, options) {
|
|
|
378
427
|
plans
|
|
379
428
|
};
|
|
380
429
|
}
|
|
430
|
+
function findSplitCandidates(document) {
|
|
431
|
+
const operation = getOperation(document);
|
|
432
|
+
const candidates = [];
|
|
433
|
+
function walk2(selectionSet, depth) {
|
|
434
|
+
for (const selection of selectionSet.selections) {
|
|
435
|
+
if (selection.kind === Kind.INLINE_FRAGMENT) {
|
|
436
|
+
walk2(selection.selectionSet, depth);
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if (selection.kind !== Kind.FIELD || !selection.selectionSet) continue;
|
|
440
|
+
const isMarker = hasResponseKey(selection, SYS_ID_ALIAS) || hasResponseKey(selection, TYPENAME_ALIAS);
|
|
441
|
+
if (depth > 0 && selection.name.value !== "items" && !isMarker) {
|
|
442
|
+
candidates.push({ field: selection, size: print(selection).length });
|
|
443
|
+
}
|
|
444
|
+
walk2(selection.selectionSet, depth + 1);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
walk2(operation.selectionSet, 0);
|
|
448
|
+
return candidates;
|
|
449
|
+
}
|
|
450
|
+
function planSplitsWithSizeBudget(document, options) {
|
|
451
|
+
const first = planSplits(document, {
|
|
452
|
+
autoSplitNestedCollections: options.autoSplitNestedCollections
|
|
453
|
+
});
|
|
454
|
+
if (!options.autoSplitOnSize) return first;
|
|
455
|
+
let outer = first.document;
|
|
456
|
+
const plans = [...first.plans];
|
|
457
|
+
let remainingIterations = findSplitCandidates(outer).length;
|
|
458
|
+
while (options.measure(outer) > options.maxQuerySize && remainingIterations-- > 0) {
|
|
459
|
+
const candidates = findSplitCandidates(outer);
|
|
460
|
+
if (candidates.length === 0) break;
|
|
461
|
+
const target = candidates.reduce(
|
|
462
|
+
(largest, candidate) => candidate.size > largest.size ? candidate : largest
|
|
463
|
+
);
|
|
464
|
+
const next = planSplits(outer, {
|
|
465
|
+
autoSplitNestedCollections: false,
|
|
466
|
+
forceSplit: (field) => field === target.field
|
|
467
|
+
});
|
|
468
|
+
outer = next.document;
|
|
469
|
+
plans.push(...next.plans);
|
|
470
|
+
}
|
|
471
|
+
return { document: outer, plans };
|
|
472
|
+
}
|
|
381
473
|
function stringArgument(name, value) {
|
|
382
474
|
return {
|
|
383
475
|
kind: Kind.ARGUMENT,
|
|
@@ -546,6 +638,10 @@ function graphqlEndpoint(space, environment) {
|
|
|
546
638
|
space
|
|
547
639
|
)}/environments/${encodeURIComponent(environment)}`;
|
|
548
640
|
}
|
|
641
|
+
function printOutgoingQuery(document, context) {
|
|
642
|
+
const printed = print(stripInternalDirectives(document));
|
|
643
|
+
return context.minifyQuery === false ? printed : minifyQuery(printed);
|
|
644
|
+
}
|
|
549
645
|
function parseRetryAfter(header) {
|
|
550
646
|
if (!header) return void 0;
|
|
551
647
|
const seconds = Number(header);
|
|
@@ -605,13 +701,17 @@ ${annotated}
|
|
|
605
701
|
` : message;
|
|
606
702
|
}
|
|
607
703
|
async function rawRequest(document, variables, context) {
|
|
608
|
-
const query =
|
|
609
|
-
const
|
|
610
|
-
query,
|
|
611
|
-
variables: pickDeclaredVariables(document, variables)
|
|
612
|
-
});
|
|
704
|
+
const query = printOutgoingQuery(document, context);
|
|
705
|
+
const pickedVariables = pickDeclaredVariables(document, variables);
|
|
613
706
|
const url = graphqlEndpoint(context.space, context.environment);
|
|
614
|
-
|
|
707
|
+
const usePersistedQuery = context.automaticPersistedQueries === true;
|
|
708
|
+
const sha256Hash = usePersistedQuery ? await sha256Hex(query) : void 0;
|
|
709
|
+
async function send(includeQuery) {
|
|
710
|
+
const body = JSON.stringify({
|
|
711
|
+
...includeQuery ? { query } : {},
|
|
712
|
+
variables: pickedVariables,
|
|
713
|
+
...sha256Hash ? { extensions: persistedQueryExtensions(sha256Hash) } : {}
|
|
714
|
+
});
|
|
615
715
|
let response;
|
|
616
716
|
try {
|
|
617
717
|
const init = {
|
|
@@ -681,6 +781,21 @@ async function rawRequest(document, variables, context) {
|
|
|
681
781
|
});
|
|
682
782
|
}
|
|
683
783
|
return payload.data;
|
|
784
|
+
}
|
|
785
|
+
let mustIncludeQuery = !usePersistedQuery;
|
|
786
|
+
return withRetries(async () => {
|
|
787
|
+
if (mustIncludeQuery) {
|
|
788
|
+
return send(true);
|
|
789
|
+
}
|
|
790
|
+
try {
|
|
791
|
+
return await send(false);
|
|
792
|
+
} catch (error) {
|
|
793
|
+
if (isFetchContentfulError(error) && error.code === "GRAPHQL" && error.errors?.some(isPersistedQueryNotFoundError)) {
|
|
794
|
+
mustIncludeQuery = true;
|
|
795
|
+
return await send(true);
|
|
796
|
+
}
|
|
797
|
+
throw error;
|
|
798
|
+
}
|
|
684
799
|
}, context.retry);
|
|
685
800
|
}
|
|
686
801
|
|
|
@@ -886,6 +1001,8 @@ var DEFAULT_RETRIES = 5;
|
|
|
886
1001
|
var DEFAULT_RETRY_DELAY_MS = 250;
|
|
887
1002
|
var DEFAULT_MAX_RETRY_DELAY_MS = 8e3;
|
|
888
1003
|
var DEFAULT_SPLIT_BATCH_SIZE = 50;
|
|
1004
|
+
var DEFAULT_MAX_QUERY_SIZE = 7500;
|
|
1005
|
+
var DEFAULT_MAX_QUERY_SIZE_WITH_APQ = 15500;
|
|
889
1006
|
function isRecord4(value) {
|
|
890
1007
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
891
1008
|
}
|
|
@@ -936,6 +1053,7 @@ function resolveContext(options) {
|
|
|
936
1053
|
maxDelayMs: options.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS
|
|
937
1054
|
};
|
|
938
1055
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1056
|
+
const automaticPersistedQueries = options.automaticPersistedQueries ?? false;
|
|
939
1057
|
const context = {
|
|
940
1058
|
space,
|
|
941
1059
|
environment,
|
|
@@ -946,9 +1064,15 @@ function resolveContext(options) {
|
|
|
946
1064
|
fetch: fetchImpl,
|
|
947
1065
|
retry,
|
|
948
1066
|
autoSplitNestedCollections: options.autoSplitNestedCollections ?? true,
|
|
1067
|
+
autoSplitOnSize: options.autoSplitOnSize ?? true,
|
|
1068
|
+
// APQ lifts Contentful's size ceiling from 8 KB to 16 KB, so the budget
|
|
1069
|
+
// that triggers a size-driven split follows suit unless overridden.
|
|
1070
|
+
maxQuerySize: options.maxQuerySize ?? (automaticPersistedQueries ? DEFAULT_MAX_QUERY_SIZE_WITH_APQ : DEFAULT_MAX_QUERY_SIZE),
|
|
949
1071
|
splitBatchSize: options.splitBatchSize ?? DEFAULT_SPLIT_BATCH_SIZE,
|
|
950
1072
|
annotateQueryOnError: options.annotateQueryOnError ?? true,
|
|
951
|
-
unresolvableLinks: options.unresolvableLinks ?? "omit"
|
|
1073
|
+
unresolvableLinks: options.unresolvableLinks ?? "omit",
|
|
1074
|
+
minifyQuery: options.minifyQuery ?? true,
|
|
1075
|
+
automaticPersistedQueries
|
|
952
1076
|
};
|
|
953
1077
|
if (options.onUnresolvableLink) {
|
|
954
1078
|
context.onUnresolvableLink = options.onUnresolvableLink;
|
|
@@ -1040,8 +1164,16 @@ function cleanupMarkers(data, plans) {
|
|
|
1040
1164
|
}
|
|
1041
1165
|
}
|
|
1042
1166
|
}
|
|
1167
|
+
function byteLength(text) {
|
|
1168
|
+
return new TextEncoder().encode(text).length;
|
|
1169
|
+
}
|
|
1043
1170
|
async function executeDocument(document, variables, context) {
|
|
1044
|
-
const { document: outer, plans } =
|
|
1171
|
+
const { document: outer, plans } = planSplitsWithSizeBudget(document, {
|
|
1172
|
+
autoSplitNestedCollections: context.autoSplitNestedCollections,
|
|
1173
|
+
autoSplitOnSize: context.autoSplitOnSize,
|
|
1174
|
+
maxQuerySize: context.maxQuerySize,
|
|
1175
|
+
measure: (doc) => byteLength(printOutgoingQuery(doc, context))
|
|
1176
|
+
});
|
|
1045
1177
|
const data = await rawRequest(outer, variables, context);
|
|
1046
1178
|
await Promise.all(plans.map((plan) => resolvePlan(plan, data, variables, context)));
|
|
1047
1179
|
cleanupMarkers(data, plans);
|
|
@@ -1119,6 +1251,6 @@ function createFetchContentful(defaults = {}) {
|
|
|
1119
1251
|
}
|
|
1120
1252
|
var index_default = fetchContentful;
|
|
1121
1253
|
|
|
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 };
|
|
1254
|
+
export { FetchContentfulError, UNRESOLVABLE_LINK_CODE, annotateQuery, clearLocaleCache, collectAtPath, createFetchContentful, index_default as default, fetchContentful, getLocales, injectRootArgs, inlineFragments, isFetchContentfulError, isUnresolvableLinkError, minifyQuery, omitUnresolvedLinks, partitionUnresolvableLinks, readEnvSettings, shapeData, unwrapSingleRoot };
|
|
1123
1255
|
//# sourceMappingURL=index.mjs.map
|
|
1124
1256
|
//# sourceMappingURL=index.mjs.map
|