From 045f82bbe87d67569383c1b5f735fb001fb505ea Mon Sep 17 00:00:00 2001
From: Catalin Pinte <1243434+cond0r@users.noreply.github.com>
Date: Wed, 14 Dec 2022 14:49:23 +0200
Subject: [PATCH] Fix auth & wishlist
---
.../customer/get-logged-in-customer.ts | 2 +-
.../src/api/endpoints/login/login.ts | 14 ++++----
.../src/api/endpoints/signup/signup.ts | 36 +++++++++----------
.../src/api/endpoints/wishlist/add-item.ts | 7 ++--
.../api/endpoints/wishlist/get-wishlist.ts | 7 ++--
.../src/api/endpoints/wishlist/remove-item.ts | 12 ++++---
.../api/operations/get-customer-wishlist.ts | 32 +++++++++--------
.../bigcommerce/src/api/operations/login.ts | 20 ++++-------
.../src/api/utils/fetch-graphql-api.ts | 4 +--
.../src/api/utils/get-customer-id.ts | 2 +-
packages/bigcommerce/src/api/utils/types.ts | 12 +++++++
packages/bigcommerce/src/lib/normalize.ts | 15 ++++++++
.../commerce/src/api/endpoints/wishlist.ts | 2 +-
packages/commerce/src/api/utils/index.ts | 4 ++-
packages/commerce/src/schemas/customer.ts | 18 +++++-----
packages/commerce/src/schemas/whishlist.ts | 10 +++---
packages/commerce/src/types/login.ts | 2 +-
site/components/auth/LoginView.tsx | 5 ++-
site/components/auth/SignUpView.tsx | 4 +--
.../CustomerMenuContent.module.css | 4 +--
.../CustomerMenuContent.tsx | 8 +----
.../common/UserNav/UserNav.module.css | 11 +++---
site/components/common/UserNav/UserNav.tsx | 13 +++----
.../WishlistButton/WishlistButton.tsx | 4 +--
.../wishlist/WishlistCard/WishlistCard.tsx | 1 +
site/pages/wishlist.tsx | 12 +++----
site/tsconfig.json | 4 +--
27 files changed, 139 insertions(+), 126 deletions(-)
diff --git a/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts b/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts
index 9076ab584..e4f7e7747 100644
--- a/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts
+++ b/packages/bigcommerce/src/api/endpoints/customer/get-logged-in-customer.ts
@@ -34,7 +34,7 @@ const getLoggedInCustomer: CustomerEndpoint['handlers']['getLoggedInCustomer'] =
getLoggedInCustomerQuery,
undefined,
{
- 'Set-Cookie': `${config.customerCookie}=${token}`,
+ cookie: `${config.customerCookie}=${token}`,
}
)
const { customer } = data
diff --git a/packages/bigcommerce/src/api/endpoints/login/login.ts b/packages/bigcommerce/src/api/endpoints/login/login.ts
index 3ad1419aa..dda5f6bf7 100644
--- a/packages/bigcommerce/src/api/endpoints/login/login.ts
+++ b/packages/bigcommerce/src/api/endpoints/login/login.ts
@@ -11,12 +11,12 @@ const login: LoginEndpoint['handlers']['login'] = async ({
commerce,
}) => {
try {
- const res = new Response()
- await commerce.login({ variables: { email, password }, config, res })
- return {
- status: res.status,
- headers: res.headers,
- }
+ const response = await commerce.login({
+ variables: { email, password },
+ config,
+ })
+
+ return response
} catch (error) {
// Check if the email and password didn't match an existing account
if (error instanceof FetcherError) {
@@ -24,7 +24,7 @@ const login: LoginEndpoint['handlers']['login'] = async ({
invalidCredentials.test(error.message)
? 'Cannot find an account that matches the provided credentials'
: error.message,
- { status: error.status || 401 }
+ { status: 401 }
)
} else {
throw error
diff --git a/packages/bigcommerce/src/api/endpoints/signup/signup.ts b/packages/bigcommerce/src/api/endpoints/signup/signup.ts
index 94499f96a..2aca4c93b 100644
--- a/packages/bigcommerce/src/api/endpoints/signup/signup.ts
+++ b/packages/bigcommerce/src/api/endpoints/signup/signup.ts
@@ -1,6 +1,5 @@
import type { SignupEndpoint } from '.'
import { CommerceAPIError } from '@vercel/commerce/api/utils/errors'
-
import { BigcommerceApiError } from '../../utils/errors'
const signup: SignupEndpoint['handlers']['signup'] = async ({
@@ -22,28 +21,25 @@ const signup: SignupEndpoint['handlers']['signup'] = async ({
},
]),
})
+
+ // Login the customer right after creating it
+ const response = await commerce.login({
+ variables: { email, password },
+ config,
+ })
+
+ return response
} catch (error) {
- if (error instanceof BigcommerceApiError && error.status === 422) {
- const hasEmailError = '0.email' in error.data?.errors
- // If there's an error with the email, it most likely means it's duplicated
- if (hasEmailError) {
- throw new CommerceAPIError('Email already in use', {
- status: 400,
- code: 'duplicated_email',
- })
- }
- } else {
- throw error
+ // Display all validation errors from BigCommerce in a single error message
+ if (error instanceof BigcommerceApiError && error.status >= 400) {
+ const message = Object.values(error.data.errors).join('
')
+ throw new CommerceAPIError(message, {
+ status: 400,
+ code: 'invalid_request',
+ })
}
- }
- const res = new Response()
-
- // Login the customer right after creating it
- await commerce.login({ variables: { email, password }, res, config })
-
- return {
- headers: res.headers,
+ throw error
}
}
diff --git a/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts b/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts
index cc1cdf951..128390ca3 100644
--- a/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts
+++ b/packages/bigcommerce/src/api/endpoints/wishlist/add-item.ts
@@ -1,6 +1,7 @@
import { parseWishlistItem } from '../../utils/parse-item'
import getCustomerId from '../../utils/get-customer-id'
import type { WishlistEndpoint } from '.'
+import { normalizeWishlist } from '../../../lib/normalize'
const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
body: { customerToken, item },
@@ -31,7 +32,7 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
}),
})
return {
- data,
+ data: normalizeWishlist(data),
}
}
@@ -47,7 +48,9 @@ const addItem: WishlistEndpoint['handlers']['addItem'] = async ({
)
// Returns Wishlist
- return { data }
+ return {
+ data: normalizeWishlist(data),
+ }
}
export default addItem
diff --git a/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts b/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts
index c0f57fb92..325adce54 100644
--- a/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts
+++ b/packages/bigcommerce/src/api/endpoints/wishlist/get-wishlist.ts
@@ -1,5 +1,4 @@
import { CommerceAPIError } from '@vercel/commerce/api/utils/errors'
-import type { Wishlist } from '@vercel/commerce/types/wishlist'
import type { WishlistEndpoint } from '.'
import getCustomerId from '../../utils/get-customer-id'
@@ -9,8 +8,6 @@ const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({
config,
commerce,
}) => {
- let result: { data?: Wishlist } = {}
-
if (customerToken) {
const customerId =
customerToken && (await getCustomerId({ customerToken, config }))
@@ -25,10 +22,10 @@ const getWishlist: WishlistEndpoint['handlers']['getWishlist'] = async ({
config,
})
- result = { data: wishlist }
+ return { data: wishlist }
}
- return { data: result.data ?? null }
+ return { data: null }
}
export default getWishlist
diff --git a/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts b/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts
index 36b0a09db..4a79728a2 100644
--- a/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts
+++ b/packages/bigcommerce/src/api/endpoints/wishlist/remove-item.ts
@@ -1,7 +1,9 @@
-import type { Wishlist } from '@vercel/commerce/types/wishlist'
-import getCustomerId from '../../utils/get-customer-id'
import type { WishlistEndpoint } from '.'
+import type { BCWishlist } from '../../utils/types'
+
+import getCustomerId from '../../utils/get-customer-id'
import { CommerceAPIError } from '@vercel/commerce/api/utils/errors'
+import { normalizeWishlist } from '../../../lib/normalize'
// Return wishlist info
const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({
@@ -11,6 +13,7 @@ const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({
}) => {
const customerId =
customerToken && (await getCustomerId({ customerToken, config }))
+
const { wishlist } =
(customerId &&
(await commerce.getCustomerWishlist({
@@ -23,13 +26,12 @@ const removeItem: WishlistEndpoint['handlers']['removeItem'] = async ({
throw new CommerceAPIError('Wishlist not found', { status: 400 })
}
- const result = await config.storeApiFetch<{ data: Wishlist } | null>(
+ const result = await config.storeApiFetch<{ data: BCWishlist } | null>(
`/v3/wishlists/${wishlist.id}/items/${itemId}`,
{ method: 'DELETE' }
)
- const data = result?.data ?? null
- return { data }
+ return { data: result?.data ? normalizeWishlist(result.data) : null }
}
export default removeItem
diff --git a/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts b/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts
index cdfd05acf..e94297204 100644
--- a/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts
+++ b/packages/bigcommerce/src/api/operations/get-customer-wishlist.ts
@@ -2,13 +2,11 @@ import type {
OperationContext,
OperationOptions,
} from '@vercel/commerce/api/operations'
-import type {
- GetCustomerWishlistOperation,
- Wishlist,
-} from '@vercel/commerce/types/wishlist'
-import type { RecursivePartial, RecursiveRequired } from '../utils/types'
+import type { GetCustomerWishlistOperation } from '@vercel/commerce/types/wishlist'
+import type { RecursivePartial, BCWishlist } from '../utils/types'
import { BigcommerceConfig, Provider } from '..'
-import getAllProducts, { ProductEdge } from './get-all-products'
+import { ProductEdge } from './get-all-products'
+import { normalizeWishlist } from '../../lib/normalize'
export default function getCustomerWishlistOperation({
commerce,
@@ -41,18 +39,22 @@ export default function getCustomerWishlistOperation({
}): Promise {
config = commerce.getConfig(config)
- const { data = [] } = await config.storeApiFetch<
- RecursivePartial<{ data: Wishlist[] }>
- >(`/v3/wishlists?customer_id=${variables.customerId}`)
+ const { data = [] } = await config.storeApiFetch<{ data: BCWishlist[] }>(
+ `/v3/wishlists?customer_id=${variables.customerId}`
+ )
const wishlist = data[0]
if (includeProducts && wishlist?.items?.length) {
- const ids = wishlist.items
- ?.map((item) => (item?.productId ? String(item?.productId) : null))
- .filter((id): id is string => !!id)
+ const ids = []
- if (ids?.length) {
+ for (let i = 0; i < wishlist.items.length; i++) {
+ if (wishlist.items[i].product_id) {
+ ids.push(String(wishlist.items[i]?.product_id))
+ }
+ }
+
+ if (ids.length) {
const graphqlData = await commerce.getAllProducts({
variables: { first: 50, ids },
config,
@@ -66,7 +68,7 @@ export default function getCustomerWishlistOperation({
}, {})
// Populate the wishlist items with the graphql products
wishlist.items.forEach((item) => {
- const product = item && productsById[Number(item.productId)]
+ const product = item && productsById[Number(item.product_id)]
if (item && product) {
// @ts-ignore Fix this type when the wishlist type is properly defined
item.product = product
@@ -75,7 +77,7 @@ export default function getCustomerWishlistOperation({
}
}
- return { wishlist: wishlist as RecursiveRequired }
+ return { wishlist: wishlist && normalizeWishlist(wishlist) }
}
return getCustomerWishlist
diff --git a/packages/bigcommerce/src/api/operations/login.ts b/packages/bigcommerce/src/api/operations/login.ts
index dad3b48b1..96bdb5119 100644
--- a/packages/bigcommerce/src/api/operations/login.ts
+++ b/packages/bigcommerce/src/api/operations/login.ts
@@ -22,26 +22,23 @@ export default function loginOperation({
async function login(opts: {
variables: T['variables']
config?: BigcommerceConfig
- res: Response
}): Promise
async function login(
opts: {
variables: T['variables']
config?: BigcommerceConfig
- res: Response
} & OperationOptions
): Promise
async function login({
query = loginMutation,
variables,
- res: response,
config,
}: {
query?: string
variables: T['variables']
- res: Response
+
config?: BigcommerceConfig
}): Promise {
config = commerce.getConfig(config)
@@ -50,6 +47,9 @@ export default function loginOperation({
query,
{ variables }
)
+
+ const headers = new Headers()
+
// Bigcommerce returns a Set-Cookie header with the auth cookie
let cookie = res.headers.get('Set-Cookie')
@@ -63,19 +63,13 @@ export default function loginOperation({
cookie = cookie.replace(/; SameSite=none/gi, '; SameSite=lax')
}
- const prevCookie = response.headers.get('Set-Cookie')
- const newCookie = concatHeader(prevCookie, cookie)
-
- if (newCookie) {
- res.headers.set(
- 'Set-Cookie',
- String(Array.isArray(newCookie) ? newCookie.join(',') : newCookie)
- )
- }
+ headers.set('Set-Cookie', cookie)
}
return {
result: data.login?.result,
+ headers,
+ status: res.status,
}
}
diff --git a/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts b/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts
index 9072b2432..c98010422 100644
--- a/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts
+++ b/packages/bigcommerce/src/api/utils/fetch-graphql-api.ts
@@ -7,7 +7,7 @@ const fetchGraphqlApi: (getConfig: () => BigcommerceConfig) => GraphQLFetcher =
async (
query: string,
{ variables, preview } = {},
- options: { headers?: HeadersInit } = {}
+ headers?: HeadersInit
): Promise => {
// log.warn(query)
const config = getConfig()
@@ -16,7 +16,7 @@ const fetchGraphqlApi: (getConfig: () => BigcommerceConfig) => GraphQLFetcher =
method: 'POST',
headers: {
Authorization: `Bearer ${config.apiToken}`,
- ...options.headers,
+ ...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify({
diff --git a/packages/bigcommerce/src/api/utils/get-customer-id.ts b/packages/bigcommerce/src/api/utils/get-customer-id.ts
index e66bb5d30..e77951948 100644
--- a/packages/bigcommerce/src/api/utils/get-customer-id.ts
+++ b/packages/bigcommerce/src/api/utils/get-customer-id.ts
@@ -20,7 +20,7 @@ async function getCustomerId({
getCustomerIdQuery,
undefined,
{
- 'Set-Cookie': `${config.customerCookie}=${customerToken}`,
+ cookie: `${config.customerCookie}=${customerToken}`,
}
)
diff --git a/packages/bigcommerce/src/api/utils/types.ts b/packages/bigcommerce/src/api/utils/types.ts
index 56f9c1728..9a75cda55 100644
--- a/packages/bigcommerce/src/api/utils/types.ts
+++ b/packages/bigcommerce/src/api/utils/types.ts
@@ -5,3 +5,15 @@ export type RecursivePartial = {
export type RecursiveRequired = {
[P in keyof T]-?: RecursiveRequired
}
+
+export interface BCWishlist {
+ id: number
+ items: {
+ id: number
+ customer_id: number
+ is_public: boolean
+ product_id: number
+ variant_id: number
+ }[]
+ token: string
+}
diff --git a/packages/bigcommerce/src/lib/normalize.ts b/packages/bigcommerce/src/lib/normalize.ts
index e83394c91..885694122 100644
--- a/packages/bigcommerce/src/lib/normalize.ts
+++ b/packages/bigcommerce/src/lib/normalize.ts
@@ -5,8 +5,10 @@ import type { Category, Brand } from '@vercel/commerce/types/site'
import type { BigcommerceCart, BCCategory, BCBrand } from '../types'
import type { ProductNode } from '../api/operations/get-all-products'
import type { definitions } from '../api/definitions/store-content'
+import type { BCWishlist } from '../api/utils/types'
import getSlug from './get-slug'
+import { Wishlist } from '@vercel/commerce/types/wishlist'
function normalizeProductOption(productOption: any) {
const {
@@ -137,3 +139,16 @@ export function normalizeBrand(brand: BCBrand): Brand {
path: `/${slug}`,
}
}
+
+export function normalizeWishlist(wishlist: BCWishlist): Wishlist {
+ return {
+ id: String(wishlist.id),
+ token: wishlist.token,
+ items: wishlist.items.map((item: any) => ({
+ id: String(item.id),
+ productId: String(item.product_id),
+ variantId: String(item.variant_id),
+ product: item.product,
+ })),
+ }
+}
diff --git a/packages/commerce/src/api/endpoints/wishlist.ts b/packages/commerce/src/api/endpoints/wishlist.ts
index 6eec02ced..3cefded5c 100644
--- a/packages/commerce/src/api/endpoints/wishlist.ts
+++ b/packages/commerce/src/api/endpoints/wishlist.ts
@@ -35,7 +35,7 @@ const wishlistEndpoint: GetAPISchema<
if (req.method === 'GET') {
const body = getWishlistBodySchema.parse({
customerToken,
- includeProducts: !!products,
+ includeProducts: input.includeProducts ?? !!products,
})
output = await handlers['getWishlist']({ ...ctx, body })
}
diff --git a/packages/commerce/src/api/utils/index.ts b/packages/commerce/src/api/utils/index.ts
index 3fd50d00e..082029d0c 100644
--- a/packages/commerce/src/api/utils/index.ts
+++ b/packages/commerce/src/api/utils/index.ts
@@ -44,7 +44,9 @@ export const transformRequest = (req: NextApiRequest, path: string) => {
body = JSON.stringify(req.body)
}
- return new NextRequest(`https://${req.headers.host}/api/commerce/${path}`, {
+ const url = new URL(req.url || '/', `https://${req.headers.host}`)
+
+ return new NextRequest(url, {
headers,
method: req.method,
body,
diff --git a/packages/commerce/src/schemas/customer.ts b/packages/commerce/src/schemas/customer.ts
index a48ba06ab..6ff017cfd 100644
--- a/packages/commerce/src/schemas/customer.ts
+++ b/packages/commerce/src/schemas/customer.ts
@@ -5,14 +5,16 @@ export const getCustomerAddressBodySchema = z.object({
})
export const customerSchema = z.object({
- id: z.string(),
- firstName: z.string(),
- lastName: z.string(),
- email: z.string().optional(),
- phone: z.string().optional(),
- company: z.string().optional(),
- notes: z.string().optional(),
- acceptsMarketing: z.boolean().optional(),
+ customer: z.object({
+ id: z.string(),
+ firstName: z.string(),
+ lastName: z.string(),
+ email: z.string().optional(),
+ phone: z.string().optional(),
+ company: z.string().optional(),
+ notes: z.string().optional(),
+ acceptsMarketing: z.boolean().optional(),
+ }),
})
export const addressSchema = z.object({
diff --git a/packages/commerce/src/schemas/whishlist.ts b/packages/commerce/src/schemas/whishlist.ts
index f1b406235..58c9a2865 100644
--- a/packages/commerce/src/schemas/whishlist.ts
+++ b/packages/commerce/src/schemas/whishlist.ts
@@ -5,7 +5,7 @@ export const wishlistSchemaItem = z.object({
id: z.string(),
productId: z.string(),
variantId: z.string(),
- product: productSchema,
+ product: productSchema.optional(),
})
export const wishlistSchema = z.object({
@@ -15,7 +15,7 @@ export const wishlistSchema = z.object({
})
export const getWishlistBodySchema = z.object({
- customerAccessToken: z.string(),
+ customerToken: z.string().optional(),
includeProducts: z.boolean(),
})
@@ -25,17 +25,17 @@ export const wishlistItemBodySchema = z.object({
})
export const addItemBodySchema = z.object({
- cartId: z.string().optional(),
+ customerToken: z.string(),
item: wishlistItemBodySchema,
})
export const updateItemBodySchema = z.object({
- cartId: z.string(),
+ customerToken: z.string(),
itemId: z.string(),
item: wishlistItemBodySchema,
})
export const removeItemBodySchema = z.object({
- cartId: z.string(),
+ customerToken: z.string(),
itemId: z.string(),
})
diff --git a/packages/commerce/src/types/login.ts b/packages/commerce/src/types/login.ts
index e032b1827..1fb06101a 100644
--- a/packages/commerce/src/types/login.ts
+++ b/packages/commerce/src/types/login.ts
@@ -26,6 +26,6 @@ export type LoginSchema = {
}
export type LoginOperation = {
- data: { result?: string }
+ data: { result?: string; status?: number; headers?: Headers }
variables: unknown
}
diff --git a/site/components/auth/LoginView.tsx b/site/components/auth/LoginView.tsx
index c7d399f94..26e649cda 100644
--- a/site/components/auth/LoginView.tsx
+++ b/site/components/auth/LoginView.tsx
@@ -31,7 +31,6 @@ const LoginView: React.FC = () => {
email,
password,
})
- setLoading(false)
closeModal()
} catch ({ errors }) {
if (errors instanceof Array) {
@@ -39,15 +38,15 @@ const LoginView: React.FC = () => {
} else {
setMessage('Unexpected error')
}
- setLoading(false)
setDisabled(false)
+ } finally {
+ setLoading(false)
}
}
const handleValidation = useCallback(() => {
// Test for Alphanumeric password
const validPassword = /^(?=.*[a-zA-Z])(?=.*[0-9])/.test(password)
-
// Unable to send form unless fields are valid.
if (dirty) {
setDisabled(!validate(email) || password.length < 7 || !validPassword)
diff --git a/site/components/auth/SignUpView.tsx b/site/components/auth/SignUpView.tsx
index fa71bf95d..cc6c629be 100644
--- a/site/components/auth/SignUpView.tsx
+++ b/site/components/auth/SignUpView.tsx
@@ -38,7 +38,6 @@ const SignUpView: FC = () => {
lastName,
password,
})
- setLoading(false)
closeModal()
} catch ({ errors }) {
console.error(errors)
@@ -47,8 +46,9 @@ const SignUpView: FC = () => {
} else {
setMessage('Unexpected error')
}
- setLoading(false)
setDisabled(false)
+ } finally {
+ setLoading(false)
}
}
diff --git a/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.module.css b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.module.css
index 93a183a2b..4cdec856f 100644
--- a/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.module.css
+++ b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.module.css
@@ -4,7 +4,6 @@
z-index: 10;
height: 100vh;
min-width: 100vw;
- transition: none;
}
@media screen(lg) {
@@ -18,8 +17,7 @@
.link {
@apply text-primary flex cursor-pointer px-6 py-3
transition ease-in-out duration-150 leading-6
- font-medium items-center capitalize w-full box-border
- outline-0;
+ font-medium items-center capitalize w-full box-border outline-0;
}
.link:hover {
diff --git a/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.tsx b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.tsx
index 992d17178..83516da21 100644
--- a/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.tsx
+++ b/site/components/common/UserNav/CustomerMenuContent/CustomerMenuContent.tsx
@@ -35,13 +35,7 @@ export default function CustomerMenuContent() {
}
return (
-