diff --git a/composable-ui/src/components/checkout/checkout-provider/checkout-provider.tsx b/composable-ui/src/components/checkout/checkout-provider/checkout-provider.tsx index 28fe54b..ef1fb55 100644 --- a/composable-ui/src/components/checkout/checkout-provider/checkout-provider.tsx +++ b/composable-ui/src/components/checkout/checkout-provider/checkout-provider.tsx @@ -108,6 +108,7 @@ export const CheckoutProvider = ({ children }: CheckoutProviderProps) => { */ const [checkoutState, setCheckoutState] = useState({ + cartId: cart.id || '', config: { billingSameAsShipping: true, gdpr: false, diff --git a/composable-ui/src/components/checkout/step-1/form-guest.tsx b/composable-ui/src/components/checkout/step-1/form-guest.tsx index a23362d..808f32e 100644 --- a/composable-ui/src/components/checkout/step-1/form-guest.tsx +++ b/composable-ui/src/components/checkout/step-1/form-guest.tsx @@ -6,7 +6,7 @@ import { InputField } from '@composable/ui' import { CHECKOUT_FORM_KEY } from '../constants' import { CheckoutInput } from '@composable/types' -type FormData = Omit +type FormData = Omit type GuestFormProps = Pick< UseCheckoutFormProps, diff --git a/composable-ui/src/server/api/routers/commerce/procedures/checkout/create-order.ts b/composable-ui/src/server/api/routers/commerce/procedures/checkout/create-order.ts index ece21cc..930bd4b 100644 --- a/composable-ui/src/server/api/routers/commerce/procedures/checkout/create-order.ts +++ b/composable-ui/src/server/api/routers/commerce/procedures/checkout/create-order.ts @@ -6,6 +6,7 @@ export const createOrder = protectedProcedure .input( z.object({ checkout: z.object({ + cartId: z.string(), customer: z.object({ id: z.string().optional(), email: z.string(), diff --git a/composable-ui/src/utils/__mocks__/api.ts b/composable-ui/src/utils/__mocks__/api.ts index 49ecc9d..2512398 100644 --- a/composable-ui/src/utils/__mocks__/api.ts +++ b/composable-ui/src/utils/__mocks__/api.ts @@ -28,21 +28,27 @@ export const api = { items: [ { __typename: 'BannerSplit', + id: '23385feb-cd12-4122-8105-bf5da178d70c', }, { __typename: 'BannerFull', + id: '2d095fdd-e3ea-4f7a-907a-359ef1d0593d', }, { __typename: 'BannerTextOnly', + id: '40433348-1eb0-43fd-99dc-090c79972512', }, { __typename: 'BannerTextOnly', + id: 'a609a45e-f3f2-4cfc-8709-29a08153c9ac', }, { __typename: 'Grid', + id: '9fad8aa3-6e8d-43e2-b79a-492409b49003', }, { __typename: 'CommerceConnector', + id: '662f1110-8a51-4a11-9a8e-730a43d6867d', }, ], }, diff --git a/packages/commerce-generic/package.json b/packages/commerce-generic/package.json index 0f74d4b..d328015 100644 --- a/packages/commerce-generic/package.json +++ b/packages/commerce-generic/package.json @@ -10,7 +10,9 @@ "ts": "tsc --noEmit --incremental" }, "dependencies": { - "@composable/types": "workspace:*" + "@composable/types": "workspace:*", + "@types/node-persist": "^3.1.5", + "node-persist": "^3.1.3" }, "devDependencies": { "eslint-config-custom": "workspace:*", diff --git a/packages/commerce-generic/src/data/generate-cart-data.ts b/packages/commerce-generic/src/data/generate-cart-data.ts new file mode 100644 index 0000000..1f4ddfc --- /dev/null +++ b/packages/commerce-generic/src/data/generate-cart-data.ts @@ -0,0 +1,44 @@ +import { Cart, CartItem } from '@composable/types' +import products from './products.json' +import { randomUUID } from 'crypto' + +const findProductById = (id: string) => { + return products.find((product) => product.id === id) ?? products[0] +} + +export const generateEmptyCart = (cartId?: string): Cart => ({ + id: cartId || randomUUID(), + items: [], + summary: {}, +}) + +export const generateCartItem = (productId: string, quantity: number) => { + const _product = findProductById(productId) + return { + brand: _product.brand, + category: _product.category, + id: _product.id, + image: _product.images[0], + name: _product.name, + price: _product.price, + quantity: quantity ?? 1, + sku: _product.sku, + slug: _product.slug, + type: _product.type, + } +} + +export const calculateCartSummary = (cartItems: CartItem[]) => { + const subtotal = cartItems.reduce((_subtotal, item) => { + return _subtotal + item.price * (item.quantity ?? 1) + }, 0) + const taxes = subtotal * 0.07 + const total = subtotal + taxes + + return { + subtotalPrice: subtotal.toFixed(2), + taxes: taxes.toFixed(2), + totalPrice: total.toFixed(2), + shipping: 'Free', + } +} diff --git a/packages/commerce-generic/src/data/generateCartData.ts b/packages/commerce-generic/src/data/generateCartData.ts deleted file mode 100644 index 50d0e3e..0000000 --- a/packages/commerce-generic/src/data/generateCartData.ts +++ /dev/null @@ -1,38 +0,0 @@ -import products from './products.json' - -const findProductById = (id: string) => { - return products.find((product) => product.id === id) ?? products[0] -} - -export const generateCartData = ({ - productId, - quantity, -}: { productId?: string; quantity?: number } = {}) => { - const _product = productId ? findProductById(productId) : products[0] - const subtotal = _product.price * (quantity ?? 1) - const taxes = subtotal * 0.07 - const total = subtotal + taxes - - return { - items: [ - { - brand: _product.brand, - category: _product.category, - id: _product.id, - image: _product.images[0], - name: _product.name, - price: _product.price, - quantity: quantity ?? 1, - sku: _product.sku, - slug: _product.slug, - type: _product.type, - }, - ], - summary: { - subtotalPrice: subtotal.toFixed(2), - taxes: taxes.toFixed(2), - totalPrice: total.toFixed(2), - shipping: 'Free', - }, - } -} diff --git a/packages/commerce-generic/src/data/mock-storage.ts b/packages/commerce-generic/src/data/mock-storage.ts new file mode 100644 index 0000000..6b1961c --- /dev/null +++ b/packages/commerce-generic/src/data/mock-storage.ts @@ -0,0 +1,41 @@ +/** + * IMPORTANT: The following implementation is purely for demonstration purposes and should NEVER be used in a Production environment. + * + * The current persistence mechanism leverages storing data as JSON documents in the serverless functions' file system. + * Keep in mind that these files may be destroyed and recreated by the Cloud Provider (e.g., Vercel or Netlify) at any time. + * As a result, users with a working cart containing items may experience data loss and need to start anew if serverless + * functions are reinitialized. This is not suitable for a Production environment where data persistence and reliability are critical. + */ +import storage from 'node-persist' +import path from 'path' +import os from 'os' +import { Order, Cart } from '@composable/types' + +const storageFolderPath = path.join(os.tmpdir(), 'composable-ui-storage') + +storage.init({ + dir: storageFolderPath, +}) + +export const getOrder = async (orderId: string): Promise => { + return storage.getItem(`order-${orderId}`) +} + +export const saveOrder = async (order: Order) => { + await storage.setItem(`order-${order.id}`, order) + return order +} + +export const getCart = async (cartId: string): Promise => { + return storage.getItem(`cart-${cartId}`) +} + +export const saveCart = async (cart: Cart) => { + await storage.setItem(`cart-${cart.id}`, cart) + return cart +} + +export const deleteCart = async (cartId: string) => { + const result = await storage.del(`cart-${cartId}`) + return result.removed +} diff --git a/packages/commerce-generic/src/services/cart/add-cart-item.ts b/packages/commerce-generic/src/services/cart/add-cart-item.ts index ce2ac6c..de08f0c 100644 --- a/packages/commerce-generic/src/services/cart/add-cart-item.ts +++ b/packages/commerce-generic/src/services/cart/add-cart-item.ts @@ -1,6 +1,10 @@ import { CommerceService } from '@composable/types' -import { generateCartData } from '../../data/generateCartData' -import cart from '../../data/cart.json' +import { getCart, saveCart } from '../../data/mock-storage' +import { + generateCartItem, + calculateCartSummary, + generateEmptyCart, +} from '../../data/generate-cart-data' export const addCartItem: CommerceService['addCartItem'] = async ({ cartId, @@ -8,11 +12,19 @@ export const addCartItem: CommerceService['addCartItem'] = async ({ quantity, variantId, }) => { - const { items, summary } = generateCartData({ productId, quantity }) + const cart = (await getCart(cartId)) || generateEmptyCart(cartId) - return { - ...cart, - items, - summary, + const isProductInCartAlready = cart.items.some( + (item) => item.id === productId + ) + + if (isProductInCartAlready) { + cart.items.find((item) => item.id === productId)!.quantity++ + } else { + const newItem = generateCartItem(productId, quantity) + cart.items.push(newItem) } + cart.summary = calculateCartSummary(cart.items) + + return saveCart(cart) } diff --git a/packages/commerce-generic/src/services/cart/create-cart.ts b/packages/commerce-generic/src/services/cart/create-cart.ts index e6ad84b..887d2fc 100644 --- a/packages/commerce-generic/src/services/cart/create-cart.ts +++ b/packages/commerce-generic/src/services/cart/create-cart.ts @@ -1,6 +1,7 @@ import { CommerceService } from '@composable/types' -import cart from '../../data/cart.json' +import { saveCart } from '../../data/mock-storage' +import { generateEmptyCart } from '../../data/generate-cart-data' export const createCart: CommerceService['createCart'] = async () => { - return cart + return saveCart(generateEmptyCart()) } diff --git a/packages/commerce-generic/src/services/cart/delete-cart-item.ts b/packages/commerce-generic/src/services/cart/delete-cart-item.ts index 4c72091..7189377 100644 --- a/packages/commerce-generic/src/services/cart/delete-cart-item.ts +++ b/packages/commerce-generic/src/services/cart/delete-cart-item.ts @@ -1,9 +1,22 @@ import { CommerceService } from '@composable/types' -import cart from '../../data/cart.json' +import { getCart, saveCart } from '../../data/mock-storage' + +import { calculateCartSummary } from '../../data/generate-cart-data' export const deleteCartItem: CommerceService['deleteCartItem'] = async ({ cartId, productId, }) => { - return cart + const cart = await getCart(cartId) + + if (!cart) { + throw new Error( + `[deleteCartItem] Could not found cart with requested cart id: ${cartId}` + ) + } + + cart.items = cart.items.filter((item) => item.id !== productId) + cart.summary = calculateCartSummary(cart.items) + + return saveCart(cart) } diff --git a/packages/commerce-generic/src/services/cart/get-cart.ts b/packages/commerce-generic/src/services/cart/get-cart.ts index 8b4a1be..13240f9 100644 --- a/packages/commerce-generic/src/services/cart/get-cart.ts +++ b/packages/commerce-generic/src/services/cart/get-cart.ts @@ -1,17 +1,10 @@ import { CommerceService } from '@composable/types' -import { generateCartData } from '../../data/generateCartData' -import cart from '../../data/cart.json' +import { getCart as getCartFromStorage } from '../../data/mock-storage' export const getCart: CommerceService['getCart'] = async ({ cartId }) => { if (!cartId) { return null } - const { items, summary } = generateCartData() - - return { - ...cart, - items, - summary, - } + return (await getCartFromStorage(cartId)) || null } diff --git a/packages/commerce-generic/src/services/cart/update-cart-item.ts b/packages/commerce-generic/src/services/cart/update-cart-item.ts index ac0b6ab..a780aa9 100644 --- a/packages/commerce-generic/src/services/cart/update-cart-item.ts +++ b/packages/commerce-generic/src/services/cart/update-cart-item.ts @@ -1,17 +1,32 @@ import { CommerceService } from '@composable/types' -import { generateCartData } from '../../data/generateCartData' -import cart from '../../data/cart.json' +import { getCart, saveCart } from '../../data/mock-storage' + +import { calculateCartSummary } from '../../data/generate-cart-data' export const updateCartItem: CommerceService['updateCartItem'] = async ({ cartId, productId, quantity, }) => { - const { items, summary } = generateCartData({ productId, quantity }) + const cart = await getCart(cartId) + + if (!cart) { + throw new Error( + `[updateCartItem] Could not found cart with requested cart id: ${cartId}` + ) + } + + const cartItem = cart.items.find((item) => item.id === productId) - return { - ...cart, - items, - summary, + if (!cartItem) { + throw new Error( + `[updateCartItem] Could not found cart item with requested product id: ${productId}` + ) } + + cartItem.quantity = quantity + + cart.summary = calculateCartSummary(cart.items) + + return saveCart(cart) } diff --git a/packages/commerce-generic/src/services/checkout/create-order.ts b/packages/commerce-generic/src/services/checkout/create-order.ts index 42961ae..c9857ee 100644 --- a/packages/commerce-generic/src/services/checkout/create-order.ts +++ b/packages/commerce-generic/src/services/checkout/create-order.ts @@ -1,16 +1,48 @@ -import { CommerceService } from '@composable/types' -import { generateCartData } from '../../data/generateCartData' -import order from '../../data/order.json' +import { Cart, CheckoutInput, CommerceService, Order } from '@composable/types' +import { getCart } from '../../data/mock-storage' +import { saveOrder } from '../../data/mock-storage' import shippingMethods from '../../data/shipping-methods.json' +import { randomUUID } from 'crypto' -export const createOrder: CommerceService['createOrder'] = async ({ - checkout, -}) => { - const cartItems = generateCartData() +const generateOrderFromCart = ( + cart: Cart, + checkoutInput: CheckoutInput +): Order => { return { - ...order, - ...cartItems, + id: randomUUID(), + status: 'complete', + payment: 'unpaid', + shipping: 'unfulfilled', + customer: { + email: checkoutInput.customer.email, + }, + shipping_address: { + phone_number: '', + city: '', + ...checkoutInput.shipping_address, + }, + billing_address: { + phone_number: '', + city: '', + ...checkoutInput.billing_address, + }, shipping_method: shippingMethods[0], created_at: Date.now(), + items: cart.items, + summary: cart.summary, } } + +export const createOrder: CommerceService['createOrder'] = async ({ + checkout, +}) => { + const cart = await getCart(checkout.cartId) + + if (!cart) { + throw new Error( + `[createOrder] Could not find cart by id: ${checkout.cartId}` + ) + } + + return saveOrder(generateOrderFromCart(cart, checkout)) +} diff --git a/packages/commerce-generic/src/services/checkout/get-order.ts b/packages/commerce-generic/src/services/checkout/get-order.ts index 73438f3..4f5da70 100644 --- a/packages/commerce-generic/src/services/checkout/get-order.ts +++ b/packages/commerce-generic/src/services/checkout/get-order.ts @@ -1,13 +1,17 @@ import { CommerceService } from '@composable/types' -import { generateCartData } from '../../data/generateCartData' +import { getOrder as getOrerFromStorage } from '../../data/mock-storage' import order from '../../data/order.json' import shippingMethods from '../../data/shipping-methods.json' export const getOrder: CommerceService['getOrder'] = async ({ orderId }) => { - const cartItems = generateCartData() + const order = await getOrerFromStorage(orderId) + + if (!order) { + throw new Error(`[getOrder] Could not found order: ${orderId}`) + } + return { ...order, - ...cartItems, shipping_method: shippingMethods[0], created_at: Date.now(), } diff --git a/packages/types/src/commerce/checkout.ts b/packages/types/src/commerce/checkout.ts index 0e4f335..55ae93c 100644 --- a/packages/types/src/commerce/checkout.ts +++ b/packages/types/src/commerce/checkout.ts @@ -1,4 +1,5 @@ export interface CheckoutInput { + cartId: string customer: { id?: string email: string diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec4b212..2bb9d75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + importers: .: @@ -204,6 +208,12 @@ importers: '@composable/types': specifier: workspace:* version: link:../types + '@types/node-persist': + specifier: ^3.1.5 + version: 3.1.5 + node-persist: + specifier: ^3.1.3 + version: 3.1.3 devDependencies: eslint-config-custom: specifier: workspace:* @@ -228,7 +238,7 @@ importers: version: 8.8.0(eslint@7.32.0) eslint-config-turbo: specifier: latest - version: 1.8.8(eslint@7.32.0) + version: 1.10.16(eslint@7.32.0) eslint-plugin-react: specifier: 7.28.0 version: 7.28.0(eslint@7.32.0) @@ -3252,6 +3262,7 @@ packages: /@emotion/memoize@0.7.4: resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} + requiresBuild: true optional: true /@emotion/memoize@0.8.0: @@ -6405,6 +6416,12 @@ packages: form-data: 3.0.1 dev: false + /@types/node-persist@3.1.5: + resolution: {integrity: sha512-etovBJhoENhCxgmxf1gEnu4Wsq/JQy2lTaXVypuA85KkY0A7/pGbgwELtXTgwj2DVgRUb6IAhG8GHEqLPCogVA==} + dependencies: + '@types/node': 18.15.11 + dev: false + /@types/node@16.18.22: resolution: {integrity: sha512-LJSIirgASa1LicFGTUFwDY7BfKDtLIbijqDLkH47LxEo/jtdrtiZ4/kLPD99bEQhTcPcuh6KhDllHqRxygJD2w==} dev: false @@ -7196,6 +7213,7 @@ packages: /array-find-index@1.0.2: resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -7330,6 +7348,7 @@ packages: /async-each@1.0.6: resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==} + requiresBuild: true dev: false optional: true @@ -7607,6 +7626,7 @@ packages: /big-integer@1.6.51: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} + requiresBuild: true dev: false optional: true @@ -7617,6 +7637,7 @@ packages: /binary-extensions@1.13.1: resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -7626,6 +7647,7 @@ packages: /bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + requiresBuild: true dependencies: file-uri-to-path: 1.0.0 dev: false @@ -7689,6 +7711,7 @@ packages: /bplist-parser@0.1.1: resolution: {integrity: sha512-2AEM0FXy8ZxVLBuqX0hqt1gDwcnz2zygEkQ6zaD5Wko/sB9paUNwlpawrFtKeHUAQUOzjVy9AO4oeonqIHKA9Q==} + requiresBuild: true dependencies: big-integer: 1.6.51 dev: false @@ -7965,6 +7988,7 @@ packages: /camelcase-keys@2.1.0: resolution: {integrity: sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: camelcase: 2.1.1 map-obj: 1.0.1 @@ -7974,6 +7998,7 @@ packages: /camelcase@2.1.1: resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -8047,6 +8072,7 @@ packages: /chokidar@2.1.8: resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==} deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies + requiresBuild: true dependencies: anymatch: 2.0.0 async-each: 1.0.6 @@ -8767,6 +8793,7 @@ packages: /currently-unhandled@0.4.1: resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: array-find-index: 1.0.2 dev: false @@ -8826,6 +8853,7 @@ packages: /decamelize@1.2.0: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -9431,13 +9459,13 @@ packages: eslint: 7.32.0 dev: false - /eslint-config-turbo@1.8.8(eslint@7.32.0): - resolution: {integrity: sha512-+yT22sHOT5iC1sbBXfLIdXfbZuiv9bAyOXsxTxFCWelTeFFnANqmuKB3x274CFvf7WRuZ/vYP/VMjzU9xnFnxA==} + /eslint-config-turbo@1.10.16(eslint@7.32.0): + resolution: {integrity: sha512-O3NQI72bQHV7FvSC6lWj66EGx8drJJjuT1kuInn6nbMLOHdMBhSUX/8uhTAlHRQdlxZk2j9HtgFCIzSc93w42g==} peerDependencies: eslint: '>6.6.0' dependencies: eslint: 7.32.0 - eslint-plugin-turbo: 1.8.8(eslint@7.32.0) + eslint-plugin-turbo: 1.10.16(eslint@7.32.0) dev: false /eslint-import-resolver-node@0.3.7: @@ -9612,11 +9640,12 @@ packages: string.prototype.matchall: 4.0.8 dev: false - /eslint-plugin-turbo@1.8.8(eslint@7.32.0): - resolution: {integrity: sha512-zqyTIvveOY4YU5jviDWw9GXHd4RiKmfEgwsjBrV/a965w0PpDwJgEUoSMB/C/dU310Sv9mF3DSdEjxjJLaw6rA==} + /eslint-plugin-turbo@1.10.16(eslint@7.32.0): + resolution: {integrity: sha512-ZjrR88MTN64PNGufSEcM0tf+V1xFYVbeiMeuIqr0aiABGomxFLo4DBkQ7WI4WzkZtWQSIA2sP+yxqSboEfL9MQ==} peerDependencies: eslint: '>6.6.0' dependencies: + dotenv: 16.0.3 eslint: 7.32.0 dev: false @@ -10002,6 +10031,7 @@ packages: /file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + requiresBuild: true dev: false optional: true @@ -10060,6 +10090,7 @@ packages: /find-up@1.1.2: resolution: {integrity: sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: path-exists: 2.1.0 pinkie-promise: 2.0.1 @@ -10448,6 +10479,7 @@ packages: /get-stdin@4.0.1: resolution: {integrity: sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -11099,6 +11131,7 @@ packages: /indent-string@2.1.0: resolution: {integrity: sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: repeating: 2.0.1 dev: false @@ -11299,6 +11332,7 @@ packages: /is-binary-path@1.0.1: resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: binary-extensions: 1.13.1 dev: false @@ -11414,6 +11448,7 @@ packages: /is-finite@1.1.0: resolution: {integrity: sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -11590,6 +11625,7 @@ packages: /is-utf8@0.2.1: resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} + requiresBuild: true dev: false optional: true @@ -12541,6 +12577,7 @@ packages: /load-json-file@1.1.0: resolution: {integrity: sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: graceful-fs: 4.2.11 parse-json: 2.2.0 @@ -12676,6 +12713,7 @@ packages: /loud-rejection@1.6.0: resolution: {integrity: sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: currently-unhandled: 0.4.1 signal-exit: 3.0.7 @@ -12756,6 +12794,7 @@ packages: /map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -12859,6 +12898,7 @@ packages: /meow@3.7.0: resolution: {integrity: sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: camelcase-keys: 2.1.0 decamelize: 1.2.0 @@ -13133,6 +13173,7 @@ packages: /nan@2.17.0: resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==} + requiresBuild: true dev: false optional: true @@ -13408,6 +13449,11 @@ packages: vm-browserify: 1.1.2 dev: false + /node-persist@3.1.3: + resolution: {integrity: sha512-CaFv+kSZtsc+VeDRldK1yR47k1vPLBpzYB9re2z7LIwITxwBtljMq3s8VQnnr+x3E8pQfHbc5r2IyJsBLJhtXg==} + engines: {node: '>=10.12.0'} + dev: false + /node-releases@2.0.10: resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} @@ -13717,6 +13763,7 @@ packages: /os-homedir@1.0.2: resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -13880,6 +13927,7 @@ packages: /parse-json@2.2.0: resolution: {integrity: sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: error-ex: 1.3.2 dev: false @@ -13931,11 +13979,13 @@ packages: /path-dirname@1.0.2: resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==} + requiresBuild: true dev: false /path-exists@2.1.0: resolution: {integrity: sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: pinkie-promise: 2.0.1 dev: false @@ -13973,6 +14023,7 @@ packages: /path-type@1.1.0: resolution: {integrity: sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: graceful-fs: 4.2.11 pify: 2.3.0 @@ -14030,6 +14081,7 @@ packages: /pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -14046,6 +14098,7 @@ packages: /pinkie-promise@2.0.1: resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: pinkie: 2.0.4 dev: false @@ -14054,6 +14107,7 @@ packages: /pinkie@2.0.4: resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -14859,6 +14913,7 @@ packages: /read-pkg-up@1.0.1: resolution: {integrity: sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: find-up: 1.1.2 read-pkg: 1.1.0 @@ -14877,6 +14932,7 @@ packages: /read-pkg@1.1.0: resolution: {integrity: sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: load-json-file: 1.1.0 normalize-package-data: 2.5.0 @@ -14917,6 +14973,7 @@ packages: /readdirp@2.2.1: resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} engines: {node: '>=0.10'} + requiresBuild: true dependencies: graceful-fs: 4.2.11 micromatch: 3.1.10 @@ -14935,6 +14992,7 @@ packages: /redent@1.0.0: resolution: {integrity: sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: indent-string: 2.1.0 strip-indent: 1.0.1 @@ -15154,6 +15212,7 @@ packages: /repeating@2.0.1: resolution: {integrity: sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: is-finite: 1.1.0 dev: false @@ -16034,6 +16093,7 @@ packages: /strip-bom@2.0.0: resolution: {integrity: sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: is-utf8: 0.2.1 dev: false @@ -16061,6 +16121,7 @@ packages: resolution: {integrity: sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==} engines: {node: '>=0.10.0'} hasBin: true + requiresBuild: true dependencies: get-stdin: 4.0.1 dev: false @@ -16462,6 +16523,7 @@ packages: /trim-newlines@1.0.0: resolution: {integrity: sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==} engines: {node: '>=0.10.0'} + requiresBuild: true dev: false optional: true @@ -16936,6 +16998,7 @@ packages: /untildify@2.1.0: resolution: {integrity: sha512-sJjbDp2GodvkB0FZZcn7k6afVisqX5BZD7Yq3xp4nN2O15BBK0cLm3Vwn2vQaF7UDS0UUsrQMkkplmDI5fskig==} engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: os-homedir: 1.0.2 dev: false @@ -16944,6 +17007,7 @@ packages: /upath@1.2.0: resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} engines: {node: '>=4'} + requiresBuild: true dev: false optional: true