diff --git a/packages/api-core/src/util/buildContext.js b/packages/api-core/src/util/buildContext.js index b3bfd85e864..c12d302c40a 100644 --- a/packages/api-core/src/util/buildContext.js +++ b/packages/api-core/src/util/buildContext.js @@ -1,6 +1,60 @@ import Logger from "@reactioncommerce/logger"; import ReactionError from "@reactioncommerce/reaction-error"; +// Simple in-memory lock map to prevent concurrent account creation for the same user +// within a single Node.js process. This is intentionally minimal and can be replaced +// by a more advanced, distributed solution by swapping out the logic that calls +// `createAccount` (for example, via a custom context or plugin). +const accountCreationPromisesByUserId = new Map(); + +/** + * @name ensureAccountForUser + * @summary Ensure that an account exists for the given user ID, creating it if needed. + * Concurrent calls for the same user ID are coalesced so that only a single + * `createAccount` invocation happens per user at a time. + * @param {Object} context - GraphQL request context + * @param {String} userId - User ID for which to ensure an account exists + * @returns {Object|null} Account document or null if it could not be created/found + */ +async function ensureAccountForUser(context, userId) { + if (!userId || typeof context.auth?.accountByUserId !== "function") return null; + + // Fast path: if an account already exists, return it immediately + const existingAccount = await context.auth.accountByUserId(context, userId); + if (existingAccount) return existingAccount; + + // If we already have an in-flight creation for this user, await it instead of + // starting another one. + if (accountCreationPromisesByUserId.has(userId)) { + return accountCreationPromisesByUserId.get(userId); + } + + const creationPromise = (async () => { + let account; + try { + Logger.debug(`Creating missing account for user ID ${userId}`); + account = await context.mutations.createAccount(context.getInternalContext(), { + emails: context.user.emails && context.user.emails.map((rec) => ({ ...rec, provides: rec.provides || "default" })), + name: context.user.name, + profile: context.user.profile || {}, + userId + }); + } catch (error) { + // We might have had a unique index error if account already exists due to timing + account = await context.auth.accountByUserId(context, userId); + if (!account) Logger.error(error, "Creating missing account failed"); + } finally { + // Ensure we do not hold onto stale promises indefinitely + accountCreationPromisesByUserId.delete(userId); + } + + return account || null; + })(); + + accountCreationPromisesByUserId.set(userId, creationPromise); + return creationPromise; +} + /** * @name buildContext * @method @@ -60,24 +114,10 @@ export default async function buildContext(context, request = {}) { let account; let permissions; if (userId && typeof context.auth.accountByUserId === "function") { - account = await context.auth.accountByUserId(context, userId); - - // Create an account the first time a user makes a request - if (!account) { - try { - Logger.debug(`Creating missing account for user ID ${userId}`); - account = await context.mutations.createAccount(context.getInternalContext(), { - emails: context.user.emails && context.user.emails.map((rec) => ({ ...rec, provides: rec.provides || "default" })), - name: context.user.name, - profile: context.user.profile || {}, - userId - }); - } catch (error) { - // We might have had a unique index error if account already exists due to timing - account = await context.auth.accountByUserId(context, userId); - if (!account) Logger.error(error, "Creating missing account failed"); - } - } + // Create an account the first time a user makes a request. Concurrent calls + // for the same user will wait for the same in-flight creation rather than + // attempting multiple inserts. + account = await ensureAccountForUser(context, userId); if (typeof context.auth.permissionsByUserId === "function") { permissions = await context.auth.permissionsByUserId(context, userId); } diff --git a/packages/api-core/src/util/buildContext.test.js b/packages/api-core/src/util/buildContext.test.js index ba07512657e..1fb21da92fc 100644 --- a/packages/api-core/src/util/buildContext.test.js +++ b/packages/api-core/src/util/buildContext.test.js @@ -73,3 +73,61 @@ test("properly mutates the context object with user", async () => { userId: fakeUser._id }); }); + +test("coalesces concurrent account creation for the same user into a single createAccount call", async () => { + const userId = "CONCURRENT_USER_ID"; + const concurrentUser = { _id: userId, emails: [{ address: "test@example.com" }], name: "Concurrent User", profile: {} }; + + let createdAccount = null; + + const concurrentAccountByUserId = jest.fn().mockName("concurrentAccountByUserId").mockImplementation(async () => createdAccount); + + const createAccount = jest.fn().mockName("createAccount").mockImplementation(async () => { + // Simulate async work and DB round trip + await new Promise((resolve) => setTimeout(resolve, 10)); + createdAccount = { _id: "CONCURRENT_ACCOUNT_ID", userId }; + return createdAccount; + }); + + const concurrentAuth = { + accountByUserId: concurrentAccountByUserId, + permissionsByUserId: jest.fn().mockResolvedValue([]) + }; + + const makeContext = () => { + const ctx = { + auth: concurrentAuth, + collections: mockContext.collections, + getFunctionsOfType: mockContext.getFunctionsOfType, + mutations: { + createAccount + }, + queries: { + primaryShopId: () => "PRIMARY_SHOP_ID" + }, + userPermissions: [], + validatePermissions: mockContext.validatePermissions + }; + + // Minimal stub to satisfy buildContext's use of context.getInternalContext() + ctx.getInternalContext = function getInternalContext() { + return { + ...this, + isInternalCall: true + }; + }; + + return ctx; + }; + + const contexts = [makeContext(), makeContext(), makeContext(), makeContext(), makeContext()]; + + await Promise.all(contexts.map((ctx) => buildContext(ctx, { user: concurrentUser }))); + + expect(createAccount).toHaveBeenCalledTimes(1); + contexts.forEach((ctx) => { + expect(ctx.account).toEqual(createdAccount); + expect(ctx.accountId).toEqual(createdAccount._id); + expect(ctx.userId).toEqual(userId); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 925e1ff8823..31643de78eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -138,7 +138,7 @@ importers: '@babel/plugin-transform-runtime': 7.18.10_@babel+core@7.19.0 '@babel/preset-env': 7.19.0_@babel+core@7.19.0 babel-core: 7.0.0-bridge.0_@babel+core@7.19.0 - babel-eslint: 10.1.0_eslint@8.23.1 + babel-eslint: 10.1.0_eslint@7.32.0 babel-jest: 24.9.0_@babel+core@7.19.0 apps/reaction: @@ -266,14 +266,14 @@ importers: '@reactioncommerce/file-collections-sa-gridfs': link:../../packages/file-collections-sa-gridfs '@reactioncommerce/logger': link:../../packages/logger '@reactioncommerce/random': link:../../packages/random - '@snyk/protect': 1.1179.0 + '@snyk/protect': 1.1301.0 graphql: 16.6.0 nodemailer: 6.8.0 semver: 6.3.0 sharp: 0.30.7 devDependencies: '@reactioncommerce/data-factory': 1.0.1 - '@reactioncommerce/eslint-config': 2.2.2_ihki3xsivcrciip6mkoznh225e + '@reactioncommerce/eslint-config': 2.2.2_5fgs6jzieh4fuxr2njjameh3sm faker: 4.1.0 nock: 11.4.0 node-fetch: 2.6.7 @@ -4324,23 +4324,6 @@ packages: - supports-color dev: true - /@eslint/eslintrc/1.3.2: - resolution: {integrity: sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.4.0 - globals: 13.17.0 - ignore: 5.2.0 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - /@google-cloud/common/0.17.0: resolution: {integrity: sha512-HRZLSU762E6HaKoGfJGa8W95yRjb9rY7LePhjaHK9ILAnFacMuUGVamDbTHu1csZomm1g3tZTtXfX/aAhtie/Q==} engines: {node: '>=4.0.0'} @@ -4626,17 +4609,6 @@ packages: tslib: 2.4.1 dev: false - /@humanwhocodes/config-array/0.10.4: - resolution: {integrity: sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - /@humanwhocodes/config-array/0.5.0: resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==} engines: {node: '>=10.10.0'} @@ -4648,15 +4620,6 @@ packages: - supports-color dev: true - /@humanwhocodes/gitignore-to-minimatch/1.0.2: - resolution: {integrity: sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==} - dev: true - - /@humanwhocodes/module-importer/1.0.1: - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - dev: true - /@humanwhocodes/object-schema/1.2.1: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true @@ -5257,7 +5220,7 @@ packages: transitivePeerDependencies: - supports-color - /@reactioncommerce/eslint-config/2.2.2_ihki3xsivcrciip6mkoznh225e: + /@reactioncommerce/eslint-config/2.2.2_5fgs6jzieh4fuxr2njjameh3sm: resolution: {integrity: sha512-E4BlfLmQBr5Sjt3LP9tAbgCKXZ2rQ4/Lpvle6khHUcuh33NcuBEInN+uStY6koPHAvnKgwn6MZ0LTYzVnKwSVA==} engines: {node: '>=8.1.0'} peerDependencies: @@ -5271,14 +5234,14 @@ packages: eslint-plugin-react-hooks: ~4.3.0 eslint-plugin-you-dont-need-lodash-underscore: ^6.10.0 dependencies: - eslint: 8.23.1 - eslint-plugin-import: 2.25.4_eslint@8.23.1 - eslint-plugin-jest: 26.9.0_eslint@8.23.1 - eslint-plugin-jsx-a11y: 6.5.1_eslint@8.23.1 - eslint-plugin-node: 11.1.0_eslint@8.23.1 - eslint-plugin-promise: 6.0.1_eslint@8.23.1 - eslint-plugin-react: 7.28.0_eslint@8.23.1 - eslint-plugin-react-hooks: 4.3.0_eslint@8.23.1 + eslint: 7.32.0 + eslint-plugin-import: 2.25.4_eslint@7.32.0 + eslint-plugin-jest: 26.9.0_kkakuhmjlh4cax7cqty6on2xkm + eslint-plugin-jsx-a11y: 6.5.1_eslint@7.32.0 + eslint-plugin-node: 11.1.0_eslint@7.32.0 + eslint-plugin-promise: 6.0.1_eslint@7.32.0 + eslint-plugin-react: 7.28.0_eslint@7.32.0 + eslint-plugin-react-hooks: 4.3.0_eslint@7.32.0 eslint-plugin-you-dont-need-lodash-underscore: 6.12.0 dev: true @@ -5318,8 +5281,8 @@ packages: '@sinonjs/commons': 1.8.3 dev: false - /@snyk/protect/1.1179.0: - resolution: {integrity: sha512-pQrtYAITDQABaE7zHUZyqajt3IXE4fmC21EEpByRrQCVRWSWbu+wP8ML1iLPVm3J2OK6o5vIANPz/JL0GYDuxg==} + /@snyk/protect/1.1301.0: + resolution: {integrity: sha512-0aON7ba3qewYvZw781Zia+K9iHK8dmUe8HRIZ0hXDV8PhjET35hULlheEUH9TeNTk1mPv/+lhGOcEGIBspboqw==} engines: {node: '>=10'} hasBin: true dev: false @@ -5569,26 +5532,6 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree/5.37.0: - resolution: {integrity: sha512-JkFoFIt/cx59iqEDSgIGnQpCTRv96MQnXCYvJi7QhBC24uyuzbD8wVbajMB1b9x4I0octYFJ3OwjAwNqk1AjDA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 5.37.0 - '@typescript-eslint/visitor-keys': 5.37.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.3.7 - tsutils: 3.21.0 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/typescript-estree/5.37.0_typescript@2.9.2: resolution: {integrity: sha512-JkFoFIt/cx59iqEDSgIGnQpCTRv96MQnXCYvJi7QhBC24uyuzbD8wVbajMB1b9x4I0octYFJ3OwjAwNqk1AjDA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5610,24 +5553,6 @@ packages: - supports-color dev: true - /@typescript-eslint/utils/5.37.0_eslint@8.23.1: - resolution: {integrity: sha512-jUEJoQrWbZhmikbcWSMDuUSxEE7ID2W/QCV/uz10WtQqfOuKZUqFGjqLJ+qhDd17rjgp+QJPqTdPIBWwoob2NQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@types/json-schema': 7.0.11 - '@typescript-eslint/scope-manager': 5.37.0 - '@typescript-eslint/types': 5.37.0 - '@typescript-eslint/typescript-estree': 5.37.0 - eslint: 8.23.1 - eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.23.1 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - /@typescript-eslint/utils/5.37.0_kkakuhmjlh4cax7cqty6on2xkm: resolution: {integrity: sha512-jUEJoQrWbZhmikbcWSMDuUSxEE7ID2W/QCV/uz10WtQqfOuKZUqFGjqLJ+qhDd17rjgp+QJPqTdPIBWwoob2NQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5694,26 +5619,12 @@ packages: acorn: 7.4.1 dev: true - /acorn-jsx/5.3.2_acorn@8.8.0: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.8.0 - dev: true - /acorn/7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} hasBin: true dev: true - /acorn/8.8.0: - resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - /agent-base/4.3.0: resolution: {integrity: sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==} engines: {node: '>= 4.0.0'} @@ -5935,10 +5846,6 @@ packages: dependencies: sprintf-js: 1.0.3 - /argparse/2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true - /aria-query/4.2.2: resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==} engines: {node: '>=6.0'} @@ -6165,7 +6072,7 @@ packages: '@babel/core': 7.19.0 dev: true - /babel-eslint/10.1.0_eslint@8.23.1: + /babel-eslint/10.1.0_eslint@7.32.0: resolution: {integrity: sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==} engines: {node: '>=6'} deprecated: babel-eslint is now @babel/eslint-parser. This package will no longer receive updates. @@ -6176,7 +6083,7 @@ packages: '@babel/parser': 7.19.0 '@babel/traverse': 7.19.0 '@babel/types': 7.19.0 - eslint: 8.23.1 + eslint: 7.32.0 eslint-visitor-keys: 1.3.0 resolve: 1.22.1 transitivePeerDependencies: @@ -8009,34 +7916,6 @@ packages: - supports-color dev: true - /eslint-module-utils/2.7.4_oxfrjumrtiktpkw7r2zaom7f74: - resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - dependencies: - debug: 3.2.7 - eslint: 8.23.1 - eslint-import-resolver-node: 0.3.6 - transitivePeerDependencies: - - supports-color - dev: true - /eslint-plugin-es/3.0.1_eslint@7.32.0: resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} engines: {node: '>=8.10.0'} @@ -8048,18 +7927,7 @@ packages: regexpp: 3.2.0 dev: true - /eslint-plugin-es/3.0.1_eslint@8.23.1: - resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} - engines: {node: '>=8.10.0'} - peerDependencies: - eslint: '>=4.19.1' - dependencies: - eslint: 8.23.1 - eslint-utils: 2.1.0 - regexpp: 3.2.0 - dev: true - - /eslint-plugin-import/2.25.4_eslint@8.23.1: + /eslint-plugin-import/2.25.4_eslint@7.32.0: resolution: {integrity: sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==} engines: {node: '>=4'} peerDependencies: @@ -8073,9 +7941,9 @@ packages: array.prototype.flat: 1.3.0 debug: 2.6.9 doctrine: 2.1.0 - eslint: 8.23.1 + eslint: 7.32.0 eslint-import-resolver-node: 0.3.6 - eslint-module-utils: 2.7.4_oxfrjumrtiktpkw7r2zaom7f74 + eslint-module-utils: 2.7.4_gw5q3jyuku4zrugqrckhwmprvm has: 1.0.3 is-core-module: 2.10.0 is-glob: 4.0.3 @@ -8140,7 +8008,7 @@ packages: - typescript dev: true - /eslint-plugin-jest/26.9.0_eslint@8.23.1: + /eslint-plugin-jest/26.9.0_kkakuhmjlh4cax7cqty6on2xkm: resolution: {integrity: sha512-TWJxWGp1J628gxh2KhaH1H1paEdgE2J61BBF1I59c6xWeL5+D1BzMxGDN/nXAfX+aSkR5u80K+XhskK6Gwq9ng==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -8153,14 +8021,14 @@ packages: jest: optional: true dependencies: - '@typescript-eslint/utils': 5.37.0_eslint@8.23.1 - eslint: 8.23.1 + '@typescript-eslint/utils': 5.37.0_kkakuhmjlh4cax7cqty6on2xkm + eslint: 7.32.0 transitivePeerDependencies: - supports-color - typescript dev: true - /eslint-plugin-jsx-a11y/6.5.1_eslint@8.23.1: + /eslint-plugin-jsx-a11y/6.5.1_eslint@7.32.0: resolution: {integrity: sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==} engines: {node: '>=4.0'} peerDependencies: @@ -8174,7 +8042,7 @@ packages: axobject-query: 2.2.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 8.23.1 + eslint: 7.32.0 has: 1.0.3 jsx-ast-utils: 3.3.3 language-tags: 1.0.5 @@ -8218,21 +8086,6 @@ packages: semver: 6.3.0 dev: true - /eslint-plugin-node/11.1.0_eslint@8.23.1: - resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==} - engines: {node: '>=8.10.0'} - peerDependencies: - eslint: '>=5.16.0' - dependencies: - eslint: 8.23.1 - eslint-plugin-es: 3.0.1_eslint@8.23.1 - eslint-utils: 2.1.0 - ignore: 5.2.0 - minimatch: 3.1.2 - resolve: 1.22.1 - semver: 6.3.0 - dev: true - /eslint-plugin-promise/6.0.1_eslint@7.32.0: resolution: {integrity: sha512-uM4Tgo5u3UWQiroOyDEsYcVMOo7re3zmno0IZmB5auxoaQNIceAbXEkSt8RNrKtaYehARHG06pYK6K1JhtP0Zw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -8242,22 +8095,13 @@ packages: eslint: 7.32.0 dev: true - /eslint-plugin-promise/6.0.1_eslint@8.23.1: - resolution: {integrity: sha512-uM4Tgo5u3UWQiroOyDEsYcVMOo7re3zmno0IZmB5auxoaQNIceAbXEkSt8RNrKtaYehARHG06pYK6K1JhtP0Zw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - dependencies: - eslint: 8.23.1 - dev: true - - /eslint-plugin-react-hooks/4.3.0_eslint@8.23.1: + /eslint-plugin-react-hooks/4.3.0_eslint@7.32.0: resolution: {integrity: sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 dependencies: - eslint: 8.23.1 + eslint: 7.32.0 dev: true /eslint-plugin-react-hooks/4.6.0_eslint@7.32.0: @@ -8269,7 +8113,7 @@ packages: eslint: 7.32.0 dev: true - /eslint-plugin-react/7.28.0_eslint@8.23.1: + /eslint-plugin-react/7.28.0_eslint@7.32.0: resolution: {integrity: sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==} engines: {node: '>=4'} peerDependencies: @@ -8278,7 +8122,7 @@ packages: array-includes: 3.1.5 array.prototype.flatmap: 1.3.0 doctrine: 2.1.0 - eslint: 8.23.1 + eslint: 7.32.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.3 minimatch: 3.1.2 @@ -8330,14 +8174,6 @@ packages: estraverse: 4.3.0 dev: true - /eslint-scope/7.1.1: - resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - /eslint-utils/2.1.0: resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} engines: {node: '>=6'} @@ -8355,16 +8191,6 @@ packages: eslint-visitor-keys: 2.1.0 dev: true - /eslint-utils/3.0.0_eslint@8.23.1: - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 8.23.1 - eslint-visitor-keys: 2.1.0 - dev: true - /eslint-visitor-keys/1.3.0: resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} engines: {node: '>=4'} @@ -8429,54 +8255,6 @@ packages: - supports-color dev: true - /eslint/8.23.1: - resolution: {integrity: sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - dependencies: - '@eslint/eslintrc': 1.3.2 - '@humanwhocodes/config-array': 0.10.4 - '@humanwhocodes/gitignore-to-minimatch': 1.0.2 - '@humanwhocodes/module-importer': 1.0.1 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.23.1 - eslint-visitor-keys: 3.3.0 - espree: 9.4.0 - esquery: 1.4.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.17.0 - globby: 11.1.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.0 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - js-sdsl: 4.1.4 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.1 - regexpp: 3.2.0 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - dev: true - /espree/7.3.1: resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==} engines: {node: ^10.12.0 || >=12.0.0} @@ -8486,15 +8264,6 @@ packages: eslint-visitor-keys: 1.3.0 dev: true - /espree/9.4.0: - resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.8.0 - acorn-jsx: 5.3.2_acorn@8.8.0 - eslint-visitor-keys: 3.3.0 - dev: true - /esprima/4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -8891,6 +8660,7 @@ packages: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 + dev: false /find-yarn-workspace-root2/1.2.16: resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} @@ -9009,7 +8779,7 @@ packages: resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} engines: {node: '>= 4.0'} os: [darwin] - deprecated: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2 + deprecated: Upgrade to fsevents v2 to mitigate potential security issues requiresBuild: true dependencies: bindings: 1.5.0 @@ -9185,13 +8955,6 @@ packages: dependencies: is-glob: 4.0.3 - /glob-parent/6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - dependencies: - is-glob: 4.0.3 - dev: true - /glob/6.0.4: resolution: {integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==} dependencies: @@ -9335,6 +9098,7 @@ packages: /grapheme-splitter/1.0.4: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: false /graphql-fields/2.0.3: resolution: {integrity: sha512-x3VE5lUcR4XCOxPIqaO4CE+bTK8u6gVouOdpQX9+EKHr+scqtK5Pp/l8nIGqIpN1TUlkKE6jDCCycm/WtLRAwA==} @@ -9443,6 +9207,7 @@ packages: /graphql/14.7.0: resolution: {integrity: sha512-l0xWZpoPKpppFzMfvVyFmp9vLN7w/ZZJPefUicMCepfJeQ8sMcztloGYY9DfjVPo6tIUDzU5Hw3MUbIjj9AVVA==} engines: {node: '>= 6.x'} + deprecated: 'No longer supported; please update to a newer version. Details: https://github.com/graphql/graphql-js#version-support' dependencies: iterall: 1.3.0 dev: false @@ -10948,10 +10713,6 @@ packages: resolution: {integrity: sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==} dev: false - /js-sdsl/4.1.4: - resolution: {integrity: sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==} - dev: true - /js-tokens/3.0.2: resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==} dev: true @@ -10966,13 +10727,6 @@ packages: argparse: 1.0.10 esprima: 4.0.1 - /js-yaml/4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - dependencies: - argparse: 2.0.1 - dev: true - /jsbn/0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} @@ -11238,6 +10992,7 @@ packages: engines: {node: '>=10'} dependencies: p-locate: 5.0.0 + dev: false /lodash._baseiteratee/4.7.0: resolution: {integrity: sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==} @@ -12335,6 +12090,7 @@ packages: engines: {node: '>=10'} dependencies: p-limit: 3.1.0 + dev: false /p-map/2.1.0: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} @@ -14168,15 +13924,6 @@ packages: resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} dev: false - /tsutils/3.21.0: - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - dependencies: - tslib: 1.14.1 - dev: true - /tsutils/3.21.0_typescript@2.9.2: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'}