From 8873d6fa5d6b3aeaf5e6481a8761c2866ac8cdd2 Mon Sep 17 00:00:00 2001 From: Catalin Pinte <1243434+cond0r@users.noreply.github.com> Date: Wed, 19 Oct 2022 09:34:59 +0300 Subject: [PATCH] Fix switchable runtimes --- packages/bigcommerce/package.json | 5 +- .../api/endpoints/checkout/get-checkout.ts | 17 +++- .../src/api/endpoints/login/login.ts | 3 +- .../src/api/endpoints/signup/signup.ts | 4 +- .../bigcommerce/src/api/operations/login.ts | 7 +- packages/commerce/src/api/endpoints/cart.ts | 4 +- .../src/api/endpoints/catalog/products.ts | 5 +- .../commerce/src/api/endpoints/checkout.ts | 6 +- .../src/api/endpoints/customer/address.ts | 3 +- .../src/api/endpoints/customer/card.ts | 7 +- .../src/api/endpoints/customer/index.ts | 2 +- packages/commerce/src/api/endpoints/index.ts | 76 +---------------- packages/commerce/src/api/endpoints/login.ts | 5 +- packages/commerce/src/api/endpoints/logout.ts | 3 +- packages/commerce/src/api/endpoints/signup.ts | 4 +- .../commerce/src/api/endpoints/wishlist.ts | 7 +- packages/commerce/src/api/index.ts | 3 - packages/commerce/src/api/operations.ts | 5 +- .../commerce/src/api/utils/edge-handler.ts | 82 +++++++++++++++++++ packages/commerce/src/api/utils/errors.ts | 7 +- packages/commerce/src/api/utils/index.ts | 49 +++++++++++ .../commerce/src/api/utils/node-handler.ts | 81 ++++++++++++++++++ .../commerce/src/api/utils/parse-output.ts | 11 --- packages/commerce/src/api/utils/types.ts | 4 +- .../src/api/utils/validate-handlers.ts | 5 +- .../commerce/src/api/utils/validate-method.ts | 12 +-- packages/commercejs/package.json | 3 +- .../api/endpoints/checkout/get-checkout.ts | 2 +- .../src/api/endpoints/checkout/index.ts | 5 +- .../src/api/endpoints/login/login.ts | 36 ++++---- .../commercejs/src/customer/use-customer.tsx | 20 ++--- .../src/api/endpoints/login/login.ts | 2 +- .../src/api/utils/cookie-handler.ts | 3 +- packages/saleor/src/api/operations/login.ts | 3 +- .../saleor/src/api/utils/is-allowed-method.ts | 22 ----- packages/shopify/src/api/operations/login.ts | 3 +- .../api/endpoints/checkout/get-checkout.ts | 3 +- packages/swell/src/api/cart/index.ts | 1 - packages/swell/src/api/catalog/index.ts | 1 - packages/swell/src/api/catalog/products.ts | 1 - packages/swell/src/api/customers/index.ts | 1 - packages/swell/src/api/customers/logout.ts | 1 - packages/swell/src/api/customers/signup.ts | 1 - packages/swell/src/api/operations/login.ts | 8 +- packages/swell/src/api/utils/fetch.ts | 2 - .../swell/src/api/utils/is-allowed-method.ts | 28 ------- packages/vendure/src/api/operations/login.ts | 7 +- pnpm-lock.yaml | 22 +++++ 48 files changed, 335 insertions(+), 257 deletions(-) create mode 100644 packages/commerce/src/api/utils/edge-handler.ts create mode 100644 packages/commerce/src/api/utils/node-handler.ts delete mode 100644 packages/commerce/src/api/utils/parse-output.ts delete mode 100644 packages/saleor/src/api/utils/is-allowed-method.ts delete mode 100644 packages/swell/src/api/cart/index.ts delete mode 100644 packages/swell/src/api/catalog/index.ts delete mode 100644 packages/swell/src/api/catalog/products.ts delete mode 100644 packages/swell/src/api/customers/index.ts delete mode 100644 packages/swell/src/api/customers/logout.ts delete mode 100644 packages/swell/src/api/customers/signup.ts delete mode 100644 packages/swell/src/api/utils/fetch.ts delete mode 100644 packages/swell/src/api/utils/is-allowed-method.ts diff --git a/packages/bigcommerce/package.json b/packages/bigcommerce/package.json index dadbe63ac..551d0ea68 100644 --- a/packages/bigcommerce/package.json +++ b/packages/bigcommerce/package.json @@ -53,7 +53,9 @@ "cookie": "^0.4.1", "immutability-helper": "^3.1.1", "js-cookie": "^3.0.1", - "lodash.debounce": "^4.0.8" + "jsonwebtoken": "^8.5.1", + "lodash.debounce": "^4.0.8", + "uuidv4": "^6.2.13" }, "peerDependencies": { "next": "^12", @@ -65,6 +67,7 @@ "@taskr/esnext": "^1.1.0", "@taskr/watch": "^1.1.0", "@types/cookie": "^0.4.1", + "@types/jsonwebtoken": "^8.5.7", "@types/lodash.debounce": "^4.0.6", "@types/node": "^17.0.8", "@types/node-fetch": "^2.6.2", diff --git a/packages/bigcommerce/src/api/endpoints/checkout/get-checkout.ts b/packages/bigcommerce/src/api/endpoints/checkout/get-checkout.ts index 2ee692ae8..64e2c91bf 100644 --- a/packages/bigcommerce/src/api/endpoints/checkout/get-checkout.ts +++ b/packages/bigcommerce/src/api/endpoints/checkout/get-checkout.ts @@ -1,8 +1,5 @@ import type { CheckoutEndpoint } from '.' import getCustomerId from '../../utils/get-customer-id' -import jwt from '@tsndr/cloudflare-worker-jwt' -import { uuid } from '@cfworker/uuid' -import { NextResponse } from 'next/server' const fullCheckout = true @@ -24,6 +21,7 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ method: 'POST', } ) + const customerId = customerToken && (await getCustomerId({ customerToken, config })) @@ -33,6 +31,17 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ return { redirectTo: data.checkout_url } } } else { + // Dynamically import uuid & jsonwebtoken based on the runtime + const { uuid } = + process.env.NEXT_RUNTIME === 'edge' + ? await import('@cfworker/uuid') + : await import('uuidv4') + + const jwt = + process.env.NEXT_RUNTIME === 'edge' + ? await import('@tsndr/cloudflare-worker-jwt') + : await import('jsonwebtoken') + const dateCreated = Math.round(new Date().getTime() / 1000) const payload = { iss: config.storeApiClientId, @@ -80,7 +89,7 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ ` - return new NextResponse(html, { + return new Response(html, { headers: { 'Content-Type': 'text/html', }, diff --git a/packages/bigcommerce/src/api/endpoints/login/login.ts b/packages/bigcommerce/src/api/endpoints/login/login.ts index ec763385e..3ad1419aa 100644 --- a/packages/bigcommerce/src/api/endpoints/login/login.ts +++ b/packages/bigcommerce/src/api/endpoints/login/login.ts @@ -1,6 +1,5 @@ import type { LoginEndpoint } from '.' -import { NextResponse } from 'next/server' import { FetcherError } from '@vercel/commerce/utils/errors' import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' @@ -12,7 +11,7 @@ const login: LoginEndpoint['handlers']['login'] = async ({ commerce, }) => { try { - const res = new NextResponse(null) + const res = new Response() await commerce.login({ variables: { email, password }, config, res }) return { status: res.status, diff --git a/packages/bigcommerce/src/api/endpoints/signup/signup.ts b/packages/bigcommerce/src/api/endpoints/signup/signup.ts index d40c6ceac..94499f96a 100644 --- a/packages/bigcommerce/src/api/endpoints/signup/signup.ts +++ b/packages/bigcommerce/src/api/endpoints/signup/signup.ts @@ -1,6 +1,4 @@ import type { SignupEndpoint } from '.' - -import { NextResponse } from 'next/server' import { CommerceAPIError } from '@vercel/commerce/api/utils/errors' import { BigcommerceApiError } from '../../utils/errors' @@ -39,7 +37,7 @@ const signup: SignupEndpoint['handlers']['signup'] = async ({ } } - const res = new NextResponse() + const res = new Response() // Login the customer right after creating it await commerce.login({ variables: { email, password }, res, config }) diff --git a/packages/bigcommerce/src/api/operations/login.ts b/packages/bigcommerce/src/api/operations/login.ts index c6e8e694d..dad3b48b1 100644 --- a/packages/bigcommerce/src/api/operations/login.ts +++ b/packages/bigcommerce/src/api/operations/login.ts @@ -7,7 +7,6 @@ import type { LoginMutation } from '../../../schema' import type { RecursivePartial } from '../utils/types' import concatHeader from '../utils/concat-cookie' import type { BigcommerceConfig, Provider } from '..' -import type { NextResponse } from 'next/server' export const loginMutation = /* GraphQL */ ` mutation login($email: String!, $password: String!) { @@ -23,14 +22,14 @@ export default function loginOperation({ async function login(opts: { variables: T['variables'] config?: BigcommerceConfig - res: NextResponse + res: Response }): Promise async function login( opts: { variables: T['variables'] config?: BigcommerceConfig - res: NextResponse + res: Response } & OperationOptions ): Promise @@ -42,7 +41,7 @@ export default function loginOperation({ }: { query?: string variables: T['variables'] - res: NextResponse + res: Response config?: BigcommerceConfig }): Promise { config = commerce.getConfig(config) diff --git a/packages/commerce/src/api/endpoints/cart.ts b/packages/commerce/src/api/endpoints/cart.ts index f567c1225..a1a33cc40 100644 --- a/packages/commerce/src/api/endpoints/cart.ts +++ b/packages/commerce/src/api/endpoints/cart.ts @@ -1,11 +1,9 @@ import type { GetAPISchema } from '..' import type { CartSchema } from '../../types/cart' -import parse from '../utils/parse-output' +import { parse, getInput } from '../utils' import validateHandlers from '../utils/validate-handlers' -import { getInput } from '../utils' - import { getCartBodySchema, addItemBodySchema, diff --git a/packages/commerce/src/api/endpoints/catalog/products.ts b/packages/commerce/src/api/endpoints/catalog/products.ts index d2c25a4a8..19d1723ee 100644 --- a/packages/commerce/src/api/endpoints/catalog/products.ts +++ b/packages/commerce/src/api/endpoints/catalog/products.ts @@ -6,7 +6,7 @@ import { searchProductBodySchema, searchProductsSchema, } from '../../../schemas/product' -import parse from '../../utils/parse-output' +import { parse } from '../../utils' const productsEndpoint: GetAPISchema< any, @@ -27,8 +27,7 @@ const productsEndpoint: GetAPISchema< const res = await handlers['getProducts']({ ...ctx, body }) res.headers = { - 'Cache-Control': - 'max-age=0, s-maxage=3600, stale-while-revalidate=60, public', + 'Cache-Control': 'max-age=0, s-maxage=3600, stale-while-revalidate, public', ...res.headers, } diff --git a/packages/commerce/src/api/endpoints/checkout.ts b/packages/commerce/src/api/endpoints/checkout.ts index 506ea8dc6..58542bdf2 100644 --- a/packages/commerce/src/api/endpoints/checkout.ts +++ b/packages/commerce/src/api/endpoints/checkout.ts @@ -7,10 +7,8 @@ import { submitCheckoutBodySchema, } from '../../schemas/checkout' +import { parse, getInput } from '../utils' import validateHandlers from '../utils/validate-handlers' -import parse from '../utils/parse-output' -import { z } from 'zod' -import { getInput } from '../utils' const checkoutEndpoint: GetAPISchema< any, @@ -31,7 +29,7 @@ const checkoutEndpoint: GetAPISchema< if (req.method === 'GET') { const body = getCheckoutBodySchema.parse({ ...input, cartId }) const res = await handlers['getCheckout']({ ...ctx, body }) - return parse(res, checkoutSchema.optional().or(z.string())) + return parse(res, checkoutSchema.optional()) } // Create checkout diff --git a/packages/commerce/src/api/endpoints/customer/address.ts b/packages/commerce/src/api/endpoints/customer/address.ts index 8b840344a..725c636ba 100644 --- a/packages/commerce/src/api/endpoints/customer/address.ts +++ b/packages/commerce/src/api/endpoints/customer/address.ts @@ -1,7 +1,6 @@ import type { CustomerAddressSchema } from '../../../types/customer/address' import type { GetAPISchema } from '../..' -import parse from '../../utils/parse-output' import validateHandlers from '../../utils/validate-handlers' import { @@ -11,7 +10,7 @@ import { updateAddressBodySchema, } from '../../../schemas/customer' -import { getInput } from '../../utils' +import { parse, getInput } from '../../utils' import { getCartBodySchema } from '../../../schemas/cart' // create a function that returns a function diff --git a/packages/commerce/src/api/endpoints/customer/card.ts b/packages/commerce/src/api/endpoints/customer/card.ts index 828c1b158..dbac43151 100644 --- a/packages/commerce/src/api/endpoints/customer/card.ts +++ b/packages/commerce/src/api/endpoints/customer/card.ts @@ -3,16 +3,15 @@ import type { GetAPISchema } from '../..' import { z } from 'zod' -import parse from '../../utils/parse-output' -import validateHandlers from '../../utils/validate-handlers' - import { cardSchema, addCardBodySchema, deleteCardBodySchema, updateCardBodySchema, } from '../../../schemas/customer' -import { getInput } from '../../utils' +import { parse, getInput } from '../../utils' + +import validateHandlers from '../../utils/validate-handlers' const customerCardEndpoint: GetAPISchema< any, diff --git a/packages/commerce/src/api/endpoints/customer/index.ts b/packages/commerce/src/api/endpoints/customer/index.ts index ab2eb64d6..92872c840 100644 --- a/packages/commerce/src/api/endpoints/customer/index.ts +++ b/packages/commerce/src/api/endpoints/customer/index.ts @@ -1,7 +1,7 @@ import type { CustomerSchema } from '../../../types/customer' import type { GetAPISchema } from '../..' -import parse from '../../utils/parse-output' +import { parse } from '../../utils' import validateHandlers from '../../utils/validate-handlers' import { customerSchema } from '../../../schemas/customer' diff --git a/packages/commerce/src/api/endpoints/index.ts b/packages/commerce/src/api/endpoints/index.ts index 5385c869f..d5ed49ce9 100644 --- a/packages/commerce/src/api/endpoints/index.ts +++ b/packages/commerce/src/api/endpoints/index.ts @@ -1,7 +1,5 @@ -import type { APIProvider, CommerceAPI, EndpointHandler } from '..' - -import { NextRequest, NextResponse } from 'next/server' -import { normalizeApiError } from '../utils/errors' +import edgeHandler from '../utils/edge-handler' +import nodeHandler from '../utils/node-handler' /** * Next.js Commerce API endpoints handler. Based on the path, it will call the corresponding endpoint handler, @@ -9,72 +7,4 @@ import { normalizeApiError } from '../utils/errors' * @param {CommerceAPI} commerce The Commerce API instance. * @param endpoints An object containing the handlers for each endpoint. */ -export default function createEndpoints

( - commerce: CommerceAPI

, - endpoints: Record) => EndpointHandler> -) { - const endpointsKeys = Object.keys(endpoints) - const handlers = endpointsKeys.reduce>( - (acc, endpoint) => - Object.assign(acc, { - [endpoint]: endpoints[endpoint](commerce), - }), - {} - ) - - return async (req: NextRequest) => { - try { - const { pathname } = new URL(req.url) - - /** - * Get the current endpoint by removing the leading and trailing slash & base path. - * Csovers: /api/commerce/cart & /checkout - */ - const endpoint = pathname - .replace('/api/commerce/', '') - .replace(/^\/|\/$/g, '') - - // Check if the handler for this path exists and return a 404 if it doesn't - if (!endpointsKeys.includes(endpoint)) { - throw new Error( - `Endpoint "${endpoint}" not implemented. Please use one of the available api endpoints: ${endpointsKeys.join( - ', ' - )}` - ) - } - - /** - * Executes the handler for this endpoint, provided by the provider, - * parses the input body and returns the parsed output - */ - const output = await handlers[endpoint](req) - - // If the output is a NextResponse, return it directly (E.g. checkout page & validateMethod util) - if (output instanceof NextResponse) { - return output - } - - // If the output contains a redirectTo property, return a NextResponse with the redirect - if (output.redirectTo) { - return NextResponse.redirect(output.redirectTo, { - headers: output.headers, - }) - } - - const { data = null, errors, status, headers } = output - - return NextResponse.json( - { data, errors }, - { - status, - headers, - } - ) - } catch (error) { - const output = normalizeApiError(error) - return output instanceof NextResponse - ? output - : NextResponse.json(output, { status: output.status ?? 500 }) - } - } -} +export default process.env.NEXT_RUNTIME === 'edge' ? edgeHandler : nodeHandler diff --git a/packages/commerce/src/api/endpoints/login.ts b/packages/commerce/src/api/endpoints/login.ts index d6640d7e6..565d0ab83 100644 --- a/packages/commerce/src/api/endpoints/login.ts +++ b/packages/commerce/src/api/endpoints/login.ts @@ -1,9 +1,9 @@ import type { GetAPISchema } from '..' import type { LoginSchema } from '../../types/login' +import { getInput } from '../utils' import validateHandlers from '../utils/validate-handlers' -import { getInput } from '../utils' import { loginBodySchema } from '../../schemas/auth' const loginEndpoint: GetAPISchema< @@ -16,9 +16,10 @@ const loginEndpoint: GetAPISchema< POST: handlers['login'], GET: handlers['login'], }) + const input = await getInput(req) const body = loginBodySchema.parse(input) - return await handlers['login']({ ...ctx, body }) + return handlers['login']({ ...ctx, body }) } export default loginEndpoint diff --git a/packages/commerce/src/api/endpoints/logout.ts b/packages/commerce/src/api/endpoints/logout.ts index c3a214eba..9f9fe23de 100644 --- a/packages/commerce/src/api/endpoints/logout.ts +++ b/packages/commerce/src/api/endpoints/logout.ts @@ -3,7 +3,6 @@ import type { LogoutSchema } from '../../types/logout' import { logoutBodySchema } from '../../schemas/auth' import validateHandlers from '../utils/validate-handlers' -import { normalizeApiError } from '../utils/errors' const logoutEndpoint: GetAPISchema< any, @@ -21,7 +20,7 @@ const logoutEndpoint: GetAPISchema< typeof redirectTo === 'string' ? { redirectTo } : {} ) - return await handlers['logout']({ ...ctx, body }) + return handlers['logout']({ ...ctx, body }) } export default logoutEndpoint diff --git a/packages/commerce/src/api/endpoints/signup.ts b/packages/commerce/src/api/endpoints/signup.ts index f77a2c05d..de25e7dc8 100644 --- a/packages/commerce/src/api/endpoints/signup.ts +++ b/packages/commerce/src/api/endpoints/signup.ts @@ -1,9 +1,9 @@ import type { GetAPISchema } from '..' import type { SignupSchema } from '../../types/signup' +import { getInput } from '../utils' import validateHandlers from '../utils/validate-handlers' -import { getInput } from '../utils' import { signupBodySchema } from '../../schemas/auth' const signupEndpoint: GetAPISchema< @@ -21,7 +21,7 @@ const signupEndpoint: GetAPISchema< const cartId = cookies.get(config.cartCookie) const body = signupBodySchema.parse({ ...input, cartId }) - return await handlers['signup']({ ...ctx, body }) + return handlers['signup']({ ...ctx, body }) } export default signupEndpoint diff --git a/packages/commerce/src/api/endpoints/wishlist.ts b/packages/commerce/src/api/endpoints/wishlist.ts index 94ceb86f3..6eec02ced 100644 --- a/packages/commerce/src/api/endpoints/wishlist.ts +++ b/packages/commerce/src/api/endpoints/wishlist.ts @@ -1,9 +1,8 @@ import type { GetAPISchema } from '..' import type { WishlistSchema } from '../../types/wishlist' -import validateHandlers from '../utils/validate-handlers' +import { parse, getInput } from '../utils' -import { getInput } from '../utils' import { wishlistSchema, addItemBodySchema, @@ -11,7 +10,7 @@ import { getWishlistBodySchema, } from '../../schemas/whishlist' -import parse from '../utils/parse-output' +import validateHandlers from '../utils/validate-handlers' const wishlistEndpoint: GetAPISchema< any, @@ -53,7 +52,7 @@ const wishlistEndpoint: GetAPISchema< output = await handlers['removeItem']({ ...ctx, body }) } - return output ? parse(output, wishlistSchema.optional()) : { status: 40 } + return output ? parse(output, wishlistSchema.optional()) : { status: 405 } } export default wishlistEndpoint diff --git a/packages/commerce/src/api/index.ts b/packages/commerce/src/api/index.ts index cd5d1e275..7cd52eb94 100644 --- a/packages/commerce/src/api/index.ts +++ b/packages/commerce/src/api/index.ts @@ -1,5 +1,4 @@ import type { NextRequest } from 'next/server' - import type { APIEndpoint, APIHandler, APIResponse } from './utils/types' import type { CartSchema } from '../types/cart' import type { CustomerSchema } from '../types/customer' @@ -12,7 +11,6 @@ import type { CheckoutSchema } from '../types/checkout' import type { CustomerCardSchema } from '../types/customer/card' import type { CustomerAddressSchema } from '../types/customer/address' -import { geolocation } from '@vercel/edge' import { withOperationCallback } from './utils/with-operation-callback' import { @@ -140,7 +138,6 @@ export function getEndpoint< config: cfg, handlers: context.handlers, options: context.options ?? {}, - geolocation: geolocation(req), }) } } diff --git a/packages/commerce/src/api/operations.ts b/packages/commerce/src/api/operations.ts index 52de3bee0..7d4350f4d 100644 --- a/packages/commerce/src/api/operations.ts +++ b/packages/commerce/src/api/operations.ts @@ -8,7 +8,6 @@ import type { GetProductOperation, } from '../types/product' import type { APIProvider, CommerceAPI } from '.' -import { NextResponse } from 'next/server' const noop = () => { throw new Error('Not implemented') @@ -44,14 +43,14 @@ export type Operations

= { (opts: { variables: T['variables'] config?: P['config'] - res: NextResponse + res: Response }): Promise ( opts: { variables: T['variables'] config?: P['config'] - res: NextResponse + res: Response } & OperationOptions ): Promise } diff --git a/packages/commerce/src/api/utils/edge-handler.ts b/packages/commerce/src/api/utils/edge-handler.ts new file mode 100644 index 000000000..165d0bbcc --- /dev/null +++ b/packages/commerce/src/api/utils/edge-handler.ts @@ -0,0 +1,82 @@ +import type { APIProvider, CommerceAPI, EndpointHandler } from '..' +import type { NextRequest } from 'next/server' + +import { normalizeApiError } from './errors' + +export default function edgeApi

( + commerce: CommerceAPI

, + endpoints: Record) => EndpointHandler> +) { + const endpointsKeys = Object.keys(endpoints) + + const handlers = endpointsKeys.reduce>( + (acc, endpoint) => + Object.assign(acc, { + [endpoint]: endpoints[endpoint](commerce), + }), + {} + ) + + return async (req: NextRequest) => { + try { + const { pathname } = new URL(req.url) + + /** + * Get the current endpoint by removing the leading and trailing slash & base path. + * Csovers: /api/commerce/cart & /checkout + */ + const endpoint = pathname + .replace('/api/commerce/', '') + .replace(/^\/|\/$/g, '') + + // Check if the handler for this path exists and return a 404 if it doesn't + if (!endpointsKeys.includes(endpoint)) { + throw new Error( + `Endpoint "${endpoint}" not implemented. Please use one of the available api endpoints: ${endpointsKeys.join( + ', ' + )}` + ) + } + + /** + * Executes the handler for this endpoint, provided by the provider, + * parses the input body and returns the parsed output + */ + const output = await handlers[endpoint](req) + + // If the output is a Response, return it directly (E.g. checkout page & validateMethod util) + if (output instanceof Response) { + return output + } + + const { headers } = output + + // If the output contains a redirectTo property, return a Response with the redirect + if (output.redirectTo) { + return new Response(null, { + status: 302, + headers: { + ...headers, + Location: output.redirectTo, + }, + }) + } + + // Otherwise, return a JSON response with the output data or errors returned by the handler + const { data = null, errors, status } = output + return new Response(JSON.stringify({ data, errors }), { + status, + headers, + }) + } catch (error) { + const output = normalizeApiError(error) + if (output instanceof Response) { + return output + } + const { status = 500, ...rest } = output + return output instanceof Response + ? output + : new Response(JSON.stringify(rest), { status }) + } + } +} diff --git a/packages/commerce/src/api/utils/errors.ts b/packages/commerce/src/api/utils/errors.ts index 3f244b44a..5d54f8a75 100644 --- a/packages/commerce/src/api/utils/errors.ts +++ b/packages/commerce/src/api/utils/errors.ts @@ -1,13 +1,14 @@ -import { NextRequest, NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + import { CommerceError } from '../../utils/errors' import { ZodError } from 'zod' export class CommerceAPIResponseError extends Error { status: number - res: NextResponse + res: Response data: any - constructor(msg: string, res: NextResponse, data?: any) { + constructor(msg: string, res: Response, data?: any) { super(msg) this.name = 'CommerceApiError' this.status = res.status diff --git a/packages/commerce/src/api/utils/index.ts b/packages/commerce/src/api/utils/index.ts index def9ef29c..5f316c2b6 100644 --- a/packages/commerce/src/api/utils/index.ts +++ b/packages/commerce/src/api/utils/index.ts @@ -1,3 +1,52 @@ +import type { NextApiRequest } from 'next' +import type { ZodSchema } from 'zod' +import type { APIResponse } from './types' + import { NextRequest } from 'next/server' +/** + * Parses the output data of the API handler and returns a valid APIResponse + * or throws an error if the data is invalid. + * @param res APIResponse + * @param parser ZodSchema + */ +export const parse = (res: APIResponse, parser: ZodSchema) => { + if (res.data) { + res.data = parser.parse(res.data) + } + return res +} + +/** + * Returns the body of the request as a JSON object. + * @param req NextRequest + */ export const getInput = (req: NextRequest) => req.json().catch(() => ({})) + +/** + * Convert NextApiRequest to NextRequest + * @param req NextApiRequest + * @param path string + */ +export const transformRequest = (req: NextApiRequest, path: string) => { + let body + const headers = new Headers() + + for (let i = 0; i < req.rawHeaders.length; i += 2) { + headers.append(req.rawHeaders[i], req.rawHeaders[i + 1]) + } + + if ( + req.method === 'POST' || + req.method === 'PUT' || + req.method === 'DELETE' + ) { + body = JSON.stringify(req.body) + } + + return new NextRequest(`https://${req.headers.host}/api/commerce/${path}`, { + headers, + method: req.method, + body, + }) +} diff --git a/packages/commerce/src/api/utils/node-handler.ts b/packages/commerce/src/api/utils/node-handler.ts new file mode 100644 index 000000000..ca48e1691 --- /dev/null +++ b/packages/commerce/src/api/utils/node-handler.ts @@ -0,0 +1,81 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import type { APIProvider, CommerceAPI, EndpointHandler } from '..' + +import { normalizeApiError } from './errors' +import { transformRequest } from '.' + +export default function nodeApi

( + commerce: CommerceAPI

, + endpoints: { + [key: string]: (commerce: CommerceAPI

) => EndpointHandler + } +) { + const paths = Object.keys(endpoints) + + const handlers = paths.reduce>( + (acc, path) => + Object.assign(acc, { + [path]: endpoints[path](commerce), + }), + {} + ) + + return async (req: NextApiRequest, res: NextApiResponse) => { + try { + if (!req.query.commerce) { + throw new Error( + 'Invalid configuration. Please make sure that the /pages/api/commerce/[[...commerce]].ts route is configured correctly, and it passes the commerce instance.' + ) + } + + /** + * Get the url path + */ + const path = Array.isArray(req.query.commerce) + ? req.query.commerce.join('/') + : req.query.commerce + + // Check if the handler for this path exists and return a 404 if it doesn't + if (!paths.includes(path)) { + throw new Error( + `Endpoint handler not implemented. Please use one of the available api endpoints: ${paths.join( + ', ' + )}` + ) + } + + const newReq = transformRequest(req, path) + const output = await handlers[path](newReq) + + if (output instanceof Response) { + return res.end(output.body) + } + + const { status, errors, data, redirectTo, headers } = output + + if (headers) { + for (const [key, value] of Object.entries(headers)) { + res.setHeader(key, value) + } + } + + if (redirectTo) { + return res.redirect(redirectTo) + } + + res.status(status || 200).json({ + data, + errors, + }) + } catch (error) { + const output = normalizeApiError(error) + + if (output instanceof Response) { + return res.end(output.body) + } + + const { status = 500, ...rest } = normalizeApiError(error) + res.status(status).json(rest) + } + } +} diff --git a/packages/commerce/src/api/utils/parse-output.ts b/packages/commerce/src/api/utils/parse-output.ts deleted file mode 100644 index 677ae5016..000000000 --- a/packages/commerce/src/api/utils/parse-output.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { ZodSchema } from 'zod' -import { APIResponse } from './types' - -export const parseOutput = (res: APIResponse, parser: ZodSchema) => { - if (res.data) { - res.data = parser.parse(res.data) - } - return res -} - -export default parseOutput diff --git a/packages/commerce/src/api/utils/types.ts b/packages/commerce/src/api/utils/types.ts index a12438e24..b9946f4ae 100644 --- a/packages/commerce/src/api/utils/types.ts +++ b/packages/commerce/src/api/utils/types.ts @@ -1,5 +1,4 @@ -import { NextRequest } from 'next/server' -import type { Geo } from '@vercel/edge' +import type { NextRequest } from 'next/server' import type { CommerceAPI } from '..' export type ErrorData = { message: string; code?: string } @@ -27,7 +26,6 @@ export type APIHandlerContext< config: C['provider']['config'] handlers: H options: Options - geolocation: Geo } export type APIHandler< diff --git a/packages/commerce/src/api/utils/validate-handlers.ts b/packages/commerce/src/api/utils/validate-handlers.ts index 876c91c4a..3e4ed2ab7 100644 --- a/packages/commerce/src/api/utils/validate-handlers.ts +++ b/packages/commerce/src/api/utils/validate-handlers.ts @@ -1,13 +1,10 @@ import type { NextRequest } from 'next/server' - +import type { APIHandler } from './types' import validateMethod, { HTTP_METHODS } from './validate-method' -import { APIHandler } from './types' - /** * Validates the request method and throws an error if it's not allowed, or if the handler is not implemented. * and stops the execution of the handler. * @param req The request object. - * @param res The response object. * @param allowedOperations An object containing the handlers for each method. * @throws Error when the method is not allowed or the handler is not implemented. */ diff --git a/packages/commerce/src/api/utils/validate-method.ts b/packages/commerce/src/api/utils/validate-method.ts index c08263247..945427131 100644 --- a/packages/commerce/src/api/utils/validate-method.ts +++ b/packages/commerce/src/api/utils/validate-method.ts @@ -1,9 +1,9 @@ -import { NextRequest, NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' import { CommerceAPIResponseError } from './errors' export type HTTP_METHODS = 'OPTIONS' | 'GET' | 'POST' | 'PUT' | 'DELETE' -export default function isAllowedMethod( +export default function validateMethod( req: NextRequest, allowedMethods: HTTP_METHODS[] ) { @@ -14,15 +14,15 @@ export default function isAllowedMethod( if (!req.method || !methods.includes(req.method)) { throw new CommerceAPIResponseError( `The HTTP ${req.method} method is not supported at this route.`, - NextResponse.json( - { + new Response( + JSON.stringify({ errors: [ { code: 'invalid_method', message: `The HTTP ${req.method} method is not supported at this route.`, }, ], - }, + }), { status: 405, headers: { @@ -36,7 +36,7 @@ export default function isAllowedMethod( if (req.method === 'OPTIONS') { throw new CommerceAPIResponseError( 'This is a CORS preflight request.', - new NextResponse(null, { + new Response(null, { status: 204, headers: { Allow: methods.join(', '), diff --git a/packages/commercejs/package.json b/packages/commercejs/package.json index 0ef28bb9b..c3e28f123 100644 --- a/packages/commercejs/package.json +++ b/packages/commercejs/package.json @@ -51,7 +51,7 @@ "@vercel/commerce": "workspace:*", "cookie": "^0.4.1", "js-cookie": "^3.0.1", - "@tsndr/cloudflare-worker-jwt": "^2.1.0", + "jsonwebtoken": "^8.5.1", "lodash.debounce": "^4.0.8" }, "peerDependencies": { @@ -66,6 +66,7 @@ "@types/chec__commerce.js": "^2.8.4", "@types/cookie": "^0.4.1", "@types/js-cookie": "^3.0.2", + "@types/jsonwebtoken": "^8.5.7", "@types/lodash.debounce": "^4.0.6", "@types/node": "^17.0.8", "@types/react": "^18.0.14", diff --git a/packages/commercejs/src/api/endpoints/checkout/get-checkout.ts b/packages/commercejs/src/api/endpoints/checkout/get-checkout.ts index cf5fee0c1..f6923f0e3 100644 --- a/packages/commercejs/src/api/endpoints/checkout/get-checkout.ts +++ b/packages/commercejs/src/api/endpoints/checkout/get-checkout.ts @@ -1,3 +1,3 @@ -export default function getCheckout(...args: any[]) { +export default function getCheckout(..._args: any[]) { return Promise.resolve({ data: null }) } diff --git a/packages/commercejs/src/api/endpoints/checkout/index.ts b/packages/commercejs/src/api/endpoints/checkout/index.ts index 6a4eb4864..1536c130a 100644 --- a/packages/commercejs/src/api/endpoints/checkout/index.ts +++ b/packages/commercejs/src/api/endpoints/checkout/index.ts @@ -3,16 +3,15 @@ import checkoutEndpoint from '@vercel/commerce/api/endpoints/checkout' import type { CheckoutSchema } from '@vercel/commerce/types/checkout' import type { CommercejsAPI } from '../..' -import submitCheckout from './submit-checkout' import getCheckout from './get-checkout' +import submitCheckout from './submit-checkout' export type CheckoutAPI = GetAPISchema - export type CheckoutEndpoint = CheckoutAPI['endpoint'] export const handlers: CheckoutEndpoint['handlers'] = { - submitCheckout, getCheckout, + submitCheckout, } const checkoutApi = createEndpoint({ diff --git a/packages/commercejs/src/api/endpoints/login/login.ts b/packages/commercejs/src/api/endpoints/login/login.ts index 49d05d63a..c2abbc5cb 100644 --- a/packages/commercejs/src/api/endpoints/login/login.ts +++ b/packages/commercejs/src/api/endpoints/login/login.ts @@ -1,7 +1,9 @@ +import type { LoginEndpoint } from '.' + import { serialize } from 'cookie' + import sdkFetcherFunction from '../../utils/sdk-fetch' import { getDeploymentUrl } from '../../../utils/get-deployment-url' -import type { LoginEndpoint } from '.' const login: LoginEndpoint['handlers']['login'] = async ({ req, @@ -10,27 +12,23 @@ const login: LoginEndpoint['handlers']['login'] = async ({ const sdkFetcher: typeof sdkFetcherFunction = sdkFetch const redirectUrl = getDeploymentUrl() const { searchParams } = new URL(req.url) - try { - const loginToken = searchParams.get('token') + const loginToken = searchParams.get('token') - if (!loginToken) { - return { redirectTo: redirectUrl } - } - const { jwt } = await sdkFetcher('customer', 'getToken', loginToken, false) - - return { - redirectTo: redirectUrl, - headers: { - 'Set-Cookie': serialize(customerCookie, jwt, { - secure: process.env.NODE_ENV === 'production', - maxAge: 60 * 60 * 24, - path: '/', - }), - }, - } - } catch { + if (!loginToken) { return { redirectTo: redirectUrl } } + + const { jwt } = await sdkFetcher('customer', 'getToken', loginToken, false) + + return { + headers: { + 'Set-Cookie': serialize(customerCookie, jwt, { + secure: process.env.NODE_ENV === 'production', + maxAge: 60 * 60 * 24, + path: '/', + }), + }, + } } export default login diff --git a/packages/commercejs/src/customer/use-customer.tsx b/packages/commercejs/src/customer/use-customer.tsx index 9241d1c36..bfcf79193 100644 --- a/packages/commercejs/src/customer/use-customer.tsx +++ b/packages/commercejs/src/customer/use-customer.tsx @@ -1,16 +1,14 @@ -import Cookies from 'js-cookie' -import { - decode, - type JwtData as CoreJwtData, -} from '@tsndr/cloudflare-worker-jwt' -import { SWRHook } from '@vercel/commerce/utils/types' -import useCustomer, { - UseCustomer, -} from '@vercel/commerce/customer/use-customer' -import { CUSTOMER_COOKIE, API_URL } from '../constants' +import type { SWRHook } from '@vercel/commerce/utils/types' import type { CustomerHook } from '@vercel/commerce/types/customer' -type JwtData = CoreJwtData & { +import Cookies from 'js-cookie' +import { decode, type JwtPayload } from 'jsonwebtoken' +import useCustomer, { + type UseCustomer, +} from '@vercel/commerce/customer/use-customer' +import { CUSTOMER_COOKIE, API_URL } from '../constants' + +type JwtData = JwtPayload & { cid: string } diff --git a/packages/kibocommerce/src/api/endpoints/login/login.ts b/packages/kibocommerce/src/api/endpoints/login/login.ts index 0f1bc6b32..f88f8d2a2 100644 --- a/packages/kibocommerce/src/api/endpoints/login/login.ts +++ b/packages/kibocommerce/src/api/endpoints/login/login.ts @@ -30,7 +30,7 @@ const login: LoginEndpoint['handlers']['login'] = async ({ token.accessTokenExpiration ? { expires: cookieExpirationDate } : {} ) - return { data: response, headers: { 'Set-Cookie': authCookie } } + return { data: null, headers: { 'Set-Cookie': authCookie } } } catch (error) { // Check if the email and password didn't match an existing account if ( diff --git a/packages/kibocommerce/src/api/utils/cookie-handler.ts b/packages/kibocommerce/src/api/utils/cookie-handler.ts index 854e57ed3..7b2fa4f99 100644 --- a/packages/kibocommerce/src/api/utils/cookie-handler.ts +++ b/packages/kibocommerce/src/api/utils/cookie-handler.ts @@ -1,10 +1,9 @@ import { KiboCommerceConfig } from './../index' import { getCookieExpirationDate } from '../../lib/get-cookie-expiration-date' import { prepareSetCookie } from '../../lib/prepare-set-cookie' -import { setCookies } from '../../lib/set-cookie' import getAnonymousShopperToken from './get-anonymous-shopper-token' -import { NextRequest, NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' const parseCookie = (cookieValue?: any) => { return cookieValue diff --git a/packages/saleor/src/api/operations/login.ts b/packages/saleor/src/api/operations/login.ts index e19dfd49c..eac85e90b 100644 --- a/packages/saleor/src/api/operations/login.ts +++ b/packages/saleor/src/api/operations/login.ts @@ -3,7 +3,6 @@ import type { Provider, SaleorConfig } from '..' import { throwUserErrors } from '../../utils' import * as Mutation from '../../utils/mutations' -import type { NextResponse } from 'next/server' export default function loginOperation({ commerce, @@ -15,7 +14,7 @@ export default function loginOperation({ }: { query?: string variables: any - res: NextResponse + res: Response config?: SaleorConfig }): Promise { config = commerce.getConfig(config) diff --git a/packages/saleor/src/api/utils/is-allowed-method.ts b/packages/saleor/src/api/utils/is-allowed-method.ts deleted file mode 100644 index cbaab2251..000000000 --- a/packages/saleor/src/api/utils/is-allowed-method.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next' - -export default function isAllowedMethod(req: NextApiRequest, res: NextApiResponse, allowedMethods: string[]) { - const methods = allowedMethods.includes('OPTIONS') ? allowedMethods : [...allowedMethods, 'OPTIONS'] - - if (!req.method || !methods.includes(req.method)) { - res.status(405) - res.setHeader('Allow', methods.join(', ')) - res.end() - return false - } - - if (req.method === 'OPTIONS') { - res.status(200) - res.setHeader('Allow', methods.join(', ')) - res.setHeader('Content-Length', '0') - res.end() - return false - } - - return true -} diff --git a/packages/shopify/src/api/operations/login.ts b/packages/shopify/src/api/operations/login.ts index d66c5a28c..3f4419a4a 100644 --- a/packages/shopify/src/api/operations/login.ts +++ b/packages/shopify/src/api/operations/login.ts @@ -7,7 +7,6 @@ import { throwUserErrors, } from '../../utils' import { CustomerAccessTokenCreateMutation } from '../../../schema' -import type { NextResponse } from 'next/server' export default function loginOperation({ commerce, @@ -19,7 +18,7 @@ export default function loginOperation({ }: { query?: string variables: T['variables'] - res: NextResponse + res: Response config?: ShopifyConfig }): Promise { config = commerce.getConfig(config) diff --git a/packages/spree/src/api/endpoints/checkout/get-checkout.ts b/packages/spree/src/api/endpoints/checkout/get-checkout.ts index 1492680ea..a99c774ab 100644 --- a/packages/spree/src/api/endpoints/checkout/get-checkout.ts +++ b/packages/spree/src/api/endpoints/checkout/get-checkout.ts @@ -1,4 +1,3 @@ -import { NextResponse } from 'next/server' import type { CheckoutEndpoint } from '.' const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ @@ -27,7 +26,7 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({ ` - return new NextResponse(html, { + return new Response(html, { headers: { 'content-type': 'text/html', }, diff --git a/packages/swell/src/api/cart/index.ts b/packages/swell/src/api/cart/index.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/cart/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/catalog/index.ts b/packages/swell/src/api/catalog/index.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/catalog/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/catalog/products.ts b/packages/swell/src/api/catalog/products.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/catalog/products.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/customers/index.ts b/packages/swell/src/api/customers/index.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/customers/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/customers/logout.ts b/packages/swell/src/api/customers/logout.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/customers/logout.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/customers/signup.ts b/packages/swell/src/api/customers/signup.ts deleted file mode 100644 index ea9b101e1..000000000 --- a/packages/swell/src/api/customers/signup.ts +++ /dev/null @@ -1 +0,0 @@ -export default function () {} diff --git a/packages/swell/src/api/operations/login.ts b/packages/swell/src/api/operations/login.ts index b6fb11e87..60508073c 100644 --- a/packages/swell/src/api/operations/login.ts +++ b/packages/swell/src/api/operations/login.ts @@ -3,7 +3,7 @@ import type { OperationOptions, } from '@vercel/commerce/api/operations' import type { LoginOperation } from '@vercel/commerce/types/login' -import type { NextResponse } from 'next/server' + import { Provider, SwellConfig } from '..' export default function loginOperation({ @@ -12,14 +12,14 @@ export default function loginOperation({ async function login(opts: { variables: T['variables'] config?: Partial - res: NextResponse + res: Response }): Promise async function login( opts: { variables: T['variables'] config?: Partial - res: NextResponse + res: Response } & OperationOptions ): Promise @@ -30,7 +30,7 @@ export default function loginOperation({ }: { query?: string variables: T['variables'] - res: NextResponse + res: Response config?: Partial }): Promise { const config = commerce.getConfig(cfg) diff --git a/packages/swell/src/api/utils/fetch.ts b/packages/swell/src/api/utils/fetch.ts deleted file mode 100644 index 0b8367102..000000000 --- a/packages/swell/src/api/utils/fetch.ts +++ /dev/null @@ -1,2 +0,0 @@ -import zeitFetch from '@vercel/fetch' -export default zeitFetch() diff --git a/packages/swell/src/api/utils/is-allowed-method.ts b/packages/swell/src/api/utils/is-allowed-method.ts deleted file mode 100644 index 78bbba568..000000000 --- a/packages/swell/src/api/utils/is-allowed-method.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next' - -export default function isAllowedMethod( - req: NextApiRequest, - res: NextApiResponse, - allowedMethods: string[] -) { - const methods = allowedMethods.includes('OPTIONS') - ? allowedMethods - : [...allowedMethods, 'OPTIONS'] - - if (!req.method || !methods.includes(req.method)) { - res.status(405) - res.setHeader('Allow', methods.join(', ')) - res.end() - return false - } - - if (req.method === 'OPTIONS') { - res.status(200) - res.setHeader('Allow', methods.join(', ')) - res.setHeader('Content-Length', '0') - res.end() - return false - } - - return true -} diff --git a/packages/vendure/src/api/operations/login.ts b/packages/vendure/src/api/operations/login.ts index abddca1c7..bcfa471ab 100644 --- a/packages/vendure/src/api/operations/login.ts +++ b/packages/vendure/src/api/operations/login.ts @@ -7,7 +7,6 @@ import type { LoginOperation } from '@vercel/commerce/types/login' import type { LoginMutation } from '../../../schema' import { Provider, VendureConfig } from '..' import { loginMutation } from '../../utils/mutations/log-in-mutation' -import type { NextResponse } from 'next/server' export default function loginOperation({ commerce, @@ -15,14 +14,14 @@ export default function loginOperation({ async function login(opts: { variables: T['variables'] config?: Partial - res: NextResponse + res: Response }): Promise async function login( opts: { variables: T['variables'] config?: Partial - res: NextResponse + res: Response } & OperationOptions ): Promise @@ -34,7 +33,7 @@ export default function loginOperation({ }: { query?: string variables: T['variables'] - res: NextResponse + res: Response config?: Partial }): Promise { const config = commerce.getConfig(cfg) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53e8a196c..d9b906df0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,7 @@ importers: '@taskr/watch': ^1.1.0 '@tsndr/cloudflare-worker-jwt': ^2.1.0 '@types/cookie': ^0.4.1 + '@types/jsonwebtoken': ^8.5.7 '@types/lodash.debounce': ^4.0.6 '@types/node': ^17.0.8 '@types/node-fetch': ^2.6.2 @@ -28,6 +29,7 @@ importers: cookie: ^0.4.1 immutability-helper: ^3.1.1 js-cookie: ^3.0.1 + jsonwebtoken: ^8.5.1 lint-staged: ^12.1.7 lodash.debounce: ^4.0.8 next: ^12.0.8 @@ -37,6 +39,7 @@ importers: taskr: ^1.1.0 taskr-swc: ^0.0.1 typescript: ^4.7.4 + uuidv4: ^6.2.13 dependencies: '@cfworker/uuid': 1.12.4 '@tsndr/cloudflare-worker-jwt': 2.1.0 @@ -44,12 +47,15 @@ importers: cookie: 0.4.2 immutability-helper: 3.1.1 js-cookie: 3.0.1 + jsonwebtoken: 8.5.1 lodash.debounce: 4.0.8 + uuidv4: 6.2.13 devDependencies: '@taskr/clear': 1.1.0 '@taskr/esnext': 1.1.0 '@taskr/watch': 1.1.0 '@types/cookie': 0.4.1 + '@types/jsonwebtoken': 8.5.9 '@types/lodash.debounce': 4.0.7 '@types/node': 17.0.45 '@types/node-fetch': 2.6.2 @@ -3193,6 +3199,10 @@ packages: /@types/scheduler/0.16.2: resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} + /@types/uuid/8.3.4: + resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} + dev: false + /@types/ws/8.5.3: resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} dependencies: @@ -9473,6 +9483,18 @@ packages: lodash.isplainobject: 4.0.6 dev: false + /uuid/8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + + /uuidv4/6.2.13: + resolution: {integrity: sha512-AXyzMjazYB3ovL3q051VLH06Ixj//Knx7QnUSi1T//Ie3io6CpsPu9nVMOx5MoLWh6xV0B9J0hIaxungxXUbPQ==} + dependencies: + '@types/uuid': 8.3.4 + uuid: 8.3.2 + dev: false + /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true