@fourtwelvelabs/fetch-contentful 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +301 -0
- package/dist/index.cjs +977 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +324 -0
- package/dist/index.d.ts +324 -0
- package/dist/index.mjs +961 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +76 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { DocumentNode } from 'graphql';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Public types for fetch-contentful.
|
|
5
|
+
*
|
|
6
|
+
* This file is type-only (no runtime code) and is excluded from coverage.
|
|
7
|
+
*/
|
|
8
|
+
/** JSON-compatible variable values accepted by the GraphQL endpoint. */
|
|
9
|
+
type GraphQLVariables = Record<string, unknown>;
|
|
10
|
+
/** Next.js-specific fetch extensions (App Router data cache). */
|
|
11
|
+
interface NextFetchOptions {
|
|
12
|
+
revalidate?: number | false;
|
|
13
|
+
tags?: string[];
|
|
14
|
+
}
|
|
15
|
+
/** Error categories a {@link FetchContentfulError} can carry. */
|
|
16
|
+
type FetchContentfulErrorCode = 'CONFIG' | 'NETWORK' | 'HTTP' | 'GRAPHQL' | 'STITCH' | 'LOCALE';
|
|
17
|
+
/** Shape of a GraphQL error object returned by Contentful. */
|
|
18
|
+
interface ContentfulGraphQLError {
|
|
19
|
+
message: string;
|
|
20
|
+
locations?: Array<{
|
|
21
|
+
line: number;
|
|
22
|
+
column: number;
|
|
23
|
+
}>;
|
|
24
|
+
path?: Array<string | number>;
|
|
25
|
+
extensions?: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
/** Options accepted by {@link fetchContentful}. */
|
|
28
|
+
interface FetchContentfulOptions<TVariables extends GraphQLVariables = GraphQLVariables> {
|
|
29
|
+
/** GraphQL variables for the query. */
|
|
30
|
+
variables?: TVariables;
|
|
31
|
+
/**
|
|
32
|
+
* Contentful environment id.
|
|
33
|
+
* Defaults to `process.env.NEXT_PUBLIC_CONTENTFUL_ENVIRONMENT`, then `"master"`.
|
|
34
|
+
*/
|
|
35
|
+
environment?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Contentful space id.
|
|
38
|
+
* Defaults to `process.env.NEXT_PUBLIC_CONTENTFUL_SPACE`. Required.
|
|
39
|
+
*/
|
|
40
|
+
space?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Use the Preview API. Selects the preview token by default, is injected
|
|
43
|
+
* as a `preview: true` argument on root query fields (see
|
|
44
|
+
* {@link FetchContentfulOptions.autoInjectArgs}), and auto-fills a
|
|
45
|
+
* `$preview: Boolean` variable when your query declares one.
|
|
46
|
+
* Defaults to `false`.
|
|
47
|
+
*/
|
|
48
|
+
preview?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Locale code (e.g. `"en-US"`). Injected as a `locale` argument on root
|
|
51
|
+
* query fields (see {@link FetchContentfulOptions.autoInjectArgs}),
|
|
52
|
+
* auto-fills a `$locale: String` variable when your query declares one,
|
|
53
|
+
* and is applied to generated subqueries.
|
|
54
|
+
*/
|
|
55
|
+
locale?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Automatically add `preview: true` / `locale: "..."` arguments to the
|
|
58
|
+
* root fields of your query based on the `preview` and `locale` options,
|
|
59
|
+
* so queries never need to declare or thread those arguments themselves.
|
|
60
|
+
* Contentful cascades both arguments to all nested fields. Arguments you
|
|
61
|
+
* write explicitly are never overridden. Defaults to `true`.
|
|
62
|
+
*/
|
|
63
|
+
autoInjectArgs?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* When `true`, the provided `locale` is validated against the locales
|
|
66
|
+
* configured in Contentful (fetched once per space/environment and cached).
|
|
67
|
+
*/
|
|
68
|
+
validateLocale?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Access token. Defaults to `process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN`
|
|
71
|
+
* when `preview` is true, otherwise `process.env.CONTENTFUL_ACCESS_TOKEN`
|
|
72
|
+
* (with `NEXT_PUBLIC_`-prefixed fallbacks for client-side use).
|
|
73
|
+
*/
|
|
74
|
+
token?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Number of retry attempts after the first failed request (per network
|
|
77
|
+
* request, including subqueries). Defaults to `5`.
|
|
78
|
+
*/
|
|
79
|
+
retries?: number;
|
|
80
|
+
/** Base delay in ms for exponential backoff. Defaults to `250`. */
|
|
81
|
+
retryDelayMs?: number;
|
|
82
|
+
/** Maximum backoff delay in ms. Defaults to `8000`. */
|
|
83
|
+
maxRetryDelayMs?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Automatically split nested reference collections (any `*Collection`
|
|
86
|
+
* field that is not a root query field) into their own subqueries to stay
|
|
87
|
+
* under Contentful's query complexity limits. Defaults to `true`.
|
|
88
|
+
* One-to-one references can be split by annotating them with `@split`.
|
|
89
|
+
*/
|
|
90
|
+
autoSplitNestedCollections?: boolean;
|
|
91
|
+
/** How many parent entry ids to resolve per subquery request. Defaults to `50`. */
|
|
92
|
+
splitBatchSize?: number;
|
|
93
|
+
/**
|
|
94
|
+
* Whether to unwrap single-root responses. When the (optionally shaped)
|
|
95
|
+
* response object has exactly one root key — e.g. a query for only
|
|
96
|
+
* `siteSettingsCollection` — the promise resolves with that key's value
|
|
97
|
+
* directly instead of a one-key wrapper object. Responses with multiple
|
|
98
|
+
* root fields are always returned as-is. Defaults to `true`.
|
|
99
|
+
*/
|
|
100
|
+
unwrapRootField?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Whether to run the response through the collection shaper
|
|
103
|
+
* (`fooCollection.items` → `foo`) before resolving. When `false`, the data
|
|
104
|
+
* resolves in Contentful's raw wire shape and the return type reflects
|
|
105
|
+
* that. Defaults to `true`.
|
|
106
|
+
*/
|
|
107
|
+
shapeResponseData?: boolean;
|
|
108
|
+
/** Custom fetch implementation (useful for tests). Defaults to `globalThis.fetch`. */
|
|
109
|
+
fetch?: typeof fetch;
|
|
110
|
+
/** AbortSignal forwarded to every underlying fetch call. */
|
|
111
|
+
signal?: AbortSignal;
|
|
112
|
+
/** Next.js App Router data-cache options, forwarded to fetch. */
|
|
113
|
+
next?: NextFetchOptions;
|
|
114
|
+
/** Standard fetch cache mode, forwarded to fetch. */
|
|
115
|
+
cache?: RequestCache;
|
|
116
|
+
}
|
|
117
|
+
/** A single locale as configured in Contentful. */
|
|
118
|
+
interface ContentfulLocale {
|
|
119
|
+
code: string;
|
|
120
|
+
name: string;
|
|
121
|
+
default: boolean;
|
|
122
|
+
fallbackCode: string | null;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Recursively rewrites Contentful collection wrappers at the type level:
|
|
126
|
+
* every `fooCollection: { items: T[] }` becomes `foo: T[]`, preserving
|
|
127
|
+
* nullability. Mirrors the runtime behavior of `shapeData`.
|
|
128
|
+
*/
|
|
129
|
+
type ShapeCollections<T> = T extends ReadonlyArray<infer U> ? Array<ShapeCollections<U>> : T extends object ? {
|
|
130
|
+
[K in keyof T as ShapedKey<K, T[K]>]: ShapedValue<K, T[K]>;
|
|
131
|
+
} : T;
|
|
132
|
+
type IsCollectionEntry<K, V> = K extends `${infer Base}Collection` ? Base extends '' ? false : NonNullable<V> extends {
|
|
133
|
+
items: ReadonlyArray<unknown>;
|
|
134
|
+
} ? true : false : false;
|
|
135
|
+
type ShapedKey<K, V> = IsCollectionEntry<K, V> extends true ? K extends `${infer Base}Collection` ? Base : K : K;
|
|
136
|
+
type ShapedValue<K, V> = IsCollectionEntry<K, V> extends true ? NonNullable<V> extends {
|
|
137
|
+
items: ReadonlyArray<infer I>;
|
|
138
|
+
} ? Array<ShapeCollections<I>> | Extract<V, null | undefined> : never : ShapeCollections<V>;
|
|
139
|
+
/** `true` when `T` is a union of two or more members. */
|
|
140
|
+
type IsUnion<T, U = T> = T extends unknown ? [U] extends [T] ? false : true : never;
|
|
141
|
+
/**
|
|
142
|
+
* The type-level twin of the single-root unwrap: when `T` is an object with
|
|
143
|
+
* exactly one statically-known key, resolves to that key's value type;
|
|
144
|
+
* otherwise resolves to `T` unchanged. Objects typed with index signatures
|
|
145
|
+
* (like the default `Record<string, unknown>`) are left as-is, since their
|
|
146
|
+
* keys aren't statically known.
|
|
147
|
+
*/
|
|
148
|
+
type UnwrapSingleRoot<T> = T extends ReadonlyArray<unknown> ? T : T extends object ? string extends keyof T ? T : [keyof T] extends [never] ? T : IsUnion<keyof T> extends true ? T : T[keyof T] : T;
|
|
149
|
+
|
|
150
|
+
/** Error thrown (i.e. the rejection value) for every failure in fetch-contentful. */
|
|
151
|
+
declare class FetchContentfulError extends Error {
|
|
152
|
+
readonly code: FetchContentfulErrorCode;
|
|
153
|
+
readonly status: number | undefined;
|
|
154
|
+
readonly errors: ContentfulGraphQLError[] | undefined;
|
|
155
|
+
/** Internal: whether a retry may succeed. */
|
|
156
|
+
readonly retryable: boolean;
|
|
157
|
+
/** Internal: server-requested retry delay (from Retry-After), in ms. */
|
|
158
|
+
readonly retryAfterMs: number | undefined;
|
|
159
|
+
constructor(message: string, options: {
|
|
160
|
+
code: FetchContentfulErrorCode;
|
|
161
|
+
status?: number;
|
|
162
|
+
errors?: ContentfulGraphQLError[];
|
|
163
|
+
retryable?: boolean;
|
|
164
|
+
retryAfterMs?: number;
|
|
165
|
+
cause?: unknown;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/** Type guard for {@link FetchContentfulError}. */
|
|
169
|
+
declare function isFetchContentfulError(value: unknown): value is FetchContentfulError;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Environment-derived configuration.
|
|
173
|
+
*
|
|
174
|
+
* Two families of variable names are supported, checked in this order:
|
|
175
|
+
*
|
|
176
|
+
* 1. Framework-neutral names — `CONTENTFUL_SPACE_ID`, `CONTENTFUL_ENVIRONMENT`,
|
|
177
|
+
* `CONTENTFUL_ACCESS_TOKEN`, `CONTENTFUL_PREVIEW_ACCESS_TOKEN`.
|
|
178
|
+
* 2. `NEXT_PUBLIC_`-prefixed equivalents for Next.js projects that call
|
|
179
|
+
* Contentful from the browser.
|
|
180
|
+
*
|
|
181
|
+
* IMPORTANT: every lookup below is written as a literal
|
|
182
|
+
* `process.env.SOME_NAME` property access, never a dynamic `process.env[x]`.
|
|
183
|
+
* Next.js exposes `NEXT_PUBLIC_` variables to client bundles by
|
|
184
|
+
* string-replacing those literal expressions at build time — dynamic access
|
|
185
|
+
* is invisible to the inliner and resolves to `undefined` in the browser.
|
|
186
|
+
* Do not "refactor" these into a loop or a name table.
|
|
187
|
+
*
|
|
188
|
+
* This module reads the already-populated `process.env`; loading `.env`
|
|
189
|
+
* files from disk is deliberately left to the platform (Next.js, Vite,
|
|
190
|
+
* dotenv, ...), whose precedence rules would otherwise be duplicated here.
|
|
191
|
+
*/
|
|
192
|
+
interface EnvSettings {
|
|
193
|
+
space: string | undefined;
|
|
194
|
+
environment: string | undefined;
|
|
195
|
+
token: string | undefined;
|
|
196
|
+
previewToken: string | undefined;
|
|
197
|
+
}
|
|
198
|
+
/** Reads Contentful settings from `process.env` (neutral names first). */
|
|
199
|
+
declare function readEnvSettings(): EnvSettings;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Contentful cascades `preview` and `locale` from a root query field down to
|
|
203
|
+
* every nested field, so injecting the arguments at the root is equivalent to
|
|
204
|
+
* threading them through the whole query by hand — without the boilerplate.
|
|
205
|
+
*/
|
|
206
|
+
interface InjectableArgs {
|
|
207
|
+
preview: boolean;
|
|
208
|
+
locale: string | undefined;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Adds `preview: true` and/or `locale: "..."` arguments to every root field
|
|
212
|
+
* of the operation, unless the author already wrote that argument. Nested
|
|
213
|
+
* fields are left untouched — Contentful inherits both arguments downward.
|
|
214
|
+
*/
|
|
215
|
+
declare function injectRootArgs(document: DocumentNode, args: InjectableArgs): DocumentNode;
|
|
216
|
+
|
|
217
|
+
interface RetryConfig {
|
|
218
|
+
/** Number of retries after the initial attempt. */
|
|
219
|
+
retries: number;
|
|
220
|
+
/** Base backoff delay in ms. */
|
|
221
|
+
baseDelayMs: number;
|
|
222
|
+
/** Maximum backoff delay in ms. */
|
|
223
|
+
maxDelayMs: number;
|
|
224
|
+
/** Random source, injectable for deterministic tests. */
|
|
225
|
+
random?: () => number;
|
|
226
|
+
/** Sleep implementation, injectable for deterministic tests. */
|
|
227
|
+
sleep?: (ms: number) => Promise<void>;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
interface LocalesContext {
|
|
231
|
+
space: string;
|
|
232
|
+
environment: string;
|
|
233
|
+
preview: boolean;
|
|
234
|
+
token: string;
|
|
235
|
+
fetch: typeof fetch;
|
|
236
|
+
retry: RetryConfig;
|
|
237
|
+
signal?: AbortSignal;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Returns the locales configured in Contentful for a space/environment.
|
|
241
|
+
* Results are cached in-module, so repeated fetches are free; failed
|
|
242
|
+
* lookups are evicted so they can be retried later.
|
|
243
|
+
*/
|
|
244
|
+
declare function getLocales(context: LocalesContext): Promise<ContentfulLocale[]>;
|
|
245
|
+
/** Clears the in-module locale cache (mainly useful in tests). */
|
|
246
|
+
declare function clearLocaleCache(): void;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Recursively rewrites Contentful collection wrappers:
|
|
250
|
+
* `{ fooCollection: { items: [...] } }` becomes `{ foo: [...] }`.
|
|
251
|
+
* Non-collection data is returned untouched (deeply copied).
|
|
252
|
+
*/
|
|
253
|
+
declare function shapeData<T>(data: T): ShapeCollections<T>;
|
|
254
|
+
/**
|
|
255
|
+
* Unwraps a single-root response: `{ siteSettings: X }` becomes `X` when
|
|
256
|
+
* `siteSettings` is the only root key. Anything else — multiple roots,
|
|
257
|
+
* arrays, primitives — is returned untouched.
|
|
258
|
+
*/
|
|
259
|
+
declare function unwrapSingleRoot<T>(data: T): UnwrapSingleRoot<T>;
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Replaces every named fragment spread with an equivalent inline fragment so
|
|
263
|
+
* split planning can see the whole query shape uniformly.
|
|
264
|
+
*/
|
|
265
|
+
declare function inlineFragments(document: DocumentNode): DocumentNode;
|
|
266
|
+
|
|
267
|
+
/** Collect every object reachable at a response path, flattening arrays. */
|
|
268
|
+
declare function collectAtPath(data: unknown, path: string[]): Record<string, unknown>[];
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Fetches data from Contentful's GraphQL API.
|
|
272
|
+
*
|
|
273
|
+
* - Splits nested reference collections (and `@split`-annotated references)
|
|
274
|
+
* into recursive subqueries to stay under query complexity limits, then
|
|
275
|
+
* stitches everything back together.
|
|
276
|
+
* - Retries transient failures with exponential backoff.
|
|
277
|
+
* - Rejects if any request in the tree fails; resolves only when the full
|
|
278
|
+
* query has succeeded.
|
|
279
|
+
* - Shapes the response so every `fooCollection.items` becomes `foo`
|
|
280
|
+
* (disable with `shapeResponseData: false` to receive the raw wire shape).
|
|
281
|
+
*/
|
|
282
|
+
declare function fetchContentful<TData = Record<string, unknown>, TVariables extends GraphQLVariables = GraphQLVariables>(query: string | DocumentNode, options: FetchContentfulOptions<TVariables> & {
|
|
283
|
+
shapeResponseData: false;
|
|
284
|
+
unwrapRootField: false;
|
|
285
|
+
}): Promise<TData>;
|
|
286
|
+
declare function fetchContentful<TData = Record<string, unknown>, TVariables extends GraphQLVariables = GraphQLVariables>(query: string | DocumentNode, options: FetchContentfulOptions<TVariables> & {
|
|
287
|
+
shapeResponseData: false;
|
|
288
|
+
}): Promise<UnwrapSingleRoot<TData>>;
|
|
289
|
+
declare function fetchContentful<TData = Record<string, unknown>, TVariables extends GraphQLVariables = GraphQLVariables>(query: string | DocumentNode, options: FetchContentfulOptions<TVariables> & {
|
|
290
|
+
unwrapRootField: false;
|
|
291
|
+
}): Promise<ShapeCollections<TData>>;
|
|
292
|
+
declare function fetchContentful<TData = Record<string, unknown>, TVariables extends GraphQLVariables = GraphQLVariables>(query: string | DocumentNode, options?: FetchContentfulOptions<TVariables>): Promise<UnwrapSingleRoot<ShapeCollections<TData>>>;
|
|
293
|
+
/**
|
|
294
|
+
* Creates a `fetchContentful` bound to default options — the recommended way
|
|
295
|
+
* to configure the utility once per project:
|
|
296
|
+
*
|
|
297
|
+
* ```ts
|
|
298
|
+
* // lib/contentful.ts
|
|
299
|
+
* export const fetchContentful = createFetchContentful({
|
|
300
|
+
* space: 'abc123',
|
|
301
|
+
* locale: 'en-US',
|
|
302
|
+
* retries: 3,
|
|
303
|
+
* });
|
|
304
|
+
* ```
|
|
305
|
+
*
|
|
306
|
+
* Per-call options win over defaults (top-level shallow merge). Note that
|
|
307
|
+
* `shapeResponseData: false` only changes the *return type* when written on
|
|
308
|
+
* the call itself, so prefer setting it per call.
|
|
309
|
+
*/
|
|
310
|
+
declare function createFetchContentful<TDefaultVariables extends GraphQLVariables = GraphQLVariables>(defaults?: FetchContentfulOptions<TDefaultVariables>): {
|
|
311
|
+
<TData = Record<string, unknown>, TVariables extends GraphQLVariables = GraphQLVariables>(query: string | DocumentNode, options: FetchContentfulOptions<TVariables> & {
|
|
312
|
+
shapeResponseData: false;
|
|
313
|
+
unwrapRootField: false;
|
|
314
|
+
}): Promise<TData>;
|
|
315
|
+
<TData = Record<string, unknown>, TVariables extends GraphQLVariables = GraphQLVariables>(query: string | DocumentNode, options: FetchContentfulOptions<TVariables> & {
|
|
316
|
+
shapeResponseData: false;
|
|
317
|
+
}): Promise<UnwrapSingleRoot<TData>>;
|
|
318
|
+
<TData = Record<string, unknown>, TVariables extends GraphQLVariables = GraphQLVariables>(query: string | DocumentNode, options: FetchContentfulOptions<TVariables> & {
|
|
319
|
+
unwrapRootField: false;
|
|
320
|
+
}): Promise<ShapeCollections<TData>>;
|
|
321
|
+
<TData = Record<string, unknown>, TVariables extends GraphQLVariables = GraphQLVariables>(query: string | DocumentNode, options?: FetchContentfulOptions<TVariables>): Promise<UnwrapSingleRoot<ShapeCollections<TData>>>;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
export { type ContentfulGraphQLError, type ContentfulLocale, FetchContentfulError, type FetchContentfulErrorCode, type FetchContentfulOptions, type GraphQLVariables, type NextFetchOptions, type ShapeCollections, type UnwrapSingleRoot, clearLocaleCache, collectAtPath, createFetchContentful, fetchContentful as default, fetchContentful, getLocales, injectRootArgs, inlineFragments, isFetchContentfulError, readEnvSettings, shapeData, unwrapSingleRoot };
|