@prismicio/client
Version:
The official JavaScript + TypeScript client library for Prismic
926 lines (924 loc) • 32.9 kB
JavaScript
import { filter } from "./filter.js";
import { devMsg } from "./lib/devMsg.js";
import { getPreviewCookie } from "./lib/getPreviewCookie.js";
import { request } from "./lib/request.js";
import { throttledWarn } from "./lib/throttledWarn.js";
import { ForbiddenError, NotFoundError, ParsingError, PreviewTokenExpiredError, PrismicError, RefExpiredError, RefNotFoundError, RepositoryNotFoundError } from "./errors.js";
import { asLink } from "./helpers/asLink.js";
import { buildQueryURL } from "./buildQueryURL.js";
import { getRepositoryEndpoint } from "./getRepositoryEndpoint.js";
import { getRepositoryName } from "./getRepositoryName.js";
import { isRepositoryEndpoint } from "./isRepositoryEndpoint.js";
//#region src/Client.ts
const MAX_PAGE_SIZE = 100;
const REPOSITORY_CACHE_TTL = 5e3;
const GET_ALL_QUERY_DELAY = 500;
const MAX_INVALID_REF_RETRY_ATTEMPTS = 3;
/**
* A client for fetching content from a Prismic repository.
*
* @see {@link https://prismic.io/docs/fetch-content}
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client}
*/
var Client = class {
/**
* The client's Content API endpoint.
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#config-options}
*/
documentAPIEndpoint;
/**
* The secure token used for the Content API.
*
* @see {@link https://prismic.io/docs/fetch-content#content-visibility}
*/
accessToken;
/**
* A list of route resolver objects that define how a document's `url`
* property is resolved.
*
* @see {@link https://prismic.io/docs/routes}
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#config-options}
*/
routes;
/**
* The URL used for link or content relationship fields that point to an
* archived or deleted page.
*
* @see {@link https://prismic.io/docs/routes}
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#config-options}
*/
brokenRoute;
/**
* Default parameters sent with each Content API request. These parameters can
* be overridden on each method.
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#config-options}
*/
defaultParams;
/**
* The `fetch` function used to make network requests.
*
* @default The global `fetch` function.
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#config-options}
*/
fetchFn;
/**
* The default `fetch` options sent with each Content API request. These
* parameters can be overriden on each method.
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#config-options}
*/
fetchOptions;
#repositoryName;
#getRef;
#autoPreviews = true;
#autoPreviewsRequest;
#cachedRepository;
#cachedRepositoryExpiration = 0;
/**
* @param repositoryNameOrEndpoint - The Prismic repository name or full
* Content API endpoint for the repository.
* @param config - Client configuration.
*/
constructor(repositoryNameOrEndpoint, config = {}) {
var _globalThis$fetch;
const { documentAPIEndpoint, accessToken, ref, routes, brokenRoute, defaultParams, fetchOptions = {}, fetch = (_globalThis$fetch = globalThis.fetch) === null || _globalThis$fetch === void 0 ? void 0 : _globalThis$fetch.bind(globalThis) } = config;
if (isRepositoryEndpoint(repositoryNameOrEndpoint)) {
try {
this.repositoryName = getRepositoryName(repositoryNameOrEndpoint);
} catch {
console.warn(`[/client] A repository name could not be inferred from the provided endpoint (\`${repositoryNameOrEndpoint}\`). Some methods will be disabled. Create the client using a repository name to prevent this warning. For more details, see ${devMsg("prefer-repository-name")}`);
}
this.documentAPIEndpoint = documentAPIEndpoint || repositoryNameOrEndpoint;
} else {
this.repositoryName = repositoryNameOrEndpoint;
this.documentAPIEndpoint = documentAPIEndpoint || getRepositoryEndpoint(repositoryNameOrEndpoint);
}
if (!fetch) throw new PrismicError("A valid fetch implementation was not provided. In environments where fetch is not available, a fetch implementation must be provided via a polyfill or the `fetch` option.", void 0, void 0);
if (typeof fetch !== "function") throw new PrismicError(`fetch must be a function, but received: ${typeof fetch}`, void 0, void 0);
if (!isRepositoryEndpoint(this.documentAPIEndpoint)) throw new PrismicError(`documentAPIEndpoint is not a valid URL: ${documentAPIEndpoint}`, void 0, void 0);
if (isRepositoryEndpoint(repositoryNameOrEndpoint) && documentAPIEndpoint && repositoryNameOrEndpoint !== documentAPIEndpoint) console.warn(`[/client] Multiple incompatible endpoints were provided. Create the client using a repository name to prevent this error. For more details, see ${devMsg("prefer-repository-name")}`);
if (process.env.NODE_ENV === "development" && /\.prismic\.io\/(?!api\/v2\/?)/i.test(this.documentAPIEndpoint)) throw new PrismicError("@prismicio/client only supports Prismic Rest API V2. Please provide only the repository name to the first createClient() parameter or use the getRepositoryEndpoint() helper to generate a valid Rest API V2 endpoint URL.", void 0, void 0);
if (process.env.NODE_ENV === "development" && /\.prismic\.io$/i.test(new URL(this.documentAPIEndpoint).hostname) && !/\.cdn\.prismic\.io$/i.test(new URL(this.documentAPIEndpoint).hostname)) console.warn(`[/client] The client was created with a non-CDN endpoint. Convert it to the CDN endpoint for better performance. For more details, see ${devMsg("endpoint-must-use-cdn")}`);
this.accessToken = accessToken;
this.routes = routes;
this.brokenRoute = brokenRoute;
this.defaultParams = defaultParams;
this.fetchOptions = fetchOptions;
this.fetchFn = fetch;
this.graphQLFetch = this.graphQLFetch.bind(this);
if (ref) this.queryContentFromRef(ref);
}
/** The Prismic repository's name. */
set repositoryName(value) {
this.#repositoryName = value;
}
/** The Prismic repository's name. */
get repositoryName() {
if (!this.#repositoryName) throw new PrismicError(`A repository name is required for this method but one could not be inferred from the provided API endpoint (\`${this.documentAPIEndpoint}\`). To fix this error, provide a repository name when creating the client. For more details, see ${devMsg("prefer-repository-name")}`, void 0, void 0);
return this.#repositoryName;
}
/** @deprecated Replace with `documentAPIEndpoint`. */
set endpoint(value) {
this.documentAPIEndpoint = value;
}
/** @deprecated Replace with `documentAPIEndpoint`. */
get endpoint() {
return this.documentAPIEndpoint;
}
/**
* Enables the client to automatically query content from a preview session.
*
* @example
*
* ```ts
* client.enableAutoPreviews()
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#enableautopreviews}
*/
enableAutoPreviews() {
this.#autoPreviews = true;
}
/**
* Enables the client to automatically query content from a preview session
* using an HTTP request object.
*
* @example
*
* ```ts
* client.enableAutoPreviewsFromReq(req)
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#enableautopreviewsfromreq}
*/
enableAutoPreviewsFromReq(request$1) {
this.enableAutoPreviews();
this.#autoPreviewsRequest = request$1;
}
/**
* Disables the client from automatically querying content from a preview
* session.
*
* @example
*
* ```ts
* client.disableAutoPreviews()
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#disableautopreviews}
*/
disableAutoPreviews() {
this.#autoPreviews = false;
this.#autoPreviewsRequest = void 0;
}
/**
* Fetches pages based on the `params` argument. Results are paginated.
*
* @example
*
* ```ts
* const response = await client.get({ pageSize: 10 })
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#get}
*/
async get(params) {
return await (await this.#internalGet(params)).json();
}
/**
* Fetches the first page returned based on the `params` argument.
*
* @example
*
* ```ts
* const page = await client.getFirst()
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getfirst}
*/
async getFirst(params) {
const actualParams = (params === null || params === void 0 ? void 0 : params.page) || (params === null || params === void 0 ? void 0 : params.pageSize) ? params : {
...params,
pageSize: 1
};
const response = await this.#internalGet(actualParams);
const { results } = await response.clone().json();
if (results[0]) return results[0];
throw new NotFoundError("No documents were returned", response.url, void 0);
}
/**
* Fetches all pages based on the `params` argument. This method may make
* multiple network requests to fetch all matching pages.
*
* @example
*
* ```ts
* const pages = await client.dangerouslyGetAll()
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#dangerouslygetall}
*/
async dangerouslyGetAll(params = {}) {
var _this$defaultParams;
const { limit = Infinity,...actualParams } = params;
const resolvedParams = {
...actualParams,
pageSize: Math.min(limit, actualParams.pageSize || ((_this$defaultParams = this.defaultParams) === null || _this$defaultParams === void 0 ? void 0 : _this$defaultParams.pageSize) || MAX_PAGE_SIZE)
};
const documents = [];
let latestResult;
while ((!latestResult || latestResult.next_page) && documents.length < limit) {
const page = latestResult ? latestResult.page + 1 : void 0;
latestResult = await this.get({
...resolvedParams,
page
});
documents.push(...latestResult.results);
if (latestResult.next_page) await new Promise((res) => setTimeout(res, GET_ALL_QUERY_DELAY));
}
return documents.slice(0, limit);
}
/**
* Fetches a page with a specific ID.
*
* @example
*
* ```ts
* const page = await client.getByID("WW4bKScAAMAqmluX")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getbyid}
*/
async getByID(id, params) {
return await this.getFirst(appendFilters(params, filter.at("document.id", id)));
}
/**
* Fetches pages with specific IDs. Results are paginated.
*
* @example
*
* ```ts
* const response = await client.getByIDs([
* "WW4bKScAAMAqmluX",
* "U1kTRgEAAC8A5ldS",
* ])
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getbyids}
*/
async getByIDs(ids, params) {
return await this.get(appendFilters(params, filter.in("document.id", ids)));
}
/**
* Fetches pages with specific IDs. This method may make multiple network
* requests to fetch all matching pages.
*
* @example
*
* ```ts
* const pages = await client.getAllByIDs([
* "WW4bKScAAMAqmluX",
* "U1kTRgEAAC8A5ldS",
* ])
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getallbyids}
*/
async getAllByIDs(ids, params) {
return await this.dangerouslyGetAll(appendFilters(params, filter.in("document.id", ids)));
}
/**
* Fetches a page with a specific UID and type.
*
* @example
*
* ```ts
* const page = await client.getByUID("blog_post", "my-first-post")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getbyuid}
*/
async getByUID(documentType, uid, params) {
return await this.getFirst(appendFilters(params, filter.at("document.type", documentType), filter.at(`my.${documentType}.uid`, uid)));
}
/**
* Fetches pages with specific UIDs and a specific type. Results are
* paginated.
*
* @example
*
* ```ts
* const response = await client.getByUIDs("blog_post", [
* "my-first-post",
* "my-second-post",
* ])
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getbyuids}
*/
async getByUIDs(documentType, uids, params) {
return await this.get(appendFilters(params, filter.at("document.type", documentType), filter.in(`my.${documentType}.uid`, uids)));
}
/**
* Fetches pages with specific UIDs and a specific type. This method may make
* multiple network requests to fetch all matching pages.
*
* @example
*
* ```ts
* const pages = await client.getAllByUIDs("blog_post", [
* "my-first-post",
* "my-second-post",
* ])
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getallbyuids}
*/
async getAllByUIDs(documentType, uids, params) {
return await this.dangerouslyGetAll(appendFilters(params, filter.at("document.type", documentType), filter.in(`my.${documentType}.uid`, uids)));
}
/**
* Fetches a specific single type page.
*
* @example
*
* ```ts
* const page = await client.getSingle("settings")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getsingle}
*/
async getSingle(documentType, params) {
return await this.getFirst(appendFilters(params, filter.at("document.type", documentType)));
}
/**
* Fetches pages with a specific type. Results are paginated.
*
* @example
*
* ```ts
* const response = await client.getByType("blog_post")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getbytype}
*/
async getByType(documentType, params) {
return await this.get(appendFilters(params, filter.at("document.type", documentType)));
}
/**
* Fetches pages with a specific type. This method may make multiple network
* requests to fetch all matching documents.
*
* @example
*
* ```ts
* const pages = await client.getAllByType("blog_post")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getallbytype}
*/
async getAllByType(documentType, params) {
return await this.dangerouslyGetAll(appendFilters(params, filter.at("document.type", documentType)));
}
/**
* Fetches pages with a specific tag. Results are paginated.
*
* @example
*
* ```ts
* const response = await client.getByTag("featured")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getbytag}
*/
async getByTag(tag, params) {
return await this.get(appendFilters(params, filter.any("document.tags", [tag])));
}
/**
* Fetches pages with a specific tag. This method may make multiple network
* requests to fetch all matching documents.
*
* @example
*
* ```ts
* const pages = await client.getAllByTag("featured")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getallbytag}
*/
async getAllByTag(tag, params) {
return await this.dangerouslyGetAll(appendFilters(params, filter.any("document.tags", [tag])));
}
/**
* Fetches pages with every tag from a list of tags. Results are paginated.
*
* @example
*
* ```ts
* const response = await client.getByEveryTag(["featured", "homepage"])
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getbyeverytag}
*/
async getByEveryTag(tags, params) {
return await this.get(appendFilters(params, filter.at("document.tags", tags)));
}
/**
* Fetches pages with every tag from a list of tags. This method may make
* multiple network requests to fetch all matching pages.
*
* @example
*
* ```ts
* const pages = await client.getAllByEveryTag(["featured", "homepage"])
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getallbyeverytag}
*/
async getAllByEveryTag(tags, params) {
return await this.dangerouslyGetAll(appendFilters(params, filter.at("document.tags", tags)));
}
/**
* Fetches pages with at least one tag from a list of tags. Results are
* paginated.
*
* @example
*
* ```ts
* const response = await client.getBySomeTags(["featured", "homepage"])
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getbysometags}
*/
async getBySomeTags(tags, params) {
return await this.get(appendFilters(params, filter.any("document.tags", tags)));
}
/**
* Fetches pages with at least one tag from a list of tags. This method may
* make multiple network requests to fetch all matching documents.
*
* @example
*
* ```ts
* const pages = await client.getAllBySomeTags(["featured", "homepage"])
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getallbysometags}
*/
async getAllBySomeTags(tags, params) {
return await this.dangerouslyGetAll(appendFilters(params, filter.any("document.tags", tags)));
}
/**
* Fetches metadata about the client's Prismic repository.
*
* @example
*
* ```ts
* const repository = await client.getRepository()
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getrepository}
*/
async getRepository(params) {
if (this.#cachedRepository && this.#cachedRepositoryExpiration > Date.now()) return this.#cachedRepository;
const url = new URL(this.documentAPIEndpoint);
const accessToken = (params === null || params === void 0 ? void 0 : params.accessToken) || this.accessToken;
if (accessToken) url.searchParams.set("access_token", accessToken);
const response = await this.#request(url, params);
if (response.ok) {
this.#cachedRepository = await response.json();
this.#cachedRepositoryExpiration = Date.now() + REPOSITORY_CACHE_TTL;
return this.#cachedRepository;
}
if (response.status === 404) throw new RepositoryNotFoundError(`Prismic repository not found. Check that "${this.documentAPIEndpoint}" is pointing to the correct repository.`, url.toString(), void 0);
return await this.#throwContentAPIError(response, url.toString());
}
/**
* Fetches the repository's active refs.
*
* @example
*
* ```ts
* const refs = await client.getRefs()
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getrefs}
*/
async getRefs(params) {
return (await this.getRepository(params)).refs;
}
/**
* Fetches a ref by its ID.
*
* @example
*
* ```ts
* const ref = await client.getRefByID("YhE3YhEAACIA4321")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getrefbyid}
*/
async getRefByID(id, params) {
const ref = (await this.getRefs(params)).find((ref$1) => ref$1.id === id);
if (!ref) throw new PrismicError(`Ref with ID "${id}" could not be found.`, void 0, void 0);
return ref;
}
/**
* Fetches a ref by its label. A release ref's label is its name shown in the
* Page Builder.
*
* @example
*
* ```ts
* const ref = await client.getRefByLabel("My Release")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getrefbylabel}
*/
async getRefByLabel(label, params) {
const ref = (await this.getRefs(params)).find((ref$1) => ref$1.label === label);
if (!ref) throw new PrismicError(`Ref with label "${label}" could not be found.`, void 0, void 0);
return ref;
}
/**
* Fetches the repository's master ref.
*
* @example
*
* ```ts
* const masterRef = await client.getMasterRef()
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getmasterref}
*/
async getMasterRef(params) {
const ref = (await this.getRefs(params)).find((ref$1) => ref$1.isMasterRef);
if (!ref) throw new PrismicError("Master ref could not be found.", void 0, void 0);
return ref;
}
/**
* Fetches the repository's active releases.
*
* @example
*
* ```ts
* const releases = await client.getReleases()
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getreleases}
*/
async getReleases(params) {
return (await this.getRefs(params)).filter((ref) => !ref.isMasterRef);
}
/**
* Fetches a release with a specific ID.
*
* @example
*
* ```ts
* const release = await client.getReleaseByID("YhE3YhEAACIA4321")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getreleasebyid}
*/
async getReleaseByID(id, params) {
const release = (await this.getReleases(params)).find((ref) => ref.id === id);
if (!release) throw new PrismicError(`Release with ID "${id}" could not be found.`, void 0, void 0);
return release;
}
/**
* Fetches a release by its label. A release ref's label is its name shown in
* the Page Builder.
*
* @example
*
* ```ts
* const release = await client.getReleaseByLabel("My Release")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#getreleasebylabel}
*/
async getReleaseByLabel(label, params) {
const release = (await this.getReleases(params)).find((ref) => ref.label === label);
if (!release) throw new PrismicError(`Release with label "${label}" could not be found.`, void 0, void 0);
return release;
}
/**
* Fetches the repository's page tags.
*
* @example
*
* ```ts
* const tags = await client.getTags()
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#gettags}
*/
async getTags(params) {
const repository = await this.getRepository(params);
const form = repository.forms.tags;
if (form) {
const url = new URL(form.action);
if (this.accessToken) url.searchParams.set("access_token", this.accessToken);
const response = await this.#request(url, params);
if (response.ok) return await response.json();
}
return repository.tags;
}
/**
* Builds a Content API query URL with a set of parameters.
*
* @example
*
* ```ts
* const url = await client.buildQueryURL({
* filters: [filter.at("document.type", "blog_post")],
* })
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#buildqueryurl}
*/
async buildQueryURL({ signal, fetchOptions,...params } = {}) {
const ref = params.ref || await this.#getResolvedRef({
accessToken: params.accessToken,
signal,
fetchOptions
});
const integrationFieldsRef = params.integrationFieldsRef || (await this.getRepository({
accessToken: params.accessToken,
signal,
fetchOptions
})).integrationFieldsRef || void 0;
return buildQueryURL(this.documentAPIEndpoint, {
...this.defaultParams,
...params,
ref,
integrationFieldsRef,
routes: params.routes || this.routes,
brokenRoute: params.brokenRoute || this.brokenRoute,
accessToken: params.accessToken || this.accessToken
});
}
/**
* Fetches a previewed page's URL using a preview token and page ID.
*
* @example
*
* ```ts
* const url = await client.resolvePreviewURL({
* linkResolver,
* defaultURL: "/",
* })
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#resolvepreviewurl}
*/
async resolvePreviewURL(args) {
let documentID = args.documentID;
let previewToken = args.previewToken;
if (typeof globalThis.location !== "undefined") {
const searchParams = new URLSearchParams(globalThis.location.search);
documentID = documentID || searchParams.get("documentId");
previewToken = previewToken || searchParams.get("token");
} else if (this.#autoPreviewsRequest) {
if ("query" in this.#autoPreviewsRequest) {
var _this$autoPreviewsReq, _this$autoPreviewsReq2;
documentID = documentID || ((_this$autoPreviewsReq = this.#autoPreviewsRequest.query) === null || _this$autoPreviewsReq === void 0 ? void 0 : _this$autoPreviewsReq.documentId);
previewToken = previewToken || ((_this$autoPreviewsReq2 = this.#autoPreviewsRequest.query) === null || _this$autoPreviewsReq2 === void 0 ? void 0 : _this$autoPreviewsReq2.token);
} else if ("url" in this.#autoPreviewsRequest && this.#autoPreviewsRequest.url) {
const searchParams = new URL(this.#autoPreviewsRequest.url, "missing-host://").searchParams;
documentID = documentID || searchParams.get("documentId");
previewToken = previewToken || searchParams.get("token");
}
}
if (documentID != null && previewToken != null) {
const url = asLink(await this.getByID(documentID, {
ref: previewToken,
lang: "*",
signal: args.signal,
fetchOptions: args.fetchOptions
}), { linkResolver: args.linkResolver });
if (typeof url === "string") return url;
}
return args.defaultURL;
}
/**
* Configures the client to query the latest published content. This is the
* client's default mode.
*
* @example
*
* ```ts
* client.queryLatestContent()
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#querylatestcontent}
*/
queryLatestContent() {
this.#getRef = void 0;
}
/**
* Configures the client to query content from a release with a specific ID.
*
* @example
*
* ```ts
* client.queryContentFromReleaseByID("YhE3YhEAACIA4321")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#querycontentfromreleasebyid}
*/
queryContentFromReleaseByID(id) {
this.#getRef = async (params) => {
return (await this.getReleaseByID(id, params)).ref;
};
}
/**
* Configures the client to query content from a release with a specific
* label.
*
* @example
*
* ```ts
* client.queryContentFromReleaseByLabel("My Release")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#querycontentfromreleasebylabel}
*/
queryContentFromReleaseByLabel(label) {
this.#getRef = async (params) => {
return (await this.getReleaseByLabel(label, params)).ref;
};
}
/**
* Configures the client to query content from a specific ref.
*
* @example
*
* ```ts
* client.queryContentFromRef("my-ref")
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#querycontentfromref}
*/
queryContentFromRef(ref) {
this.#getRef = typeof ref === "string" ? () => ref : ref;
}
/**
* A preconfigured `fetch()` function for Prismic's GraphQL API that can be
* provided to GraphQL clients.
*
* @example
*
* ```ts
* import { createClient, getGraphQLEndpoint } from "@prismicio/client"
*
* const client = createClient("example-prismic-repo")
* const graphQLClient = new ApolloClient({
* link: new HttpLink({
* uri: getGraphQLEndpoint(client.repositoryName),
* // Provide `client.graphQLFetch` as the fetch implementation.
* fetch: client.graphQLFetch,
* // Using GET is required.
* useGETForQueries: true,
* }),
* cache: new InMemoryCache(),
* })
* ```
*
* @see {@link https://prismic.io/docs/technical-reference/prismicio-client/v7#graphqlfetch}
*/
async graphQLFetch(input, init) {
const params = {
accessToken: this.accessToken,
fetchOptions: this.fetchOptions
};
const repository = await this.getRepository(params);
const ref = await this.#getResolvedRef(params);
const headers = {};
headers["prismic-ref"] = ref;
if (this.accessToken) headers["authorization"] = `Token ${this.accessToken}`;
if (repository.integrationFieldsRef) headers["prismic-integration-field-ref"] = repository.integrationFieldsRef;
for (const [key, value] of Object.entries((init === null || init === void 0 ? void 0 : init.headers) ?? {})) headers[key.toLowerCase()] = value;
const url = new URL(typeof input === "string" ? input : input.url);
const query = (url.searchParams.get("query") ?? "").replace(/(\n| )*( |{|})(\n| )*/gm, (_chars, _spaces, brackets) => brackets);
url.searchParams.set("query", query);
url.searchParams.set("ref", ref);
return await this.fetchFn(url.toString(), {
...init,
headers
});
}
/**
* Returns the ref needed to query based on the client's current state. This
* method may make a network request to fetch a ref or resolve the user's ref
* thunk.
*
* If auto previews are enabled, the preview ref takes priority.
*
* The following strategies are used depending on the client's state:
*
* - If the user called `queryLatestContent`: Use the repository's master ref.
* The ref is cached for 5 seconds. After 5 seconds, a new master ref is
* fetched.
* - If the user called `queryContentFromReleaseByID`: Use the release's ref.
* The ref is cached for 5 seconds. After 5 seconds, a new ref for the
* release is fetched.
* - If the user called `queryContentFromReleaseByLabel`: Use the release's ref.
* The ref is cached for 5 seconds. After 5 seconds, a new ref for the
* release is fetched.
* - If the user called `queryContentFromRef`: Use the provided ref. Fall back
* to the master ref if the ref is not a string.
*/
async #getResolvedRef(params) {
var _this$getRef;
if (this.#autoPreviews) {
var _this$autoPreviewsReq3, _globalThis$document;
const previewRef = getPreviewCookie((((_this$autoPreviewsReq3 = this.#autoPreviewsRequest) === null || _this$autoPreviewsReq3 === void 0 ? void 0 : _this$autoPreviewsReq3.headers) ? "get" in this.#autoPreviewsRequest.headers ? this.#autoPreviewsRequest.headers.get("cookie") : this.#autoPreviewsRequest.headers.cookie : (_globalThis$document = globalThis.document) === null || _globalThis$document === void 0 ? void 0 : _globalThis$document.cookie) ?? "");
if (previewRef) return previewRef;
}
const ref = await ((_this$getRef = this.#getRef) === null || _this$getRef === void 0 ? void 0 : _this$getRef.call(this, params));
if (ref) return ref;
return (await this.getMasterRef(params)).ref;
}
/**
* Performs a low-level Content API request with the given parameters.
* Automatically retries if an invalid ref is used.
*/
async #internalGet(params, attempt = 1) {
const url = await this.buildQueryURL(params);
const response = await this.#request(new URL(url), params);
if (response.ok) return response;
try {
return await this.#throwContentAPIError(response, url);
} catch (error) {
if ((error instanceof RefNotFoundError || error instanceof RefExpiredError) && attempt < MAX_INVALID_REF_RETRY_ATTEMPTS) {
var _error$message$match;
if (!(params === null || params === void 0 ? void 0 : params.ref)) this.#cachedRepository = void 0;
const masterRef = (_error$message$match = error.message.match(/master ref is: (?<ref>.*)$/i)) === null || _error$message$match === void 0 || (_error$message$match = _error$message$match.groups) === null || _error$message$match === void 0 ? void 0 : _error$message$match.ref;
if (!masterRef) throw error;
throttledWarn(`[/client] The ref (${new URL(url).searchParams.get("ref")}) was ${error instanceof RefNotFoundError ? "invalid" : "expired"}. Now retrying with the latest master ref (${masterRef}). If you were previewing content, the response will not include draft content.`);
return await this.#internalGet({
...params,
ref: masterRef
}, attempt + 1);
}
throw error;
}
}
/**
* Throws an error based on a Content API response. Only call in known-errored
* states.
*/
async #throwContentAPIError(response, url) {
switch (response.status) {
case 400: {
const json = await response.clone().json();
throw new ParsingError(json.message, url, json);
}
case 401: {
const json = await response.clone().json();
throw new ForbiddenError(json.message, url, json);
}
case 404: {
const json = await response.clone().json();
switch (json.type) {
case "api_notfound_error": throw new RefNotFoundError(json.message, url, json);
case "api_security_error": if (/preview token.*expired/i.test(json.message)) throw new PreviewTokenExpiredError(json.message, url, json);
default: throw new NotFoundError(json.message, url, json);
}
}
case 410: {
const json = await response.clone().json();
throw new RefExpiredError(json.message, url, json);
}
default: throw new PrismicError(void 0, url, await response.text());
}
}
/** Performs a low-level network request with the client's fetch options. */
async #request(url, params) {
var _this$fetchOptions, _params$fetchOptions, _params$fetchOptions2, _this$fetchOptions2;
return await request(url, {
...this.fetchOptions,
...params === null || params === void 0 ? void 0 : params.fetchOptions,
headers: {
...(_this$fetchOptions = this.fetchOptions) === null || _this$fetchOptions === void 0 ? void 0 : _this$fetchOptions.headers,
...params === null || params === void 0 || (_params$fetchOptions = params.fetchOptions) === null || _params$fetchOptions === void 0 ? void 0 : _params$fetchOptions.headers
},
signal: (params === null || params === void 0 || (_params$fetchOptions2 = params.fetchOptions) === null || _params$fetchOptions2 === void 0 ? void 0 : _params$fetchOptions2.signal) || (params === null || params === void 0 ? void 0 : params.signal) || ((_this$fetchOptions2 = this.fetchOptions) === null || _this$fetchOptions2 === void 0 ? void 0 : _this$fetchOptions2.signal)
}, this.fetchFn);
}
};
/** Appends filters to a params object. */
function appendFilters(params = {}, ...filters) {
return {
...params,
filters: [...params.filters ?? [], ...filters]
};
}
//#endregion
export { Client };
//# sourceMappingURL=Client.js.map