From 6ea1e294c684c7c202e2ee9eadc9e9c71d0581a4 Mon Sep 17 00:00:00 2001 From: artem ash Date: Fri, 1 Mar 2024 21:31:14 -0800 Subject: [PATCH 1/2] @hanzo/cart --> @hanzo/commerce core: created ObsLineItemRef for arch clarity in certain situations core: added getFacetsValue to service admin: move many packages into peerDep some renaming and cleanup of components simplified mutators concept in facet widgets (now StringMutator and StringArrayMutator) created util/useSkuAndFacetParams hook for common buy pages remove commerceJs from tree for now --- packages/auth/package.json | 14 +- .../commerce/components/category-view.tsx | 197 --- .../components/facet-toggles-widget/index.tsx | 44 +- .../commerce/components/facets-widget.tsx | 9 +- packages/commerce/components/index.ts | 6 +- ...sx => select-category-and-item-widget.tsx} | 6 +- .../select-item-in-category-view.tsx | 200 +++ packages/commerce/package.json | 20 +- packages/commerce/service/commerce-service.ts | 11 +- .../commerceJs/@types/akasha.d.ts | 6 - .../hanzo-adapter/commerceJs/Commerce.test.ts | 1119 ----------------- .../impl/hanzo-adapter/commerceJs/Commerce.ts | 749 ----------- .../impl/hanzo-adapter/commerceJs/LineItem.ts | 61 - .../impl/hanzo-adapter/commerceJs/Order.ts | 360 ------ .../impl/hanzo-adapter/commerceJs/Product.ts | 125 -- .../impl/hanzo-adapter/commerceJs/User.ts | 65 - .../impl/hanzo-adapter/commerceJs/index.ts | 13 - .../impl/hanzo-adapter/commerceJs/types.ts | 205 --- .../impl/hanzo-adapter/commerceJs/utils.ts | 153 --- .../service/impl/standalone/service.ts | 34 +- packages/commerce/types/index.ts | 18 + packages/commerce/util/index.ts | 3 + .../commerce/util/use-sku-and-facet-params.ts | 157 +++ pnpm-lock.yaml | 518 +------- 24 files changed, 479 insertions(+), 3614 deletions(-) delete mode 100644 packages/commerce/components/category-view.tsx rename packages/commerce/components/{category-and-item-widget.tsx => select-category-and-item-widget.tsx} (93%) create mode 100644 packages/commerce/components/select-item-in-category-view.tsx delete mode 100644 packages/commerce/service/impl/hanzo-adapter/commerceJs/@types/akasha.d.ts delete mode 100644 packages/commerce/service/impl/hanzo-adapter/commerceJs/Commerce.test.ts delete mode 100644 packages/commerce/service/impl/hanzo-adapter/commerceJs/Commerce.ts delete mode 100644 packages/commerce/service/impl/hanzo-adapter/commerceJs/LineItem.ts delete mode 100644 packages/commerce/service/impl/hanzo-adapter/commerceJs/Order.ts delete mode 100644 packages/commerce/service/impl/hanzo-adapter/commerceJs/Product.ts delete mode 100644 packages/commerce/service/impl/hanzo-adapter/commerceJs/User.ts delete mode 100644 packages/commerce/service/impl/hanzo-adapter/commerceJs/index.ts delete mode 100644 packages/commerce/service/impl/hanzo-adapter/commerceJs/types.ts delete mode 100644 packages/commerce/service/impl/hanzo-adapter/commerceJs/utils.ts create mode 100644 packages/commerce/util/use-sku-and-facet-params.ts diff --git a/packages/auth/package.json b/packages/auth/package.json index a5a446a3..4f27578b 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -28,23 +28,21 @@ "clean": "rm -rf dist && rm -rf node_modules" }, "dependencies": { + "firebase-admin": "^12.0.0" + }, + "peerDependencies": { "@hanzo/ui": "^1.0.8", "@hookform/resolvers": "^3.3.4", "firebase": "^10.8.0", - "firebase-admin": "^12.0.0", "lucide-react": "^0.336.0", + "react-hook-form": "^7.50.1", "mobx": "^6.12.0", "mobx-react-lite": "^4.0.5", - "react-hook-form": "^7.50.1", - "zod": "3.21.4" - }, - "peerDependencies": { "next": "^14.1.0", - "react": "^18.2.0" + "react": "^18.2.0", + "zod": "3.21.4" }, "devDependencies": { - "@testing-library/jest-dom": "^6.4.2", - "@testing-library/react": "^14.2.1", "@types/react": "^18.2.48", "cross-fetch": "^4.0.0", "typescript": "^5.3.3" diff --git a/packages/commerce/components/category-view.tsx b/packages/commerce/components/category-view.tsx deleted file mode 100644 index 7c6b39f0..00000000 --- a/packages/commerce/components/category-view.tsx +++ /dev/null @@ -1,197 +0,0 @@ -'use client' -import React, { useState } from 'react' -import Image from 'next/image' - -import { cn } from '@hanzo/ui/util' -import { Label, RadioGroup, RadioGroupItem, Skeleton } from '@hanzo/ui/primitives' - -import { formatPrice } from '../util' -import { Icons } from './Icons' -import AddToCartWidget from './add-to-cart-widget' -import ProductSelectionRadioGroup from './product-selection-radio-group' -import type { Category, LineItem } from '../types' -import ProductSelectionMobilePicker from './product-selection-mobile-picker' - -const CategoryView: React.FC & { - category: Category - isLoading?: boolean - mobile?: boolean -}> = ({ - category, - className, - isLoading = false, - mobile = false, - ...props -}) => { - - const [selectedItem, setSelectedItem] = useState(category?.products[0] as LineItem) - - const waiting = (): boolean => (isLoading || !category) - - const CategoryImage: React.FC<{ className?: string }> = ({ - className = '' - }) => { - - if (waiting()) { - // deliberately not Skeleton to have a better overall pulse effect. - return
- } - - if (!category.img) { - return ( -
-
- ) - } - - return ( -
-
-
- {category.title} -
-
-
- ) - } - - const AvailableAmounts: React.FC<{ className?: string }> = ({ - className = '' - }) => { - - const onSelection = (sku: string) => { - - const selectedItem = category.products.find((item) => (item.sku === sku)) - if (selectedItem) { - setSelectedItem(selectedItem as LineItem) - } - } - - const soleOption = !waiting() && category.products.length === 1 - const mobilePicker = !waiting() && (mobile && category.products.length > 8) - - return waiting() ? ( - - ) : ( -
-
-
{`Available size${soleOption ? '' : 's'}`}
-
-
- {soleOption ? ( -

{category.products[0].titleAsOption}

- ) : ( mobilePicker ? ( - - ) : ( - - ) - )} -
- ) - } - - const TitleArea: React.FC<{ className?: string }> = ({ - className = '' - }) => ( - waiting() ? () : ( - -
-

- - {category.title.split(', ').map((s, i) => (

{s}

))} -
- - {category.title} - -

- {selectedItem ? ( -
- {selectedItem.titleAsOption + ': ' + formatPrice(selectedItem.price)} -
- ) : ''} -
- )) - - const Desc: React.FC<{ className?: string }> = ({ - className = '' - }) => ( - waiting() ? () : ( -

{category.desc}

- )) - - return mobile ? ( -
- -
- { waiting() ? ( ) : (<> - - - )} -
- - - {selectedItem ? ( - - ) : null} -
- ) : ( -
-
-
- -
-
-
- - -
-
- - {selectedItem && !isLoading ? () : null} -
-
-
-
- - {selectedItem && !isLoading ? () : null} -
-
- ) - } - - - export default CategoryView \ No newline at end of file diff --git a/packages/commerce/components/facet-toggles-widget/index.tsx b/packages/commerce/components/facet-toggles-widget/index.tsx index ef4251af..5ddc6add 100644 --- a/packages/commerce/components/facet-toggles-widget/index.tsx +++ b/packages/commerce/components/facet-toggles-widget/index.tsx @@ -4,22 +4,12 @@ import React, { useState } from 'react' import { ToggleGroup, ToggleGroupItem,} from "@hanzo/ui/primitives" import { cn } from '@hanzo/ui/util' -import type { FacetValueDesc } from '../../types' +import type { FacetValueDesc, StringMutator, StringArrayMutator } from '../../types' import FacetImage from './facet-image' -interface FacetMutatorSingle { - val: string | null - set(v: string | null): void -} - -interface FacetMutatorMultiple { - val: string[] | null - set(v: string[] | null): void -} - const FacetTogglesWidget: React.FC<{ facetValues: FacetValueDesc[] - mutator: FacetMutatorSingle | FacetMutatorMultiple + mutator: StringMutator | StringArrayMutator multiple?: boolean className?: string isMobile?: boolean @@ -34,20 +24,13 @@ const FacetTogglesWidget: React.FC<{ const [last, setLast] = useState(undefined) const handleChangeMultiple = (selected: string[]) => { - (mutator as FacetMutatorMultiple).set(selected) - if (selected.length === 1) { - setLast(selected[0]) - } - else { - setLast(undefined) - } + (mutator as StringArrayMutator).set(selected) + setLast(selected.length === 1 ? selected[0] : undefined) } const handleChangeSingle = (selected: string) => { - (mutator as FacetMutatorSingle).set(selected) - if (selected) { - setLast(selected) - } + (mutator as StringMutator).set(selected) + if (selected) { setLast(selected) } } const roundedToSpread: any = {} @@ -55,10 +38,15 @@ const FacetTogglesWidget: React.FC<{ roundedToSpread.rounded = 'xl' } + const val = multiple ? + (mutator as StringArrayMutator).get() + : + (mutator as StringMutator).get() + return ( @@ -91,8 +79,4 @@ const FacetTogglesWidget: React.FC<{ ) } -export { - FacetTogglesWidget as default, - type FacetMutatorSingle, - type FacetMutatorMultiple -} +export default FacetTogglesWidget diff --git a/packages/commerce/components/facets-widget.tsx b/packages/commerce/components/facets-widget.tsx index 6ef696ba..f30d9ccc 100644 --- a/packages/commerce/components/facets-widget.tsx +++ b/packages/commerce/components/facets-widget.tsx @@ -3,17 +3,14 @@ import React, { type PropsWithChildren } from 'react' import { cn } from '@hanzo/ui/util' -import type { FacetsDesc } from '../types' +import type { FacetsDesc, StringMutator, StringArrayMutator } from '../types' -import FacetTogglesWidget, { - type FacetMutatorSingle, - type FacetMutatorMultiple -} from './facet-toggles-widget' +import FacetTogglesWidget from './facet-toggles-widget' const FacetsWidget: React.FC ( `${item.titleAsOption}, ${formatPrice(item.price)}${(withQuantity && item.quantity > 0) ? ` (${item.quantity})` : ''}` ) -const CategoryAndItemWidget: React.FC<{ +const SelectCategoryAndItemWidget: React.FC<{ categoryLevel: number parentLevelToken: string categoryLevelValues: FacetValueDesc[] @@ -56,7 +56,7 @@ const CategoryAndItemWidget: React.FC<{ @@ -81,4 +81,4 @@ const CategoryAndItemWidget: React.FC<{ ) }) -export default CategoryAndItemWidget +export default SelectCategoryAndItemWidget diff --git a/packages/commerce/components/select-item-in-category-view.tsx b/packages/commerce/components/select-item-in-category-view.tsx new file mode 100644 index 00000000..df748f2c --- /dev/null +++ b/packages/commerce/components/select-item-in-category-view.tsx @@ -0,0 +1,200 @@ +'use client' +import React, { useState } from 'react' +import Image from 'next/image' +import { observer } from 'mobx-react-lite' + +import { cn } from '@hanzo/ui/util' +import { Skeleton } from '@hanzo/ui/primitives' + +import { useCommerce } from '../service' +import type { Category, LineItem, ObsLineItemRef } from '../types' +import { formatPrice } from '../util' +import { Icons } from './Icons' + +import AddToCartWidget from './add-to-cart-widget' +import ProductSelectionRadioGroup from './product-selection-radio-group' +import ProductSelectionMobilePicker from './product-selection-mobile-picker' + +const SelectItemInCategoryView: React.FC & { + category: Category + lineItemRef: ObsLineItemRef + handleItemSelected: (sku: string) => void + isLoading?: boolean + mobile?: boolean +}> = ({ + category, + lineItemRef, + handleItemSelected, + className, + isLoading = false, + mobile = false, + ...props +}) => { + + const waiting = (): boolean => (isLoading || !category) + + const CategoryImage: React.FC<{ className?: string }> = ({ className = '' }) => { + + if (waiting()) { + // deliberately not Skeleton to have a better overall pulse effect. + return
+ } + + if (!category.img) { + return ( +
+
+ ) + } + + return ( +
+
+
+ {category.title} +
+
+
+ ) + } + + const AvailableAmounts: React.FC<{ className?: string }> = observer(({ className = '' }) => { + + const soleOption = !waiting() && category.products.length === 1 + const mobilePicker = !waiting() && (mobile && category.products.length > 8) + + return waiting() ? ( + + ) : ( +
+
+
{`Available size${soleOption ? '' : 's'}`}
+
+
+ {soleOption ? ( +

{category.products[0].titleAsOption}

+ ) : ( mobilePicker ? ( + + ) : ( + + ) + )} +
+ ) + }) + + const AddToCartArea: React.FC<{ className?: string }> = observer(({ className = '' }) => ( + (lineItemRef.item && !isLoading) ? ( + + ) : ( +
+ ) + )) + + const TitleArea: React.FC<{ className?: string }> = observer(({ className = '' }) => ( + waiting() ? () : ( + +
+

+ + {category.title.split(', ').map((s, i) => (

{s}

))} +
+ + {category.title} + +

+ {lineItemRef.item?.sku ? ( +
+ {lineItemRef.item.titleAsOption + ': ' + formatPrice(lineItemRef.item.price)} +
+ ) : ''} +
+ ))) + + const Desc: React.FC<{ className?: string }> = ({ className = '' }) => ( + waiting() ? ( + + ) : ( +

{category.desc}

+ ) + ) + + return mobile ? ( +
+
+ {waiting() ? ( ) : (<> + + + )} +
+ + + +
+ ) : ( +
+
+
+ +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ ) +} + + +export default SelectItemInCategoryView \ No newline at end of file diff --git a/packages/commerce/package.json b/packages/commerce/package.json index b88dc010..ebf5f6d8 100644 --- a/packages/commerce/package.json +++ b/packages/commerce/package.json @@ -1,6 +1,6 @@ { - "name": "@hanzo/cart", - "version": "0.5.9", + "name": "@hanzo/commerce", + "version": "1.0.8", "description": "Library with shopping cart components.", "publishConfig": { "registry": "https://registry.npmjs.org/", @@ -20,25 +20,24 @@ "hanzo" ], "scripts": { - "lat": "npm show @hanzo/cart version", + "lat": "npm show @hanzo/commerce version", "pub": "npm publish", "build": "tsc", "tc": "tsc", "clean": "rm -rf dist && rm -rf node_modules" }, "dependencies": { - "@hanzo/ui": "^1.0.8", - "@hanzo/auth": "^1.0.9", "@hookform/resolvers": "^3.3.2", - "akasha": "^0.1.13", - "firebase": "^10.8.0", "lucide-react": "^0.307.0", - "midstream": "^2.0.2", - "mobx": "^6.12.0", - "mobx-react-lite": "^4.0.5", + "next-usequerystate": "^1.17.0", "react-mobile-picker": "^1.0.0" }, "peerDependencies": { + "@hanzo/auth": "^1.0.9", + "@hanzo/ui": "^1.0.8", + "firebase": "^10.8.0", + "mobx": "^6.12.0", + "mobx-react-lite": "^4.0.5", "next": "^14.1.0", "react": "^18.2.0", "react-dom": "^18.2.0" @@ -47,7 +46,6 @@ "@types/react": "^18.2.48", "@types/react-dom": "^18.2.18", "cross-fetch": "^4.0.0", - "hanzo.js": "^2.5.13", "typescript": "^5.3.3" } } diff --git a/packages/commerce/service/commerce-service.ts b/packages/commerce/service/commerce-service.ts index ce555a77..d2903d3d 100644 --- a/packages/commerce/service/commerce-service.ts +++ b/packages/commerce/service/commerce-service.ts @@ -1,10 +1,11 @@ import type { Category, FacetsValue, - LineItem + LineItem , + ObsLineItemRef } from '../types' -interface CommerceService { +interface CommerceService extends ObsLineItemRef { get cartItems(): LineItem[] get cartTotal(): number @@ -20,6 +21,7 @@ interface CommerceService { * An empty value object specifies all Category's and all LineItem's, * */ setFacets(value: FacetsValue): Category[] + get facetsValue(): FacetsValue // returns a copy get specifiedItems(): LineItem[] get specifiedCategories(): Category[] @@ -33,8 +35,13 @@ interface CommerceService { * For convenience, so widgets can share state. * "current" is unrelated to what is "specified", * ie, facets' values + * + * note: for ObsLineItemRef, there is also + * get item(): LineItem | undefined + * which simply delegates to this function * */ get currentItem(): LineItem | undefined + } export { diff --git a/packages/commerce/service/impl/hanzo-adapter/commerceJs/@types/akasha.d.ts b/packages/commerce/service/impl/hanzo-adapter/commerceJs/@types/akasha.d.ts deleted file mode 100644 index 0764fb7b..00000000 --- a/packages/commerce/service/impl/hanzo-adapter/commerceJs/@types/akasha.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module 'akasha' { - const set: (key: string, val: any) => void - const get: (key: string) => any - const remove: (key: string) => void - const clear: () => void -} diff --git a/packages/commerce/service/impl/hanzo-adapter/commerceJs/Commerce.test.ts b/packages/commerce/service/impl/hanzo-adapter/commerceJs/Commerce.test.ts deleted file mode 100644 index 2630e275..00000000 --- a/packages/commerce/service/impl/hanzo-adapter/commerceJs/Commerce.test.ts +++ /dev/null @@ -1,1119 +0,0 @@ -import Commerce from './Commerce' -import Order from './Order' -import fetch from 'cross-fetch' -import type { - ICartClient, - IGeoRate, -} from './types' -import Api from 'hanzo.js' -import { - log, - setLogLevel, -} from './utils' - -// setLogLevel('test') - -const KEY = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NzIzMDc4NDcsInN1YiI6IjhBVEdFZ1owdWwiLCJqdGkiOiJGVmU0NWRyVzZPYyIsIm5hbWUiOiJ0ZXN0LXB1Ymxpc2hlZC1rZXkiLCJiaXQiOjQ1MDM2MTcwNzU2NzUxNzZ9.k82KjvI4AkRIEF5pjl_hj7nSvkNEkBctHfnbZWoEgVI' -const ENDPOINT = 'https://api-dot-hanzo-staging-249116.appspot.com' - -let analyticsArgs: any[] = [] -let analytics: any -let client: any - -beforeEach(() => { - analytics = { - track: (...args) => { - analyticsArgs = args - }, - } - - client = new Api({ - key: KEY, - endpoint: ENDPOINT, - }) - - client.fetch = fetch -}) - -afterEach(() => { - Order.clear() -}) - -describe('Commerce Bootstrapping', () => { - test('default constructor', () => { - let order = { - currency: 'usd', - items: [], - } - - let c = new Commerce(client, order) - - expect(c.client).toBe(client) - expect(c.order.currency).toBe(order.currency) - expect(c.order.type).toBe('stripe') - expect(c.order.storeId).toBe('') - expect(c.order.items).toEqual([]) - }) - - test('default cartInit', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order) - let cart = await c.cartInit() - - expect(cart.id).toEqual(expect.any(String)) - expect(c.cart.id).toBe(cart.id) - }) -}) - -describe('Commerce Add Item', () => { - test('should set an item by id', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('rbcXB3Qxcv6kNy', 1) - - let items = c.items - - expect(items.length).toBe(1) - - let item = items[0] - - expect(item.productId).toBe('rbcXB3Qxcv6kNy') - expect(item.productSlug).toBe('sad-keanu-shirt') - expect(item.quantity).toBe(1) - - expect(analyticsArgs[0]).toBe('Added Product') - expect(analyticsArgs[1].id).toBe('rbcXB3Qxcv6kNy') - expect(analyticsArgs[1].sku).toBe('sad-keanu-shirt') - expect(analyticsArgs[1].quantity).toBe(1) - - expect(c.order.total).toBe(item.price * item.quantity) - }) - - test('should set an item by id and storeId', async () => { - let order = { - storeId: 'Pkt8119gfdNGoQ', - currency: 'jpy', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('rbcXB3Qxcv6kNy', 1) - - let items = c.items - - expect(items.length).toBe(1) - - let item = items[0] - - expect(item.price).toBe(30000) - expect(c.order.total).toBe(item.price * item.quantity) - }) - - test('should set an item by id and swithc betweens', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('rbcXB3Qxcv6kNy', 1) - - let items = c.items - - expect(items.length).toBe(1) - - let item = items[0] - - expect(item.price).toBe(2500) - expect(c.order.total).toBe(item.price * item.quantity) - - await c.setStoreId('Pkt8119gfdNGoQ') - - items = c.items - - expect(items.length).toBe(1) - - item = items[0] - - expect(item.price).toBe(30000) - expect(c.order.total).toBe(item.price * item.quantity) - }) - - test('should set an item by slug', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('sad-keanu-shirt', 1) - - let items = c.items - - expect(items.length).toBe(1) - - let item = items[0] - - expect(item.productId).toBe('rbcXB3Qxcv6kNy') - expect(item.productSlug).toBe('sad-keanu-shirt') - expect(item.quantity).toBe(1) - - expect(analyticsArgs[0]).toBe('Added Product') - expect(analyticsArgs[1].id).toBe('rbcXB3Qxcv6kNy') - expect(analyticsArgs[1].sku).toBe('sad-keanu-shirt') - expect(analyticsArgs[1].quantity).toBe(1) - - expect(c.order.total).toBe(item.price * item.quantity) - }) - - test('should dedupe an item', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - c.set('sad-keanu-shirt', 1) - c.set('sad-keanu-shirt', 1) - await c.set('sad-keanu-shirt', 1) - - let items = c.items - - expect(items.length).toBe(1) - - let item = items[0] - - expect(item.productId).toBe('rbcXB3Qxcv6kNy') - expect(item.productSlug).toBe('sad-keanu-shirt') - expect(item.quantity).toBe(1) - - expect(analyticsArgs[0]).toBe('Added Product') - expect(analyticsArgs[1].id).toBe('rbcXB3Qxcv6kNy') - expect(analyticsArgs[1].sku).toBe('sad-keanu-shirt') - expect(analyticsArgs[1].quantity).toBe(1) - - expect(c.order.total).toBe(item.price * item.quantity) - }) - - test('should set and get an item by item or slug', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('sad-keanu-shirt', 1) - - let item = await c.get('rbcXB3Qxcv6kNy') - - expect(item).toBeDefined() - - if (item) { - expect(item.productId).toBe('rbcXB3Qxcv6kNy') - expect(item.productSlug).toBe('sad-keanu-shirt') - expect(item.quantity).toBe(1) - } - - item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(item.productId).toBe('rbcXB3Qxcv6kNy') - expect(item.productSlug).toBe('sad-keanu-shirt') - expect(item.quantity).toBe(1) - - expect(c.order.total).toBe(item.price * item.quantity) - } - }) - - test('should not get an item by invalid id or slug, item should not be added until synchronized', async() => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order) - - let setP = c.set('84cRXBYs9jX7wzzz', 1) - expect(c.size).toBe(0) - - await setP - - let item = await c.get('84cRXBYs9jX7wzzz') - - expect(c.size).toBe(0) - expect(item).toBeUndefined() - }) - - test('should overwrite an existing item', async() => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('sad-keanu-shirt', 1) - - let item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(item.productId).toBe('rbcXB3Qxcv6kNy') - expect(item.productSlug).toBe('sad-keanu-shirt') - expect(item.quantity).toBe(1) - } - - await c.set('sad-keanu-shirt', 2) - - item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(item.productId).toBe('rbcXB3Qxcv6kNy') - expect(item.productSlug).toBe('sad-keanu-shirt') - expect(item.quantity).toBe(2) - - expect(c.order.total).toBe(item.price * item.quantity) - } - }) - - test('should remove an item by setting quantity to 0', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('sad-keanu-shirt', 1) - await c.set('sad-keanu-shirt', 0) - - let items = c.items - - expect(items.length).toBe(0) - - expect(analyticsArgs[0]).toBe('Removed Product') - expect(analyticsArgs[1].id).toBe('rbcXB3Qxcv6kNy') - expect(analyticsArgs[1].sku).toBe('sad-keanu-shirt') - expect(analyticsArgs[1].quantity).toBe(1) - - expect(c.order.total).toBe(0) - }) - - test('should remove an item by setting quantity to -1', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('sad-keanu-shirt', 1) - await c.set('sad-keanu-shirt', -1) - - let items = c.items - - expect(items.length).toBe(0) - - expect(analyticsArgs[0]).toBe('Removed Product') - expect(analyticsArgs[1].id).toBe('rbcXB3Qxcv6kNy') - expect(analyticsArgs[1].sku).toBe('sad-keanu-shirt') - // set to 1, then set to max(0, -1) = 0, the quantity below is therefore |0-1| = 1 - expect(analyticsArgs[1].quantity).toBe(1) - - expect(c.order.total).toBe(0) - }) - - test('should not set when in an itemless mode', async () => { - let order = { - currency: 'usd', - mode: 'deposit', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('sad-keanu-shirt', 1) - - expect(c.size).toBe(0) - expect(c.order.total).toBe(0) - }) - - - test('should not be able to set subtotal directly when not in an itemless mode', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - c.order.subtotal = 100 - - expect(c.order.subtotal) - expect(c.order.total).toBe(0) - }) - - test('should be able to set subtotal directly when in an itemless mode', async () => { - let order = { - currency: 'usd', - mode: 'deposit', - } - - let c = new Commerce(client, order, [], [], analytics) - - c.order.subtotal = 100 - - expect(c.order.subtotal) - expect(c.order.total).toBe(100) - }) - - test('clear', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('sad-keanu-shirt', 1) - - expect(c.size).toBe(1) - - expect(analyticsArgs[0]).toBe('Added Product') - expect(analyticsArgs[1].id).toBe('rbcXB3Qxcv6kNy') - expect(analyticsArgs[1].sku).toBe('sad-keanu-shirt') - expect(analyticsArgs[1].quantity).toBe(1) - - await c.clear() - - expect(c.size).toBe(0) - - expect(analyticsArgs[0]).toBe('Removed Product') - expect(analyticsArgs[1].id).toBe('rbcXB3Qxcv6kNy') - expect(analyticsArgs[1].sku).toBe('sad-keanu-shirt') - expect(analyticsArgs[1].quantity).toBe(1) - - expect(c.order.total).toBe(0) - }) - - test('should clear when switching to an itemless mode', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('sad-keanu-shirt', 1) - - expect(c.size).toBe(1) - - expect(analyticsArgs[0]).toBe('Added Product') - expect(analyticsArgs[1].id).toBe('rbcXB3Qxcv6kNy') - expect(analyticsArgs[1].sku).toBe('sad-keanu-shirt') - expect(analyticsArgs[1].quantity).toBe(1) - - c.order.mode = 'deposit' - - await c.updateQueuePromise - - expect(c.size).toBe(0) - - expect(analyticsArgs[0]).toBe('Removed Product') - expect(analyticsArgs[1].id).toBe('rbcXB3Qxcv6kNy') - expect(analyticsArgs[1].sku).toBe('sad-keanu-shirt') - expect(analyticsArgs[1].quantity).toBe(1) - - expect(c.order.total).toBe(0) - }) - - test('should set subtotal directly in itemless mode', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - c.order.subtotal = 1111 - // odd negative zero issue - expect(c.order.subtotal === 0).toBe(true) - expect(c.order.total).toBe(0) - - c.order.mode = 'deposit' - - c.order.subtotal = 2222 - - expect(c.order.subtotal).toBe(2222) - expect(c.order.total).toBe(2222) - }) -}) - -describe('Commerce Rates', () => { - test('should set unfiltered shippingRate', async () => { - let order = { - currency: 'usd', - shippingAddress: { - country: 'us', - state: 'ca', - city: 'los angeles' - }, - } - - let shippingRates: IGeoRate[] = [{ - percent: 0, - cost: 1000, - }] - - let c = new Commerce(client, order, [], shippingRates, analytics) - - await c.set('sad-keanu-shirt', 1) - - let item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(c.size).toBe(1) - expect(c.order.shipping).toBe(c.order.shippingRates[0].cost) - expect(c.order.total).toBe(item.price * item.quantity + c.order.shippingRates[0].cost) - } - }) - - test('should use shippingRates filter with shippingAddress with correct city', async () => { - let order = { - currency: 'usd', - shippingAddress: { - country: 'us', - state: 'ca', - city: 'los angeles' - }, - } - - let shippingRates: IGeoRate[] = [{ - country: 'us', - state: 'ca', - city: 'los angeles', - percent: 0, - cost: 1000, - }, { - percent: 0, - cost: 2000, - }] - - let c = new Commerce(client, order, [], shippingRates, analytics) - - await c.set('sad-keanu-shirt', 1) - - let item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(c.size).toBe(1) - expect(c.order.shipping).toBe(c.order.shippingRates[0].cost) - expect(c.order.total).toBe(item.price * item.quantity + c.order.shippingRates[0].cost) - } - }) - - test('should use shippingRates filter with shippingAddress with correct postal code', async () => { - let order = { - currency: 'usd', - shippingAddress: { - country: 'us', - state: 'ca', - postalCode: '123', - city: 'los angeles' - }, - } - - let shippingRates: IGeoRate[] = [{ - country: 'us', - state: 'ca', - postalCodes: '123,234', - percent: 0, - cost: 1000, - }, { - percent: 0, - cost: 2000, - }] - - let c = new Commerce(client, order, [], shippingRates, analytics) - - await c.set('sad-keanu-shirt', 1) - - let item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(c.size).toBe(1) - expect(c.order.shipping).toBe(c.order.shippingRates[0].cost) - expect(c.order.total).toBe(item.price * item.quantity + c.order.shippingRates[0].cost) - } - }) - - test('should use shippingRates filter with shippingAddress with country filter', async () => { - let order = { - currency: 'usd', - shippingAddress: { - country: 'uk', - state: 'ca', - postalCode: '123', - city: 'los angeles' - }, - } - - let shippingRates: IGeoRate[] = [{ - country: 'us', - state: 'ca', - postalCodes: '123,234', - percent: 0, - cost: 1000, - }, { - percent: 0, - cost: 2000, - }] - - let c = new Commerce(client, order, [], shippingRates, analytics) - - await c.set('sad-keanu-shirt', 1) - - let item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(c.size).toBe(1) - expect(c.order.shipping).toBe(c.order.shippingRates[1].cost) - expect(c.order.total).toBe(item.price * item.quantity + c.order.shippingRates[1].cost) - } - }) - - test('should set unfiltered taxRate', async () => { - let order = { - currency: 'usd', - shippingAddress: { - country: 'us', - state: 'ca', - city: 'los angeles' - }, - } - - let taxRates: IGeoRate[] = [{ - percent: 0, - cost: 1000, - }] - - let c = new Commerce(client, order, taxRates, [], analytics) - - await c.set('sad-keanu-shirt', 1) - - let item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(c.size).toBe(1) - expect(c.order.tax).toBe(c.order.taxRates[0].cost) - expect(c.order.total).toBe(item.price * item.quantity + c.order.taxRates[0].cost) - } - }) - - test('should use taxRates filter with shippingAddress with correct city', async () => { - let order = { - currency: 'usd', - shippingAddress: { - country: 'us', - state: 'ca', - city: 'los angeles' - }, - } - - let taxRates: IGeoRate[] = [{ - country: 'us', - state: 'ca', - city: 'los angeles', - percent: 0, - cost: 1000, - }, { - percent: 0, - cost: 2000, - }] - - let c = new Commerce(client, order, taxRates, [], analytics) - - await c.set('sad-keanu-shirt', 1) - - let item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(c.size).toBe(1) - expect(c.order.tax).toBe(c.order.taxRates[0].cost) - expect(c.order.total).toBe(item.price * item.quantity + c.order.taxRates[0].cost) - } - }) - - test('should use taxRates filter with shippingAddress with correct postal code', async () => { - let order = { - currency: 'usd', - shippingAddress: { - country: 'us', - state: 'ca', - postalCode: '123', - city: 'los angeles' - }, - } - - let taxRates: IGeoRate[] = [{ - country: 'us', - state: 'ca', - postalCodes: '123,234', - percent: 0, - cost: 1000, - }, { - percent: 0, - cost: 2000, - }] - - let c = new Commerce(client, order, taxRates, [], analytics) - - await c.set('sad-keanu-shirt', 1) - - let item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(c.size).toBe(1) - expect(c.order.tax).toBe(c.order.taxRates[0].cost) - expect(c.order.total).toBe(item.price * item.quantity + c.order.taxRates[0].cost) - } - }) - - test('should use taxRates filter with shippingAddress with country filter', async () => { - let order = { - currency: 'usd', - shippingAddress: { - country: 'uk', - state: 'ca', - postalCode: '123', - city: 'los angeles' - }, - } - - let taxRates: IGeoRate[] = [{ - country: 'us', - state: 'ca', - postalCodes: '123,234', - percent: 0, - cost: 1000, - }, { - percent: 0, - cost: 2000, - }] - - let c = new Commerce(client, order, taxRates, [], analytics) - - await c.set('sad-keanu-shirt', 1) - - let item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(c.size).toBe(1) - expect(c.order.tax).toBe(c.order.taxRates[1].cost) - expect(c.order.total).toBe(item.price * item.quantity + c.order.taxRates[1].cost) - } - }) -}) - -describe('Commerce Coupons', () => { - test('setCoupon', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - - await c.set('sad-keanu-shirt', 1) - - let coupon = await c.setCoupon('SUCH-COUPON') - - expect(coupon).toBeDefined() - - if (coupon) { - expect(coupon.code).toBe('SUCH-COUPON') - expect(coupon.amount).toBe(500) - - let item = await c.get('sad-keanu-shirt') - - expect(item).toBeDefined() - - if (item) { - expect(c.size).toBe(1) - expect(c.order.subtotal).toBe(item.price * item.quantity) - expect(c.order.total).toBe(item.price * item.quantity - coupon.amount) - } - } - }) - - test('setCoupon fails on invalid coupon', async () => { - let order = { - currency: 'usd', - } - - let c = new Commerce(client, order, [], [], analytics) - await c.set('sad-keanu-shirt', 1) - - let coupon - try { - coupon = await c.setCoupon('BAD-COUPON') - } catch (e) { - } - - expect(coupon).not.toBeDefined() - }) -}) - -describe('Commerce Checkout', () => { - test('should checkout an order', async () => { - let order = { - currency: 'usd', - shippingAddress: { - line1: 'somewhere', - city: 'kansas city', - state: 'mo', - postalCode: '64081', - country: 'us', - }, - } - - let user = { - email: 'test@hanzo.io', - firstName: 'test', - lastName: 'test', - } - - let payment = { - account: { - name: 'test', - number: '4242424242424242', - cvc: '424', - month: '1', - year: '2040', - }, - } - - let c = new Commerce(client, order, [], [], analytics) - - c.user = user - - await c.set('sad-keanu-shirt', 1) - - let items = c.items - - expect(items.length).toBe(1) - - let item = items[0] - - expect(item.productId).toBe('rbcXB3Qxcv6kNy') - expect(item.productSlug).toBe('sad-keanu-shirt') - expect(item.quantity).toBe(1) - - expect(analyticsArgs[0]).toBe('Added Product') - expect(analyticsArgs[1].id).toBe('rbcXB3Qxcv6kNy') - expect(analyticsArgs[1].sku).toBe('sad-keanu-shirt') - expect(analyticsArgs[1].quantity).toBe(1) - - let orderFromServer = await c.checkout(payment) - - expect(orderFromServer).toBeDefined() - - if (orderFromServer) { - expect(analyticsArgs[0]).toBe('Completed Order') - expect(analyticsArgs[1].total).toBe(orderFromServer.total / 100) - expect(analyticsArgs[1].shipping).toBe(orderFromServer.shipping / 100) - expect(analyticsArgs[1].tax).toBe(orderFromServer.tax / 100) - expect(analyticsArgs[1].discount).toBe(orderFromServer.discount /100) - expect(analyticsArgs[1].coupon).toBe(orderFromServer.couponCodes ? orderFromServer.couponCodes[0] : '') - expect(analyticsArgs[1].currency).toBe('usd') - expect(c.order.number).toBeDefined() - } - }, 10000) - - test('should checkout an itemless order', async () => { - let order = { - currency: 'usd', - mode: 'contribution', - subtotal: 123456, - shippingAddress: { - line1: 'somewhere', - city: 'kansas city', - state: 'mo', - postalCode: '64081', - country: 'us', - }, - } - - let user = { - email: 'test@hanzo.io', - firstName: 'test', - lastName: 'test', - } - - let payment = { - account: { - name: 'test', - number: '4242424242424242', - cvc: '424', - month: '1', - year: '2040', - }, - } - - let c = new Commerce(client, order, [], [], analytics) - expect(c.order.subtotal).toBe(123456) - - c.user = Object.assign(c.user, user) - - let orderFromServer = await c.checkout(payment) - - expect(orderFromServer).toBeDefined() - - if (orderFromServer) { - expect(analyticsArgs[0]).toBe('Completed Order') - expect(analyticsArgs[1].total).toBe(orderFromServer.total / 100) - expect(analyticsArgs[1].shipping).toBe(orderFromServer.shipping / 100) - expect(analyticsArgs[1].tax).toBe(orderFromServer.tax / 100) - expect(analyticsArgs[1].discount).toBe(orderFromServer.discount /100) - expect(analyticsArgs[1].coupon).toBe(orderFromServer.couponCodes ? orderFromServer.couponCodes[0] : '') - expect(analyticsArgs[1].currency).toBe('usd') - expect(c.order.number).toBeDefined() - } - }, 10000) - - test('should checkout an order with metadata and templateId', async () => { - let order = { - currency: 'usd', - mode: 'contribution', - subtotal: 123456, - shippingAddress: { - line1: 'somewhere', - city: 'kansas city', - state: 'mo', - postalCode: '64081', - country: 'us', - }, - templateId: 'test', - metadata: { - data1: 1, - data2: 'test', - data3: ['a', 'b', 'c'] - }, - } - - let user = { - email: 'test@hanzo.io', - firstName: 'test', - lastName: 'test', - } - - let payment = { - account: { - name: 'test', - number: '4242424242424242', - cvc: '424', - month: '1', - year: '2040', - }, - } - - let c = new Commerce(client, order, [], [], analytics) - expect(c.order.subtotal).toBe(123456) - - c.user = Object.assign(c.user, user) - - let orderFromServer = await c.checkout(payment) - - expect(orderFromServer).toBeDefined() - - if (orderFromServer) { - expect(analyticsArgs[0]).toBe('Completed Order') - expect(analyticsArgs[1].total).toBe(orderFromServer.total / 100) - expect(analyticsArgs[1].shipping).toBe(orderFromServer.shipping / 100) - expect(analyticsArgs[1].tax).toBe(orderFromServer.tax / 100) - expect(analyticsArgs[1].discount).toBe(orderFromServer.discount /100) - expect(analyticsArgs[1].coupon).toBe(orderFromServer.couponCodes ? orderFromServer.couponCodes[0] : '') - expect(analyticsArgs[1].currency).toBe('usd') - expect(c.order.number).toBeDefined() - expect(orderFromServer.metadata).toBeDefined() - expect(orderFromServer.templateId).toBe('test') - } - }, 10000) - - test('checkouts using the same email should have the same userId', async () => { - let order = { - currency: 'usd', - mode: 'contribution', - subtotal: 123456, - shippingAddress: { - line1: 'somewhere', - city: 'kansas city', - state: 'mo', - postalCode: '64081', - country: 'us', - }, - } - - let user = { - email: 'test@hanzo.io', - firstName: 'test', - lastName: 'test', - } - - let payment = { - account: { - name: 'test', - number: '4242424242424242', - cvc: '424', - month: '1', - year: '2040', - }, - } - - let c = new Commerce(client, order, [], [], analytics) - expect(c.order.subtotal).toBe(123456) - - c.user = Object.assign(c.user, user) - - let orderFromServer = await c.checkout(payment) - - expect(orderFromServer).toBeDefined() - - let c2 = new Commerce(client, order, [], [], analytics) - expect(c2.order.subtotal).toBe(123456) - - c2.user = Object.assign(c2.user, user) - - let orderFromServer2 = await c2.checkout(payment) - - expect(orderFromServer2).toBeDefined() - - if (orderFromServer && orderFromServer2) { - expect(orderFromServer.userId).toBe(orderFromServer2.userId) - } - }, 20000) - - test('checkouts not using the same email should have different userId', async () => { - let order = { - currency: 'usd', - mode: 'contribution', - subtotal: 123456, - shippingAddress: { - line1: 'somewhere', - city: 'kansas city', - state: 'mo', - postalCode: '64081', - country: 'us', - }, - metadata: { - data1: 1, - data2: 'test', - data3: ['a', 'b', 'c'] - }, - } - - let user = { - email: 'test@hanzo.io', - firstName: 'test', - lastName: 'test', - } - - let payment = { - account: { - name: 'test', - number: '4242424242424242', - cvc: '424', - month: '1', - year: '2040', - }, - } - - let c = new Commerce(client, order, [], [], analytics) - expect(c.order.subtotal).toBe(123456) - - c.user = Object.assign(c.user, user) - - let orderFromServer = await c.checkout(payment) - - expect(orderFromServer).toBeDefined() - - let c2 = new Commerce(client, order, [], [], analytics) - expect(c2.order.subtotal).toBe(123456) - - c2.user = Object.assign(c2.user, user) - c2.user.email = 'test2@hanzo.io' - - let orderFromServer2 = await c2.checkout(payment) - - expect(orderFromServer2).toBeDefined() - - if (orderFromServer && orderFromServer2) { - expect(orderFromServer.userId).not.toBe(orderFromServer2.userId) - } - }, 20000) -}) - -describe('Commerce Persistence', () => { - test('should persist order products', async () => { - let c = new Commerce(client, {}) - - expect(c.order).toBeDefined() - - await c.set('sad-keanu-shirt', 1) - - let items = c.items - - expect(items.length).toBe(1) - - let c2 = new Commerce(client) - - await c2.bootstrapPromise - - expect(c2.order).toBeDefined() - - let items2 = c2.items - - expect(items2.length).toBe(1) - }) - - test('should persist order coupons', async () => { - let c = new Commerce(client, {}) - - expect(c.order).toBeDefined() - - await c.set('sad-keanu-shirt', 1) - await c.setCoupon('SUCH-COUPON') - - let coupon = c.order.couponCodes[0] - - expect(coupon).toBe('SUCH-COUPON') - - let c2 = new Commerce(client) - - await c2.bootstrapPromise - - expect(c2.order).toBeDefined() - - let coupon2 = c2.order.couponCodes[0] - - expect(coupon2).toBe('SUCH-COUPON') - }) -}) diff --git a/packages/commerce/service/impl/hanzo-adapter/commerceJs/Commerce.ts b/packages/commerce/service/impl/hanzo-adapter/commerceJs/Commerce.ts deleted file mode 100644 index 5c651346..00000000 --- a/packages/commerce/service/impl/hanzo-adapter/commerceJs/Commerce.ts +++ /dev/null @@ -1,749 +0,0 @@ -import { - action, - computed, - observable, - reaction, - runInAction, -} from 'mobx' - -import akasha from 'akasha' - -import LineItem from './LineItem' -import Order from './Order' -import User from './User' - -import type { - ICart, - ICartAPI, - IClient, - ICoupon, - IGeoRate, - IOrder, - IPayment, - IUser, -} from './types' - -import { - log -} from './utils' - -export type CartUpdateRequest = [string, number, boolean, boolean, boolean] -export type AnalyticsProductTransformFn = (v: any) => any - -/** - * Cart keeps track of items being added and removed from the cart/order - */ -export default class Commerce implements ICartAPI { - /** - * metadata of the current cart - */ - @observable - cart: ICart = { - id: '', - storeId: '', - email: '', - name: '', - } - - /** - * client is reference to a IClient - */ - @observable - client: IClient - - /** - * updateQueue contains the list of cart item updates so we can ensure - * updates are pushed fifo - */ - @observable - updateQueue: CartUpdateRequest[] = [] - - /** - * updateQueuePromise is a reference to the promise generated by the - * updateQueue for the purposes of awaiting outside of the direct call - */ - @observable - updateQueuePromise: Promise = new Promise((res) => { res() }) - - /** - * order is the object for tracking the user's order/cart info - */ - @observable - order: Order - - /** - * user is an object for tracking the user's contact information - */ - @observable - user: User - - /** - * payment is the object for tracking the user's payment information - */ - @observable - payment: any = {} - - /** - * payment is the object for tracking the user's payment information - */ - @observable - analytics: any - - /** - * analyticsProductTransform is a function for transforming analytics objects - * before sending them - */ - @observable - analyticsProductTransform: AnalyticsProductTransformFn - - /** - * bootstrapPromise executes after contructor completes any bootstrap - */ - @observable - bootstrapPromise: Promise - - /** - * the current storeId - */ - @observable - _storeId: string - - /** - * Create an instance of Commerce - * @param client is the http client for talking to carts - * @param order is the default order configuration - * @param taxRates is an array of IGeoRates for taxes - * @param shippingRates is an array of IGeoRates for taxes - * @param analytics is the call to the analytics library - * @param aPT is a function for transforming analytics objects before sending - * them - */ - constructor( - client: IClient, - order?: any, - taxRates: IGeoRate[] = [], - shippingRates: IGeoRate[] = [], - analytics: any = undefined, - aPT: AnalyticsProductTransformFn = (v) => v, - ) { - this.client = client - this.order = order ? new Order(order, taxRates, shippingRates, client, this) : Order.load(client, taxRates, shippingRates, this) - this._storeId = order ? order.storeId : '' - this.user = new User({}, this) - this.analytics = analytics - this.analyticsProductTransform = aPT - - this.bootstrapPromise = this.order.bootstrapPromise - // this.cartInit() - } - - /** - * @return the items on the order - */ - @computed - get items(): LineItem[] { - return this.order.items ?? [] - } - - /** - * @return the number of items on the order - */ - @computed - get size(): number { - return this.order.size - } - - /** - * Get the cart id - * @return the cartId of the current cart from storage. If there is no - * current cart, return empty - */ - @computed - get cartId(): string { - return akasha.get('cart.id') ?? '' - } - - /** - * Get the cart id - */ - @computed - get isCartInit(): boolean { - return !!this.cartId - } - - @observable - async setStoreId(sId: string): Promise { - this._storeId = sId - this.order.storeId = sId - - // refresh all items - const ps = this.items.map((li) => { - return this.refresh(li.id) - }) - - this.cartSetStore(sId) - - await Promise.all(ps) - } - - @computed - get storeId() : string { - return this._storeId - } - - /** - * Initialize the cart system. - * @return initialized or recovered cart instance - */ - @action - async cartInit(): Promise { - // check for if the cartId exists and either create a new cart or load cart - // id - if (!this.isCartInit) { - this.cart = await this.client.cart.create() - akasha.set('cart', this.cart) - } else { - this.cart.id = this.cartId - } - - runInAction(() => { - this.order = new Order(akasha.get('order'), [], [], this.client, this) - }) - - return this.cart - } - - /** - * Get the current state of a specific lineitem - * @param id the product id of a lineitem - * @return lineitem or undefined if product isn't in cart - */ - async get(id: string, ): Promise { - // Check the item on the order - let item = this.order.get(id) - - if (item) { - return item - } - - // Check the item in the queue - for (const request of this.updateQueue) { - if (request[0] !== id) { - continue - } - - // TODO: we should await the update queue and await instead - const li = new LineItem({ - id: request[0], - quantity: request[1], - locked: request[2], - ignore: request[3], - storeId: this.storeId, - }, this.client) - - try { - await li.bootstrapPromise - } catch (err) { - log('get error', err) - return - } - - return li - } - } - - /** - * Set a lineitem by product id. Add lineitem to asynchronous update queue - * @param id productId - * @param quantity amount of productId in cart - * @param locked is this lineitem modifiable in the UI - * @param ignore is this lineitem ignored by the UI (loading in progress, - * freebie etc) - * @return promise for when all set operations are completed - */ - @action - async set(id: string, quantity: number, locked=false, ignore=false, force=false): Promise { - if (this.updateQueue.length === 0) { - this.updateQueue.push([id, quantity, locked, ignore, force]) - this.updateQueuePromise = this.executeUpdates() - await this.updateQueuePromise - } else { - this.updateQueue.push([id, quantity, locked, ignore, force]) - await this.updateQueuePromise - } - } - - /** - * Refresh a lineitem by product id. Add lineitem to asynchronous update queue - * @return return the LineItem if it exists or nothing if it doesn't. - */ - @action - async refresh(id: string): Promise { - // console.log('refresh', id) - let item = await this.get(id) - if (item) { - await this.set(id, item.quantity, item.locked, item.ignore, true) - // console.log('refresh2', id) - return await this.get(id) - } - } - - /** - * Execute all queued updates - * @return promise for when all queued updates are done - */ - @action - async executeUpdates(): Promise { - const items = this.items - - if (!this.updateQueue.length) { - return - } - - let updateQueueRequest = this.updateQueue[0] - - // Resolve or escape if empty queue - if (!updateQueueRequest) { - this.updateQueue.shift() - return - } - - let [id, quantity, locked, ignore, force] = updateQueueRequest - // log('eu', id) - - // Resolve or escape if itemless mode - if (this.order.inItemlessMode && quantity > 0 && !force) { - this.updateQueue.shift() - return await this.executeUpdates() - } - - // log('eu2') - - // handle negative quantities. - if (quantity < 0) { - this.updateQueue.shift() - quantity = 0 - } - - // delete item - if (quantity === 0) { - await this.del(id) - this.updateQueue.shift() - return await this.executeUpdates() - } - - // log('eu3') - - // console.log('eu', updateQueueRequest) - - // try and update item quantity - if (await this.executeUpdateItem(id, quantity, locked, ignore) != null) { - this.updateQueue.shift() - return await this.executeUpdates() - } - - // log('eu4') - - // Fetch up to date information at time of checkout openning - // TODO: Think about revising so we don't report old prices if they changed after checkout is open - - const li = new LineItem({ - id, - quantity, - locked, - ignore, - storeId: this.storeId, - }, this.client) - - // log('eu4.5') - - try { - await li.bootstrapPromise - } catch (err) { - log('set error', err) - this.updateQueue.shift() - return await this.executeUpdates() - } - - // log('eu5', this.analytics) - - runInAction(() => { - items.push(li) - - let a = { - id: li.productId, - sku: li.productSlug, - name: li.productName, - quantity: quantity, - price: li.price / 100 - } - - if (this.analytics) { - if (this.analyticsProductTransform != null) { - a = this.analyticsProductTransform(a) - } - - this.analytics.track('Added Product', a) - } - }) - - await this.cartSetItem(li.productId, quantity) - - this.updateQueue.shift() - return this.executeUpdates() - } - - /** - * Execute update for item - * @param id productId - * @param quantity amount of productId in cart - * @param locked is this lineitem modifiable in the UI - * @param ignore is this lineitem ignored by the UI (loading in progress, - * freebie etc) - * @return LineItem if item is updated or undefined if something was invalid - */ - @action - async executeUpdateItem(id: string, quantity: number, locked: boolean, ignore: boolean): Promise { - // console.log('eui', id) - - log('eui', id) - const items = this.items - - for (const k in items) { - let item = items[k] - // ignore if not a match to id - if ( - item.id !== id && - item.productId !== id && - item.productSlug !== id && - item.storeId === this.storeId - ) { - continue - } - - if (item.storeId !== this.storeId) { - // console.log('eui2', item.storeId, this.storeId) - - item = new LineItem({ - id, - quantity, - locked, - ignore, - storeId: this.storeId, - }, this.client) - - items[k] = item - - try { - await item.bootstrapPromise - } catch (err) { - log('setItem storeId update error', err) - } - } - - // log('eu4.5') - - const oldValue = item.quantity - - item.quantity = quantity - item.locked = locked - item.ignore = ignore - - const newValue = quantity - - const deltaQuantity = newValue - oldValue - if (deltaQuantity > 0) { - let a = { - id: item.productId, - sku: item.productSlug, - name: item.productName, - quantity: deltaQuantity, - price: item.price / 100 - } - - if (this.analytics) { - if (this.analyticsProductTransform != null) { - a = this.analyticsProductTransform(a) - } - - this.analytics.track('Added Product', a) - } - - this.cartSetItem(item.productId, quantity) - } else if (deltaQuantity < 0) { - let a = { - id: item.productId, - sku: item.productSlug, - name: item.productName, - quantity: deltaQuantity, - price: item.price / 100 - } - - if (this.analytics) { - if (this.analyticsProductTransform != null) { - a = this.analyticsProductTransform(a) - } - - this.analytics.track('Removed Product', a) - } - } - - this.items[k].quantity = quantity - this.items[k].locked = locked - this.items[k].ignore = ignore - - await this.cartSetItem(item.productId, quantity) - - return this.items[k] - } - } - - /** - * Delete an item - * @param id productId - * @return LineItem that was deleted - */ - @action - async del(id: string): Promise { - const items = this.items - let itemToDeleteIndex: number = items.length - - for (const k in items) { - const item = items[k] - if( - item.productId === id || - item.productSlug === id || - item.id === id - ) { - itemToDeleteIndex = parseInt(k) - break - } - } - - if (itemToDeleteIndex >= items.length) { - return - } - - const item = items[itemToDeleteIndex] - - // Remove the itemToDelete from the items list - this.items.splice(itemToDeleteIndex, 1) - - let a: any = { - id: item.productId, - sku: item.productSlug, - name: item.productName, - quantity: item.quantity, - price: item.price / 100, - } - - if (this.analytics) { - if (this.analyticsProductTransform != null) { - a = this.analyticsProductTransform(a) - } - - this.analytics.track('Removed Product', a) - } - - await this.cartSetItem(item.productId, 0) - - runInAction(() => { - item.quantity = 0 - }) - - return item - } - - /** - * Set the cart's items directly on the server (shouldn't be used directly in high level - * operations) - * @param id productId - * @param quantity of product - * @return ICart returned from server - */ - @action - async cartSetItem(id: string, quantity: number): Promise { - if (this.isCartInit) { - // console.log('cart item') - this.cart.id = this.cartId - return this.client.cart.set({ - id: this.cartId, - productId: id, - quantity: quantity, - storeId: this.storeId, - }) - } - } - - /** - * Set the cart's store directly on the server (shouldn't be used directly in high level - * operations) - * @param storeId id of the store - * @return ICart returned from server - */ - @action - async cartSetStore(storeId: string): Promise { - if (this.isCartInit) { - this.cart.id = this.cartId - this.cart.storeId = storeId || this.storeId - return this.client.cart.update(this.cart) - } - } - - /** - * Set the cart's email directly on the server (shouldn't be used directly in high level - * operations) - * @param email user email - * @return ICart returned from server - */ - @action - async cartSetEmail(email: string): Promise { - if (this.isCartInit) { - this.cart.id = this.cartId - this.cart.email = this.user.email - return this.client.cart.update(this.cart) - } - } - - /** - * Set the cart's user name directly on the server (shouldn't be used directly in high level - * operations) - * @param name user's name - * @return ICart returned from server - */ - @action - async cartSetName(name: string): Promise { - if (this.isCartInit) { - this.cart.id = this.cartId - this.cart.name = name - return this.client.cart.update(this.cart) - } - } - - /** - * Set the cart's user name directly on the server (shouldn't be used directly in high level - * operations) - * @param name user's name - * @return ICart returned from server - */ - @action - async clear(): Promise{ - this.updateQueue.length = 0 - const itemsClone = this.items.slice(0) - - await Promise.all(itemsClone.map((item) => this.set(item.productId, 0))) - - return - } - - /** - * Apply a coupon/promoCode - * @param code coupon code/ID - * @return ICoupon returned from server - */ - @action - async setCoupon(code?: string): Promise { - if (code) { - try { - let coupon = await this.client.coupon.get(code) - if (!coupon.enabled) { - return - } - - runInAction(() => { - this.order.coupon = coupon - this.order.couponCodes = [code] - }) - - if (coupon.freeProductId) { - await this.client.product.get(coupon.freeProductId) - } - - return coupon - } catch (err) { - log('promoCode error', err) - - throw err - } - } - } - - /** - * Checkout the order - * @param payment contains the user's credit card credentials - * @return IOrder returned from server's capture endpoint - */ - @action - async checkout(payment: IPayment): Promise { - // TODO: Add support for referral programs back - let order: IOrder = Object.assign({}, this.order.data) - let user: IUser = Object.assign({}, this.user) - - let opts = { - order, - payment, - user, - } - - try { - let authorizedOrder = await this.client.checkout.authorize(opts) - - if (authorizedOrder) { - runInAction(() => { - this.order.id = (authorizedOrder as IOrder).id - this.order.userId = (authorizedOrder as IOrder).userId - }) - - let capturedOrder = await this.client.checkout.capture(this.order.id) - - if (!capturedOrder) { - throw new Error('Checkout failed for unknown reasons, please try again later.') - } - - runInAction(() => { - this.order.number = (capturedOrder as IOrder).number - }) - - let options = { - orderId: capturedOrder.id, - total: capturedOrder.total / 100, - // revenue: parseFloat(order.total/100), - shipping: capturedOrder.shipping / 100, - tax: capturedOrder.tax / 100, - discount: capturedOrder.discount / 100, - coupon: capturedOrder.couponCodes ? capturedOrder.couponCodes[0] : '', - currency: capturedOrder.currency, - products: [] as any[], - }; - - for (const item of this.items) { - let a = { - id: item.productId, - sku: item.productSlug, - name: item.productName, - quantity: item.quantity, - price: item.price / 100, - }; - - if (this.analytics) { - if (this.analyticsProductTransform != null) { - a = this.analyticsProductTransform(a) - } - - } - options.products.push(a) - } - - if (this.analytics) { - this.analytics.track('Completed Order', options); - } - - return capturedOrder - } - } catch (err) { - console.log('checkout error', err) - - throw err - } - } -} diff --git a/packages/commerce/service/impl/hanzo-adapter/commerceJs/LineItem.ts b/packages/commerce/service/impl/hanzo-adapter/commerceJs/LineItem.ts deleted file mode 100644 index dbef7278..00000000 --- a/packages/commerce/service/impl/hanzo-adapter/commerceJs/LineItem.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - observable, - computed -} from 'mobx' - -import Product from './Product' - -import type { - ILineItem, - IProductClient -} from './types' - -/** - * A combination of cart and quantity - */ -export default class LineItem extends Product implements ILineItem { - @observable - quantity: number = 1 - - @observable - locked: boolean = false - - @observable - ignore: boolean = false - - constructor(raw: any, client: IProductClient) { - super(raw, client) - - this.quantity = raw.quantity - this.locked = raw.locked - this.ignore = raw.ignore - } - - @computed - get total() { - return this.quantity * this.price - } - - @computed - get data(): ILineItem { - return { - id: this.id, - productId: this.productId, - slug: this.slug, - productSlug: this.productSlug, - name: this.name, - productName: this.name, - price: this.price, - listPrice: this.listPrice, - description: this.description, - imageURL: this.imageURL, - image: { - url: this.imageURL, - }, - quantity: this.quantity, - locked: this.locked, - ignore: this.ignore, - } - } -} - diff --git a/packages/commerce/service/impl/hanzo-adapter/commerceJs/Order.ts b/packages/commerce/service/impl/hanzo-adapter/commerceJs/Order.ts deleted file mode 100644 index 75f8907a..00000000 --- a/packages/commerce/service/impl/hanzo-adapter/commerceJs/Order.ts +++ /dev/null @@ -1,360 +0,0 @@ -import { - action, - autorun, - computed, - observable, - reaction, -} from 'mobx' - -import { - closestGeoRate -} from './utils' - -import akasha from 'akasha' - -import LineItem from './LineItem' - -import type { - IAddress, - ICartAPI, - ICoupon, - IGeoRate, - IOrder, - IOrderClient, -} from './types' - -/** - * Order contains information about what the user is buying - */ -export default class Order implements IOrder { - @observable - id: string = '' - - @observable - userId: string = '' - - @observable - items: LineItem[] - - @observable - type: string - - @observable - storeId: string - - @observable - currency: string - - @observable - mode: 'deposit' | 'contribution' | '' - - @observable - client: IOrderClient - - @observable - couponCodes: string[] = [] - - @observable - coupon: ICoupon | undefined - - @observable - taxRates: IGeoRate[] - - @observable - shippingRates: IGeoRate[] - - @observable - shippingAddress: IAddress - - @observable - number?: number - - @observable - metadata: any - - @observable - referrerId: string = '' - - @observable - templateId: string = '' - - /** - * Overwrite subtotal only available in itemless modes - */ - @observable - _subtotal: number = 0 - - /** - * bootstrapPromise executes after contructo completes any bootstrapp (mostly coupon and lineitems in this case) - */ - @observable - bootstrapPromise: Promise - - constructor( - raw: any = {}, - taxRates: IGeoRate[] = [], - shippingRates: IGeoRate[] = [], - client: IOrderClient, - cartAPI: ICartAPI, - ) { - this.client = client - this.taxRates = taxRates - this.shippingRates = shippingRates - - this.items = raw.items ? raw.items.map((x: any) => new LineItem(x, client)): [] - this.bootstrapPromise = Promise.all(this.items.map((x) => x.bootstrapPromise)) - - this.type = raw.type ?? 'stripe' - this.storeId = raw.storeId ?? '' - this.currency = (raw.currency && raw.currency.toLowerCase) ? raw.currency.toLowerCase() : 'usd' - - this.referrerId = raw.referrerId - this.mode = raw.mode ?? '' - this.subtotal = raw.subtotal ?? 0 - this.templateId = raw.templateId - - this.shippingAddress = raw.shippingAddress ?? { - country: '', - state: '', - city: '', - postalCode: '', - } - - this.metadata = raw.metadata - - if (raw.couponCodes && raw.couponCodes.length > 0) { - this.bootstrapPromise = Promise.all([cartAPI.setCoupon(raw.couponCodes[0]), this.bootstrapPromise]) - } - - // Save order on any update - autorun(() => { - Order.save(this) - }) - - // clear items when we switch to itemless mode - reaction( - () => this.mode, - () => { - if (this.inItemlessMode) { - cartAPI.clear() - } - } - ) - } - - get(id: string): LineItem | undefined { - for (const item of this.items) { - if (item.id !== id && item.productId !== id && item.productSlug !== id) { - continue - } - - return item - } - - return undefined - } - - /** - * @return the number of items on the order - */ - @computed - get size(): number { - return this.items.length - } - - @computed - get inItemlessMode(): boolean { - return this.mode === 'deposit' || this.mode === 'contribution' - } - - static load( - client: IOrderClient, - taxRates: IGeoRate[] = [], - shippingRates: IGeoRate[] = [], - cartAPI: ICartAPI, - ) { - return new Order(akasha.get('order') ?? {}, [], [], client, cartAPI) - } - - static save(order: Order) { - akasha.set('order', order.data) - } - - static clear() { - akasha.remove('order') - } - - @computed - get discount(): number { - const coupon = this.coupon - - let discount = 0 - - if (coupon != null) { - switch (coupon.type) { - case 'flat': - if ((coupon.productId == null) || (coupon.productId === '')) { - discount = (coupon.amount || 0) - } else { - for (const item of this.items) { - if (item.productId === coupon.productId) { - let { - quantity - } = item - - if (coupon.once) { - quantity = 1 - } - - discount += (coupon.amount || 0) * quantity - } - } - } - break - - case 'percent': - if ((coupon.productId == null) || (coupon.productId === '')) { - for (const item of this.items) { - let { - quantity - } = item - - if (coupon.once) { - quantity = 1 - } - - discount += (coupon.amount || 0) * item.price * quantity * 0.01 - } - } else { - for (const item of this.items) { - if (item.productId === coupon.productId) { - let { - quantity - } = item - if (coupon.once) { - quantity = 1 - } - discount += (coupon.amount || 0) * item.price * quantity * 0.01 - } - } - } - discount = Math.floor(discount) - break - } - } - - return discount - } - - @computed - get subtotal(): number { - if (this.inItemlessMode) { - return this._subtotal - } - - let subtotal = 0 - let items = this.items - - for (const item of items) { - subtotal += item.price * item.quantity - } - - return subtotal - } - - set subtotal(st: number) { - if (this.inItemlessMode) { - this._subtotal = st - } - } - - @computed - get taxRate(): IGeoRate { - let rate = { - percent: 0, - cost: 0, - } - - const country = this.shippingAddress.country - const state = this.shippingAddress.state - const city = this.shippingAddress.city - const postalCode = this.shippingAddress.postalCode - - let [gr, l, i] = closestGeoRate( - this.taxRates, - country, - state, - city, - postalCode, - this.subtotal, - ) - - return gr ?? rate - } - - @computed - get tax(): number { - const taxRate = this.taxRate - return Math.ceil((taxRate.percent != null ? taxRate.percent : 0) * this.subtotal) + - (taxRate.cost != null ? taxRate.cost : 0) - } - - @computed - get shippingRate(): IGeoRate { - let rate = { - percent: 0, - cost: 0, - } - - const country = this.shippingAddress.country - const state = this.shippingAddress.state - const city = this.shippingAddress.city - const postalCode = this.shippingAddress.postalCode - - let [gr, l, i] = closestGeoRate( - this.shippingRates, - country, - state, - city, - postalCode, - this.subtotal, - ) - - return gr ?? rate - } - - @computed - get shipping(): number { - const shippingRate = this.shippingRate - return Math.ceil((shippingRate.percent != null ? shippingRate.percent : 0) * this.subtotal) + - (shippingRate.cost != null ? shippingRate.cost : 0) - } - - @computed - get total(): number { - return this.subtotal + this.shipping + this.tax - this.discount - } - - @computed - get data(): IOrder { - return { - id: this.id, - userId: this.userId, - currency: this.currency, - items: this.items.map((item) => item.data), - shippingAddress: this.shippingAddress, - mode: this.mode, - storeId: this.storeId, - type: this.type, - subtotal: this.subtotal, - total: this.total, - tax: this.tax, - shipping: this.shipping, - discount: this.discount, - couponCodes: this.couponCodes, - metadata: this.metadata, - referrerId: this.referrerId, - templateId: this.templateId, - } - } -} diff --git a/packages/commerce/service/impl/hanzo-adapter/commerceJs/Product.ts b/packages/commerce/service/impl/hanzo-adapter/commerceJs/Product.ts deleted file mode 100644 index 994f69c4..00000000 --- a/packages/commerce/service/impl/hanzo-adapter/commerceJs/Product.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { - observable, -} from 'mobx' - -import type { - IProduct, - IProductClient, -} from './types' - -import { - log, -} from './utils' - -/** - * Product is something that goes in a cart, we sync these from the server but - * only keep the fields we care about - */ -export default class Product implements IProduct { - @observable - id: string - - @observable - productId: string - - @observable - slug: string - - @observable - productSlug: string - - @observable - name: string - - @observable - productName: string - - @observable - price: number - - @observable - listPrice: number - - @observable - description: string - - @observable - bootstrapPromise: Promise - - @observable - client: IProductClient - - @observable - imageURL: string - - @observable - image: { - url: string, - } - - @observable - storeId: string - - constructor(raw: any, client: IProductClient) { - this.client = client - - this.id = raw.id ?? '' - this.productId = raw.productId ?? '' - this.slug = raw.slug ?? '' - this.productSlug = raw.productSlug ?? '' - this.name = raw.name ?? '' - this.productName = raw.productName ?? '' - this.price = raw.price ?? 0 - this.listPrice = raw.listPrice ?? 0 - this.description = raw.description ?? '' - this.imageURL = raw.imageURL ?? '' - if (raw && raw.image) { - this.image = { - url: raw.image.url, - } - } else { - this.image = { - url: '', - } - } - this.storeId = raw.storeId ?? '' - - if (this.storeId) { - this.bootstrapPromise = (async (self) => { - try { - let path = client.client.url(`/store/${self.storeId}/product/${self.id}`) - let res - if (client.fetch) { - res = await client.fetch(`${path}?token=${client.client.getKey()}`) - } else { - res = await fetch(`${path}?token=${client.client.getKey()}`) - } - let product = await res.json() - - Object.assign(self, product) - self.productId = product.id - self.productSlug = product.slug - self.productName = product.name - self.imageURL = product.image.url - return self - } catch(err) { - log('loadProduct error', err) - throw err - } - })(this) - } else { - this.bootstrapPromise = client.product.get(this.id).then((product: IProduct): IProduct => { - Object.assign(this, product) - this.productId = product.id - this.productSlug = product.slug - this.productName = product.name - this.imageURL = product.image.url - return this - }).catch((err) => { - log('loadProduct error', err) - throw err - }) - } - } -} - diff --git a/packages/commerce/service/impl/hanzo-adapter/commerceJs/User.ts b/packages/commerce/service/impl/hanzo-adapter/commerceJs/User.ts deleted file mode 100644 index d9223118..00000000 --- a/packages/commerce/service/impl/hanzo-adapter/commerceJs/User.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { - autorun, - observable, - reaction -} from 'mobx' - -import type { - ICartAPI, - IUser, -} from './types' - -import akasha from 'akasha' - -export default class User implements IUser { - @observable - email: string - - @observable - firstName: string - - @observable - lastName: string - - constructor( - raw: any = {}, - cartAPI: ICartAPI, - ) { - this.email = raw.email ?? '' - this.firstName = raw.firstName ?? '' - this.lastName = raw.lastName ?? '' - - // Save order on any update - // autorun(() => { - // User.save(this) - // }) - - // Define reaction for storeid - reaction ( - () => this.email, - (email) => { - cartAPI.cartSetEmail(email) - } - ) - - // Define reaction for username - reaction ( - () => this.firstName + ' ' + this.lastName, - (name) => { - cartAPI.cartSetName(name) - } - ) - } - - // static load(cartAPI: ICartAPI) { - // return new User(akasha.get('user'), cartAPI) - // } - - // static save(user: User) { - // akasha.set('user', user) - // } - - // static clear(user: User) { - // akasha.remove('user') - // } -} diff --git a/packages/commerce/service/impl/hanzo-adapter/commerceJs/index.ts b/packages/commerce/service/impl/hanzo-adapter/commerceJs/index.ts deleted file mode 100644 index f62234eb..00000000 --- a/packages/commerce/service/impl/hanzo-adapter/commerceJs/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export { default as Commerce } from './Commerce' -export { default as LineItem } from './LineItem' -export { default as Order } from './Order' -export { default as Product } from './Product' -export { default as User } from './User' - -export * from './utils' - -import { type IProduct } from './types' - -export { - type IProduct -} diff --git a/packages/commerce/service/impl/hanzo-adapter/commerceJs/types.ts b/packages/commerce/service/impl/hanzo-adapter/commerceJs/types.ts deleted file mode 100644 index d0bc2b90..00000000 --- a/packages/commerce/service/impl/hanzo-adapter/commerceJs/types.ts +++ /dev/null @@ -1,205 +0,0 @@ -/** - * Cart Abstraction - */ -export interface ICart { - id: string - email: string - name: string - storeId: string -} - -/** - * CartItem Abstraction - */ -export interface ICartItem { - id: string - productId: string - quantity: number - storeId: string -} - -/** - * Product Abstraction - */ -export interface IProduct { - id: string - productId: string - slug: string - productSlug: string - name: string - productName: string - price: number - listPrice: number - description: string - image: { - url: string, - }, - imageURL: string -} - -/** - * LineItem Abstraction - */ -export interface ILineItem extends IProduct { - quantity: number - locked: boolean - ignore: boolean -} - -/** - * Order Abstraction - */ -export interface IOrder { - id: string - userId: string - currency: string - shippingAddress: IAddress - items: ILineItem[] - mode: 'deposit' | 'contribution' | '' - storeId: string - type: string - number?: number - metadata?: any - referrerId: string - templateId: string - - subtotal: number - total: number - tax: number - shipping: number - discount: number - couponCodes: string[] -} - -/** - * Payment Abstraction - */ -export interface IPayment { - account: { - name: string, - number: string, - cvc: string, - month: string, - year: string, - }, -} - -/** - * User Abstraction - */ -export interface IUser { - email: string - firstName: string - lastName: string -} - -/** - * Coupon Abstraction - */ -export interface ICoupon { - amount: number - code: string - enabled: boolean - freeProductId: string - once: boolean - productId: string - type: 'flat' | 'percent' -} - -/** - * GeoRate Abstraction - */ -export interface IGeoRate { - country?: string - state?: string - postalCodes?: string - city?: string - above?: number - below?: number - - percent: number - cost: number -} - -/** - * Address Abstraction - */ -export interface IAddress { - country: string - state: string - city: string - postalCode: string -} - -/** - * Abstraction of Cart API on the Commerce object - */ -export interface ICartAPI { - cartSetStore(storeId: string): Promise - cartSetEmail(email: string): Promise - cartSetName(name: string): Promise - setCoupon(code?: string): Promise - clear(): Promise -} - -/** - * Checkout Client Authorize Config - */ -export interface IAuthorizeConfig { - user: IUser - order: IOrder - payment: IPayment -} - -/** - * Checkout Client - */ -export interface ICheckoutClient { - checkout: { - authorize(opts: IAuthorizeConfig): Promise - capture(id: string): Promise - } -} - -/** - * Cart Client - */ -export interface ICartClient { - cart: { - set: (cartItem: ICartItem) => Promise - create: () => Promise - update: (cart: ICart) => Promise - } -} - -/** - * Coupon Client - */ -export interface ICouponClient { - coupon: { - get: (code: string) => Promise - } -} - -/** - * Product Client - */ -export interface IProductClient { - product: { - get: (id: string) => Promise - } - client: { - getKey: () => string - url: (path: string) => string - } - fetch?: (path: string) => Promise -} - -/** - * Cart representation - */ -export interface IOrderClient extends IProductClient{ -} - -export interface IClient extends ICartClient, IOrderClient, IProductClient, ICouponClient, ICheckoutClient { -} diff --git a/packages/commerce/service/impl/hanzo-adapter/commerceJs/utils.ts b/packages/commerce/service/impl/hanzo-adapter/commerceJs/utils.ts deleted file mode 100644 index e64adfb3..00000000 --- a/packages/commerce/service/impl/hanzo-adapter/commerceJs/utils.ts +++ /dev/null @@ -1,153 +0,0 @@ -import type { - IGeoRate -} from './types' - -let printLevel = 'none' - -export const setLogLevel = (level: string) => { - printLevel = level -} - -export const log = (...args: any) => { - if (printLevel != 'none') { - console.log.apply(null, args as any) - } -} - -// These functions need to be synced with the backend - -// Input sanitization for georate compared -export const clean = function(str: string | undefined | null) { - str = str ?? '' - return str.toUpperCase().replace(/\s/g, '') -} - -/** - * Check if georate matches country + state + city/postalCode - * We assume that georates are built correctly (they are pulled from server) - * @param grs list of GeoRates - * @param ctr country - * @param st state - * @param ct city - * @param pc postalCode - * @return return if it is matched and level of match - */ -export const matchesGeoRate = ( - g: IGeoRate, - country: string, - state: string, - city: string, - postalCode: string, - price: number, -): [boolean, number] => { - const ctr = clean(country) - const st = clean(state) - const ct = clean(city) - const pc = clean(postalCode) - - const ctr2 = clean(g.country) - - if (g.above) { - // above reject - if (price < g.above) { - return [false, -1] - } - } else if (g.below) { - // below reject - if (price >= g.below) { - return [false, -1] - } - } - - // Invalid input - if (!ctr && !ct && !pc) { - return [false, 0] - } - - // Country is Wild Card - if (!ctr2) { - return [true, 0] - } - - if (ctr2 === ctr) { - const st2 = clean(g.state) - - // "Country Match" - if (!st2) { - return [true, 1] - } - - if (st2 === st) { - const ct2 = clean(g.city) - - // State Match - if (!ct2 && !g.postalCodes) { - return [true, 2] - } - - // City Match - if (ct2 && (ct2 === ct)) { - return [true, 3] - } - - if (g.postalCodes) { - const codes = g.postalCodes.split(',') - for (let code of Array.from(codes)) { - // Postal Code Match - if (clean(code) === pc) { - return [true, 3] - } - } - } - - // City/Postal Code Mismatch - return [false, 2] - } - - // State Mismatch - return [false, 1] - } - - // No Match - return [false, 0] -} - -/** - * Get the closest georate from a set of georates - * @param grs list of GeoRates - * @param ctr country - * @param st state - * @param ct city - * @param pc postalCode - * @return closest georate, level of match, and index - */ -export const closestGeoRate = ( - grs: IGeoRate[], - ctr: string, - st: string, - ct: string, - pc: string, - c: number, -): [IGeoRate | undefined, number, number] => { - let retGr: IGeoRate | undefined - let currentLevel = -2 - let idx = -1 - - for (let i in grs) { - const gr = grs[i] - - const [isMatch, level] = matchesGeoRate(gr, ctr, st, ct, pc, c) - - if (isMatch && (level > currentLevel)) { - if (level === 3) { - return [gr, level, parseInt(i, 10)] - } - - retGr = grs[i] - currentLevel = level - idx = parseInt(i, 10) - } - } - - return [retGr, currentLevel, idx] -} diff --git a/packages/commerce/service/impl/standalone/service.ts b/packages/commerce/service/impl/standalone/service.ts index 55b34ae1..e200aecb 100644 --- a/packages/commerce/service/impl/standalone/service.ts +++ b/packages/commerce/service/impl/standalone/service.ts @@ -62,7 +62,9 @@ class StandaloneCommerceService specifiedItems: computed, setCurrentItem: action, currentItem: computed, - specifiedCategories: computed + item: computed, + specifiedCategories: computed, + facetsValue: computed /* NOT setFacets. It implements it's action mechanism */ }) @@ -87,6 +89,11 @@ class StandaloneCommerceService this._currentItemSku = sku } + /* ObsLineItemRef */ + get item(): LineItem | undefined { + return this.currentItem + } + get currentItem(): LineItem | undefined { if (!this._currentItemSku) return undefined @@ -103,15 +110,14 @@ class StandaloneCommerceService } } - for (let category of this.specifiedCategories) { - if (categoriesTried.includes(category.id)) continue - const foundItem = - (category.products as LineItem[]).find((item) => (item.sku === this._currentItemSku)) - if (foundItem) { - return foundItem - } - } - return undefined + let foundItem: LineItem | undefined = undefined + this._categoryMap.forEach((category, categoryId) => { + if (foundItem) return + if (categoriesTried.includes(categoryId)) return + foundItem = (category.products as LineItem[]).find((item) => (item.sku === this._currentItemSku)) + }) + + return foundItem } setFacets(sel: FacetsValue): Category[] { @@ -124,6 +130,14 @@ class StandaloneCommerceService return this.specifiedCategories } + get facetsValue(): FacetsValue { + const result: FacetsValue = {} + for( let level in this._selectedFacets ) { + result[level] = [...this._selectedFacets[level]] + } + return result + } + get specifiedCategories(): Category[] { if (Object.keys(toJS(this._selectedFacets)).length === 0) { // FacetsDesc have never been set or unset, so cannot evaluate them diff --git a/packages/commerce/types/index.ts b/packages/commerce/types/index.ts index ddb30107..b3bc5280 100644 --- a/packages/commerce/types/index.ts +++ b/packages/commerce/types/index.ts @@ -72,13 +72,31 @@ interface LineItem extends Product { decrement(): void } +interface ObsLineItemRef { + get item(): LineItem | undefined +} + + +interface StringMutator { + get(): string | null + set(v: string | null): void +} + +interface StringArrayMutator { + get(): string[] | null + set(v: string[] | null): void +} + export { type Product, type Category, type LineItem, + type ObsLineItemRef, type FacetsDesc, type FacetsValue, type FacetValueDesc, + type StringMutator, + type StringArrayMutator } diff --git a/packages/commerce/util/index.ts b/packages/commerce/util/index.ts index 3c1bfcc4..c13ac07a 100644 --- a/packages/commerce/util/index.ts +++ b/packages/commerce/util/index.ts @@ -1,3 +1,4 @@ + export function toTitleCase(str: string) { return str.replace( /\w\S*/g, @@ -23,3 +24,5 @@ export function formatPrice(price: number): string { currency: 'USD', }); } + +export { default as useSkuAndFacetParams } from './use-sku-and-facet-params' \ No newline at end of file diff --git a/packages/commerce/util/use-sku-and-facet-params.ts b/packages/commerce/util/use-sku-and-facet-params.ts new file mode 100644 index 00000000..11926d99 --- /dev/null +++ b/packages/commerce/util/use-sku-and-facet-params.ts @@ -0,0 +1,157 @@ +import { + useEffect, + useRef, +} from 'react' +import { autorun, toJS, type IReactionDisposer } from 'mobx' + +import { + useQueryState, + parseAsString, + parseAsBoolean, +} from 'next-usequerystate' + +import type { Category, FacetsValue, StringMutator } from '../types' +import { useCommerce } from '../service' + +interface GetMutator { + (level: 1 | 2): StringMutator +} + +const useSkuAndFacetParams = ( + setLoading?: (l: boolean) => void +) => { + + const cmmc = useCommerce() + + const encRef = useRef<{ + category: Category | undefined + usingSkuMode: boolean + autoRunDisposer: IReactionDisposer | undefined + }>({ + category: undefined, + usingSkuMode: false, + autoRunDisposer: undefined, + }) + + const [level1, setLevel1] = useQueryState('lev1', + parseAsString.withDefault('').withOptions({ clearOnDefault: true }) + ) + const [level2, setLevel2] = useQueryState('lev2', + parseAsString.withDefault('').withOptions({ clearOnDefault: true }) + ) + + const [skuParam, setSkuParam] = useQueryState('sku') + const [addParam, setAddParam] = useQueryState('add', + parseAsBoolean.withDefault(false).withOptions({ clearOnDefault: true }) + ) + + let message = '' + + const directMutator: GetMutator = (level: 1 | 2): StringMutator => { + + const setLevel = (value: string, level: 1 | 2 ): void => { + const facets = cmmc.facetsValue + facets[level] = [value] + cmmc.setFacets(facets) + } + + const getLevelValueSafe = (level: 1 | 2): string | null => { + const facets = cmmc.facetsValue + if (!(level in facets) || facets[level].length === 0 ) { + return null + } + return facets[level][0] + } + + return { + get: () => (getLevelValueSafe(level)), + set: (v: string) => {setLevel(v, level)} + } + } + + const paramsMutator: GetMutator = (level: 1 | 2): StringMutator => { + return level === 1 ? { + get: () => (level1), + set: setLevel1 + } : { + get: () => (level2), + set: setLevel2 + } + } + + useEffect(() => { + + const setCurrentCategoryFromSku = (sku: string) => { + const toks: string[] = sku.split('-') + const categories = cmmc.setFacets({ + 1: [toks[1]], + 2: [toks[2]] + }) + encRef.current.category = categories[0] + } + + if (skuParam) { + cmmc.setCurrentItem(skuParam) + if (addParam && cmmc.currentItem!.quantity === 0) { + cmmc.currentItem!.increment() + setAddParam(false) + } + // Marks that we've been here so no need to + // create an autorun() instance twice. + if (!encRef.current.usingSkuMode) { + setCurrentCategoryFromSku(skuParam) + encRef.current.autoRunDisposer = autorun(() => { + if (cmmc.currentItem && cmmc.currentItem.sku !== skuParam) { + setSkuParam(cmmc.currentItem.sku) + } + }) + } + encRef.current.usingSkuMode = true + } + else if (encRef.current.category) { + //const catMutators = [{val: level1, set: setLevel1} , {val: level2, set: setLevel2}] + cmmc.setCurrentItem(encRef.current.category.products[0].sku) + } + setLoading && setLoading(false) + }, [skuParam, encRef.current.category]) + + // supposed to be when component unmounts + useEffect(() => (() => { + if (encRef.current.autoRunDisposer) { + //encRef.current.autoRunDisposer() // no idea why this seems to be called prematurely and so many times + } + }), []) + + + // Level Params Mode + useEffect(() => { + + if (encRef.current.usingSkuMode) return + + const facets: FacetsValue = { } + if (level1) { facets[1] = [level1] } + if (level2) { facets[2] = [level2] } + if (level1 && level2) { + const categories = cmmc.setFacets(facets) + if (categories.length > 1) { + console.error("CAT", categories.map((c) => (c.title))) + throw new Error ( "CategoryContents: More than one specified Category should never be possible with this UI!") + } + encRef.current.category = categories[0] + } + else { + message = 'Please select an option from each group above.' + } + setLoading && setLoading(false) + }, [level1 , level2]) + + return { + category: encRef.current.category, + message, + getMutator: encRef.current.usingSkuMode ? + directMutator : paramsMutator + } + +} + +export default useSkuAndFacetParams diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 404e3b42..7d96bbc6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -304,12 +304,6 @@ importers: specifier: 3.21.4 version: 3.21.4 devDependencies: - '@testing-library/jest-dom': - specifier: ^6.4.2 - version: 6.4.2 - '@testing-library/react': - specifier: ^14.2.1 - version: 14.2.1(react-dom@18.2.0)(react@18.2.0) '@types/react': specifier: ^18.2.48 version: 18.2.61 @@ -331,18 +325,12 @@ importers: '@hookform/resolvers': specifier: ^3.3.2 version: 3.3.4(react-hook-form@7.50.1) - akasha: - specifier: ^0.1.13 - version: 0.1.13 firebase: specifier: ^10.8.0 version: 10.8.1 lucide-react: specifier: ^0.307.0 version: 0.307.0(react@18.2.0) - midstream: - specifier: ^2.0.2 - version: 2.0.2 mobx: specifier: ^6.12.0 version: 6.12.0 @@ -352,6 +340,9 @@ importers: next: specifier: ^14.1.0 version: 14.1.0(react-dom@18.2.0)(react@18.2.0) + next-usequerystate: + specifier: ^1.17.0 + version: 1.17.0(next@14.1.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -371,9 +362,6 @@ importers: cross-fetch: specifier: ^4.0.0 version: 4.0.0 - hanzo.js: - specifier: ^2.5.13 - version: 2.5.13 typescript: specifier: ^5.3.3 version: 5.3.3 @@ -633,10 +621,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /@adobe/css-tools@4.3.3: - resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==} - dev: true - /@alloc/quick-lru@5.2.0: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -4036,65 +4020,6 @@ packages: engines: {node: '>=12'} dev: false - /@testing-library/dom@9.3.4: - resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==} - engines: {node: '>=14'} - dependencies: - '@babel/code-frame': 7.23.5 - '@babel/runtime': 7.24.0 - '@types/aria-query': 5.0.4 - aria-query: 5.1.3 - chalk: 4.1.2 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - pretty-format: 27.5.1 - dev: true - - /@testing-library/jest-dom@6.4.2: - resolution: {integrity: sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==} - engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - peerDependencies: - '@jest/globals': '>= 28' - '@types/bun': latest - '@types/jest': '>= 28' - jest: '>= 28' - vitest: '>= 0.32' - peerDependenciesMeta: - '@jest/globals': - optional: true - '@types/bun': - optional: true - '@types/jest': - optional: true - jest: - optional: true - vitest: - optional: true - dependencies: - '@adobe/css-tools': 4.3.3 - '@babel/runtime': 7.24.0 - aria-query: 5.3.0 - chalk: 3.0.0 - css.escape: 1.5.1 - dom-accessibility-api: 0.6.3 - lodash: 4.17.21 - redent: 3.0.0 - dev: true - - /@testing-library/react@14.2.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-sGdjws32ai5TLerhvzThYFbpnF9XtL65Cjf+gB0Dhr29BGqK+mAeN7SURSdu+eqgET4ANcWoC7FQpkaiGvBr+A==} - engines: {node: '>=14'} - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - dependencies: - '@babel/runtime': 7.24.0 - '@testing-library/dom': 9.3.4 - '@types/react-dom': 18.2.19 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - /@tootallnate/once@2.0.0: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} @@ -4107,10 +4032,6 @@ packages: dependencies: '@types/estree': 1.0.5 - /@types/aria-query@5.0.4: - resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - dev: true - /@types/body-parser@1.19.5: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: @@ -4676,14 +4597,6 @@ packages: uri-js: 4.4.1 dev: true - /akasha@0.1.13: - resolution: {integrity: sha512-Ugtb0p7160QJN0i720+B/I6W+DLbLMaQoMeMTDlhKql+oAcVE7RNFQ2+ZyQ/SrKaVz/mITM5juT3cfgfzRZh9g==} - engines: {node: '>=4.0.0'} - dependencies: - es-cookies: 2.1.18 - es-md5: 2.7.2 - dev: false - /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -4704,11 +4617,6 @@ packages: dependencies: color-convert: 2.0.1 - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - dev: true - /ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -4742,12 +4650,6 @@ packages: tslib: 2.6.2 dev: false - /aria-query@5.1.3: - resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} - dependencies: - deep-equal: 2.2.3 - dev: true - /aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} dependencies: @@ -4855,17 +4757,6 @@ packages: dev: false optional: true - /asn1@0.2.6: - resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} - dependencies: - safer-buffer: 2.1.2 - dev: true - - /assert-plus@1.0.0: - resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} - engines: {node: '>=0.8'} - dev: true - /ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} dev: true @@ -4890,6 +4781,9 @@ packages: /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + requiresBuild: true + dev: false + optional: true /autoprefixer@10.4.17(postcss@8.4.35): resolution: {integrity: sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==} @@ -4914,14 +4808,6 @@ packages: possible-typed-array-names: 1.0.0 dev: true - /aws-sign2@0.7.0: - resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} - dev: true - - /aws4@1.12.0: - resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==} - dev: true - /axe-core@4.7.0: resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==} engines: {node: '>=4'} @@ -4943,12 +4829,6 @@ packages: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: false - /bcrypt-pbkdf@1.0.2: - resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} - dependencies: - tweetnacl: 0.14.5 - dev: true - /bignumber.js@9.1.2: resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} requiresBuild: true @@ -4985,10 +4865,6 @@ packages: dependencies: fill-range: 7.0.1 - /broken@0.1.13: - resolution: {integrity: sha512-eGalaUo0vBUTyWXmbBU3j9NE8L4NpNCldk+IFh88E6CF1uwh+nHkMaIlyWANEDh8rDBa6WTr/Evb2U+5bqE1Ww==} - dev: true - /browserslist@4.23.0: resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -5054,10 +4930,6 @@ packages: /caniuse-lite@1.0.30001591: resolution: {integrity: sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==} - /caseless@0.12.0: - resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - dev: true - /ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -5069,14 +4941,6 @@ packages: escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk@3.0.0: - resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} - engines: {node: '>=8'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -5225,8 +5089,11 @@ packages: /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + requiresBuild: true dependencies: delayed-stream: 1.0.0 + dev: false + optional: true /comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -5285,10 +5152,6 @@ packages: /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - /core-util-is@1.0.2: - resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} - dev: true - /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: false @@ -5330,10 +5193,6 @@ packages: postcss-value-parser: 4.2.0 dev: false - /css.escape@1.5.1: - resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - dev: true - /cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -5417,13 +5276,6 @@ packages: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} dev: true - /dashdash@1.14.1: - resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} - engines: {node: '>=0.10'} - dependencies: - assert-plus: 1.0.0 - dev: true - /data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} @@ -5474,30 +5326,6 @@ packages: mimic-response: 3.1.0 dev: false - /deep-equal@2.2.3: - resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} - engines: {node: '>= 0.4'} - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - es-get-iterator: 1.1.3 - get-intrinsic: 1.2.4 - is-arguments: 1.1.1 - is-array-buffer: 3.0.4 - is-date-object: 1.0.5 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - isarray: 2.0.5 - object-is: 1.1.6 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - side-channel: 1.0.6 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.14 - dev: true - /deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -5528,6 +5356,9 @@ packages: /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + requiresBuild: true + dev: false + optional: true /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} @@ -5579,14 +5410,6 @@ packages: esutils: 2.0.3 dev: true - /dom-accessibility-api@0.5.16: - resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - dev: true - - /dom-accessibility-api@0.6.3: - resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} - dev: true - /dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} dependencies: @@ -5608,13 +5431,6 @@ packages: /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - /ecc-jsbn@0.1.2: - resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} - dependencies: - jsbn: 0.1.1 - safer-buffer: 2.1.2 - dev: true - /ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} dependencies: @@ -5735,12 +5551,6 @@ packages: resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} dev: true - /es-cookies@2.1.18: - resolution: {integrity: sha512-rSAeFubCRfkKc9rd4soJukMf9TkUcjlySJDYgbL4vwzgfSn3M9NLxKz0Z3jH1AvxYgt5Wkwr6JH4Nd+ZC4kqxQ==} - dependencies: - es-is: 3.3.10 - es-object-assign: 4.1.4 - /es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} @@ -5753,30 +5563,6 @@ packages: engines: {node: '>= 0.4'} dev: true - /es-get-iterator@1.1.3: - resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - is-arguments: 1.1.1 - is-map: 2.0.2 - is-set: 2.0.2 - is-string: 1.0.7 - isarray: 2.0.5 - stop-iteration-iterator: 1.0.0 - dev: true - - /es-hasown@0.1.1: - resolution: {integrity: sha512-Fn1akAiVJaWPN6f1kqDYY3JmWqeffUQH1MZn/SHh2vAgJBgRsFbruIDJHjGrA+Rd0sYfz0ixDGtZwUtzrRPULw==} - engines: {node: '>=4.0.0'} - - /es-is@3.3.10: - resolution: {integrity: sha512-xJHoQ6GM8ZdJm0J560ZPB3u/GrWkPd4OaGpPTl1jxdSby+O7BAuBeSHI/oYV/UgJT0fpihocs6ZA550k+vOvBg==} - dependencies: - es-hasown: 0.1.1 - es-tostring: 0.1.0 - /es-iterator-helpers@1.0.17: resolution: {integrity: sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==} engines: {node: '>= 0.4'} @@ -5798,18 +5584,10 @@ packages: safe-array-concat: 1.1.0 dev: true - /es-md5@2.7.2: - resolution: {integrity: sha512-sVv3UfcN551eMa+OYKd1Or0OZC3Ei/7G6kaQJOrnT5j+w3WX8lWJmWReg72Z+bsehDHtRwR+pCYZGrbK75i06w==} - dev: false - /es-module-lexer@1.4.1: resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==} dev: true - /es-object-assign@4.1.4: - resolution: {integrity: sha512-Lux8FGW4FWAwFdJvR4wytwjQNo3EJwIkZZVo2zBOVmOxwrXTelFslbMTHOIwJd1ZDsM9emyBc39b15ldcmRF0A==} - engines: {node: '>=0.10.0'} - /es-set-tostringtag@2.0.3: resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} engines: {node: '>= 0.4'} @@ -5834,17 +5612,6 @@ packages: is-symbol: 1.0.4 dev: true - /es-tostring@0.1.0: - resolution: {integrity: sha512-CWdfVLbx5w5JJa4fmBiGUAMCer4Lg3dpwTbluL3RjOvTpAifLz4j2tSRmVm+WEv2ic/ngJmL/5oWho2Q4pPJTg==} - engines: {node: '>=4.0.0'} - - /es-xhr-promise@1.2.17: - resolution: {integrity: sha512-tO2lT05Kcp/kO1RjYvmKfoQ7fSyyn849oWDWu6hNu/sr1mGEQwEjf9J+LaoVXRr8+0ltx1eRD40J9KkE8rF+LQ==} - dependencies: - broken: 0.1.13 - es-object-assign: 4.1.4 - dev: true - /esbuild@0.17.19: resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} engines: {node: '>=12'} @@ -6314,11 +6081,6 @@ packages: /extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - /extsprintf@1.3.0: - resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} - engines: {'0': node >=0.6.0} - dev: true - /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -6484,19 +6246,6 @@ packages: cross-spawn: 7.0.3 signal-exit: 4.1.0 - /forever-agent@0.6.1: - resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - dev: true - - /form-data@2.3.3: - resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} - engines: {node: '>= 0.12'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: true - /form-data@2.5.1: resolution: {integrity: sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==} engines: {node: '>= 0.12'} @@ -6636,12 +6385,6 @@ packages: get-intrinsic: 1.2.4 dev: true - /getpass@0.1.7: - resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} - dependencies: - assert-plus: 1.0.0 - dev: true - /github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} dev: false @@ -6814,30 +6557,6 @@ packages: dev: false optional: true - /hanzo.js@2.5.13: - resolution: {integrity: sha512-2RhMARnd7eCBMVgCrqiATMMkYNSLlbsNeBAdimkYZ2wqs4HJ8OZuyAQEiUjUMibvPEHEZLTuS/Fq4+UMR6cWvQ==} - engines: {node: '>=4.0.0'} - dependencies: - broken: 0.1.13 - es-cookies: 2.1.18 - es-xhr-promise: 1.2.17 - request: 2.88.2 - dev: true - - /har-schema@2.0.0: - resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} - engines: {node: '>=4'} - dev: true - - /har-validator@5.1.5: - resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} - engines: {node: '>=6'} - deprecated: this library is no longer supported - dependencies: - ajv: 6.12.6 - har-schema: 2.0.0 - dev: true - /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true @@ -7082,15 +6801,6 @@ packages: dev: false optional: true - /http-signature@1.2.0: - resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} - engines: {node: '>=0.8', npm: '>=1.3.7'} - dependencies: - assert-plus: 1.0.0 - jsprim: 1.4.2 - sshpk: 1.18.0 - dev: true - /https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -7152,11 +6862,6 @@ packages: engines: {node: '>=0.8.19'} dev: true - /indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - dev: true - /inflection@2.0.1: resolution: {integrity: sha512-wzkZHqpb4eGrOKBl34xy3umnYHx8Si5R1U4fwmdxLo5gdH6mEK8gclckTj/qWqy4Je0bsDYe/qazZYuO7xe3XQ==} engines: {node: '>=14.0.0'} @@ -7212,14 +6917,6 @@ packages: is-alphabetical: 2.0.1 is-decimal: 2.0.1 - /is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - dev: true - /is-array-buffer@3.0.4: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} @@ -7409,10 +7106,6 @@ packages: which-typed-array: 1.1.14 dev: true - /is-typedarray@1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - dev: true - /is-weakmap@2.0.1: resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} dev: true @@ -7437,10 +7130,6 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - /isstream@0.1.2: - resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - dev: true - /iterator.prototype@1.1.2: resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} dependencies: @@ -7517,10 +7206,6 @@ packages: resolution: {integrity: sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==} dev: false - /jsbn@0.1.1: - resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - dev: true - /jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} @@ -7546,18 +7231,10 @@ packages: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true - /json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - dev: true - /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true - /json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - dev: true - /json5@1.0.2: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true @@ -7590,16 +7267,6 @@ packages: semver: 7.6.0 dev: false - /jsprim@1.4.2: - resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} - engines: {node: '>=0.6.0'} - dependencies: - assert-plus: 1.0.0 - extsprintf: 1.3.0 - json-schema: 0.4.0 - verror: 1.10.0 - dev: true - /jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -7789,6 +7456,7 @@ packages: /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false /long@5.2.3: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} @@ -7870,11 +7538,6 @@ packages: react: 18.2.0 dev: false - /lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} - hasBin: true - dev: true - /markdown-extensions@1.1.1: resolution: {integrity: sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==} engines: {node: '>=0.10.0'} @@ -8847,13 +8510,10 @@ packages: braces: 3.0.2 picomatch: 2.3.1 - /midstream@2.0.2: - resolution: {integrity: sha512-kbG2QR2tQErDEWN7Z2YM9XYOrIKXOao0/ZKZminrmvKI/UoC8va6RAB1s0VupJah3/Yeh5aUQiKoda+dIhstjA==} - dev: false - /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + requiresBuild: true /mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} @@ -8910,6 +8570,10 @@ packages: resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} engines: {node: '>=16 || 14 >=14.17'} + /mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + dev: false + /mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} dev: false @@ -9034,6 +8698,15 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /next-usequerystate@1.17.0(next@14.1.0): + resolution: {integrity: sha512-DICs3oqABUi0nTQHBpvfhvb4bY8qc5u4IuFHXIv0+5cSPse5NuR/MtHGHAxQVVdzzkwg4FvYgSsypcoaJyFJ1g==} + peerDependencies: + next: '>=13.4 <14.0.2 || ^14.0.3' + dependencies: + mitt: 3.0.1 + next: 14.1.0(react-dom@18.2.0)(react@18.2.0) + dev: false + /next@13.5.6(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw==} engines: {node: '>=16.14.0'} @@ -9213,10 +8886,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /oauth-sign@0.9.0: - resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} - dev: true - /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -9229,14 +8898,6 @@ packages: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} dev: true - /object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - dev: true - /object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -9393,10 +9054,6 @@ packages: engines: {node: '>=8'} dev: true - /performance-now@2.1.0: - resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - dev: true - /periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} dependencies: @@ -9526,15 +9183,6 @@ packages: hasBin: true dev: true - /pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 17.0.2 - dev: true - /prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} dependencies: @@ -9577,10 +9225,6 @@ packages: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: false - /psl@1.9.0: - resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - dev: true - /pump@3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} dependencies: @@ -9593,11 +9237,6 @@ packages: engines: {node: '>=6'} dev: true - /qs@6.5.3: - resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} - engines: {node: '>=0.6'} - dev: true - /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -9646,6 +9285,7 @@ packages: loose-envify: 1.4.0 react: 18.2.0 scheduler: 0.23.0 + dev: false /react-hook-form@7.50.1(react@18.2.0): resolution: {integrity: sha512-3PCY82oE0WgeOgUtIr3nYNNtNvqtJ7BZjsbxh6TnYNbXButaD5WpjOmTjdxZfheuHKR68qfeFnEDVYoSSFPMTQ==} @@ -9672,10 +9312,6 @@ packages: /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - /react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - dev: true - /react-mobile-picker@1.0.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-6xaEBdy4YzgCULRqKcYCa/T34OFRrxXQz8TYrTeRPjq8UEsuH023OpcajnxWbU4X9+kk0nbarorvMFsiaBmXHg==} peerDependencies: @@ -9864,14 +9500,6 @@ packages: victory-vendor: 36.9.1 dev: false - /redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - dev: true - /reflect.getprototypeof@1.0.5: resolution: {integrity: sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==} engines: {node: '>= 0.4'} @@ -10079,33 +9707,6 @@ packages: engines: {node: '>=0.10'} dev: false - /request@2.88.2: - resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} - engines: {node: '>= 6'} - deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 - dependencies: - aws-sign2: 0.7.0 - aws4: 1.12.0 - caseless: 0.12.0 - combined-stream: 1.0.8 - extend: 3.0.2 - forever-agent: 0.6.1 - form-data: 2.3.3 - har-validator: 5.1.5 - http-signature: 1.2.0 - is-typedarray: 1.0.0 - isstream: 0.1.2 - json-stringify-safe: 5.0.1 - mime-types: 2.1.35 - oauth-sign: 0.9.0 - performance-now: 2.1.0 - qs: 6.5.3 - safe-buffer: 5.2.1 - tough-cookie: 2.5.0 - tunnel-agent: 0.6.0 - uuid: 3.4.0 - dev: true - /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -10206,10 +9807,6 @@ packages: is-regex: 1.1.4 dev: true - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: true - /satori@0.0.44: resolution: {integrity: sha512-WKUxXC2qeyno6J3ucwwLozPL6j1HXOZiN5wIUf7iqAhlx1RUC/6ePIKHi7iPc3Cy6DYuZcJriZXxXkSdo2FQHg==} engines: {node: '>=16'} @@ -10227,6 +9824,7 @@ packages: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: loose-envify: 1.4.0 + dev: false /schema-utils@3.3.0: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} @@ -10400,29 +9998,6 @@ packages: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: false - /sshpk@1.18.0: - resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} - engines: {node: '>=0.10.0'} - hasBin: true - dependencies: - asn1: 0.2.6 - assert-plus: 1.0.0 - bcrypt-pbkdf: 1.0.2 - dashdash: 1.14.1 - ecc-jsbn: 0.1.2 - getpass: 0.1.7 - jsbn: 0.1.1 - safer-buffer: 2.1.2 - tweetnacl: 0.14.5 - dev: true - - /stop-iteration-iterator@1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} - engines: {node: '>= 0.4'} - dependencies: - internal-slot: 1.0.7 - dev: true - /stream-events@1.0.5: resolution: {integrity: sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==} requiresBuild: true @@ -10535,13 +10110,6 @@ packages: engines: {node: '>=4'} dev: true - /strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - dependencies: - min-indent: 1.0.1 - dev: true - /strip-indent@4.0.0: resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} engines: {node: '>=12'} @@ -10819,14 +10387,6 @@ packages: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} dev: false - /tough-cookie@2.5.0: - resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} - engines: {node: '>=0.8'} - dependencies: - psl: 1.9.0 - punycode: 2.3.1 - dev: true - /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -10874,10 +10434,7 @@ packages: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: safe-buffer: 5.2.1 - - /tweetnacl@0.14.5: - resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} - dev: true + dev: false /typanion@3.14.0: resolution: {integrity: sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==} @@ -11168,12 +10725,6 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - /uuid@3.4.0: - resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} - deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. - hasBin: true - dev: true - /uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -11213,15 +10764,6 @@ packages: - '@types/react-dom' dev: false - /verror@1.10.0: - resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} - engines: {'0': node >=0.6.0} - dependencies: - assert-plus: 1.0.0 - core-util-is: 1.0.2 - extsprintf: 1.3.0 - dev: true - /vfile-location@4.1.0: resolution: {integrity: sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==} dependencies: From 4468124cdb471956455b3b6dd5553940a345369e Mon Sep 17 00:00:00 2001 From: artem ash Date: Fri, 1 Mar 2024 21:35:06 -0800 Subject: [PATCH 2/2] cmmc: bump @1.0.9 --- packages/commerce/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/commerce/package.json b/packages/commerce/package.json index ebf5f6d8..537d0a35 100644 --- a/packages/commerce/package.json +++ b/packages/commerce/package.json @@ -1,6 +1,6 @@ { "name": "@hanzo/commerce", - "version": "1.0.8", + "version": "1.0.9", "description": "Library with shopping cart components.", "publishConfig": { "registry": "https://registry.npmjs.org/",