diff --git a/integration-tests/api/__tests__/admin/order.js b/integration-tests/api/__tests__/admin/order.js index 704dc277a5..fe7265ed3d 100644 --- a/integration-tests/api/__tests__/admin/order.js +++ b/integration-tests/api/__tests__/admin/order.js @@ -1,6 +1,11 @@ const { dropDatabase } = require("pg-god"); const path = require("path"); -const { ReturnReason } = require("@medusajs/medusa"); +const { + ReturnReason, + Order, + LineItem, + ProductVariant, +} = require("@medusajs/medusa"); const setupServer = require("../../../helpers/setup-server"); const { useApi } = require("../../../helpers/use-api"); @@ -83,6 +88,175 @@ describe("/admin/orders", () => { }); }); + describe("GET /admin/orders", () => { + beforeEach(async () => { + try { + await adminSeeder(dbConnection); + await orderSeeder(dbConnection); + } catch (err) { + console.log(err); + throw err; + } + + const manager = dbConnection.manager; + + const order2 = manager.create(Order, { + id: "test-order-not-payed", + customer_id: "test-customer", + email: "test@email.com", + fulfillment_status: "not_fulfilled", + payment_status: "awaiting", + billing_address: { + id: "test-billing-address", + first_name: "lebron", + }, + shipping_address: { + id: "test-shipping-address", + first_name: "lebron", + country_code: "us", + }, + region_id: "test-region", + currency_code: "usd", + tax_rate: 0, + discounts: [ + { + id: "test-discount", + code: "TEST134", + is_dynamic: false, + rule: { + id: "test-rule", + description: "Test Discount", + type: "percentage", + value: 10, + allocation: "total", + }, + is_disabled: false, + regions: [ + { + id: "test-region", + }, + ], + }, + ], + payments: [ + { + id: "test-payment", + amount: 10000, + currency_code: "usd", + amount_refunded: 0, + provider_id: "test-pay", + data: {}, + }, + ], + items: [], + }); + + await manager.save(order2); + + const li2 = manager.create(LineItem, { + id: "test-item", + fulfilled_quantity: 0, + returned_quantity: 0, + title: "Line Item", + description: "Line Item Desc", + thumbnail: "https://test.js/1234", + unit_price: 8000, + quantity: 1, + variant_id: "test-variant", + order_id: "test-order-not-payed", + }); + + await manager.save(li2); + }); + + afterEach(async () => { + const manager = dbConnection.manager; + await manager.query(`DELETE FROM "cart"`); + await manager.query(`DELETE FROM "fulfillment"`); + await manager.query(`DELETE FROM "swap"`); + await manager.query(`DELETE FROM "return"`); + await manager.query(`DELETE FROM "claim_image"`); + await manager.query(`DELETE FROM "claim_tag"`); + await manager.query(`DELETE FROM "claim_item"`); + await manager.query(`DELETE FROM "claim_order"`); + await manager.query(`DELETE FROM "line_item"`); + await manager.query(`DELETE FROM "money_amount"`); + await manager.query(`DELETE FROM "product_variant"`); + await manager.query(`DELETE FROM "product"`); + await manager.query(`DELETE FROM "shipping_method"`); + await manager.query(`DELETE FROM "shipping_option"`); + await manager.query(`DELETE FROM "discount"`); + await manager.query(`DELETE FROM "payment"`); + await manager.query(`DELETE FROM "order"`); + await manager.query(`DELETE FROM "customer"`); + await manager.query( + `UPDATE "country" SET region_id=NULL WHERE iso_2 = 'us'` + ); + await manager.query(`DELETE FROM "region"`); + await manager.query(`DELETE FROM "user"`); + }); + + it("cancels an order and increments inventory_quantity", async () => { + const api = useApi(); + const manager = dbConnection.manager; + + const initialInventoryRes = await api.get("/store/variants/test-variant"); + + expect(initialInventoryRes.data.variant.inventory_quantity).toEqual(1); + + const response = await api + .post( + `/admin/orders/test-order-not-payed/cancel`, + {}, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((err) => { + console.log(err); + }); + expect(response.status).toEqual(200); + + const secondInventoryRes = await api.get("/store/variants/test-variant"); + + expect(secondInventoryRes.data.variant.inventory_quantity).toEqual(2); + }); + + it("cancels an order but does not increment inventory_quantity of unmanaged variant", async () => { + const api = useApi(); + const manager = dbConnection.manager; + + await manager.query( + `UPDATE "product_variant" SET manage_inventory=false WHERE id = 'test-variant'` + ); + + const initialInventoryRes = await api.get("/store/variants/test-variant"); + + expect(initialInventoryRes.data.variant.inventory_quantity).toEqual(1); + + const response = await api + .post( + `/admin/orders/test-order-not-payed/cancel`, + {}, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((err) => { + console.log(err); + }); + expect(response.status).toEqual(200); + + const secondInventoryRes = await api.get("/store/variants/test-variant"); + + expect(secondInventoryRes.data.variant.inventory_quantity).toEqual(1); + }); + }); + describe("POST /admin/orders/:id/claims", () => { beforeEach(async () => { try { @@ -154,6 +328,18 @@ describe("/admin/orders", () => { ); expect(response.status).toEqual(200); + const variant = await api.get("/admin/products", { + headers: { + authorization: "Bearer test_token", + }, + }); + + // find test variant and verify that its inventory quantity has changed + const toTest = variant.data.products[0].variants.find( + (v) => v.id === "test-variant" + ); + expect(toTest.inventory_quantity).toEqual(0); + expect(response.data.order.claims[0].shipping_address_id).toEqual( "test-shipping-address" ); @@ -620,6 +806,43 @@ describe("/admin/orders", () => { }), ]); }); + + it("fails to creates a claim due to no stock on additional items", async () => { + const api = useApi(); + try { + await api.post( + "/admin/orders/test-order/claims", + { + type: "replace", + claim_items: [ + { + item_id: "test-item", + quantity: 1, + reason: "production_failure", + tags: ["fluff"], + images: ["https://test.image.com"], + }, + ], + additional_items: [ + { + variant_id: "test-variant", + quantity: 2, + }, + ], + }, + { + headers: { + authorization: "Bearer test_token", + }, + } + ); + } catch (e) { + expect(e.response.status).toEqual(400); + expect(e.response.data.message).toEqual( + "Variant with id: test-variant does not have the required inventory" + ); + } + }); }); describe("POST /admin/orders/:id/return", () => { @@ -705,6 +928,71 @@ describe("/admin/orders", () => { }), ]); }); + + it("increases inventory_quantity when return is received", async () => { + const api = useApi(); + + const returned = await api.post( + "/admin/orders/test-order/return", + { + items: [ + { + item_id: "test-item", + quantity: 1, + }, + ], + receive_now: true, + }, + { + headers: { + authorization: "Bearer test_token", + }, + } + ); + + //Find variant that should have its inventory_quantity updated + const toTest = returned.data.order.items.find( + (i) => i.id === "test-item" + ); + + expect(returned.status).toEqual(200); + expect(toTest.variant.inventory_quantity).toEqual(2); + }); + + it("does not increases inventory_quantity when return is received when inventory is not managed", async () => { + const api = useApi(); + const manager = dbConnection.manager; + + await manager.query( + `UPDATE "product_variant" SET manage_inventory=false WHERE id = 'test-variant'` + ); + + const returned = await api.post( + "/admin/orders/test-order/return", + { + items: [ + { + item_id: "test-item", + quantity: 1, + }, + ], + receive_now: true, + }, + { + headers: { + authorization: "Bearer test_token", + }, + } + ); + + //Find variant that should have its inventory_quantity updated + const toTest = returned.data.order.items.find( + (i) => i.id === "test-item" + ); + + expect(returned.status).toEqual(200); + expect(toTest.variant.inventory_quantity).toEqual(1); + }); }); describe("GET /admin/orders", () => { @@ -967,8 +1255,13 @@ describe("/admin/orders", () => { } ); + // find item to test returned quantiy for + const toTest = returnedOrderSecond.data.order.items.find( + (i) => i.id === "test-item-many" + ); + expect(returnedOrderSecond.status).toEqual(200); - expect(returnedOrderSecond.data.order.items[1].returned_quantity).toBe(3); + expect(toTest.returned_quantity).toBe(3); }); it("creates a swap and receives the items", async () => { diff --git a/integration-tests/api/__tests__/store/__snapshots__/cart.js.snap b/integration-tests/api/__tests__/store/__snapshots__/cart.js.snap new file mode 100644 index 0000000000..9f6476ca0a --- /dev/null +++ b/integration-tests/api/__tests__/store/__snapshots__/cart.js.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`/store/carts POST /store/carts/:id fails to complete cart with items inventory not/partially covered 1`] = ` +Object { + "code": "insufficient_inventory", + "message": "Variant with id: test-variant-2 does not have the required inventory", + "type": "not_allowed", +} +`; + +exports[`/store/carts POST /store/carts/:id returns early, if cart is already completed 1`] = ` +Object { + "code": "cart_incompatible_state", + "message": "Cart has already been completed", + "type": "not_allowed", +} +`; diff --git a/integration-tests/api/__tests__/store/cart.js b/integration-tests/api/__tests__/store/cart.js index 6ef5e21b54..095e75a94f 100644 --- a/integration-tests/api/__tests__/store/cart.js +++ b/integration-tests/api/__tests__/store/cart.js @@ -1,6 +1,6 @@ const { dropDatabase } = require("pg-god"); const path = require("path"); -const { Region } = require("@medusajs/medusa"); +const { Region, LineItem, Payment } = require("@medusajs/medusa"); const setupServer = require("../../../helpers/setup-server"); const { useApi } = require("../../../helpers/use-api"); @@ -15,15 +15,21 @@ describe("/store/carts", () => { let dbConnection; const doAfterEach = async (manager) => { + await manager.query(`DELETE FROM "line_item"`); + await manager.query(`DELETE FROM "money_amount"`); await manager.query(`DELETE FROM "discount"`); await manager.query(`DELETE FROM "discount_rule"`); - await manager.query(`DELETE FROM "shipping_method"`); - await manager.query(`DELETE FROM "line_item"`); - await manager.query(`DELETE FROM "cart"`); - await manager.query(`DELETE FROM "money_amount"`); await manager.query(`DELETE FROM "product_variant"`); await manager.query(`DELETE FROM "product"`); + await manager.query(`DELETE FROM "shipping_method"`); await manager.query(`DELETE FROM "shipping_option"`); + await manager.query(`DELETE FROM "payment_provider"`); + await manager.query(`DELETE FROM "payment_session"`); + await manager.query(`UPDATE "payment" SET order_id=NULL`); + await manager.query(`DELETE FROM "order"`); + await manager.query(`UPDATE "payment" SET cart_id=NULL`); + await manager.query(`DELETE FROM "cart"`); + await manager.query(`DELETE FROM "payment"`); await manager.query(`DELETE FROM "address"`); await manager.query(`DELETE FROM "customer"`); await manager.query( @@ -208,6 +214,65 @@ describe("/store/carts", () => { expect(cart.data.cart.shipping_total).toBe(1000); expect(cart.status).toEqual(200); }); + + it("complete cart with items inventory covered", async () => { + const api = useApi(); + const getRes = await api.post(`/store/carts/test-cart-2/complete-cart`); + + expect(getRes.status).toEqual(200); + + const variantRes = await api.get("/store/variants/test-variant"); + expect(variantRes.data.variant.inventory_quantity).toEqual(0); + }); + + it("returns early, if cart is already completed", async () => { + const manager = dbConnection.manager; + const api = useApi(); + await manager.query( + `UPDATE "cart" SET completed_at=current_timestamp WHERE id = 'test-cart-2'` + ); + try { + await api.post(`/store/carts/test-cart-2/complete-cart`); + } catch (error) { + expect(error.response.data).toMatchSnapshot({ + code: "not_allowed", + message: "Cart has already been completed", + code: "cart_incompatible_state", + }); + expect(error.response.status).toEqual(409); + } + }); + + it("fails to complete cart with items inventory not/partially covered", async () => { + const manager = dbConnection.manager; + + const li = manager.create(LineItem, { + id: "test-item", + title: "Line Item", + description: "Line Item Desc", + thumbnail: "https://test.js/1234", + unit_price: 8000, + quantity: 99, + variant_id: "test-variant-2", + cart_id: "test-cart-2", + }); + await manager.save(li); + + const api = useApi(); + + try { + await api.post(`/store/carts/test-cart-2/complete-cart`); + } catch (e) { + expect(e.response.data).toMatchSnapshot({ + code: "insufficient_inventory", + }); + expect(e.response.status).toBe(409); + } + + //check to see if payment has been cancelled + const res = await api.get(`/store/carts/test-cart-2`); + expect(res.data.cart.payment.canceled_at).not.toBe(null); + }); }); describe("POST /store/carts/:id/shipping-methods", () => { diff --git a/integration-tests/api/__tests__/store/orders.js b/integration-tests/api/__tests__/store/orders.js index 4958bc3235..76b0597420 100644 --- a/integration-tests/api/__tests__/store/orders.js +++ b/integration-tests/api/__tests__/store/orders.js @@ -9,12 +9,19 @@ const { ProductVariant, MoneyAmount, LineItem, + Payment, + Cart, + ShippingMethod, + Swap, } = require("@medusajs/medusa"); const setupServer = require("../../../helpers/setup-server"); const { useApi } = require("../../../helpers/use-api"); const { initDb } = require("../../../helpers/use-db"); +const swapSeeder = require("../../helpers/swap-seeder"); +const cartSeeder = require("../../helpers/cart-seeder"); + jest.setTimeout(30000); describe("/store/carts", () => { @@ -34,6 +41,92 @@ describe("/store/carts", () => { medusaProcess.kill(); }); + describe("/store/swaps", () => { + beforeEach(async () => { + try { + await cartSeeder(dbConnection); + await swapSeeder(dbConnection); + + const manager = dbConnection.manager; + await manager.query( + `UPDATE "swap" SET cart_id='test-cart-2' WHERE id = 'test-swap'` + ); + await manager.query( + `UPDATE "payment" SET swap_id=NULL WHERE id = 'test-payment-swap'` + ); + } catch (err) { + console.log(err); + throw err; + } + }); + + afterEach(async () => { + const manager = dbConnection.manager; + await manager.query( + `UPDATE "swap" SET cart_id=NULL WHERE id = 'test-swap'` + ); + + await manager.query(`DELETE FROM "payment_session"`); + await manager.query(`DELETE FROM "shipping_method"`); + await manager.query(`DELETE FROM "return_item"`); + await manager.query(`DELETE FROM "line_item"`); + await manager.query(`DELETE FROM "cart"`); + await manager.query(`DELETE FROM "payment"`); + await manager.query(`DELETE FROM "return"`); + await manager.query(`DELETE FROM "swap"`); + await manager.query(`DELETE FROM "fulfillment_item"`); + await manager.query(`DELETE FROM "fulfillment"`); + await manager.query(`DELETE FROM "shipping_method"`); + await manager.query(`DELETE FROM "money_amount"`); + await manager.query(`DELETE FROM "product_variant"`); + await manager.query(`DELETE FROM "product"`); + await manager.query(`DELETE FROM "shipping_option"`); + await manager.query(`DELETE FROM "order"`); + await manager.query(`DELETE FROM "address"`); + await manager.query(`DELETE FROM "customer"`); + await manager.query( + `UPDATE "country" SET region_id=NULL WHERE iso_2 = 'us'` + ); + await manager.query(`DELETE FROM "region"`); + }); + + it("creates a swap from a cart id", async () => { + const api = useApi(); + + const getRes = await api.post("/store/swaps", { + cart_id: "test-cart-2", + }); + expect(getRes.status).toEqual(200); + }); + + it("fails due to partial inventory", async () => { + const api = useApi(); + const manager = dbConnection.manager; + + const li = manager.create(LineItem, { + id: "test-item-with-no-stock", + title: "No Stock Item", + description: "Line Item Desc", + thumbnail: "https://test.js/1234", + unit_price: 8000, + quantity: 1, + variant_id: "test-variant-2", + cart_id: "test-cart-2", + }); + await manager.save(li); + + try { + await api.post("/store/swaps", { + cart_id: "test-cart-2", + }); + } catch (e) { + expect(e.response.data.message).toEqual( + "Variant with id: test-variant-2 does not have the required inventory" + ); + } + }); + }); + describe("GET /store/orders", () => { beforeEach(async () => { const manager = dbConnection.manager; diff --git a/integration-tests/api/helpers/cart-seeder.js b/integration-tests/api/helpers/cart-seeder.js index 617e0c630e..19697cc14e 100644 --- a/integration-tests/api/helpers/cart-seeder.js +++ b/integration-tests/api/helpers/cart-seeder.js @@ -8,9 +8,12 @@ const { ShippingOption, ShippingMethod, Address, - ProductVariant, Product, + ProductVariant, MoneyAmount, + LineItem, + Payment, + PaymentSession, } = require("@medusajs/medusa"); module.exports = async (connection, data = {}) => { @@ -189,19 +192,42 @@ module.exports = async (connection, data = {}) => { ], }); + await manager.insert(ProductVariant, { + id: "test-variant-2", + title: "test variant 2", + product_id: "test-product", + inventory_quantity: 0, + options: [ + { + option_id: "test-option", + value: "Size", + }, + ], + }); + const ma = manager.create(MoneyAmount, { variant_id: "test-variant", currency_code: "usd", amount: 1000, }); + await manager.save(ma); const ma2 = manager.create(MoneyAmount, { + variant_id: "test-variant-2", + currency_code: "usd", + amount: 8000, + }); + + await manager.save(ma2); + + const ma3 = manager.create(MoneyAmount, { variant_id: "giftcard-denom", currency_code: "usd", amount: 1000, }); - await manager.save(ma2); + + await manager.save(ma3); const cart = manager.create(Cart, { id: "test-cart", @@ -219,6 +245,45 @@ module.exports = async (connection, data = {}) => { await manager.save(cart); + const cart2 = manager.create(Cart, { + id: "test-cart-2", + customer_id: "some-customer", + email: "some-customer@email.com", + shipping_address: { + id: "test-shipping-address", + first_name: "lebron", + country_code: "us", + }, + region_id: "test-region", + currency_code: "usd", + completed_at: null, + items: [], + }); + + const pay = manager.create(Payment, { + id: "test-payment", + amount: 10000, + currency_code: "usd", + amount_refunded: 0, + provider_id: "test-pay", + data: {}, + }); + + await manager.save(pay); + + cart2.payment = pay; + + await manager.save(cart2); + + await manager.insert(PaymentSession, { + id: "test-session", + cart_id: "test-cart-2", + provider_id: "test-pay", + is_selected: true, + data: {}, + status: "authorized", + }); + await manager.insert(ShippingMethod, { id: "test-method", shipping_option_id: "test-option", @@ -226,4 +291,16 @@ module.exports = async (connection, data = {}) => { price: 1000, data: {}, }); + + const li = manager.create(LineItem, { + id: "test-item", + title: "Line Item", + description: "Line Item Desc", + thumbnail: "https://test.js/1234", + unit_price: 8000, + quantity: 1, + variant_id: "test-variant", + cart_id: "test-cart-2", + }); + await manager.save(li); }; diff --git a/integration-tests/api/package.json b/integration-tests/api/package.json index 4754b360e6..06c9f43c20 100644 --- a/integration-tests/api/package.json +++ b/integration-tests/api/package.json @@ -4,11 +4,11 @@ "main": "index.js", "license": "MIT", "scripts": { - "test": "jest --runInBand", + "test": "jest --runInBand --silent=false", "build": "babel src -d dist --extensions \".ts,.js\"" }, "dependencies": { - "@medusajs/medusa": "1.1.33-dev-1627995051381", + "@medusajs/medusa": "1.1.33-dev-1628079986095", "medusa-interfaces": "^1.1.18", "typeorm": "^0.2.31" }, @@ -19,4 +19,4 @@ "babel-preset-medusa-package": "^1.1.11", "jest": "^26.6.3" } -} +} \ No newline at end of file diff --git a/integration-tests/api/yarn.lock b/integration-tests/api/yarn.lock index 62a3ed067d..93166fad13 100644 --- a/integration-tests/api/yarn.lock +++ b/integration-tests/api/yarn.lock @@ -1265,10 +1265,10 @@ winston "^3.3.3" yargs "^15.3.1" -"@medusajs/medusa@1.1.33-dev-1627995051381": - version "1.1.33-dev-1627995051381" - resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.1.33-dev-1627995051381.tgz#def214374c31daca1f37c2dd5ee55f119236555f" - integrity sha512-yeH/YscYfWn4jYF+YSPyexMgAaDOfjvKm2xH95C+T9w4Ct08z06uBw0G0klIKPatNTirfD7HVgvg2vWb+ChDWA== +"@medusajs/medusa@1.1.33-dev-1628079986095": + version "1.1.33-dev-1628079986095" + resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.1.33-dev-1628079986095.tgz#0e8aa3bd83174366d1266823f093f21968b0f6e4" + integrity sha512-zqvz8+NL5+3Ba5uRS6Z2SaXpRTF2r6B+o0HGY8TjkAmvf2pMKyfIOGpBLtnhezDpDb9mkLdiHqrV23lNE+5pAw== dependencies: "@hapi/joi" "^16.1.8" "@medusajs/medusa-cli" "^1.1.14" diff --git a/package.json b/package.json index d44803d4e7..55840eefd9 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "test:fixtures": "jest --config=docs-util/jest.config.js --runInBand" }, "dependencies": { + "global": "^4.4.0", "import-from": "^3.0.0", "oas-normalize": "^2.3.1", "swagger-inline": "^3.2.2" diff --git a/packages/medusa-core-utils/src/errors.js b/packages/medusa-core-utils/src/errors.js index 88250a081b..a81ec59943 100644 --- a/packages/medusa-core-utils/src/errors.js +++ b/packages/medusa-core-utils/src/errors.js @@ -12,6 +12,11 @@ export const MedusaErrorTypes = { NOT_ALLOWED: "not_allowed", } +export const MedusaErrorCodes = { + INSUFFICIENT_INVENTORY: "insufficient_inventory", + CART_INCOMPATIBLE_STATE: "cart_incompatible_state", +} + /** * Standardized error to be used across Medusa project. * @extends Error @@ -22,19 +27,22 @@ class MedusaError extends Error { * @param type {MedusaErrorType} - the type of error. * @param params {Array} - Error params. */ - constructor(name, message, ...params) { + constructor(type, message, code, ...params) { super(...params) if (Error.captureStackTrace) { Error.captureStackTrace(this, MedusaError) } - this.name = name + this.type = type + this.name = type + this.code = code this.message = message this.date = new Date() } } MedusaError.Types = MedusaErrorTypes +MedusaError.Codes = MedusaErrorCodes export default MedusaError diff --git a/packages/medusa/src/api/routes/store/carts/__tests__/complete-cart.js b/packages/medusa/src/api/routes/store/carts/__tests__/complete-cart.js index f973c3dbf5..80e98067b3 100644 --- a/packages/medusa/src/api/routes/store/carts/__tests__/complete-cart.js +++ b/packages/medusa/src/api/routes/store/carts/__tests__/complete-cart.js @@ -104,8 +104,8 @@ describe("POST /store/carts/:id", () => { ) }) - it("Call CartService retrieve 0 times", () => { - expect(CartServiceMock.retrieve).toHaveBeenCalledTimes(0) + it("Call CartService retrieve 1 time", () => { + expect(CartServiceMock.retrieve).toHaveBeenCalledTimes(1) }) it("returns 200", () => { diff --git a/packages/medusa/src/api/routes/store/carts/complete-cart.js b/packages/medusa/src/api/routes/store/carts/complete-cart.js index e1997fcafc..df72d086fa 100644 --- a/packages/medusa/src/api/routes/store/carts/complete-cart.js +++ b/packages/medusa/src/api/routes/store/carts/complete-cart.js @@ -71,7 +71,20 @@ export default async (req, res) => { const { key, error } = await idempotencyKeyService.workStage( idempotencyKey.idempotency_key, async manager => { - const cart = await cartService + let cart = await cartService.withTransaction(manager).retrieve(id) + + if (cart.completed_at) { + return { + response_code: 409, + response_body: { + code: MedusaError.Codes.CART_INCOMPATIBLE_STATE, + message: "Cart has already been completed", + type: MedusaError.Types.NOT_ALLOWED, + }, + } + } + + cart = await cartService .withTransaction(manager) .authorizePayment(id, { ...req.request_context, @@ -85,7 +98,11 @@ export default async (req, res) => { ) { return { response_code: 200, - response_body: { data: cart }, + response_body: { + data: cart, + payment_status: cart.payment_session.status, + type: "cart", + }, } } } @@ -122,17 +139,17 @@ export default async (req, res) => { switch (cart.type) { case "swap": { const swapId = cart.metadata?.swap_id - order = await swapService + let swap = await swapService .withTransaction(manager) .registerCartCompletion(swapId) - order = await swapService + swap = await swapService .withTransaction(manager) - .retrieve(order.id, { relations: ["shipping_address"] }) + .retrieve(swap.id, { relations: ["shipping_address"] }) return { response_code: 200, - response_body: { data: order }, + response_body: { data: swap, type: "swap" }, } } // case "payment_link": @@ -168,7 +185,19 @@ export default async (req, res) => { return { response_code: 200, - response_body: { data: order }, + response_body: { data: order, type: "order" }, + } + } else if ( + error && + error.code === MedusaError.Codes.INSUFFICIENT_INVENTORY + ) { + return { + response_code: 409, + response_body: { + message: error.message, + type: error.type, + code: error.code, + }, } } else { throw error @@ -192,7 +221,7 @@ export default async (req, res) => { return { response_code: 200, - response_body: { data: order }, + response_body: { data: order, type: "order" }, } } ) @@ -230,7 +259,6 @@ export default async (req, res) => { res.status(idempotencyKey.response_code).json(idempotencyKey.response_body) } catch (error) { - console.log(error) throw error } } diff --git a/packages/medusa/src/services/__mocks__/cart.js b/packages/medusa/src/services/__mocks__/cart.js index b674e63e9b..6c99bcd2c7 100644 --- a/packages/medusa/src/services/__mocks__/cart.js +++ b/packages/medusa/src/services/__mocks__/cart.js @@ -260,6 +260,9 @@ export const CartServiceMock = { if (cartId === IdMap.getId("cartWithPaySessions")) { return Promise.resolve(carts.cartWithPaySessions) } + if (cartId === IdMap.getId("test-cart2")) { + return Promise.resolve(carts.testCart) + } throw new MedusaError(MedusaError.Types.NOT_FOUND, "cart not found") }), addLineItem: jest.fn().mockImplementation((cartId, lineItem) => { diff --git a/packages/medusa/src/services/__mocks__/inventory.js b/packages/medusa/src/services/__mocks__/inventory.js new file mode 100644 index 0000000000..db8fcdad8d --- /dev/null +++ b/packages/medusa/src/services/__mocks__/inventory.js @@ -0,0 +1,20 @@ +import { MedusaError } from "medusa-core-utils" + +export const InventoryServiceMock = { + withTransaction: function() { + return this + }, + adjustInventory: jest.fn().mockReturnValue((_variantId, _quantity) => { + return Promise.resolve({}) + }), + confirmInventory: jest.fn().mockImplementation((variantId, quantity) => { + if (quantity < 10) { + return true + } else { + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + `Variant with id: ${variantId} does not have the required inventory` + ) + } + }), +} diff --git a/packages/medusa/src/services/__mocks__/product-variant.js b/packages/medusa/src/services/__mocks__/product-variant.js index e1cced6d78..aa17e7ce2f 100644 --- a/packages/medusa/src/services/__mocks__/product-variant.js +++ b/packages/medusa/src/services/__mocks__/product-variant.js @@ -111,6 +111,46 @@ const giftCardVar = { title: "100 USD", } +const outOfStockBackOrder = { + id: "bo", + title: "variant_popular", + inventory_quantity: 0, + allow_backorder: true, + manage_inventory: true, +} + +const outOfStockNoBackOrder = { + id: "no_bo", + title: "variant_popular", + inventory_quantity: 0, + allow_backorder: false, + manage_inventory: true, +} + +const outOfStockNoManage = { + id: "no_manage", + title: "variant_popular", + inventory_quantity: 0, + allow_backorder: false, + manage_inventory: false, +} + +const StockOf10Manage = { + id: "10_man", + title: "variant_popular", + inventory_quantity: 10, + allow_backorder: false, + manage_inventory: true, +} + +const StockOf1Manage = { + id: "1_man", + title: "variant_popular", + inventory_quantity: 1, + allow_backorder: false, + manage_inventory: true, +} + export const variants = { one: variant1, two: variant2, @@ -171,17 +211,21 @@ export const ProductVariantServiceMock = { if (variantId === IdMap.getId("testVariant")) { return Promise.resolve(testVariant) } - }), - canCoverQuantity: jest.fn().mockImplementation((variantId, quantity) => { - if (variantId === IdMap.getId("can-cover")) { - return Promise.resolve(true) + if (variantId === "bo") { + return Promise.resolve(outOfStockBackOrder) } - - if (variantId === IdMap.getId("cannot-cover")) { - return Promise.resolve(false) + if (variantId === "no_bo") { + return Promise.resolve(outOfStockNoBackOrder) + } + if (variantId === "no_manage") { + return Promise.resolve(outOfStockNoManage) + } + if (variantId === "10_man") { + return Promise.resolve(StockOf10Manage) + } + if (variantId === "1_man") { + return Promise.resolve(StockOf1Manage) } - - return Promise.reject(new Error("Not found")) }), getRegionPrice: jest.fn().mockImplementation((variantId, regionId) => { if (variantId === IdMap.getId("eur-10-us-12")) { diff --git a/packages/medusa/src/services/__tests__/cart.js b/packages/medusa/src/services/__tests__/cart.js index 5a29db6ca6..eeadb861fd 100644 --- a/packages/medusa/src/services/__tests__/cart.js +++ b/packages/medusa/src/services/__tests__/cart.js @@ -1,6 +1,8 @@ import _ from "lodash" import { IdMap, MockRepository, MockManager } from "medusa-test-utils" import CartService from "../cart" +import { InventoryServiceMock } from "../__mocks__/inventory" +import { MedusaError } from "medusa-core-utils" const eventBusService = { emit: jest.fn(), @@ -311,10 +313,18 @@ describe("CartService", () => { }, } - const productVariantService = { - canCoverQuantity: jest - .fn() - .mockImplementation(id => id !== IdMap.getId("cannot-cover")), + const inventoryService = { + ...InventoryServiceMock, + confirmInventory: jest.fn().mockImplementation((variantId, _quantity) => { + if (variantId !== IdMap.getId("cannot-cover")) { + return true + } else { + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + `Variant with id: ${variantId} does not have the required inventory` + ) + } + }), } const cartRepository = MockRepository({ @@ -349,9 +359,9 @@ describe("CartService", () => { totalsService, cartRepository, lineItemService, - productVariantService, eventBusService, shippingOptionService, + inventoryService, }) beforeEach(() => { @@ -449,7 +459,11 @@ describe("CartService", () => { await expect( cartService.addLineItem(IdMap.getId("cartWithLine"), lineItem) - ).rejects.toThrow(`Inventory doesn't cover the desired quantity`) + ).rejects.toThrow( + `Variant with id: ${IdMap.getId( + "cannot-cover" + )} does not have the required inventory` + ) }) it("throws if inventory isn't covered", async () => { @@ -463,7 +477,11 @@ describe("CartService", () => { await expect( cartService.addLineItem(IdMap.getId("cartWithLine"), lineItem) - ).rejects.toThrow(`Inventory doesn't cover the desired quantity`) + ).rejects.toThrow( + `Variant with id: ${IdMap.getId( + "cannot-cover" + )} does not have the required inventory` + ) }) }) @@ -635,8 +653,9 @@ describe("CartService", () => { return this }, } - const productVariantService = { - canCoverQuantity: jest + const inventoryService = { + ...InventoryServiceMock, + confirmInventory: jest .fn() .mockImplementation(id => id !== IdMap.getId("cannot-cover")), } @@ -669,9 +688,9 @@ describe("CartService", () => { manager: MockManager, totalsService, cartRepository, - productVariantService, lineItemService, eventBusService, + inventoryService, }) beforeEach(() => { diff --git a/packages/medusa/src/services/__tests__/claim.js b/packages/medusa/src/services/__tests__/claim.js index feae278cb3..ab0585a39a 100644 --- a/packages/medusa/src/services/__tests__/claim.js +++ b/packages/medusa/src/services/__tests__/claim.js @@ -1,6 +1,7 @@ import _ from "lodash" import { IdMap, MockRepository, MockManager } from "medusa-test-utils" import ClaimService from "../claim" +import { InventoryServiceMock } from "../__mocks__/inventory" const withTransactionMock = jest.fn() const eventBusService = { @@ -73,6 +74,14 @@ describe("ClaimService", () => { }, } + const inventoryService = { + ...InventoryServiceMock, + withTransaction: function() { + withTransactionMock("inventory") + return this + }, + } + const claimItemService = { create: jest.fn(), withTransaction: function() { @@ -88,6 +97,7 @@ describe("ClaimService", () => { returnService, lineItemService, claimItemService, + inventoryService, eventBusService, }) @@ -125,6 +135,18 @@ describe("ClaimService", () => { 1 ) + expect(inventoryService.confirmInventory).toHaveBeenCalledTimes(1) + expect(inventoryService.confirmInventory).toHaveBeenCalledWith( + "var_123", + 1 + ) + expect(withTransactionMock).toHaveBeenCalledWith("inventory") + expect(inventoryService.adjustInventory).toHaveBeenCalledTimes(1) + expect(inventoryService.adjustInventory).toHaveBeenCalledWith( + "var_123", + -1 + ) + expect(withTransactionMock).toHaveBeenCalledWith("claimItem") expect(claimItemService.create).toHaveBeenCalledTimes(1) expect(claimItemService.create).toHaveBeenCalledWith({ @@ -213,6 +235,24 @@ describe("ClaimService", () => { ).rejects.toThrow(`Claims must have at least one claim item.`) }) + it("fails if additional items are not in stock", async () => { + try { + const res = await claimService.create({ + ...testClaim, + additional_items: [ + { + variant_id: "var_123", + quantity: 25, + }, + ], + }) + console.warn(res) + } catch (e) { + expect(e.message).toEqual( + `Variant with id: var_123 does not have the required inventory` + ) + } + }) it.each( [ [false, false], diff --git a/packages/medusa/src/services/__tests__/inventory.js b/packages/medusa/src/services/__tests__/inventory.js new file mode 100644 index 0000000000..9b6eb90e0d --- /dev/null +++ b/packages/medusa/src/services/__tests__/inventory.js @@ -0,0 +1,91 @@ +import { MockManager } from "medusa-test-utils" +import InventoryService from "../inventory" +import { ProductVariantServiceMock } from "../__mocks__/product-variant" + +describe("InventoryService", () => { + describe("confirmInventory", () => { + const inventoryService = new InventoryService({ + manager: MockManager, + productVariantService: ProductVariantServiceMock, + }) + + beforeEach(async () => { + jest.clearAllMocks() + }) + + it("returns false when inventory is managed, and no back orders are allowed and the quantity is larger than inventory", async () => { + await expect( + inventoryService.confirmInventory("no_bo", 10) + ).rejects.toThrow( + `Variant with id: no_bo does not have the required inventory` + ) + }) + it("returns true when variant is out of stock but allows back orders", async () => { + const result = await inventoryService.confirmInventory("bo", 100) + expect(result).toEqual(true) + }) + it("returns true when variant is out of stock but inventory quantity is not managed", async () => { + const result = await inventoryService.confirmInventory("no_manage", 10000) + expect(result).toEqual(true) + }) + it("returns true when managed variant inventory_quantity > requested quantity", async () => { + const result = await inventoryService.confirmInventory("10_man", 5) + expect(result).toEqual(true) + }) + it("returns false when managed variant inventory_quantity < requested quantity", async () => { + await expect( + inventoryService.confirmInventory("10_man", 50) + ).rejects.toThrow( + `Variant with id: 10_man does not have the required inventory` + ) + }) + }) + describe("adjustInventory", () => { + const inventoryService = new InventoryService({ + manager: MockManager, + productVariantService: ProductVariantServiceMock, + }) + + beforeEach(async () => { + jest.clearAllMocks() + }) + + it("should not call update in productVariantService because variant is not managed", async () => { + await inventoryService.adjustInventory("no_manage", 1000) + expect(ProductVariantServiceMock.update).toHaveBeenCalledTimes(0) + }) + + it("should call update in productVariantService once", async () => { + await inventoryService.adjustInventory("10_man", 10) + expect(ProductVariantServiceMock.update).toHaveBeenCalledTimes(1) + expect(ProductVariantServiceMock.update).toHaveBeenCalledWith( + { + id: "10_man", + title: "variant_popular", + inventory_quantity: 10, + allow_backorder: false, + manage_inventory: true, + }, + { + inventory_quantity: 20, + } + ) + }) + + it("should update update once for 1man", async () => { + await inventoryService.adjustInventory("1_man", -1) + expect(ProductVariantServiceMock.update).toHaveBeenCalledWith( + { + id: "1_man", + title: "variant_popular", + inventory_quantity: 1, + allow_backorder: false, + manage_inventory: true, + }, + { + inventory_quantity: 0, + } + ) + }) + }) +}) diff --git a/packages/medusa/src/services/__tests__/order.js b/packages/medusa/src/services/__tests__/order.js index e9667752ba..c13df7cbc9 100644 --- a/packages/medusa/src/services/__tests__/order.js +++ b/packages/medusa/src/services/__tests__/order.js @@ -1,5 +1,6 @@ import { IdMap, MockManager, MockRepository } from "medusa-test-utils" import OrderService from "../order" +import { InventoryServiceMock } from "../__mocks__/inventory" describe("OrderService", () => { const totalsService = { @@ -37,6 +38,10 @@ describe("OrderService", () => { }, } + const inventoryService = { + ...InventoryServiceMock, + } + describe("create", () => { const orderRepo = MockRepository({ create: f => f }) const orderService = new OrderService({ @@ -95,6 +100,9 @@ describe("OrderService", () => { return Promise.resolve(payment.status || "authorized") }, updatePayment: jest.fn(), + cancelPayment: jest.fn().mockImplementation(payment => { + return Promise.resolve({ ...payment, status: "cancelled" }) + }), withTransaction: function() { return this }, @@ -153,6 +161,7 @@ describe("OrderService", () => { regionService, eventBusService, cartService, + inventoryService, }) beforeEach(async () => { @@ -186,7 +195,10 @@ describe("OrderService", () => { gift_cards: [], discounts: [], shipping_methods: [{ id: "method_1" }], - items: [{ id: "item_1" }, { id: "item_2" }], + items: [ + { id: "item_1", variant_id: "variant-1", quantity: 1 }, + { id: "item_2", variant_id: "variant-2", quantity: 1 }, + ], total: 100, } @@ -231,6 +243,16 @@ describe("OrderService", () => { } ) + expect(inventoryService.adjustInventory).toHaveBeenCalledTimes(2) + expect(inventoryService.adjustInventory).toHaveBeenCalledWith( + "variant-2", + -1 + ) + expect(inventoryService.adjustInventory).toHaveBeenCalledWith( + "variant-1", + -1 + ) + expect(lineItemService.update).toHaveBeenCalledTimes(2) expect(lineItemService.update).toHaveBeenCalledWith("item_1", { order_id: "id", @@ -272,7 +294,10 @@ describe("OrderService", () => { ], discounts: [], shipping_methods: [{ id: "method_1" }], - items: [{ id: "item_1" }, { id: "item_2" }], + items: [ + { id: "item_1", variant_id: "variant-1", quantity: 1 }, + { id: "item_2", variant_id: "variant-2", quantity: 1 }, + ], subtotal: 100, total: 100, } @@ -361,7 +386,10 @@ describe("OrderService", () => { billing_address_id: "1234", discounts: [], shipping_methods: [{ id: "method_1" }], - items: [{ id: "item_1" }, { id: "item_2" }], + items: [ + { id: "item_1", variant_id: "variant-1", quantity: 1 }, + { id: "item_2", variant_id: "variant-2", quantity: 1 }, + ], total: 0, } orderService.cartService_.retrieve = () => Promise.resolve(cart) @@ -395,6 +423,45 @@ describe("OrderService", () => { expect(orderRepo.save).toHaveBeenCalledWith(order) }) + + it("fails because an item does not have the required inventory", async () => { + const cart = { + id: "cart_id", + email: "test@test.com", + customer_id: "cus_1234", + payment: { + id: "testpayment", + amount: 100, + status: "authorized", + }, + region_id: "test", + region: { + id: "test", + currency_code: "eur", + name: "test", + tax_rate: 25, + }, + gift_cards: [], + shipping_address_id: "1234", + billing_address_id: "1234", + discounts: [], + shipping_methods: [{ id: "method_1" }], + items: [ + { id: "item_1", variant_id: "variant-1", quantity: 12 }, + { id: "item_2", variant_id: "variant-2", quantity: 1 }, + ], + total: 100, + } + orderService.cartService_.retrieve = () => Promise.resolve(cart) + const res = orderService.createFromCart(cart) + await expect(res).rejects.toThrow( + "Variant with id: variant-1 does not have the required inventory" + ) + //check to see if payment is cancelled + expect( + orderService.paymentProviderService_.cancelPayment + ).toHaveBeenCalledTimes(1) + }) }) describe("retrieve", () => { @@ -545,6 +612,10 @@ describe("OrderService", () => { status: "pending", fulfillments: [{ id: "fulfillment_test" }], payments: [{ id: "payment_test" }], + items: [ + { id: "item_1", variant_id: "variant-1", quantity: 12 }, + { id: "item_2", variant_id: "variant-2", quantity: 1 }, + ], }) } }, @@ -571,6 +642,7 @@ describe("OrderService", () => { paymentProviderService, fulfillmentService, eventBusService, + inventoryService, }) beforeEach(async () => { @@ -578,7 +650,15 @@ describe("OrderService", () => { }) it("calls order model functions", async () => { - await orderService.cancel(IdMap.getId("not-fulfilled-order")) + try { + const order = await orderService.retrieve( + IdMap.getId("not-fulfilled-order") + ) + console.warn(order) + await orderService.cancel(IdMap.getId("not-fulfilled-order")) + } catch (e) { + console.warn(e) + } expect(paymentProviderService.cancelPayment).toHaveBeenCalledTimes(1) expect(paymentProviderService.cancelPayment).toHaveBeenCalledWith({ @@ -590,6 +670,16 @@ describe("OrderService", () => { id: "fulfillment_test", }) + expect(inventoryService.adjustInventory).toHaveBeenCalledTimes(2) + expect(inventoryService.adjustInventory).toHaveBeenCalledWith( + "variant-1", + 12 + ) + expect(inventoryService.adjustInventory).toHaveBeenCalledWith( + "variant-2", + 1 + ) + expect(orderRepo.save).toHaveBeenCalledTimes(1) expect(orderRepo.save).toHaveBeenCalledWith({ fulfillment_status: "canceled", @@ -597,6 +687,18 @@ describe("OrderService", () => { status: "canceled", fulfillments: [{ id: "fulfillment_test" }], payments: [{ id: "payment_test" }], + items: [ + { + id: "item_1", + quantity: 12, + variant_id: "variant-1", + }, + { + id: "item_2", + quantity: 1, + variant_id: "variant-2", + }, + ], }) }) diff --git a/packages/medusa/src/services/__tests__/product-variant.js b/packages/medusa/src/services/__tests__/product-variant.js index ec9418ca3e..08950022fc 100644 --- a/packages/medusa/src/services/__tests__/product-variant.js +++ b/packages/medusa/src/services/__tests__/product-variant.js @@ -384,6 +384,29 @@ describe("ProductVariantService", () => { }) }) + it("successfully updates variant inventory_quantity", async () => { + await productVariantService.update(IdMap.getId("ironman"), { + title: "new title", + inventory_quantity: 98, + }) + + expect(eventBusService.emit).toHaveBeenCalledTimes(1) + expect(eventBusService.emit).toHaveBeenCalledWith( + "product-variant.updated", + { + id: IdMap.getId("ironman"), + fields: ["title", "inventory_quantity"], + } + ) + + expect(productVariantRepository.save).toHaveBeenCalledTimes(1) + expect(productVariantRepository.save).toHaveBeenCalledWith({ + id: IdMap.getId("ironman"), + inventory_quantity: 98, + title: "new title", + }) + }) + it("successfully updates variant prices", async () => { await productVariantService.update(IdMap.getId("ironman"), { title: "new title", @@ -809,73 +832,4 @@ describe("ProductVariantService", () => { expect(result).toBe(undefined) }) }) - - describe("canCoverQuantity", () => { - const productVariantRepository = MockRepository({ - findOne: query => { - if (query.where.id === IdMap.getId("no-manageable-ironman")) { - return Promise.resolve({ manage_inventory: false }) - } - if (query.where.id === IdMap.getId("backorder-ironman")) { - return Promise.resolve({ allow_backorder: true }) - } - if (query.where.id === IdMap.getId("no-ironman")) { - return Promise.resolve({ - inventory_quantity: 5, - manage_inventory: true, - allow_backorder: false, - }) - } - return Promise.resolve({ - inventory_quantity: 20, - }) - }, - }) - - const productVariantService = new ProductVariantService({ - manager: MockManager, - eventBusService, - productVariantRepository, - }) - - beforeEach(() => { - jest.clearAllMocks() - }) - - it("returns true if there is more inventory than requested", async () => { - const res = await productVariantService.canCoverQuantity( - IdMap.getId("ironman"), - 10 - ) - - expect(res).toEqual(true) - }) - - it("returns true if inventory not managed", async () => { - const res = await productVariantService.canCoverQuantity( - IdMap.getId("no-manageable-ironman"), - 10 - ) - - expect(res).toEqual(true) - }) - - it("returns true if backorders allowed", async () => { - const res = await productVariantService.canCoverQuantity( - IdMap.getId("backorder-ironman"), - 10 - ) - - expect(res).toEqual(true) - }) - - it("returns false if insufficient inventory", async () => { - const res = await productVariantService.canCoverQuantity( - IdMap.getId("no-ironman"), - 20 - ) - - expect(res).toEqual(false) - }) - }) }) diff --git a/packages/medusa/src/services/__tests__/return.js b/packages/medusa/src/services/__tests__/return.js index 06c254842b..8ebe7b9605 100644 --- a/packages/medusa/src/services/__tests__/return.js +++ b/packages/medusa/src/services/__tests__/return.js @@ -1,5 +1,7 @@ import { IdMap, MockManager, MockRepository } from "medusa-test-utils" +import idMap from "medusa-test-utils/dist/id-map" import ReturnService from "../return" +import { InventoryServiceMock } from "../__mocks__/inventory" describe("ReturnService", () => { // describe("requestReturn", () => { @@ -196,11 +198,13 @@ describe("ReturnService", () => { id: IdMap.getId("test-line"), quantity: 10, returned_quantity: 0, + variant_id: "test-variant", }, { id: IdMap.getId("test-line-2"), quantity: 10, returned_quantity: 0, + variant_id: "test-variant-2", }, ], payments: [{ id: "payment_test" }], @@ -221,12 +225,29 @@ describe("ReturnService", () => { }, } + const inventoryService = { + adjustInventory: jest.fn((variantId, quantity) => { + return Promise.resolve({}) + }), + confirmInventory: jest.fn((variantId, quantity) => { + if (quantity < 10) { + return true + } else { + return false + } + }), + withTransaction: function() { + return this + }, + } + const returnService = new ReturnService({ manager: MockManager, totalsService, lineItemService, orderService, returnRepository, + inventoryService, }) beforeEach(async () => { @@ -268,6 +289,12 @@ describe("ReturnService", () => { returned_quantity: 10, } ) + + expect(inventoryService.adjustInventory).toHaveBeenCalledTimes(1) + expect(inventoryService.adjustInventory).toHaveBeenCalledWith( + "test-variant", + 10 + ) }) it("successfully receives a return with requires_action status", async () => { @@ -280,6 +307,16 @@ describe("ReturnService", () => { 1000 ) + expect(inventoryService.adjustInventory).toHaveBeenCalledTimes(2) + expect(inventoryService.adjustInventory).toHaveBeenCalledWith( + "test-variant", + 10 + ) + expect(inventoryService.adjustInventory).toHaveBeenCalledWith( + "test-variant-2", + 10 + ) + expect(returnRepository.save).toHaveBeenCalledTimes(1) expect(returnRepository.save).toHaveBeenCalledWith({ id: IdMap.getId("test-return-2"), diff --git a/packages/medusa/src/services/__tests__/swap.js b/packages/medusa/src/services/__tests__/swap.js index da17fad783..447e9f0814 100644 --- a/packages/medusa/src/services/__tests__/swap.js +++ b/packages/medusa/src/services/__tests__/swap.js @@ -1,5 +1,6 @@ import { IdMap, MockRepository, MockManager } from "medusa-test-utils" import SwapService from "../swap" +import { InventoryServiceMock } from "../__mocks__/inventory" const eventBusService = { emit: jest.fn(), @@ -685,44 +686,51 @@ describe("SwapService", () => { Date.now = jest.fn(() => 1572393600000) }) + const eventBusService = { + emit: jest.fn().mockReturnValue(Promise.resolve()), + withTransaction: function() { + return this + }, + } + + const totalsService = { + getTotal: () => { + return Promise.resolve(100) + }, + } + + const shippingOptionService = { + updateShippingMethod: () => { + return Promise.resolve() + }, + withTransaction: function() { + return this + }, + } + + const paymentProviderService = { + getStatus: jest.fn(() => { + return Promise.resolve("authorized") + }), + updatePayment: jest.fn(() => { + return Promise.resolve() + }), + withTransaction: function() { + return this + }, + } + + const inventoryService = { + ...InventoryServiceMock, + withTransaction: function() { + return this + }, + } + describe("success", () => { - const eventBusService = { - emit: jest.fn().mockReturnValue(Promise.resolve()), - withTransaction: function() { - return this - }, - } - - const totalsService = { - getTotal: () => { - return Promise.resolve(100) - }, - } - - const shippingOptionService = { - updateShippingMethod: () => { - return Promise.resolve() - }, - withTransaction: function() { - return this - }, - } - - const paymentProviderService = { - getStatus: jest.fn(() => { - return Promise.resolve("authorized") - }), - updatePayment: jest.fn(() => { - return Promise.resolve() - }), - withTransaction: function() { - return this - }, - } - const existing = { cart: { - items: [{ id: "1" }], + items: [{ id: "1", variant_id: "variant", quantity: 2 }], shipping_methods: [{ id: "method_1" }], payment: { good: "yes", @@ -744,6 +752,7 @@ describe("SwapService", () => { paymentProviderService, eventBusService, shippingOptionService, + inventoryService, }) it("creates a shipment", async () => { @@ -753,6 +762,15 @@ describe("SwapService", () => { good: "yes", }) + expect(inventoryService.confirmInventory).toHaveBeenCalledWith( + "variant", + 2 + ) + expect(inventoryService.adjustInventory).toHaveBeenCalledWith( + "variant", + -2 + ) + expect(swapRepo.save).toHaveBeenCalledWith({ ...existing, difference_due: 100, @@ -761,6 +779,45 @@ describe("SwapService", () => { }) }) }) + + describe("failure", () => { + const existing = { + cart: { + items: [{ id: "1", variant_id: "variant", quantity: 25 }], + shipping_methods: [{ id: "method_1" }], + payment: { + good: "yes", + }, + shipping_address_id: 1234, + }, + other: "data", + } + + const swapRepo = MockRepository({ + findOneWithRelations: () => Promise.resolve(existing), + }) + + const swapService = new SwapService({ + manager: MockManager, + eventBusService, + swapRepository: swapRepo, + totalsService, + paymentProviderService, + eventBusService, + shippingOptionService, + inventoryService, + }) + + it("throws an error because inventory is to low", async () => { + try { + await swapService.registerCartCompletion(IdMap.getId("swap")) + } catch (e) { + expect(e.message).toEqual( + `Variant with id: variant does not have the required inventory` + ) + } + }) + }) }) describe("processDifference", () => { diff --git a/packages/medusa/src/services/cart.js b/packages/medusa/src/services/cart.js index 850a1cec7b..bc39376708 100644 --- a/packages/medusa/src/services/cart.js +++ b/packages/medusa/src/services/cart.js @@ -31,6 +31,7 @@ class CartService extends BaseService { totalsService, addressRepository, paymentSessionRepository, + inventoryService, }) { super() @@ -84,6 +85,9 @@ class CartService extends BaseService { /** @private @const {PaymentSessionRepository} */ this.paymentSessionRepository_ = paymentSessionRepository + + /** @private @const {InventoryService} */ + this.inventoryService_ = inventoryService } withTransaction(transactionManager) { @@ -109,6 +113,7 @@ class CartService extends BaseService { totalsService: this.totalsService_, addressRepository: this.addressRepository_, giftCardService: this.giftCardService_, + inventoryService: this.inventoryService_, }) cloned.transactionManager_ = transactionManager @@ -135,27 +140,6 @@ class CartService extends BaseService { * @typedef {LineItemContent[]} LineItemContentArray */ - /** - * Confirms if the contents of a line item is covered by the inventory. - * To be covered a variant must either not have its inventory managed or it - * must allow backorders or it must have enough inventory to cover the request. - * If the content is made up of multiple variants it will return true if all - * variants can be covered. If the content consists of a single variant it will - * return true if the variant is covered. - * @param {(LineItemContent | LineItemContentArray)} - the content of the line - * item - * @param {number} - the quantity of the line item - * @return {boolean} true if the inventory covers the line item. - */ - async confirmInventory_(variantId, quantity) { - // If the line item is not stock tracked we don't have double check it - if (!variantId) { - return true - } - - return this.productVariantService_.canCoverQuantity(variantId, quantity) - } - transformQueryForTotals_(config) { let { select, relations } = config @@ -454,19 +438,10 @@ class CartService extends BaseService { // simply update the quantity of the existing line item if (currentItem) { const newQuantity = currentItem.quantity + lineItem.quantity - - // Confirm inventory - const hasInventory = await this.confirmInventory_( - lineItem.variant_id, - newQuantity - ) - - if (!hasInventory) { - throw new MedusaError( - MedusaError.Types.NOT_ALLOWED, - "Inventory doesn't cover the desired quantity" - ) - } + // Confirm inventory or throw error + await this.inventoryService_ + .withTransaction(manager) + .confirmInventory(lineItem.variant_id, newQuantity) await this.lineItemService_ .withTransaction(manager) @@ -474,18 +449,10 @@ class CartService extends BaseService { quantity: newQuantity, }) } else { - // Confirm inventory - const hasInventory = await this.confirmInventory_( - lineItem.variant_id, - lineItem.quantity - ) - - if (!hasInventory) { - throw new MedusaError( - MedusaError.Types.NOT_ALLOWED, - "Inventory doesn't cover the desired quantity" - ) - } + // Confirm inventory or throw error + await this.inventoryService_ + .withTransaction(manager) + .confirmInventory(lineItem.variant_id, lineItem.quantity) await this.lineItemService_.withTransaction(manager).create({ ...lineItem, @@ -541,10 +508,9 @@ class CartService extends BaseService { } if (lineItemUpdate.quantity) { - const hasInventory = await this.confirmInventory_( - lineItemExists.variant_id, - lineItemUpdate.quantity - ) + const hasInventory = await this.inventoryService_ + .withTransaction(manager) + .confirmInventory(lineItemExists.variant_id, lineItemUpdate.quantity) if (!hasInventory) { throw new MedusaError( diff --git a/packages/medusa/src/services/claim.js b/packages/medusa/src/services/claim.js index 72cb5491ce..0a1e334675 100644 --- a/packages/medusa/src/services/claim.js +++ b/packages/medusa/src/services/claim.js @@ -26,6 +26,7 @@ class ClaimService extends BaseService { shippingOptionService, claimItemService, regionService, + inventoryService, eventBusService, }) { super() @@ -63,6 +64,9 @@ class ClaimService extends BaseService { /** @private @constant {TotalsService} */ this.totalsService_ = totalsService + /** @private @constant {InventoryService} */ + this.inventoryService_ = inventoryService + /** @private @constant {EventBus} */ this.eventBus_ = eventBusService @@ -88,6 +92,7 @@ class ClaimService extends BaseService { claimItemService: this.claimItemService_, eventBusService: this.eventBus_, totalsService: this.totalsService_, + inventoryService: this.inventoryService_, shippingOptionService: this.shippingOptionService_, }) @@ -232,6 +237,12 @@ class ClaimService extends BaseService { toRefund = await this.totalsService_.getRefundTotal(order, lines) } + for (const item of additional_items) { + await this.inventoryService_ + .withTransaction(manager) + .confirmInventory(item.variant_id, item.quantity) + } + const newItems = await Promise.all( additional_items.map(i => this.lineItemService_ @@ -240,6 +251,11 @@ class ClaimService extends BaseService { ) ) + for (const newItem of newItems) { + await this.inventoryService_ + .withTransaction(manager) + .adjustInventory(newItem.variant_id, -newItem.quantity) + } const evaluatedNoNotification = no_notification !== undefined ? no_notification : order.no_notification diff --git a/packages/medusa/src/services/idempotency-key.js b/packages/medusa/src/services/idempotency-key.js index 20190aa975..65a07d0a42 100644 --- a/packages/medusa/src/services/idempotency-key.js +++ b/packages/medusa/src/services/idempotency-key.js @@ -148,7 +148,7 @@ class IdempotencyKeyService extends BaseService { */ async workStage(idempotencyKey, func) { try { - return this.atomicPhase_(async manager => { + return await this.atomicPhase_(async manager => { let key const { recovery_point, response_code, response_body } = await func( diff --git a/packages/medusa/src/services/inventory.js b/packages/medusa/src/services/inventory.js new file mode 100644 index 0000000000..82cb33953a --- /dev/null +++ b/packages/medusa/src/services/inventory.js @@ -0,0 +1,87 @@ +import { BaseService } from "medusa-interfaces" +import { MedusaError } from "medusa-core-utils" +const fs = require("fs") + +class InventoryService extends BaseService { + constructor({ manager, productVariantService }) { + super() + + /** @private @const {EntityManager} */ + this.manager_ = manager + + /** @private @const {ProductVariantRepository_} */ + this.productVariantService_ = productVariantService + } + + withTransaction(transactionManager) { + if (!transactionManager) { + return this + } + + const cloned = new InventoryService({ + manager: transactionManager, + productVariantService: this.productVariantService_, + }) + + cloned.transactionManager_ = transactionManager + + return cloned + } + + /** + * Updates the inventory of a variant based on a given adjustment. + * @params {string} variantId - the id of the variant to update + * @params {number} adjustment - the number to adjust the inventory quantity by + * @return {Promise} resolves to the update result. + */ + async adjustInventory(variantId, adjustment) { + //if variantId is undefined – ergo. a custom item – then do nothing + if (typeof variantId === "undefined") { + return + } + + return this.atomicPhase_(async manager => { + const variant = await this.productVariantService_.retrieve(variantId) + //if inventory is managed then update + if (variant.manage_inventory) { + return await this.productVariantService_ + .withTransaction(manager) + .update(variant, { + inventory_quantity: variant.inventory_quantity + adjustment, + }) + } + }) + } + /** + * Checks if the inventory of a variant can cover a given quantity. Will + * return true if the variant doesn't have managed inventory or if the variant + * allows backorders or if the inventory quantity is greater than `quantity`. + * @params {string} variantId - the id of the variant to check + * @params {number} quantity - the number of units to check availability for + * @return {boolean} true if the inventory covers the quantity + */ + async confirmInventory(variantId, quantity) { + //if variantId is undefined then confirm inventory as it + //is a custom item that is not managed + if (typeof variantId === "undefined") { + return true + } + + const variant = await this.productVariantService_.retrieve(variantId) + const { inventory_quantity, allow_backorder, manage_inventory } = variant + const isCovered = + !manage_inventory || allow_backorder || inventory_quantity >= quantity + + if (!isCovered) { + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + `Variant with id: ${variant.id} does not have the required inventory`, + MedusaError.Codes.INSUFFICIENT_INVENTORY + ) + } + + return isCovered + } +} + +export default InventoryService diff --git a/packages/medusa/src/services/order.js b/packages/medusa/src/services/order.js index af5bd541c6..a252a87a26 100644 --- a/packages/medusa/src/services/order.js +++ b/packages/medusa/src/services/order.js @@ -39,6 +39,7 @@ class OrderService extends BaseService { addressRepository, giftCardService, draftOrderService, + inventoryService, eventBusService, }) { super() @@ -93,6 +94,9 @@ class OrderService extends BaseService { /** @private @constant {DraftOrderService} */ this.draftOrderService_ = draftOrderService + + /** @private @constant {InventoryService} */ + this.inventoryService_ = inventoryService } withTransaction(manager) { @@ -118,6 +122,7 @@ class OrderService extends BaseService { giftCardService: this.giftCardService_, addressRepository: this.addressRepository_, draftOrderService: this.draftOrderService_, + inventoryService: this.inventoryService_, }) cloned.transactionManager_ = manager @@ -451,6 +456,21 @@ class OrderService extends BaseService { ) } + const { payment, region, total } = cart + + for (const item of cart.items) { + try { + await this.inventoryService_ + .withTransaction(manager) + .confirmInventory(item.variant_id, item.quantity) + } catch (err) { + await this.paymentProviderService_ + .withTransaction(manager) + .cancelPayment(payment) + throw err + } + } + const exists = await this.existsByCartId(cart.id) if (exists) { throw new MedusaError( @@ -459,7 +479,6 @@ class OrderService extends BaseService { ) } - const { payment, region, total } = cart // Would be the case if a discount code is applied that covers the item // total if (total !== 0) { @@ -551,6 +570,12 @@ class OrderService extends BaseService { .update(item.id, { order_id: result.id }) } + for (const item of cart.items) { + await this.inventoryService_ + .withTransaction(manager) + .adjustInventory(item.variant_id, -item.quantity) + } + await this.eventBus_ .withTransaction(manager) .emit(OrderService.Events.PLACED, { @@ -864,7 +889,7 @@ class OrderService extends BaseService { async cancel(orderId) { return this.atomicPhase_(async manager => { const order = await this.retrieve(orderId, { - relations: ["fulfillments", "payments"], + relations: ["fulfillments", "payments", "items"], }) if (order.payment_status !== "awaiting") { @@ -882,6 +907,12 @@ class OrderService extends BaseService { ) ) + for (const item of order.items) { + await this.inventoryService_ + .withTransaction(manager) + .adjustInventory(item.variant_id, item.quantity) + } + for (const p of order.payments) { await this.paymentProviderService_ .withTransaction(manager) diff --git a/packages/medusa/src/services/payment-provider.js b/packages/medusa/src/services/payment-provider.js index 7c0552ddd8..6d2a412392 100644 --- a/packages/medusa/src/services/payment-provider.js +++ b/packages/medusa/src/services/payment-provider.js @@ -253,6 +253,7 @@ class PaymentProviderService extends BaseService { amount: total, currency_code: region.currency_code, data: paymentData, + cart_id: cart.id, }) return paymentRepo.save(created) diff --git a/packages/medusa/src/services/product-variant.js b/packages/medusa/src/services/product-variant.js index b474865043..8e8a9e8b3c 100644 --- a/packages/medusa/src/services/product-variant.js +++ b/packages/medusa/src/services/product-variant.js @@ -260,7 +260,7 @@ class ProductVariantService extends BaseService { ) } - const { prices, options, metadata, ...rest } = update + const { prices, options, metadata, inventory_quantity, ...rest } = update if (prices) { for (const price of prices) { @@ -291,11 +291,16 @@ class ProductVariantService extends BaseService { variant.metadata = this.setMetadata_(variant, metadata) } + if (typeof inventory_quantity === "number") { + variant.inventory_quantity = inventory_quantity + } + for (const [key, value] of Object.entries(rest)) { variant[key] = value } const result = await variantRepo.save(variant) + await this.eventBus_ .withTransaction(manager) .emit(ProductVariantService.Events.UPDATED, { @@ -518,23 +523,6 @@ class ProductVariantService extends BaseService { }) } - /** - * Checks if the inventory of a variant can cover a given quantity. Will - * return true if the variant doesn't have managed inventory or if the variant - * allows backorders or if the inventory quantity is greater than `quantity`. - * @params {string} variantId - the id of the variant to check - * @params {number} quantity - the number of units to check availability for - * @return {boolean} true if the inventory covers the quantity - */ - async canCoverQuantity(variantId, quantity) { - const variant = await this.retrieve(variantId) - - const { inventory_quantity, allow_backorder, manage_inventory } = variant - return ( - !manage_inventory || allow_backorder || inventory_quantity >= quantity - ) - } - /** * @param {Object} selector - the query object for find * @return {Promise} the result of the find operation diff --git a/packages/medusa/src/services/return.js b/packages/medusa/src/services/return.js index c7ae661064..ff62959d5c 100644 --- a/packages/medusa/src/services/return.js +++ b/packages/medusa/src/services/return.js @@ -16,6 +16,7 @@ class ReturnService extends BaseService { shippingOptionService, returnReasonService, fulfillmentProviderService, + inventoryService, orderService, }) { super() @@ -43,6 +44,8 @@ class ReturnService extends BaseService { this.returnReasonService_ = returnReasonService + this.inventoryService_ = inventoryService + /** @private @const {OrderService} */ this.orderService_ = orderService } @@ -61,6 +64,7 @@ class ReturnService extends BaseService { shippingOptionService: this.shippingOptionService_, fulfillmentProviderService: this.fulfillmentProviderService_, returnReasonService: this.returnReasonService_, + inventoryService: this.inventoryService_, orderService: this.orderService_, }) @@ -513,6 +517,15 @@ class ReturnService extends BaseService { }) } + for (const line of newLines) { + const orderItem = order.items.find(i => i.id === line.item_id) + if (orderItem) { + await this.inventoryService_ + .withTransaction(manager) + .adjustInventory(orderItem.variant_id, line.received_quantity) + } + } + return result }) } diff --git a/packages/medusa/src/services/swap.js b/packages/medusa/src/services/swap.js index f105fba730..81a61d3a85 100644 --- a/packages/medusa/src/services/swap.js +++ b/packages/medusa/src/services/swap.js @@ -31,6 +31,7 @@ class SwapService extends BaseService { shippingOptionService, fulfillmentService, orderService, + inventoryService, }) { super() @@ -64,6 +65,9 @@ class SwapService extends BaseService { /** @private @const {ShippingOptionService} */ this.shippingOptionService_ = shippingOptionService + /** @private @const {InventoryService} */ + this.inventoryService_ = inventoryService + /** @private @const {EventBusService} */ this.eventBus_ = eventBusService } @@ -84,6 +88,7 @@ class SwapService extends BaseService { paymentProviderService: this.paymentProviderService_, shippingOptionService: this.shippingOptionService_, orderService: this.orderService_, + inventoryService: this.inventoryService_, fulfillmentService: this.fulfillmentService_, }) @@ -621,6 +626,14 @@ class SwapService extends BaseService { const cart = swap.cart + const items = swap.cart.items + + for (const item of items) { + await this.inventoryService_ + .withTransaction(manager) + .confirmInventory(item.variant_id, item.quantity) + } + const total = await this.totalsService_.getTotal(cart) if (total > 0) { @@ -651,6 +664,12 @@ class SwapService extends BaseService { swap_id: swapId, order_id: swap.order_id, }) + + for (const item of items) { + await this.inventoryService_ + .withTransaction(manager) + .adjustInventory(item.variant_id, -item.quantity) + } } const now = new Date() diff --git a/packages/medusa/yarn.lock b/packages/medusa/yarn.lock index 18014cfca8..47b6343e44 100644 --- a/packages/medusa/yarn.lock +++ b/packages/medusa/yarn.lock @@ -1229,10 +1229,10 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" -"@medusajs/medusa-cli@^1.1.13": - version "1.1.13" - resolved "https://registry.yarnpkg.com/@medusajs/medusa-cli/-/medusa-cli-1.1.13.tgz#1a7f28c0912b257b7eee1eb1eeaccc4b8bc20ad1" - integrity sha512-7DK8l1wY/s9Wd1oka/+lkMmI+szC8F4u0hxcHod509oQnX8kS+UfXtQ0cVNiyDGJoc7GqB7teDILiUbBr3HI4w== +"@medusajs/medusa-cli@^1.1.14": + version "1.1.14" + resolved "https://registry.yarnpkg.com/@medusajs/medusa-cli/-/medusa-cli-1.1.14.tgz#3abe5ae2aeba9126a83912d73f4212a259513aad" + integrity sha512-rmC54zBn5Sz+03/i+CzKzIdiCP3YJi/aQhhnJG80+w4nMkH/9lt///4NlbySYFYtIw7ORq8zkOza3//LVKRk4A== dependencies: "@babel/polyfill" "^7.8.7" "@babel/runtime" "^7.9.6" @@ -1242,15 +1242,26 @@ configstore "5.0.1" core-js "^3.6.5" dotenv "^8.2.0" + execa "^5.1.1" fs-exists-cached "^1.0.0" + fs-extra "^10.0.0" + hosted-git-info "^4.0.2" inquirer "^8.0.0" + is-valid-path "^0.1.1" joi-objectid "^3.0.1" meant "^1.0.1" medusa-core-utils "^0.1.27" netrc-parser "^3.1.6" open "^8.0.6" + ora "^5.4.1" + pg-god "^1.0.11" + prompts "^2.4.1" regenerator-runtime "^0.13.5" resolve-cwd "^3.0.0" + stack-trace "^0.0.10" + ulid "^2.3.0" + url "^0.11.0" + winston "^3.3.3" yargs "^15.3.1" "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents": @@ -1270,6 +1281,98 @@ readdirp "^2.2.1" upath "^1.1.1" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@oclif/command@^1", "@oclif/command@^1.5.20", "@oclif/command@^1.6.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@oclif/command/-/command-1.8.0.tgz#c1a499b10d26e9d1a611190a81005589accbb339" + integrity sha512-5vwpq6kbvwkQwKqAoOU3L72GZ3Ta8RRrewKj9OJRolx28KLJJ8Dg9Rf7obRwt5jQA9bkYd8gqzMTrI7H3xLfaw== + dependencies: + "@oclif/config" "^1.15.1" + "@oclif/errors" "^1.3.3" + "@oclif/parser" "^3.8.3" + "@oclif/plugin-help" "^3" + debug "^4.1.1" + semver "^7.3.2" + +"@oclif/config@^1", "@oclif/config@^1.15.1": + version "1.17.0" + resolved "https://registry.yarnpkg.com/@oclif/config/-/config-1.17.0.tgz#ba8639118633102a7e481760c50054623d09fcab" + integrity sha512-Lmfuf6ubjQ4ifC/9bz1fSCHc6F6E653oyaRXxg+lgT4+bYf9bk+nqrUpAbrXyABkCqgIBiFr3J4zR/kiFdE1PA== + dependencies: + "@oclif/errors" "^1.3.3" + "@oclif/parser" "^3.8.0" + debug "^4.1.1" + globby "^11.0.1" + is-wsl "^2.1.1" + tslib "^2.0.0" + +"@oclif/errors@^1.2.1", "@oclif/errors@^1.2.2", "@oclif/errors@^1.3.3": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@oclif/errors/-/errors-1.3.5.tgz#a1e9694dbeccab10fe2fe15acb7113991bed636c" + integrity sha512-OivucXPH/eLLlOT7FkCMoZXiaVYf8I/w1eTAM1+gKzfhALwWTusxEx7wBmW0uzvkSg/9ovWLycPaBgJbM3LOCQ== + dependencies: + clean-stack "^3.0.0" + fs-extra "^8.1" + indent-string "^4.0.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +"@oclif/linewrap@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@oclif/linewrap/-/linewrap-1.0.0.tgz#aedcb64b479d4db7be24196384897b5000901d91" + integrity sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw== + +"@oclif/parser@^3.8.0", "@oclif/parser@^3.8.3": + version "3.8.5" + resolved "https://registry.yarnpkg.com/@oclif/parser/-/parser-3.8.5.tgz#c5161766a1efca7343e1f25d769efbefe09f639b" + integrity sha512-yojzeEfmSxjjkAvMRj0KzspXlMjCfBzNRPkWw8ZwOSoNWoJn+OCS/m/S+yfV6BvAM4u2lTzX9Y5rCbrFIgkJLg== + dependencies: + "@oclif/errors" "^1.2.2" + "@oclif/linewrap" "^1.0.0" + chalk "^2.4.2" + tslib "^1.9.3" + +"@oclif/plugin-help@^3": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-3.2.2.tgz#063ee08cee556573a5198fbdfdaa32796deba0ed" + integrity sha512-SPZ8U8PBYK0n4srFjCLedk0jWU4QlxgEYLCXIBShJgOwPhTTQknkUlsEwaMIevvCU4iCQZhfMX+D8Pz5GZjFgA== + dependencies: + "@oclif/command" "^1.5.20" + "@oclif/config" "^1.15.1" + "@oclif/errors" "^1.2.2" + chalk "^4.1.0" + indent-string "^4.0.0" + lodash.template "^4.4.0" + string-width "^4.2.0" + strip-ansi "^6.0.0" + widest-line "^3.1.0" + wrap-ansi "^4.0.0" + +"@oclif/screen@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-1.0.4.tgz#b740f68609dfae8aa71c3a6cab15d816407ba493" + integrity sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw== + "@sideway/address@^4.1.0": version "4.1.2" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.2.tgz#811b84333a335739d3969cfc434736268170cad1" @@ -1466,13 +1569,23 @@ ansi-align@^3.0.0: dependencies: string-width "^3.0.0" -ansi-escapes@^4.2.1: +ansi-escapes@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" @@ -1490,13 +1603,18 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" +ansicolors@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" + integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1550,6 +1668,11 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -1719,10 +1842,10 @@ babel-preset-jest@^25.5.0: babel-plugin-jest-hoist "^25.5.0" babel-preset-current-node-syntax "^0.1.2" -babel-preset-medusa-package@^1.1.12: - version "1.1.12" - resolved "https://registry.yarnpkg.com/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.12.tgz#d2acba935813ff080ecac40664bad413432bfcc8" - integrity sha512-haNteSlZu6uZUbUr+361JDi+h5Ky1WAzThJe0Q6EswfdvsqEbmGRibCQHZXW4S2LLr9d5UAoeX7azPDRWC2u0A== +babel-preset-medusa-package@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.13.tgz#9dc4e64e08436fb7b3536cef0f363a535e126474" + integrity sha512-Q9t06udxwMnfwyx7gyxoUKiZj/dtYSSXBtQ+K4ntY1hzMhOK2hBBInuiTgnLQS1cxc4j+FN2oYYPCpspX/acaw== dependencies: "@babel/plugin-proposal-class-properties" "^7.12.1" "@babel/plugin-proposal-decorators" "^7.12.1" @@ -1999,12 +2122,20 @@ capture-exit@^2.0.0: dependencies: rsvp "^4.8.4" +cardinal@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" + integrity sha1-fMEFXYItISlU0HsIXeolHMe8VQU= + dependencies: + ansicolors "~0.3.2" + redeyed "~2.1.0" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^2.0.0, chalk@^2.1.0: +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2064,6 +2195,13 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-stack@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-3.0.1.tgz#155bf0b2221bf5f4fba89528d24c5953f17fe3a8" + integrity sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg== + dependencies: + escape-string-regexp "4.0.0" + cli-boxes@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" @@ -2076,11 +2214,51 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-progress@^3.4.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.9.0.tgz#25db83447deb812e62d05bac1af9aec5387ef3d4" + integrity sha512-g7rLWfhAo/7pF+a/STFH/xPyosaL1zgADhI0OM83hl3c7S43iGvJWEAV2QuDOnQ8i6EMBj/u4+NTd0d5L+4JfA== + dependencies: + colors "^1.1.2" + string-width "^4.2.0" + cli-spinners@^2.5.0: version "2.6.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== +cli-ux@^5.4.9: + version "5.6.3" + resolved "https://registry.yarnpkg.com/cli-ux/-/cli-ux-5.6.3.tgz#eecdb2e0261171f2b28f2be6b18c490291c3a287" + integrity sha512-/oDU4v8BiDjX2OKcSunGH0iGDiEtj2rZaGyqNuv9IT4CgcSMyVWAMfn0+rEHaOc4n9ka78B0wo1+N1QX89f7mw== + dependencies: + "@oclif/command" "^1.6.0" + "@oclif/errors" "^1.2.1" + "@oclif/linewrap" "^1.0.0" + "@oclif/screen" "^1.0.3" + ansi-escapes "^4.3.0" + ansi-styles "^4.2.0" + cardinal "^2.1.1" + chalk "^4.1.0" + clean-stack "^3.0.0" + cli-progress "^3.4.0" + extract-stack "^2.0.0" + fs-extra "^8.1" + hyperlinker "^1.0.0" + indent-string "^4.0.0" + is-wsl "^2.2.0" + js-yaml "^3.13.1" + lodash "^4.17.11" + natural-orderby "^2.0.1" + object-treeify "^1.1.4" + password-prompt "^1.1.2" + semver "^7.3.2" + string-width "^4.2.0" + strip-ansi "^6.0.0" + supports-color "^8.1.0" + supports-hyperlinks "^2.1.0" + tslib "^2.0.0" + cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" @@ -2175,7 +2353,7 @@ colorette@^1.2.2: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== -colors@^1.2.1: +colors@^1.1.2, colors@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== @@ -2346,7 +2524,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -2540,6 +2718,13 @@ diff-sequences@^25.2.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -2676,6 +2861,11 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -2770,7 +2960,7 @@ espree@^6.1.2: acorn-jsx "^5.2.0" eslint-visitor-keys "^1.1.0" -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -2856,6 +3046,21 @@ execa@^3.2.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -2979,6 +3184,11 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +extract-stack@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/extract-stack/-/extract-stack-2.0.0.tgz#11367bc865bfcd9bc0db3123e5edb57786f11f9b" + integrity sha512-AEo4zm+TenK7zQorGK1f9mJ8L14hnTDi2ZQPR+Mub1NX8zimka1mXpV5LpH8x9HoUmFSHZCfLHqWvp0Y4FxxzQ== + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -2994,6 +3204,17 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-glob@^3.1.1: + version "3.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" + integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -3009,6 +3230,13 @@ fast-safe-stringify@^2.0.4: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== +fastq@^1.6.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807" + integrity sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw== + dependencies: + reusify "^1.0.4" + fb-watchman@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" @@ -3159,6 +3387,24 @@ fs-exists-cached@^1.0.0: resolved "https://registry.yarnpkg.com/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz#cf25554ca050dc49ae6656b41de42258989dcbce" integrity sha1-zyVVTKBQ3EmuZla0HeQiWJidy84= +fs-extra@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" + integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^8.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-readdir-recursive@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" @@ -3232,6 +3478,11 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -3252,7 +3503,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0, glob-parent@~5.1.0: +glob-parent@^5.0.0, glob-parent@^5.1.2, glob-parent@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -3290,6 +3541,18 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globby@^11.0.1: + version "11.0.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" + integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -3307,7 +3570,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== @@ -3398,6 +3661,13 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hosted-git-info@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" + integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== + dependencies: + lru-cache "^6.0.0" + html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" @@ -3451,6 +3721,16 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +hyperlinker@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" + integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ== + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -3473,6 +3753,11 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + import-fresh@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -3499,6 +3784,11 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -3720,6 +4010,11 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= + is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -3740,6 +4035,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-glob@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= + dependencies: + is-extglob "^1.0.0" + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -3767,6 +4069,13 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-invalid-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-invalid-path/-/is-invalid-path-0.1.0.tgz#307a855b3cf1a938b44ea70d2c61106053714f34" + integrity sha1-MHqFWzzxqTi0TqcNLGEQYFNxTzQ= + dependencies: + is-glob "^2.0.0" + is-nan@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" @@ -3864,6 +4173,13 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-valid-path@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-valid-path/-/is-valid-path-0.1.1.tgz#110f9ff74c37f663e1ec7915eb451f2db93ac9df" + integrity sha1-EQ+f90w39mPh7HkV60UfLbk6yd8= + dependencies: + is-invalid-path "^0.1.0" + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -4436,6 +4752,22 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + jsonwebtoken@^8.2.0, jsonwebtoken@^8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" @@ -4552,6 +4884,11 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -4607,7 +4944,22 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21: +lodash.template@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4719,28 +5071,28 @@ medusa-core-utils@^0.1.27: "@hapi/joi" "^16.1.8" joi-objectid "^3.0.1" -medusa-core-utils@^1.1.18: - version "1.1.18" - resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-1.1.18.tgz#2c2c49eeee796493a81bfab58db3f1ef164e6b1b" - integrity sha512-xPE+yrC2cvcWdVQwDUDGJGF0WeRYEdkt/qDtzMCtxsed772YPQ+g2DB1IRi9Kae88P127w8pj9XNGQWPYDsOBA== +medusa-core-utils@^1.1.19: + version "1.1.19" + resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-1.1.19.tgz#d792cfd487e9fd80c41fed0ffbfdf6d677777c22" + integrity sha512-tNEpRFh0siqHSyTNtvMzsckyar2+TcM78xdoiC9qtkdYvNEgou5dXm4YRH0pBtYQSOyOaSll6nUwqFM1p2zhOg== dependencies: joi "^17.3.0" joi-objectid "^3.0.1" -medusa-interfaces@^1.1.19: - version "1.1.19" - resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-1.1.19.tgz#8fa162bbc40a6ad4c632f65dfa531a01a0157ab8" - integrity sha512-lSebIMjg3JMjmY+wkdvKN1feb5R1p+ZaVNUpBGFh9MQpf5Lgb20OUTpuSoofVYjN0+/nTKHTuyQan6uDuDaP4Q== +medusa-interfaces@^1.1.20: + version "1.1.20" + resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-1.1.20.tgz#66fcb1e351cae95305714c239393ba63c96fe3e0" + integrity sha512-54WIK9E65QVecqcaLuoJFvhJQclQnOb2q6ASk3sdZZk3QPl6mVGqg7dKJZwU3xSnhoQ3Up7cPtduGpvXHTMmIw== dependencies: - medusa-core-utils "^1.1.18" + medusa-core-utils "^1.1.19" -medusa-test-utils@^1.1.21: - version "1.1.21" - resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-1.1.21.tgz#f7fedb6af4aee0cbf8926769e6d721e09ebf4a59" - integrity sha512-+Hqtb/opWvpEGuCb0zXtYbilHc3j5MGr3pxldwULHxMy9oJmDMME0eF2znd6aQADQipdq2wXGNaBZNG5V6vEwQ== +medusa-test-utils@^1.1.22: + version "1.1.22" + resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-1.1.22.tgz#2839fa8023f6f4a7a1170bfdb3a27443923efe3b" + integrity sha512-ZaQmQ+hjrQNs9XsmmROXKJ2QZ23FAD29jLAhUVIwxd11NQ/gZof2F2dgqWyA6sIXKn82tMCTgpiE3KWDtnbA7Q== dependencies: "@babel/plugin-transform-classes" "^7.9.5" - medusa-core-utils "^1.1.18" + medusa-core-utils "^1.1.19" randomatic "^3.1.1" merge-descriptors@1.0.1: @@ -4753,6 +5105,11 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + methods@^1.1.1, methods@^1.1.2, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -4777,7 +5134,7 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2: +micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== @@ -4923,6 +5280,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +natural-orderby@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/natural-orderby/-/natural-orderby-2.0.3.tgz#8623bc518ba162f8ff1cdb8941d74deb0fdcc016" + integrity sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q== + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -5032,7 +5394,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -5073,6 +5435,11 @@ object-keys@^1.0.12, object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +object-treeify@^1.1.4: + version "1.1.33" + resolved "https://registry.yarnpkg.com/object-treeify/-/object-treeify-1.1.33.tgz#f06fece986830a3cba78ddd32d4c11d1f76cdf40" + integrity sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A== + object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" @@ -5132,7 +5499,7 @@ one-time@^1.0.0: dependencies: fn.name "1.x.x" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -5160,7 +5527,7 @@ optionator@^0.8.1, optionator@^0.8.3: type-check "~0.3.2" word-wrap "~1.2.3" -ora@^5.3.0: +ora@^5.3.0, ora@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== @@ -5321,6 +5688,14 @@ passport@^0.4.0: passport-strategy "1.x.x" pause "0.0.1" +password-prompt@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/password-prompt/-/password-prompt-1.1.2.tgz#85b2f93896c5bd9e9f2d6ff0627fa5af3dc00923" + integrity sha512-bpuBhROdrhuN3E7G/koAju0WjVw9/uQOG5Co5mokNj0MiOSBVZS1JTwM4zl55hu0WFmIEFvO9cU9sJQiBIYeIA== + dependencies: + ansi-escapes "^3.1.0" + cross-spawn "^6.0.5" + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -5356,6 +5731,11 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + pause@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" @@ -5371,6 +5751,18 @@ pg-connection-string@^2.5.0: resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34" integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ== +pg-god@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pg-god/-/pg-god-1.0.11.tgz#5bc73a5ccb0fc5b439177462d9712ef4d1b83c9a" + integrity sha512-bW14qUfEt3jDruac0Pq9O1Pi6vH5cgLKVx3l8IdBrJS7GX0wBS4b6rlfTrU8373MCAXauP6x8VkD0rNE3mTxCw== + dependencies: + "@oclif/command" "^1" + "@oclif/config" "^1" + "@oclif/plugin-help" "^3" + cli-ux "^5.4.9" + pg "^8.3.0" + tslib "^1" + pg-int8@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" @@ -5381,6 +5773,11 @@ pg-pool@^3.3.0: resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.3.0.tgz#12d5c7f65ea18a6e99ca9811bd18129071e562fc" integrity sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg== +pg-pool@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.4.1.tgz#0e71ce2c67b442a5e862a9c182172c37eda71e9c" + integrity sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ== + pg-protocol@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.5.0.tgz#b5dd452257314565e2d54ab3c132adc46565a6a0" @@ -5397,6 +5794,19 @@ pg-types@^2.1.0: postgres-date "~1.0.4" postgres-interval "^1.1.0" +pg@^8.3.0: + version "8.7.1" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.7.1.tgz#9ea9d1ec225980c36f94e181d009ab9f4ce4c471" + integrity sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA== + dependencies: + buffer-writer "2.0.0" + packet-reader "1.0.0" + pg-connection-string "^2.5.0" + pg-pool "^3.4.1" + pg-protocol "^1.5.0" + pg-types "^2.1.0" + pgpass "1.x" + pg@^8.5.1: version "8.6.0" resolved "https://registry.yarnpkg.com/pg/-/pg-8.6.0.tgz#e222296b0b079b280cce106ea991703335487db2" @@ -5517,7 +5927,7 @@ promise.prototype.finally@^3.1.2: es-abstract "^1.17.0-next.0" function-bind "^1.1.1" -prompts@^2.0.1: +prompts@^2.0.1, prompts@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61" integrity sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ== @@ -5551,6 +5961,11 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -5580,6 +5995,16 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + random-bytes@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" @@ -5696,6 +6121,13 @@ realpath-native@^2.0.0: resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" integrity sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q== +redeyed@~2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" + integrity sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs= + dependencies: + esprima "~4.0.0" + redis-commands@1.7.0, redis-commands@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" @@ -5932,6 +6364,11 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -5956,6 +6393,13 @@ run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + rxjs@^6.6.0, rxjs@^6.6.6: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" @@ -6128,7 +6572,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== @@ -6293,7 +6737,7 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -stack-trace@0.0.x: +stack-trace@0.0.x, stack-trace@^0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= @@ -6341,6 +6785,14 @@ string-length@^3.1.0: astral-regex "^1.0.0" strip-ansi "^5.2.0" +string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string-width@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -6394,6 +6846,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -6471,7 +6930,14 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: +supports-color@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== @@ -6631,11 +7097,16 @@ triple-beam@^1.2.0, triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== -tslib@^1.9.0: +tslib@^1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + tslib@^2.0.3: version "2.2.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" @@ -6769,6 +7240,16 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -6825,6 +7306,14 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -6998,7 +7487,7 @@ winston-transport@^4.4.0: readable-stream "^2.3.7" triple-beam "^1.2.0" -winston@^3.2.1: +winston@^3.2.1, winston@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170" integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw== @@ -7018,6 +7507,15 @@ word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wrap-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-4.0.0.tgz#b3570d7c70156159a2d42be5cc942e957f7b1131" + integrity sha512-uMTsj9rDb0/7kk1PbcbCcwvHUxp60fGDB/NNXpVa0Q+ic/e7y5+BwTxKfQ33VYgDppSwi/FBzpetYzo8s6tfbg== + dependencies: + ansi-styles "^3.2.0" + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -7027,6 +7525,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" diff --git a/yarn.lock b/yarn.lock index a2806317a1..fc7fb786b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4296,6 +4296,11 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -5109,6 +5114,14 @@ glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +global@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -6833,6 +6846,13 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -7894,6 +7914,11 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"