next-drupal
Advanced tools
Comparing version
@@ -6,2 +6,46 @@ # Change Log | ||
# [2.0.0-alpha.0](https://github.com/chapter-three/next-drupal/compare/next-drupal@1.6.0...next-drupal@2.0.0-alpha.0) (2024-01-05) | ||
### Bug Fixes | ||
* **next-drupal:** fix "403 forbidden" jest failures ([22d5742](https://github.com/chapter-three/next-drupal/commit/22d57420342b0573fdcf697c80f219f723adbc5e)), closes [#603](https://github.com/chapter-three/next-drupal/issues/603) | ||
* **next-drupal:** fix broken build by using named Jsona export ([f2f398d](https://github.com/chapter-three/next-drupal/commit/f2f398de57366c17908b7b54301d5966f39e73bf)) | ||
* feat(next-drupal)!: add App Router support for Server Components ([410a5df](https://github.com/chapter-three/next-drupal/commit/410a5dfec62df2c9de0adf37feaa615a1f35e98a)), closes [#442](https://github.com/chapter-three/next-drupal/issues/442) | ||
### Features | ||
* **next-drupal:** add Next.js v14 support ([32a5a0a](https://github.com/chapter-three/next-drupal/commit/32a5a0a582c5faa93f8ac1a9633a89c60f5fd627)), closes [#578](https://github.com/chapter-three/next-drupal/issues/578) | ||
### BREAKING CHANGES | ||
* The useMenu() client hook has moved out of the main entry point and into its own | ||
entry point. Any import or require of that function needs to be updated: | ||
Old usage: | ||
```js | ||
import { useMenu } from "next-drupal" | ||
``` | ||
New usage: | ||
```js | ||
import { useMenu } from "next-drupal/navigation" | ||
``` | ||
# [1.6.0](https://github.com/chapter-three/next-drupal/compare/next-drupal@1.5.1...next-drupal@1.6.0) (2022-12-06) | ||
**Note:** Version bump only for package next-drupal | ||
## [1.5.1](https://github.com/chapter-three/next-drupal/compare/next-drupal@1.5.0...next-drupal@1.5.1) (2022-12-06) | ||
@@ -8,0 +52,0 @@ |
@@ -1,15 +0,117 @@ | ||
export * from "./get-access-token"; | ||
export * from "./get-menu"; | ||
export * from "./get-paths"; | ||
export * from "./get-resource-collection"; | ||
export * from "./preview"; | ||
export * from "./get-resource-type"; | ||
export * from "./get-resource"; | ||
export * from "./get-search-index"; | ||
export * from "./get-view"; | ||
export * from "./types"; | ||
export * from "./use-menu"; | ||
export * from "./translate-path"; | ||
export { deserialize, buildUrl, getJsonApiIndex, getJsonApiPathForResourceType, syncDrupalPreviewRoutes, } from "./utils"; | ||
export * from "./client"; | ||
export * from "./jsonapi-errors"; | ||
import { A as AccessToken, D as DrupalMenuLinkContent, J as JsonApiWithLocaleOptions, a as JsonApiParams, b as JsonApiResource, c as DrupalTranslatedPath, L as Locale, d as JsonApiError } from './types-fwfx7L3J.js'; | ||
export { B as BaseUrl, u as DataCache, E as DrupalBlock, f as DrupalClientAuth, i as DrupalClientAuthAccessToken, h as DrupalClientAuthClientIdSecret, g as DrupalClientAuthUsernamePassword, e as DrupalClientOptions, I as DrupalFile, K as DrupalFileMeta, H as DrupalMedia, z as DrupalNode, C as DrupalParagraph, w as DrupalSearchApiFacet, M as DrupalTaxonomyTerm, N as DrupalUser, O as DrupalView, v as FetchOptions, F as Fetcher, G as GetResourcePreviewUrlOptions, r as JsonApiCreateFileResourceBody, q as JsonApiCreateResourceBody, n as JsonApiLinks, k as JsonApiOptions, p as JsonApiResourceBodyRelationship, y as JsonApiResourceWithPath, o as JsonApiResponse, t as JsonApiSearchApiResponse, s as JsonApiUpdateResourceBody, l as JsonApiWithAuthOptions, m as JsonApiWithCacheOptions, j as Logger, x as PathAlias, P as PathPrefix, S as Serializer } from './types-fwfx7L3J.js'; | ||
import { GetStaticPathsContext, GetStaticPathsResult, GetStaticPropsContext, NextApiResponse, NextApiRequest } from 'next'; | ||
import * as jsona_lib_JsonaTypes from 'jsona/lib/JsonaTypes'; | ||
export { D as DRAFT_DATA_COOKIE_NAME, a as DRAFT_MODE_COOKIE_NAME, b as DrupalClient } from './client-K7yVwpdx.js'; | ||
declare function getAccessToken(): Promise<AccessToken>; | ||
declare function getMenu<T extends DrupalMenuLinkContent>(name: string, options?: { | ||
deserialize?: boolean; | ||
accessToken?: AccessToken; | ||
} & JsonApiWithLocaleOptions): Promise<{ | ||
items: T[]; | ||
tree: T[]; | ||
}>; | ||
declare function getPathsFromContext(types: string | string[], context: GetStaticPathsContext, options?: { | ||
params?: JsonApiParams; | ||
accessToken?: AccessToken; | ||
}): Promise<GetStaticPathsResult["paths"]>; | ||
declare function getResourceCollection<T = JsonApiResource[]>(type: string, options?: { | ||
deserialize?: boolean; | ||
accessToken?: AccessToken; | ||
} & JsonApiWithLocaleOptions): Promise<T>; | ||
declare function getResourceCollectionFromContext<T = JsonApiResource[]>(type: string, context: GetStaticPropsContext, options?: { | ||
deserialize?: boolean; | ||
params?: JsonApiParams; | ||
}): Promise<T>; | ||
interface PreviewOptions { | ||
errorMessages?: { | ||
secret?: string; | ||
slug?: string; | ||
}; | ||
} | ||
declare function DrupalPreview(options?: PreviewOptions): (request: any, response: any) => Promise<void | NextApiResponse>; | ||
declare function PreviewHandler(request?: NextApiRequest, response?: NextApiResponse, options?: PreviewOptions): Promise<void | NextApiResponse>; | ||
type GetResourcePreviewUrlOptions = JsonApiWithLocaleOptions & { | ||
isVersionable?: boolean; | ||
}; | ||
declare function getResourcePreviewUrl(slug: string, options?: GetResourcePreviewUrlOptions): Promise<any>; | ||
declare function getResourceTypeFromContext(context: GetStaticPropsContext, options?: { | ||
accessToken?: AccessToken; | ||
prefix?: string; | ||
}): Promise<string>; | ||
declare function getResourceFromContext<T extends JsonApiResource>(type: string, context: GetStaticPropsContext, options?: { | ||
prefix?: string; | ||
deserialize?: boolean; | ||
params?: JsonApiParams; | ||
accessToken?: AccessToken; | ||
isVersionable?: boolean; | ||
}): Promise<T>; | ||
declare function getResourceByPath<T extends JsonApiResource>(path: string, options?: { | ||
accessToken?: AccessToken; | ||
deserialize?: boolean; | ||
isVersionable?: boolean; | ||
} & JsonApiWithLocaleOptions): Promise<T>; | ||
declare function getResource<T extends JsonApiResource>(type: string, uuid: string, options?: { | ||
accessToken?: AccessToken; | ||
deserialize?: boolean; | ||
} & JsonApiWithLocaleOptions): Promise<T>; | ||
declare function getSearchIndex<T = JsonApiResource[]>(name: string, options?: { | ||
deserialize?: boolean; | ||
accessToken?: AccessToken; | ||
} & JsonApiWithLocaleOptions): Promise<T>; | ||
declare function getSearchIndexFromContext<T = JsonApiResource[]>(name: string, context: GetStaticPropsContext, options?: { | ||
deserialize?: boolean; | ||
accessToken?: AccessToken; | ||
} & JsonApiWithLocaleOptions): Promise<T>; | ||
declare function getView<T>(name: string, options?: { | ||
deserialize?: boolean; | ||
accessToken?: AccessToken; | ||
} & JsonApiWithLocaleOptions): Promise<{ | ||
results: T; | ||
meta: Record<string, any>; | ||
links: { | ||
[key in "next" | "prev" | "self"]?: { | ||
href: "string"; | ||
}; | ||
}; | ||
}>; | ||
declare function translatePath(path: string, options?: { | ||
accessToken?: AccessToken; | ||
}): Promise<DrupalTranslatedPath>; | ||
declare function translatePathFromContext(context: GetStaticPropsContext, options?: { | ||
accessToken?: AccessToken; | ||
prefix?: string; | ||
}): Promise<DrupalTranslatedPath>; | ||
declare function deserialize(body: any, options?: any): jsona_lib_JsonaTypes.TJsonaModel | jsona_lib_JsonaTypes.TJsonaModel[]; | ||
declare function getJsonApiPathForResourceType(type: string, locale?: Locale): Promise<string>; | ||
declare function getJsonApiIndex(locale?: Locale, options?: { | ||
accessToken?: AccessToken; | ||
}): Promise<{ | ||
links: { | ||
[type: string]: { | ||
href: string; | ||
}; | ||
}; | ||
}>; | ||
declare function buildUrl(path: string, params?: string | Record<string, string> | URLSearchParams): URL; | ||
declare function syncDrupalPreviewRoutes(path: any): void; | ||
declare class JsonApiErrors extends Error { | ||
errors: JsonApiError[] | string; | ||
statusCode: number; | ||
constructor(errors: JsonApiError[], statusCode: number); | ||
private static formatMessage; | ||
} | ||
export { AccessToken, DrupalMenuLinkContent, DrupalPreview, DrupalTranslatedPath, JsonApiError, JsonApiErrors, JsonApiParams, JsonApiResource, JsonApiWithLocaleOptions, Locale, PreviewHandler, buildUrl, deserialize, getAccessToken, getJsonApiIndex, getJsonApiPathForResourceType, getMenu, getPathsFromContext, getResource, getResourceByPath, getResourceCollection, getResourceCollectionFromContext, getResourceFromContext, getResourcePreviewUrl, getResourceTypeFromContext, getSearchIndex, getSearchIndexFromContext, getView, syncDrupalPreviewRoutes, translatePath, translatePathFromContext }; |
@@ -1,1 +0,1 @@ | ||
{"name":"next-drupal","description":"Helpers for Next.js + Drupal.","version":"0.0.0-pr.377.dcf95b6d","sideEffects":false,"source":"src/index.ts","main":"dist/index.js","module":"dist/index.esm.js","types":"dist/index.d.ts","license":"MIT","publishConfig":{"access":"public"},"author":{"name":"shadcn","url":"https://twitter.com/shadcn"},"scripts":{"prepare":"microbundle --no-compress --jsx React.createElement","dev":"microbundle watch --no-compress --jsx React.createElement","test":"jest","prepublishOnly":"yarn prepare"},"keywords":["next.js","drupal","jsonapi","preview"],"bugs":{"url":"https://github.com/chapter-three/next-drupal/issues"},"homepage":"https://github.com/chapter-three/next-drupal","repository":{"type":"git","url":"https://github.com/chapter-three/next-drupal.git","directory":"packages/next-drupal"},"devDependencies":{"rollup-plugin-node-builtins":"^2.1.2","typescript":"^4.5.5"},"dependencies":{"jsona":"^1.9.7","next":"^12.2.0 || ^13","node-cache":"^5.1.2","qs":"^6.10.3","react":"^17.0.2 || ^18","react-dom":"^17.0.2 || ^18"}} | ||
{"name":"next-drupal","description":"Helpers for Next.js + Drupal.","version":"0.0.0-pr.377.f45411ae","sideEffects":false,"source":["src/index.ts","src/navigation.ts"],"type":"module","main":"dist/index.cjs","module":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./draft":{"import":{"types":"./dist/draft.d.ts","default":"./dist/draft.js"},"require":{"types":"./dist/draft.d.cts","default":"./dist/draft.cjs"}},"./navigation":{"import":{"types":"./dist/navigation.d.ts","default":"./dist/navigation.js"},"require":{"types":"./dist/navigation.d.cts","default":"./dist/navigation.cjs"}}},"license":"MIT","publishConfig":{"access":"public"},"author":{"name":"shadcn","url":"https://twitter.com/shadcn"},"scripts":{"prepare":"tsup","dev":"tsup --watch","test":"jest --verbose"},"keywords":["next.js","drupal","jsonapi","preview"],"bugs":{"url":"https://github.com/chapter-three/next-drupal/issues"},"homepage":"https://github.com/chapter-three/next-drupal","repository":{"type":"git","url":"https://github.com/chapter-three/next-drupal.git","directory":"packages/next-drupal"},"devDependencies":{"typescript":"^5.2.2"},"dependencies":{"jsona":"^1.12.1","next":"^13.4 || ^14","node-cache":"^5.1.2","qs":"^6.11.2","react":"^17.0.2 || ^18","react-dom":"^17.0.2 || ^18"}} |
@@ -0,1 +1,5 @@ | ||
import { Jsona } from "jsona" | ||
import { stringify } from "qs" | ||
import { JsonApiErrors } from "./jsonapi-errors" | ||
import { logger as defaultLogger } from "./logger" | ||
import type { | ||
@@ -8,34 +12,28 @@ GetStaticPathsContext, | ||
} from "next" | ||
import { stringify } from "qs" | ||
import Jsona from "jsona" | ||
import type { | ||
JsonApiResource, | ||
Locale, | ||
AccessToken, | ||
JsonApiResponse, | ||
JsonApiWithLocaleOptions, | ||
JsonApiParams, | ||
BaseUrl, | ||
DrupalClientAuthAccessToken, | ||
DrupalClientAuthClientIdSecret, | ||
DrupalClientAuthUsernamePassword, | ||
DrupalClientOptions, | ||
DrupalFile, | ||
DrupalMenuLinkContent, | ||
DrupalTranslatedPath, | ||
DrupalMenuLinkContent, | ||
DrupalView, | ||
FetchOptions, | ||
DrupalClientOptions, | ||
BaseUrl, | ||
JsonApiCreateFileResourceBody, | ||
JsonApiCreateResourceBody, | ||
JsonApiParams, | ||
JsonApiResource, | ||
JsonApiResourceWithPath, | ||
JsonApiResponse, | ||
JsonApiUpdateResourceBody, | ||
JsonApiWithAuthOptions, | ||
JsonApiWithCacheOptions, | ||
JsonApiWithLocaleOptions, | ||
Locale, | ||
PathAlias, | ||
PathPrefix, | ||
JsonApiResourceWithPath, | ||
PathAlias, | ||
PreviewOptions, | ||
JsonApiWithCacheOptions, | ||
JsonApiCreateResourceBody, | ||
JsonApiUpdateResourceBody, | ||
DrupalClientAuthUsernamePassword, | ||
DrupalClientAuthAccessToken, | ||
DrupalClientAuthClientIdSecret, | ||
JsonApiCreateFileResourceBody, | ||
DrupalView, | ||
DrupalFile, | ||
} from "./types" | ||
import { logger as defaultLogger } from "./logger" | ||
import { JsonApiErrors } from "./jsonapi-errors" | ||
@@ -45,2 +43,5 @@ const DEFAULT_API_PREFIX = "/jsonapi" | ||
const DEFAULT_WITH_AUTH = false | ||
export const DRAFT_DATA_COOKIE_NAME = "draftData" | ||
// See https://vercel.com/docs/workflow-collaboration/draft-mode | ||
export const DRAFT_MODE_COOKIE_NAME = "__prerender_bypass" | ||
@@ -83,6 +84,6 @@ // From simple_oauth. | ||
debug: DrupalClientOptions["debug"] | ||
frontPage: DrupalClientOptions["frontPage"] | ||
private isDebugEnabled: DrupalClientOptions["debug"] | ||
private serializer: DrupalClientOptions["serializer"] | ||
@@ -118,4 +119,2 @@ | ||
private forceIframeSameSiteCookie?: DrupalClientOptions["forceIframeSameSiteCookie"] | ||
/** | ||
@@ -148,3 +147,2 @@ * Instantiates a new DrupalClient. | ||
accessToken, | ||
forceIframeSameSiteCookie = false, | ||
throwJsonApiErrors = true, | ||
@@ -157,3 +155,3 @@ } = options | ||
this.frontPage = frontPage | ||
this.debug = debug | ||
this.isDebugEnabled = !!debug | ||
this.useDefaultResourceTypeEntry = useDefaultResourceTypeEntry | ||
@@ -168,3 +166,2 @@ this.fetcher = fetcher | ||
this.accessToken = accessToken | ||
this.forceIframeSameSiteCookie = forceIframeSameSiteCookie | ||
this.throwJsonApiErrors = throwJsonApiErrors | ||
@@ -177,3 +174,3 @@ | ||
this._debug("Debug mode is on.") | ||
this.debug("Debug mode is on.") | ||
} | ||
@@ -227,3 +224,2 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
async fetch(input: RequestInfo, init?: FetchOptions): Promise<Response> { | ||
@@ -242,3 +238,3 @@ init = { | ||
if (init?.withAuth) { | ||
this._debug(`Using authenticated request.`) | ||
this.debug(`Using authenticated request.`) | ||
@@ -255,11 +251,11 @@ if (init.withAuth === true) { | ||
if (typeof this._auth === "function") { | ||
this._debug(`Using custom auth callback.`) | ||
this.debug(`Using custom auth callback.`) | ||
init["headers"]["Authorization"] = this._auth() | ||
} else if (typeof this._auth === "string") { | ||
this._debug(`Using custom authorization header.`) | ||
this.debug(`Using custom authorization header.`) | ||
init["headers"]["Authorization"] = this._auth | ||
} else if (typeof this._auth === "object") { | ||
this._debug(`Using custom auth credentials.`) | ||
this.debug(`Using custom auth credentials.`) | ||
@@ -274,3 +270,3 @@ if (isBasicAuth(this._auth)) { | ||
// Use the built-in client_credentials grant. | ||
this._debug(`Using default auth (client_credentials).`) | ||
this.debug(`Using default auth (client_credentials).`) | ||
@@ -284,17 +280,16 @@ // Fetch an access token and add it to the request. | ||
} else if (isAccessTokenAuth(this._auth)) { | ||
init["headers"][ | ||
"Authorization" | ||
] = `${this._auth.token_type} ${this._auth.access_token}` | ||
init["headers"]["Authorization"] = | ||
`${this._auth.token_type} ${this._auth.access_token}` | ||
} | ||
} | ||
} else if (typeof init.withAuth === "string") { | ||
this._debug(`Using custom authorization header.`) | ||
this.debug(`Using custom authorization header.`) | ||
init["headers"]["Authorization"] = init.withAuth | ||
} else if (typeof init.withAuth === "function") { | ||
this._debug(`Using custom authorization callback.`) | ||
this.debug(`Using custom authorization callback.`) | ||
init["headers"]["Authorization"] = init.withAuth() | ||
} else if (isBasicAuth(init.withAuth)) { | ||
this._debug(`Using basic authorization header`) | ||
this.debug(`Using basic authorization header.`) | ||
@@ -314,5 +309,4 @@ const basic = Buffer.from( | ||
} else if (isAccessTokenAuth(init.withAuth)) { | ||
init["headers"][ | ||
"Authorization" | ||
] = `${init.withAuth.token_type} ${init.withAuth.access_token}` | ||
init["headers"]["Authorization"] = | ||
`${init.withAuth.token_type} ${init.withAuth.access_token}` | ||
} | ||
@@ -322,3 +316,3 @@ } | ||
if (this.fetcher) { | ||
this._debug(`Using custom fetcher.`) | ||
this.debug(`Using custom fetcher, fetching: ${input}`) | ||
@@ -328,3 +322,3 @@ return await this.fetcher(input, init) | ||
this._debug(`Using default fetch (polyfilled by Next.js).`) | ||
this.debug(`Using default fetch, fetching: ${input}`) | ||
@@ -352,4 +346,3 @@ return await fetch(input, init) | ||
this._debug(`Creating resource of type ${type}.`) | ||
this._debug(url.toString()) | ||
this.debug(`Creating resource of type ${type}.`) | ||
@@ -397,4 +390,3 @@ // Add type to body. | ||
this._debug(`Creating file resource for media of type ${type}.`) | ||
this._debug(url.toString()) | ||
this.debug(`Creating file resource for media of type ${type}.`) | ||
@@ -440,4 +432,3 @@ const response = await this.fetch(url.toString(), { | ||
this._debug(`Updating resource of type ${type} with id ${uuid}.`) | ||
this._debug(url.toString()) | ||
this.debug(`Updating resource of type ${type} with id ${uuid}.`) | ||
@@ -481,4 +472,3 @@ // Update body. | ||
this._debug(`Deleting resource of type ${type} with id ${uuid}.`) | ||
this._debug(url.toString()) | ||
this.debug(`Deleting resource of type ${type} with id ${uuid}.`) | ||
@@ -516,3 +506,3 @@ const response = await this.fetch(url.toString(), { | ||
if (cached) { | ||
this._debug(`Returning cached resource ${type} with id ${uuid}`) | ||
this.debug(`Returning cached resource ${type} with id ${uuid}.`) | ||
@@ -532,4 +522,3 @@ const json = JSON.parse(cached) | ||
this._debug(`Fetching resource ${type} with id ${uuid}.`) | ||
this._debug(url.toString()) | ||
this.debug(`Fetching resource ${type} with id ${uuid}.`) | ||
@@ -716,2 +705,4 @@ const response = await this.fetch(url.toString(), { | ||
this.debug(`Fetching resource by path, ${path}.`) | ||
const response = await this.fetch(url.toString(), { | ||
@@ -769,4 +760,3 @@ method: "POST", | ||
this._debug(`Fetching resource collection of type ${type}`) | ||
this._debug(url.toString()) | ||
this.debug(`Fetching resource collection of type ${type}.`) | ||
@@ -941,2 +931,4 @@ const response = await this.fetch(url.toString(), { | ||
this.debug(`Fetching translated path, ${path}.`) | ||
const response = await this.fetch(url.toString(), { | ||
@@ -1022,2 +1014,4 @@ withAuth: options.withAuth, | ||
try { | ||
this.debug(`Fetching JSON:API index.`) | ||
const response = await this.fetch(url.toString(), { | ||
@@ -1077,8 +1071,42 @@ // As per https://www.drupal.org/node/2984034 /jsonapi is public. | ||
async validateDraftUrl(searchParams: URLSearchParams): Promise<Response> { | ||
const slug = searchParams.get("slug") | ||
this.debug(`Fetching draft url validation for ${slug}.`) | ||
// Fetch the headless CMS to check if the provided `slug` exists | ||
let response: Response | ||
try { | ||
// Validate the draft url. | ||
const validateUrl = this.buildUrl("/next/draft-url").toString() | ||
response = await this.fetch(validateUrl, { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify(Object.fromEntries(searchParams.entries())), | ||
}) | ||
} catch (error) { | ||
response = new Response(JSON.stringify({ message: error.message }), { | ||
status: 401, | ||
}) | ||
} | ||
this.debug( | ||
response.status !== 200 | ||
? `Could not validate slug, ${slug}` | ||
: `Validated slug, ${slug}` | ||
) | ||
return response | ||
} | ||
async preview( | ||
request?: NextApiRequest, | ||
response?: NextApiResponse, | ||
options?: PreviewOptions | ||
request: NextApiRequest, | ||
response: NextApiResponse, | ||
options?: Parameters<NextApiResponse["setDraftMode"]>[0] | ||
) { | ||
const { slug, resourceVersion, plugin } = request.query | ||
const { slug, resourceVersion, plugin, secret, scope, ...draftData } = | ||
request.query | ||
const useDraftMode = options?.enable | ||
@@ -1090,44 +1118,52 @@ try { | ||
// Validate the preview url. | ||
const validateUrl = this.buildUrl("/next/preview-url") | ||
const result = await this.fetch(validateUrl.toString(), { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify(request.query), | ||
}) | ||
const result = await this.validateDraftUrl( | ||
new URL(request.url, `http://${request.headers.host}`).searchParams | ||
) | ||
const validationPayload = await result.json() | ||
const previewData = { | ||
resourceVersion, | ||
plugin, | ||
...validationPayload, | ||
} | ||
if (!result.ok) { | ||
this.debug(`Draft url validation error: ${validationPayload.message}`) | ||
response.statusCode = result.status | ||
return response.json(validationPayload) | ||
} | ||
return response.json(await result.json()) | ||
// Optionally turn on draft mode. | ||
if (useDraftMode) { | ||
response.setDraftMode(options) | ||
} | ||
const validationPayload = await result.json() | ||
// Turns on preview mode and adds preview data to Next.js' static context. | ||
response.setPreviewData(previewData) | ||
response.setPreviewData({ | ||
resourceVersion, | ||
plugin, | ||
...validationPayload, | ||
}) | ||
// Fix issue with cookie. | ||
// See https://github.com/vercel/next.js/discussions/32238. | ||
// See https://github.com/vercel/next.js/blob/d895a50abbc8f91726daa2d7ebc22c58f58aabbb/packages/next/server/api-utils/node.ts#L504. | ||
if (this.forceIframeSameSiteCookie) { | ||
const previous = response.getHeader("Set-Cookie") as string[] | ||
previous.forEach((cookie, index) => { | ||
previous[index] = cookie.replace( | ||
"SameSite=Lax", | ||
"SameSite=None;Secure" | ||
) | ||
}) | ||
response.setHeader(`Set-Cookie`, previous) | ||
const cookies = (response.getHeader("Set-Cookie") as string[]).map( | ||
(cookie) => cookie.replace("SameSite=Lax", "SameSite=None; Secure") | ||
) | ||
if (useDraftMode) { | ||
// Adds preview data for use in app router pages. | ||
cookies.push( | ||
`${DRAFT_DATA_COOKIE_NAME}=${encodeURIComponent( | ||
JSON.stringify({ slug, resourceVersion, ...draftData }) | ||
)}; Path=/; HttpOnly; SameSite=None; Secure` | ||
) | ||
} | ||
response.setHeader("Set-Cookie", cookies) | ||
// We can safely redirect to the slug since this has been validated on the server. | ||
// We can safely redirect to the slug since this has been validated on the | ||
// server. | ||
response.writeHead(307, { Location: slug }) | ||
this.debug(`${useDraftMode ? "Draft" : "Preview"} mode enabled.`) | ||
return response.end() | ||
} catch (error) { | ||
this.debug(`Preview failed: ${error.message}`) | ||
return response.status(422).end() | ||
@@ -1137,2 +1173,18 @@ } | ||
async previewDisable(request: NextApiRequest, response: NextApiResponse) { | ||
// Disable both preview and draft modes. | ||
response.clearPreviewData() | ||
response.setDraftMode({ enable: false }) | ||
// Delete the draft data cookie. | ||
const cookies = response.getHeader("Set-Cookie") as string[] | ||
cookies.push( | ||
`${DRAFT_DATA_COOKIE_NAME}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=None; Secure` | ||
) | ||
response.setHeader("Set-Cookie", cookies) | ||
response.writeHead(307, { Location: "/" }) | ||
response.end() | ||
} | ||
async getMenu<T = DrupalMenuLinkContent>( | ||
@@ -1159,3 +1211,3 @@ name: string, | ||
if (cached) { | ||
this._debug(`Returning cached menu items for ${name}`) | ||
this.debug(`Returning cached menu items for ${name}.`) | ||
return JSON.parse(cached) | ||
@@ -1175,4 +1227,3 @@ } | ||
this._debug(`Fetching menu items for ${name}.`) | ||
this._debug(url.toString()) | ||
this.debug(`Fetching menu items for ${name}.`) | ||
@@ -1250,2 +1301,4 @@ const response = await this.fetch(url.toString(), { | ||
this.debug(`Fetching view, ${viewId}.${displayId}.`) | ||
const response = await this.fetch(url.toString(), { | ||
@@ -1291,2 +1344,4 @@ withAuth: options.withAuth, | ||
this.debug(`Fetching search index, ${name}.`) | ||
const response = await this.fetch(url.toString(), { | ||
@@ -1370,7 +1425,7 @@ withAuth: options.withAuth, | ||
) { | ||
this._debug(`Using existing access token.`) | ||
this.debug(`Using existing access token.`) | ||
return this._token | ||
} | ||
this._debug(`Fetching new access token.`) | ||
this.debug(`Fetching new access token.`) | ||
@@ -1384,3 +1439,3 @@ const basic = Buffer.from(`${clientId}:${clientSecret}`).toString("base64") | ||
this._debug(`Using scope: ${opts.scope}`) | ||
this.debug(`Using scope: ${opts.scope}`) | ||
} | ||
@@ -1404,4 +1459,2 @@ | ||
this._debug(result) | ||
this.token = result | ||
@@ -1454,9 +1507,9 @@ | ||
private _debug(message) { | ||
!!this.debug && this.logger.debug(message) | ||
debug(message) { | ||
this.isDebugEnabled && this.logger.debug(message) | ||
} | ||
// Error handling. | ||
// If throwErrors is enable, we show errors in the Next.js overlay. | ||
// Otherwise we log the errors even if debugging is turned off. | ||
// If throwErrors is enabled, we show errors in the Next.js overlay. | ||
// Otherwise, we log the errors even if debugging is turned off. | ||
// In production, errors are always logged never thrown. | ||
@@ -1463,0 +1516,0 @@ private throwError(error: Error) { |
import { cache } from "./get-cache" | ||
import { AccessToken } from "./types" | ||
import type { AccessToken } from "./types" | ||
@@ -4,0 +4,0 @@ const CACHE_KEY = "NEXT_DRUPAL_ACCESS_TOKEN" |
@@ -1,2 +0,3 @@ | ||
import { | ||
import { buildHeaders, buildUrl, deserialize } from "./utils" | ||
import type { | ||
AccessToken, | ||
@@ -6,3 +7,2 @@ DrupalMenuLinkContent, | ||
} from "./types" | ||
import { buildHeaders, buildUrl, deserialize } from "./utils" | ||
@@ -9,0 +9,0 @@ export async function getMenu<T extends DrupalMenuLinkContent>( |
@@ -1,4 +0,4 @@ | ||
import { GetStaticPathsContext, GetStaticPathsResult } from "next" | ||
import { getResourceCollection } from "./get-resource-collection" | ||
import { AccessToken, JsonApiParams, Locale } from "./types" | ||
import type { GetStaticPathsContext, GetStaticPathsResult } from "next" | ||
import type { AccessToken, JsonApiParams, Locale } from "./types" | ||
@@ -5,0 +5,0 @@ export async function getPathsFromContext( |
@@ -1,9 +0,2 @@ | ||
import { GetStaticPropsContext } from "next" | ||
import { | ||
AccessToken, | ||
JsonApiParams, | ||
JsonApiWithLocaleOptions, | ||
JsonApiResource, | ||
} from "./types" | ||
import { | ||
buildHeaders, | ||
@@ -14,2 +7,9 @@ buildUrl, | ||
} from "./utils" | ||
import type { GetStaticPropsContext } from "next" | ||
import type { | ||
AccessToken, | ||
JsonApiParams, | ||
JsonApiResource, | ||
JsonApiWithLocaleOptions, | ||
} from "./types" | ||
@@ -16,0 +16,0 @@ export async function getResourceCollection<T = JsonApiResource[]>( |
@@ -1,4 +0,4 @@ | ||
import { GetStaticPropsContext } from "next" | ||
import { translatePathFromContext } from "./translate-path" | ||
import { AccessToken } from "./types" | ||
import type { GetStaticPropsContext } from "next" | ||
import type { AccessToken } from "./types" | ||
@@ -5,0 +5,0 @@ export async function getResourceTypeFromContext( |
@@ -1,10 +0,3 @@ | ||
import { GetStaticPropsContext } from "next" | ||
import { stringify } from "qs" | ||
import { | ||
AccessToken, | ||
JsonApiParams, | ||
JsonApiWithLocaleOptions, | ||
JsonApiResource, | ||
} from "./types" | ||
import { | ||
buildHeaders, | ||
@@ -16,2 +9,9 @@ buildUrl, | ||
} from "./utils" | ||
import type { GetStaticPropsContext } from "next" | ||
import type { | ||
AccessToken, | ||
JsonApiParams, | ||
JsonApiResource, | ||
JsonApiWithLocaleOptions, | ||
} from "./types" | ||
@@ -18,0 +18,0 @@ export async function getResourceFromContext<T extends JsonApiResource>( |
@@ -1,4 +0,8 @@ | ||
import { GetStaticPropsContext } from "next" | ||
import { AccessToken, JsonApiResource, JsonApiWithLocaleOptions } from "./types" | ||
import { buildHeaders, buildUrl, deserialize } from "./utils" | ||
import type { GetStaticPropsContext } from "next" | ||
import type { | ||
AccessToken, | ||
JsonApiResource, | ||
JsonApiWithLocaleOptions, | ||
} from "./types" | ||
@@ -5,0 +9,0 @@ export async function getSearchIndex<T = JsonApiResource[]>( |
@@ -1,3 +0,3 @@ | ||
import { AccessToken, JsonApiWithLocaleOptions } from "./types" | ||
import { buildHeaders, buildUrl, deserialize } from "./utils" | ||
import type { AccessToken, JsonApiWithLocaleOptions } from "./types" | ||
@@ -4,0 +4,0 @@ export async function getView<T>( |
@@ -11,3 +11,2 @@ export * from "./get-access-token" | ||
export * from "./types" | ||
export * from "./use-menu" | ||
export * from "./translate-path" | ||
@@ -14,0 +13,0 @@ export { |
@@ -1,2 +0,2 @@ | ||
import { JsonApiError } from "./types" | ||
import type { JsonApiError } from "./types" | ||
@@ -3,0 +3,0 @@ export class JsonApiErrors extends Error { |
@@ -1,17 +0,22 @@ | ||
import { Logger } from "." | ||
import type { Logger } from "./types" | ||
export const LOG_MESSAGE_PREFIX = "[next-drupal][log]:" | ||
export const DEBUG_MESSAGE_PREFIX = "[next-drupal][debug]:" | ||
export const WARN_MESSAGE_PREFIX = "[next-drupal][warn]:" | ||
export const ERROR_MESSAGE_PREFIX = "[next-drupal][error]:" | ||
// Default logger. Uses console. | ||
export const logger: Logger = { | ||
log(message) { | ||
console.log(`[next-drupal][log]:`, message) | ||
console.log(LOG_MESSAGE_PREFIX, message) | ||
}, | ||
debug(message) { | ||
console.debug(`[next-drupal][debug]:`, message) | ||
console.debug(DEBUG_MESSAGE_PREFIX, message) | ||
}, | ||
warn(message) { | ||
console.warn(`[next-drupal][debug]:`, message) | ||
console.warn(WARN_MESSAGE_PREFIX, message) | ||
}, | ||
error(message) { | ||
console.error(`[next-drupal][error]:`, message) | ||
console.error(ERROR_MESSAGE_PREFIX, message) | ||
}, | ||
} |
@@ -1,4 +0,4 @@ | ||
import { NextApiRequest, NextApiResponse } from "next" | ||
import { getResourceByPath } from "./get-resource" | ||
import { JsonApiWithLocaleOptions } from "./types" | ||
import type { NextApiRequest, NextApiResponse } from "next" | ||
import type { JsonApiWithLocaleOptions } from "./types" | ||
@@ -5,0 +5,0 @@ interface PreviewOptions { |
@@ -1,4 +0,4 @@ | ||
import { GetStaticPropsContext } from "next" | ||
import { AccessToken, DrupalTranslatedPath } from "./types" | ||
import { buildHeaders, buildUrl, getPathFromContext } from "./utils" | ||
import type { GetStaticPropsContext } from "next" | ||
import type { AccessToken, DrupalTranslatedPath } from "./types" | ||
@@ -5,0 +5,0 @@ export async function translatePath( |
@@ -142,12 +142,2 @@ export type DrupalClientOptions = { | ||
accessTokenScope?: string | ||
/** | ||
* If set to true, the preview cookie will be set with SameSite=None,Secure. | ||
* | ||
* * **Default value**: `false` | ||
* * **Required**: *No* | ||
* | ||
* [Documentation](https://next-drupal.org/docs/client/configuration#forceiframesamesitecookie) | ||
*/ | ||
forceIframeSameSiteCookie?: boolean | ||
} | ||
@@ -414,9 +404,2 @@ | ||
export interface PreviewOptions { | ||
errorMessages?: { | ||
secret?: string | ||
slug?: string | ||
} | ||
} | ||
export type GetResourcePreviewUrlOptions = JsonApiWithLocaleOptions & { | ||
@@ -423,0 +406,0 @@ isVersionable?: boolean |
@@ -1,6 +0,6 @@ | ||
import { GetStaticPropsContext } from "next" | ||
import Jsona from "jsona" | ||
import { Jsona } from "jsona" | ||
import { stringify } from "qs" | ||
import { getAccessToken } from "./get-access-token" | ||
import { AccessToken, Locale } from "./types" | ||
import { stringify } from "qs" | ||
import type { GetStaticPropsContext } from "next" | ||
import type { AccessToken, Locale } from "./types" | ||
@@ -92,5 +92,4 @@ const JSONAPI_PREFIX = process.env.DRUPAL_JSONAPI_PREFIX || "/jsonapi" | ||
if (process.env.UNSTABLE_DRUPAL_ACCESS_TOKEN) { | ||
headers[ | ||
"Authorization" | ||
] = `Bearer ${process.env.UNSTABLE_DRUPAL_ACCESS_TOKEN}` | ||
headers["Authorization"] = | ||
`Bearer ${process.env.UNSTABLE_DRUPAL_ACCESS_TOKEN}` | ||
@@ -126,4 +125,4 @@ return headers | ||
: prefix | ||
? `${prefix}/${slug}` | ||
: slug | ||
? `${prefix}/${slug}` | ||
: slug | ||
} | ||
@@ -130,0 +129,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
1
-50%37
-35.09%23
-50%Yes
NaN500239
-55.54%50
-1.96%6641
-52.18%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated