@prismicio/client
Version:
The official JavaScript + TypeScript client library for Prismic
1,259 lines (1,258 loc) • 44.9 kB
JavaScript
var __defProp = Object.defineProperty;
var __typeError = (msg) => {
throw TypeError(msg);
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var _repositoryName;
import { appendFilters } from "./lib/appendFilters.js";
import { castThunk } from "./lib/castThunk.js";
import { devMsg } from "./lib/devMsg.js";
import { everyTagFilter } from "./lib/everyTagFilter.js";
import { findMasterRef } from "./lib/findMasterRef.js";
import { findRefByID } from "./lib/findRefByID.js";
import { findRefByLabel } from "./lib/findRefByLabel.js";
import { getPreviewCookie } from "./lib/getPreviewCookie.js";
import { minifyGraphQLQuery } from "./lib/minifyGraphQLQuery.js";
import { someTagsFilter } from "./lib/someTagsFilter.js";
import { throttledLog } from "./lib/throttledLog.js";
import { typeFilter } from "./lib/typeFilter.js";
import { ForbiddenError } from "./errors/ForbiddenError.js";
import { NotFoundError } from "./errors/NotFoundError.js";
import { ParsingError } from "./errors/ParsingError.js";
import { PreviewTokenExpiredError } from "./errors/PreviewTokenExpired.js";
import { PrismicError } from "./errors/PrismicError.js";
import { RefExpiredError } from "./errors/RefExpiredError.js";
import { RefNotFoundError } from "./errors/RefNotFoundError.js";
import { RepositoryNotFoundError } from "./errors/RepositoryNotFoundError.js";
import { asLink } from "./helpers/asLink.js";
import { BaseClient } from "./BaseClient.js";
import { buildQueryURL } from "./buildQueryURL.js";
import { filter } from "./filter.js";
import { getRepositoryEndpoint } from "./getRepositoryEndpoint.js";
import { getRepositoryName } from "./getRepositoryName.js";
import { isRepositoryEndpoint } from "./isRepositoryEndpoint.js";
const MAX_PAGE_SIZE = 100;
const REPOSITORY_CACHE_TTL = 5e3;
const GET_ALL_QUERY_DELAY = 500;
const DEFAULT_RETRY_AFTER_MS = 1e3;
const MAX_INVALID_REF_RETRY_ATTEMPTS = 3;
var RefStateMode;
(function(RefStateMode2) {
RefStateMode2["Master"] = "Master";
RefStateMode2["ReleaseID"] = "ReleaseID";
RefStateMode2["ReleaseLabel"] = "ReleaseLabel";
RefStateMode2["Manual"] = "Manual";
})(RefStateMode || (RefStateMode = {}));
class Client extends BaseClient {
/**
* Creates a Prismic client that can be used to query a repository.
*
* If used in an environment where a global `fetch` function is unavailable,
* such as in some Node.js versions, the `fetch` option must be provided as
* part of the `options` parameter.
*
* @param repositoryNameOrEndpoint - The Prismic repository name or full Rest
* API V2 endpoint for the repository.
* @param options - Configuration that determines how content will be queried
* from the Prismic repository.
*
* @returns A client that can query content from the repository.
*/
constructor(repositoryNameOrEndpoint, options = {}) {
super(options);
__privateAdd(this, _repositoryName);
/**
* The Prismic REST API V2 endpoint for the repository (use
* `prismic.getRepositoryEndpoint` for the default endpoint).
*/
__publicField(this, "documentAPIEndpoint");
/**
* The secure token for accessing the API (only needed if your repository is
* set to private).
*
* {@link https://user-guides.prismic.io/en/articles/1036153-generating-an-access-token}
*/
__publicField(this, "accessToken");
/**
* A list of route resolver objects that define how a document's `url` field
* is resolved.
*
* {@link https://prismic.io/docs/route-resolver}
*/
__publicField(this, "routes");
/**
* The `brokenRoute` option allows you to define the route populated in the
* `url` property for broken link or content relationship fields. A broken
* link is a link or content relationship field whose linked document has been
* unpublished or deleted.
*
* {@link https://prismic.io/docs/route-resolver}
*/
__publicField(this, "brokenRoute");
/**
* Default parameters that will be sent with each query. These parameters can
* be overridden on each query if needed.
*/
__publicField(this, "defaultParams");
/**
* The client's ref mode state. This determines which ref is used during
* queries.
*/
__publicField(this, "refState", {
mode: RefStateMode.Master,
autoPreviewsEnabled: true
});
/**
* Cached repository value.
*/
__publicField(this, "cachedRepository");
/**
* Timestamp at which the cached repository data is considered stale.
*/
__publicField(this, "cachedRepositoryExpiration", 0);
if ((options.documentAPIEndpoint || isRepositoryEndpoint(repositoryNameOrEndpoint)) && process.env.NODE_ENV === "development") {
const documentAPIEndpoint = options.documentAPIEndpoint || repositoryNameOrEndpoint;
if (/\.prismic\.io\/(?!api\/v2\/?)/i.test(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);
}
const hostname = new URL(documentAPIEndpoint).hostname.toLowerCase();
if (hostname.endsWith(".prismic.io") && !hostname.endsWith(".cdn.prismic.io")) {
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")}`);
}
if (options.documentAPIEndpoint && isRepositoryEndpoint(repositoryNameOrEndpoint) && repositoryNameOrEndpoint !== options.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 (isRepositoryEndpoint(repositoryNameOrEndpoint)) {
this.documentAPIEndpoint = repositoryNameOrEndpoint;
try {
this.repositoryName = getRepositoryName(repositoryNameOrEndpoint);
} catch (error) {
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")}`);
}
} else {
this.documentAPIEndpoint = options.documentAPIEndpoint || getRepositoryEndpoint(repositoryNameOrEndpoint);
this.repositoryName = repositoryNameOrEndpoint;
}
this.accessToken = options.accessToken;
this.routes = options.routes;
this.brokenRoute = options.brokenRoute;
this.defaultParams = options.defaultParams;
if (options.ref) {
this.queryContentFromRef(options.ref);
}
this.graphQLFetch = this.graphQLFetch.bind(this);
}
/**
* The Prismic repository's name.
*/
set repositoryName(value) {
__privateSet(this, _repositoryName, value);
}
/**
* The Prismic repository's name.
*/
get repositoryName() {
if (!__privateGet(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 __privateGet(this, _repositoryName);
}
/**
* The Prismic REST API V2 endpoint for the repository (use
* `prismic.getRepositoryEndpoint` for the default endpoint).
*
* @deprecated Use `documentAPIEndpoint` instead.
*/
// TODO: Remove in v8.
set endpoint(value) {
this.documentAPIEndpoint = value;
}
/**
* The Prismic REST API V2 endpoint for the repository (use
* `prismic.getRepositoryEndpoint` for the default endpoint).
*
* @deprecated Use `documentAPIEndpoint` instead.
*/
// TODO: Remove in v8.
get endpoint() {
return this.documentAPIEndpoint;
}
/**
* Enables the client to automatically query content from a preview session if
* one is active in browser environments. This is enabled by default in the
* browser.
*
* For server environments, use `enableAutoPreviewsFromReq`.
*
* @example
*
* ```ts
* client.enableAutoPreviews()
* ```
*
* @see enableAutoPreviewsFromReq
*/
enableAutoPreviews() {
this.refState.autoPreviewsEnabled = true;
}
/**
* Enables the client to automatically query content from a preview session if
* one is active in server environments. This is disabled by default on the
* server.
*
* For browser environments, use `enableAutoPreviews`.
*
* @example
*
* ```ts
* // In an express app
* app.get("/", function (req, res) {
* client.enableAutoPreviewsFromReq(req)
* })
* ```
*
* @param req - An HTTP server request object containing the request's
* cookies.
*/
enableAutoPreviewsFromReq(req) {
this.refState.httpRequest = req;
this.refState.autoPreviewsEnabled = true;
}
/**
* Disables the client from automatically querying content from a preview
* session if one is active.
*
* Automatic preview content querying is enabled by default unless this method
* is called.
*
* @example
*
* ```ts
* client.disableAutoPreviews()
* ```
*/
disableAutoPreviews() {
this.refState.autoPreviewsEnabled = false;
}
/**
* Queries content from the Prismic repository.
*
* @example
*
* ```ts
* const response = await client.get()
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param params - Parameters to filter, sort, and paginate results.
*
* @returns A paginated response containing the result of the query.
*/
async get(params) {
const { data } = await this._get(params);
return data;
}
/**
* Queries content from the Prismic repository and returns only the first
* result, if any.
*
* @example
*
* ```ts
* const document = await client.getFirst()
* ```
*
* @typeParam TDocument - Type of the Prismic document returned.
*
* @param params - Parameters to filter, sort, and paginate results.
*
* @returns The first result of the query, if any.
*/
async getFirst(params) {
var _a;
const actualParams = { ...params };
if (!(params && params.page) && !(params == null ? void 0 : params.pageSize)) {
actualParams.pageSize = ((_a = this.defaultParams) == null ? void 0 : _a.pageSize) ?? 1;
}
const { data, url } = await this._get(actualParams);
const firstResult = data.results[0];
if (firstResult) {
return firstResult;
}
throw new NotFoundError("No documents were returned", url, void 0);
}
/**
* **IMPORTANT**: Avoid using `dangerouslyGetAll` as it may be slower and
* require more resources than other methods. Prefer using other methods that
* filter by filters such as `getAllByType`.
*
* Queries content from the Prismic repository and returns all matching
* content. If no filters are provided, all documents will be fetched.
*
* This method may make multiple network requests to query all matching
* content.
*
* @example
*
* ```ts
* const response = await client.dangerouslyGetAll()
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param params - Parameters to filter, sort, and paginate results.
*
* @returns A list of documents matching the query.
*/
async dangerouslyGetAll(params = {}) {
var _a;
const { limit = Infinity, ...actualParams } = params;
const resolvedParams = {
...actualParams,
pageSize: Math.min(limit, actualParams.pageSize || ((_a = this.defaultParams) == null ? void 0 : _a.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);
}
/**
* Queries a document from the Prismic repository with a specific ID.
*
* @remarks
* A document's UID is different from its ID. An ID is automatically generated
* for all documents and is made available on its `id` property. A UID is
* provided in the Prismic editor and is unique among all documents of its
* custom type.
*
* @example
*
* ```ts
* const document = await client.getByID("WW4bKScAAMAqmluX")
* ```
*
* @typeParam TDocument- Type of the Prismic document returned.
*
* @param id - ID of the document.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns The document with an ID matching the `id` parameter, if a matching
* document exists.
*/
async getByID(id, params) {
return await this.getFirst(appendFilters(params, filter.at("document.id", id)));
}
/**
* Queries documents from the Prismic repository with specific IDs.
*
* @remarks
* A document's UID is different from its ID. An ID is automatically generated
* for all documents and is made available on its `id` property. A UID is
* provided in the Prismic editor and is unique among all documents of its
* custom type.
*
* @example
*
* ```ts
* const response = await client.getByIDs([
* "WW4bKScAAMAqmluX",
* "U1kTRgEAAC8A5ldS",
* ])
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param ids - A list of document IDs.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A paginated response containing documents with IDs matching the
* `ids` parameter.
*/
async getByIDs(ids, params) {
return await this.get(appendFilters(params, filter.in("document.id", ids)));
}
/**
* Queries all documents from the Prismic repository with specific IDs.
*
* This method may make multiple network requests to query all matching
* content.
*
* @remarks
* A document's UID is different from its ID. An ID is automatically generated
* for all documents and is made available on its `id` property. A UID is
* provided in the Prismic editor and is unique among all documents of its
* custom type.
*
* @example
*
* ```ts
* const response = await client.getAllByIDs([
* "WW4bKScAAMAqmluX",
* "U1kTRgEAAC8A5ldS",
* ])
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param ids - A list of document IDs.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A list of documents with IDs matching the `ids` parameter.
*/
async getAllByIDs(ids, params) {
return await this.dangerouslyGetAll(appendFilters(params, filter.in("document.id", ids)));
}
/**
* Queries a document from the Prismic repository with a specific UID and
* custom type.
*
* @remarks
* A document's UID is different from its ID. An ID is automatically generated
* for all documents and is made available on its `id` property. A UID is
* provided in the Prismic editor and is unique among all documents of its
* custom type.
*
* @example
*
* ```ts
* const document = await client.getByUID("blog_post", "my-first-post")
* ```
*
* @typeParam TDocument - Type of the Prismic document returned.
*
* @param documentType - The API ID of the document's custom type.
* @param uid - UID of the document.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns The document with a UID matching the `uid` parameter, if a
* matching document exists.
*/
async getByUID(documentType, uid, params) {
return await this.getFirst(appendFilters(params, [
typeFilter(documentType),
filter.at(`my.${documentType}.uid`, uid)
]));
}
/**
* Queries document from the Prismic repository with specific UIDs and Custom
* Type.
*
* @remarks
* A document's UID is different from its ID. An ID is automatically generated
* for all documents and is made available on its `id` property. A UID is
* provided in the Prismic editor and is unique among all documents of its
* custom type.
*
* @example
*
* ```ts
* const document = await client.getByUIDs("blog_post", [
* "my-first-post",
* "my-second-post",
* ])
* ```
*
* @typeParam TDocument - Type of the Prismic document returned.
*
* @param documentType - The API ID of the document's custom type.
* @param uids - A list of document UIDs.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A paginated response containing documents with UIDs matching the
* `uids` parameter.
*/
async getByUIDs(documentType, uids, params) {
return await this.get(appendFilters(params, [
typeFilter(documentType),
filter.in(`my.${documentType}.uid`, uids)
]));
}
/**
* Queries all documents from the Prismic repository with specific UIDs and
* custom type.
*
* This method may make multiple network requests to query all matching
* content.
*
* @remarks
* A document's UID is different from its ID. An ID is automatically generated
* for all documents and is made available on its `id` property. A UID is
* provided in the Prismic editor and is unique among all documents of its
* custom type.
*
* @example
*
* ```ts
* const response = await client.getAllByUIDs([
* "my-first-post",
* "my-second-post",
* ])
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param documentType - The API ID of the document's custom type.
* @param uids - A list of document UIDs.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A list of documents with UIDs matching the `uids` parameter.
*/
async getAllByUIDs(documentType, uids, params) {
return await this.dangerouslyGetAll(appendFilters(params, [
typeFilter(documentType),
filter.in(`my.${documentType}.uid`, uids)
]));
}
/**
* Queries a singleton document from the Prismic repository for a specific
* custom type.
*
* @remarks
* A singleton document is one that is configured in Prismic to only allow one
* instance. For example, a repository may be configured to contain just one
* Settings document. This is in contrast to a repeatable custom type which
* allows multiple instances of itself.
*
* @example
*
* ```ts
* const document = await client.getSingle("settings")
* ```
*
* @typeParam TDocument - Type of the Prismic document returned.
*
* @param documentType - The API ID of the singleton custom type.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns The singleton document for the custom type, if a matching document
* exists.
*/
async getSingle(documentType, params) {
return await this.getFirst(appendFilters(params, typeFilter(documentType)));
}
/**
* Queries documents from the Prismic repository for a specific custom type.
*
* Use `getAllByType` instead if you need to query all documents for a
* specific custom type.
*
* @example
*
* ```ts
* const response = await client.getByType("blog_post")
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param documentType - The API ID of the custom type.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A paginated response containing documents of the custom type.
*/
async getByType(documentType, params) {
return await this.get(appendFilters(params, typeFilter(documentType)));
}
/**
* Queries all documents from the Prismic repository for a specific Custom
* Type.
*
* This method may make multiple network requests to query all matching
* content.
*
* @example
*
* ```ts
* const response = await client.getByType("blog_post")
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param documentType - The API ID of the custom type.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A list of all documents of the custom type.
*/
async getAllByType(documentType, params) {
return await this.dangerouslyGetAll(appendFilters(params, typeFilter(documentType)));
}
/**
* Queries documents from the Prismic repository with a specific tag.
*
* Use `getAllByTag` instead if you need to query all documents with a
* specific tag.
*
* @example
*
* ```ts
* const response = await client.getByTag("food")
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param tag - The tag that must be included on a document.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A paginated response containing documents with the tag.
*/
async getByTag(tag, params) {
return await this.get(appendFilters(params, someTagsFilter(tag)));
}
/**
* Queries all documents from the Prismic repository with a specific tag.
*
* This method may make multiple network requests to query all matching
* content.
*
* @example
*
* ```ts
* const response = await client.getAllByTag("food")
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param tag - The tag that must be included on a document.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A list of all documents with the tag.
*/
async getAllByTag(tag, params) {
return await this.dangerouslyGetAll(appendFilters(params, someTagsFilter(tag)));
}
/**
* Queries documents from the Prismic repository with specific tags. A
* document must be tagged with all of the queried tags to be included.
*
* @example
*
* ```ts
* const response = await client.getByEveryTag(["food", "fruit"])
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param tags - A list of tags that must be included on a document.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A paginated response containing documents with the tags.
*/
async getByEveryTag(tags, params) {
return await this.get(appendFilters(params, everyTagFilter(tags)));
}
/**
* Queries documents from the Prismic repository with specific tags. A
* document must be tagged with all of the queried tags to be included.
*
* This method may make multiple network requests to query all matching
* content.
*
* @example
*
* ```ts
* const response = await client.getAllByEveryTag(["food", "fruit"])
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param tags - A list of tags that must be included on a document.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A list of all documents with the tags.
*/
async getAllByEveryTag(tags, params) {
return await this.dangerouslyGetAll(appendFilters(params, everyTagFilter(tags)));
}
/**
* Queries documents from the Prismic repository with specific tags. A
* document must be tagged with at least one of the queried tags to be
* included.
*
* @example
*
* ```ts
* const response = await client.getByEveryTag(["food", "fruit"])
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param tags - A list of tags that must be included on a document.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A paginated response containing documents with at least one of the
* tags.
*/
async getBySomeTags(tags, params) {
return await this.get(appendFilters(params, someTagsFilter(tags)));
}
/**
* Queries documents from the Prismic repository with specific tags. A
* document must be tagged with at least one of the queried tags to be
* included.
*
* This method may make multiple network requests to query all matching
* content.
*
* @example
*
* ```ts
* const response = await client.getAllBySomeTags(["food", "fruit"])
* ```
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param tags - A list of tags that must be included on a document.
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A list of all documents with at least one of the tags.
*/
async getAllBySomeTags(tags, params) {
return await this.dangerouslyGetAll(appendFilters(params, someTagsFilter(tags)));
}
/**
* Returns metadata about the Prismic repository, such as its refs, releases,
* and custom types.
*
* @returns Repository metadata.
*/
async getRepository(params) {
const url = new URL(this.documentAPIEndpoint);
if (this.accessToken) {
url.searchParams.set("access_token", this.accessToken);
}
return await this.fetch(url.toString(), params);
}
/**
* Returns a list of all refs for the Prismic repository.
*
* Refs are used to identify which version of the repository's content should
* be queried. All repositories will have at least one ref pointing to the
* latest published content called the "master ref".
*
* @returns A list of all refs for the Prismic repository.
*/
async getRefs(params) {
const repository = await this.getRepository(params);
return repository.refs;
}
/**
* Returns a ref for the Prismic repository with a matching ID.
*
* @param id - ID of the ref.
*
* @returns The ref with a matching ID, if it exists.
*/
async getRefByID(id, params) {
const refs = await this.getRefs(params);
return findRefByID(refs, id);
}
/**
* Returns a ref for the Prismic repository with a matching label.
*
* @param label - Label of the ref.
*
* @returns The ref with a matching label, if it exists.
*/
async getRefByLabel(label, params) {
const refs = await this.getRefs(params);
return findRefByLabel(refs, label);
}
/**
* Returns the master ref for the Prismic repository. The master ref points to
* the repository's latest published content.
*
* @returns The repository's master ref.
*/
async getMasterRef(params) {
const refs = await this.getRefs(params);
return findMasterRef(refs);
}
/**
* Returns a list of all Releases for the Prismic repository. Releases are
* used to group content changes before publishing.
*
* @returns A list of all Releases for the Prismic repository.
*/
async getReleases(params) {
const refs = await this.getRefs(params);
return refs.filter((ref) => !ref.isMasterRef);
}
/**
* Returns a Release for the Prismic repository with a matching ID.
*
* @param id - ID of the Release.
*
* @returns The Release with a matching ID, if it exists.
*/
async getReleaseByID(id, params) {
const releases = await this.getReleases(params);
return findRefByID(releases, id);
}
/**
* Returns a Release for the Prismic repository with a matching label.
*
* @param label - Label of the ref.
*
* @returns The ref with a matching label, if it exists.
*/
async getReleaseByLabel(label, params) {
const releases = await this.getReleases(params);
return findRefByLabel(releases, label);
}
/**
* Returns a list of all tags used in the Prismic repository.
*
* @returns A list of all tags used in the repository.
*/
async getTags(params) {
try {
const tagsForm = await this.getCachedRepositoryForm("tags", params);
const url = new URL(tagsForm.action);
if (this.accessToken) {
url.searchParams.set("access_token", this.accessToken);
}
return await this.fetch(url.toString(), params);
} catch {
const repository = await this.getRepository(params);
return repository.tags;
}
}
/**
* Builds a URL used to query content from the Prismic repository.
*
* @param params - Parameters to filter, sort, and paginate the results.
*
* @returns A URL string that can be requested to query content.
*/
async buildQueryURL({ signal, fetchOptions, ...params } = {}) {
const ref = params.ref || await this.getResolvedRefString({ signal, fetchOptions });
const integrationFieldsRef = params.integrationFieldsRef || (await this.getCachedRepository({ 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
});
}
/**
* Determines the URL for a previewed document during an active preview
* session. The result of this method should be used to redirect the user to
* the document's URL.
*
* @example
*
* ```ts
* const url = client.resolvePreviewURL({
* linkResolver: (document) => `/${document.uid}`
* defaultURL: '/'
* })
* ```
*
* @param args - Arguments to configure the URL resolving.
*
* @returns The URL for the previewed document during an active preview
* session. The user should be redirected to this URL.
*/
async resolvePreviewURL(args) {
var _a, _b;
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.refState.httpRequest) {
if ("query" in this.refState.httpRequest) {
documentID = documentID || ((_a = this.refState.httpRequest.query) == null ? void 0 : _a.documentId);
previewToken = previewToken || ((_b = this.refState.httpRequest.query) == null ? void 0 : _b.token);
} else if ("url" in this.refState.httpRequest && this.refState.httpRequest.url) {
const searchParams = new URL(this.refState.httpRequest.url, "missing-host://").searchParams;
documentID = documentID || searchParams.get("documentId");
previewToken = previewToken || searchParams.get("token");
}
}
if (documentID != null && previewToken != null) {
const document = await this.getByID(documentID, {
ref: previewToken,
lang: "*",
signal: args.signal,
fetchOptions: args.fetchOptions
});
const url = asLink(document, { linkResolver: args.linkResolver });
if (typeof url === "string") {
return url;
}
}
return args.defaultURL;
}
/**
* Configures the client to query the latest published content for all future
* queries.
*
* If the `ref` parameter is provided during a query, it takes priority for
* that query.
*
* @example
*
* ```ts
* await client.queryLatestContent()
* const document = await client.getByID("WW4bKScAAMAqmluX")
* ```
*/
queryLatestContent() {
this.refState.mode = RefStateMode.Master;
}
/**
* Configures the client to query content from a specific Release identified
* by its ID for all future queries.
*
* If the `ref` parameter is provided during a query, it takes priority for
* that query.
*
* @example
*
* ```ts
* await client.queryContentFromReleaseByID("YLB7OBAAACMA7Cpa")
* const document = await client.getByID("WW4bKScAAMAqmluX")
* ```
*
* @param releaseID - The ID of the Release.
*/
queryContentFromReleaseByID(releaseID) {
this.refState = {
...this.refState,
mode: RefStateMode.ReleaseID,
releaseID
};
}
/**
* Configures the client to query content from a specific Release identified
* by its label for all future queries.
*
* If the `ref` parameter is provided during a query, it takes priority for
* that query.
*
* @example
*
* ```ts
* await client.queryContentFromReleaseByLabel("My Release")
* const document = await client.getByID("WW4bKScAAMAqmluX")
* ```
*
* @param releaseLabel - The label of the Release.
*/
queryContentFromReleaseByLabel(releaseLabel) {
this.refState = {
...this.refState,
mode: RefStateMode.ReleaseLabel,
releaseLabel
};
}
/**
* Configures the client to query content from a specific ref. The ref can be
* provided as a string or a function.
*
* If a function is provided, the ref is fetched lazily before each query. The
* function may also be asynchronous.
*
* @example
*
* ```ts
* await client.queryContentFromRef("my-ref")
* const document = await client.getByID("WW4bKScAAMAqmluX")
* ```
*
* @param ref - The ref or a function that returns the ref from which to query
* content.
*/
queryContentFromRef(ref) {
this.refState = {
...this.refState,
mode: RefStateMode.Manual,
ref
};
}
/**
* A `fetch()` function to be used with GraphQL clients configured for
* Prismic's GraphQL API. It automatically applies the necessary `prismic-ref`
* and Authorization headers. Queries will automatically be minified by
* removing whitespace where possible.
*
* @example
*
* ```ts
* const graphQLClient = new ApolloClient({
* link: new HttpLink({
* uri: prismic.getGraphQLEndpoint(repositoryName),
* // Provide `client.graphQLFetch` as the fetch implementation.
* fetch: client.graphQLFetch,
* // Using GET is required.
* useGETForQueries: true,
* }),
* cache: new InMemoryCache(),
* })
* ```
*
* @param input - The `fetch()` `input` parameter. Only strings are supported.
* @param init - The `fetch()` `init` parameter. Only plain objects are
* supported.
*
* @returns The `fetch()` Response for the request.
*
* @experimental
*/
async graphQLFetch(input, init) {
const cachedRepository = await this.getCachedRepository();
const ref = await this.getResolvedRefString();
const unsanitizedHeaders = {
"Prismic-ref": ref,
Authorization: this.accessToken ? `Token ${this.accessToken}` : "",
// Asserting `init.headers` is a Record since popular GraphQL
// libraries pass this as a Record. Header objects as input
// are unsupported.
...init ? init.headers : {}
};
if (cachedRepository.integrationFieldsRef) {
unsanitizedHeaders["Prismic-integration-field-ref"] = cachedRepository.integrationFieldsRef;
}
const headers = {};
for (const key in unsanitizedHeaders) {
if (unsanitizedHeaders[key]) {
headers[key.toLowerCase()] = unsanitizedHeaders[key];
}
}
const url = new URL(
// Asserting `input` is a string since popular GraphQL
// libraries pass this as a string. Request objects as
// input are unsupported.
input
);
url.searchParams.set("ref", ref);
const query = url.searchParams.get("query");
if (query) {
url.searchParams.set(
"query",
// Compress the GraphQL query (if it exists) by
// removing whitespace. This is done to
// optimize the query size and avoid
// hitting the upper limit of GET requests
// (2048 characters).
minifyGraphQLQuery(query)
);
}
return await this.fetchFn(url.toString(), {
...init,
headers
});
}
/**
* Returns a cached version of `getRepository` with a TTL.
*
* @returns Cached repository metadata.
*/
async getCachedRepository(params) {
if (!this.cachedRepository || Date.now() >= this.cachedRepositoryExpiration) {
this.cachedRepositoryExpiration = Date.now() + REPOSITORY_CACHE_TTL;
this.cachedRepository = await this.getRepository(params);
}
return this.cachedRepository;
}
/**
* Returns a cached Prismic repository form. Forms are used to determine API
* endpoints for types of repository data.
*
* @param name - Name of the form.
*
* @returns The repository form.
*
* @throws If a matching form cannot be found.
*/
async getCachedRepositoryForm(name, params) {
const cachedRepository = await this.getCachedRepository(params);
const form = cachedRepository.forms[name];
if (!form) {
throw new PrismicError(`Form with name "${name}" could not be found`, void 0, void 0);
}
return form;
}
/**
* 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 if available.
*
* 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.
*
* @returns The ref to use during a query.
*/
async getResolvedRefString(params) {
var _a, _b;
if (this.refState.autoPreviewsEnabled) {
let previewRef;
let cookieJar;
if ((_a = this.refState.httpRequest) == null ? void 0 : _a.headers) {
if ("get" in this.refState.httpRequest.headers && typeof this.refState.httpRequest.headers.get === "function") {
cookieJar = this.refState.httpRequest.headers.get("cookie");
} else if ("cookie" in this.refState.httpRequest.headers) {
cookieJar = this.refState.httpRequest.headers.cookie;
}
} else if ((_b = globalThis.document) == null ? void 0 : _b.cookie) {
cookieJar = globalThis.document.cookie;
}
if (cookieJar) {
previewRef = getPreviewCookie(cookieJar);
}
if (previewRef) {
return previewRef;
}
}
const cachedRepository = await this.getCachedRepository(params);
const refModeType = this.refState.mode;
if (refModeType === RefStateMode.ReleaseID) {
return findRefByID(cachedRepository.refs, this.refState.releaseID).ref;
} else if (refModeType === RefStateMode.ReleaseLabel) {
return findRefByLabel(cachedRepository.refs, this.refState.releaseLabel).ref;
} else if (refModeType === RefStateMode.Manual) {
const res = await castThunk(this.refState.ref)();
if (typeof res === "string") {
return res;
}
}
return findMasterRef(cachedRepository.refs).ref;
}
/**
* The private implementation of `this.get`. It returns the API response and
* the URL used to make the request. The URL is sometimes used in the public
* method to include in thrown errors.
*
* This method retries requests that throw `RefNotFoundError` or
* `RefExpiredError`. It contains special logic to retry with the latest
* master ref, provided in the API's error message.
*
* @typeParam TDocument - Type of Prismic documents returned.
*
* @param params - Parameters to filter, sort, and paginate results.
*
* @returns An object containing the paginated response containing the result
* of the query and the URL used to make the API request.
*/
async _get(params, attemptCount = 0) {
var _a, _b;
const url = await this.buildQueryURL(params);
try {
const data = await this.fetch(url, params);
return { data, url };
} catch (error) {
if (!(error instanceof RefNotFoundError || error instanceof RefExpiredError) || attemptCount >= MAX_INVALID_REF_RETRY_ATTEMPTS - 1) {
throw error;
}
if (!(params == null ? void 0 : params.ref)) {
this.cachedRepository = void 0;
}
const masterRef = (_b = (_a = error.message.match(/Master ref is: (?<ref>.*)$/)) == null ? void 0 : _a.groups) == null ? void 0 : _b.ref;
if (!masterRef) {
throw error;
}
const badRef = new URL(url).searchParams.get("ref");
const issue = error instanceof RefNotFoundError ? "invalid" : "expired";
throttledLog(`[/client] The ref (${badRef}) was ${issue}. Now retrying with the latest master ref (${masterRef}). If you were previewing content, the response will not include draft content.`, { level: "warn" });
return await this._get({ ...params, ref: masterRef }, attemptCount + 1);
}
}
/**
* Performs a network request using the configured `fetch` function. It
* assumes all successful responses will have a JSON content type. It also
* normalizes unsuccessful network requests.
*
* @typeParam T - The JSON response.
*
* @param url - URL to the resource to fetch.
* @param params - Prismic REST API parameters for the network request.
*
* @returns The JSON response from the network request.
*/
async fetch(url, params = {}) {
const res = await super.fetch(url, params);
if (res.status !== 404 && res.status !== 429 && res.json == null) {
throw new PrismicError(void 0, url, res.json || res.text);
}
switch (res.status) {
case 200:
case 201: {
return res.json;
}
case 400: {
throw new ParsingError(res.json.message, url, res.json);
}
case 401:
case 403: {
throw new ForbiddenError(res.json.error || res.json.message, url, res.json);
}
case 404: {
if (res.json === void 0) {
throw new RepositoryNotFoundError(`Prismic repository not found. Check that "${this.documentAPIEndpoint}" is pointing to the correct repository.`, url, url.startsWith(this.documentAPIEndpoint) ? void 0 : res.text);
}
if (res.json.type === "api_notfound_error") {
throw new RefNotFoundError(res.json.message, url, res.json);
}
if (res.json.type === "api_security_error" && /preview token.*expired/i.test(res.json.message)) {
throw new PreviewTokenExpiredError(res.json.message, url, res.json);
}
throw new NotFoundError(res.json.message, url, res.json);
}
case 410: {
throw new RefExpiredError(res.json.message, url, res.json);
}
case 429: {
const parsedRetryAfter = Number(res.headers.get("retry-after"));
const delay = Number.isNaN(parsedRetryAfter) ? DEFAULT_RETRY_AFTER_MS : parsedRetryAfter * 1e3;
return await new Promise((resolve, reject) => {
setTimeout(async () => {
try {
resolve(await this.fetch(url, params));
} catch (error) {
reject(error);
}
}, delay);
});
}
}
throw new PrismicError(void 0, url, res.json);
}
}
_repositoryName = new WeakMap();
export {
Client,
GET_ALL_QUERY_DELAY,
REPOSITORY_CACHE_TTL
};
//# sourceMappingURL=Client.js.map