diff --git a/.github/workflows/lf-production-deploy-new.yaml b/.github/workflows/lf-production-deploy-new.yaml index bdd5915e55..6b4bfe739b 100644 --- a/.github/workflows/lf-production-deploy-new.yaml +++ b/.github/workflows/lf-production-deploy-new.yaml @@ -491,4 +491,4 @@ jobs: with: service: script-executor image: ${{ needs.build-and-push-script-executor.outputs.image }} - cluster: ${{ env.CROWD_CLUSTER }} + cluster: ${{ env.CROWD_CLUSTER }} \ No newline at end of file diff --git a/.github/workflows/lf-staging-deploy-organizations-enrichment-worker.yaml b/.github/workflows/lf-staging-deploy-organizations-enrichment-worker.yaml new file mode 100644 index 0000000000..b0635923c1 --- /dev/null +++ b/.github/workflows/lf-staging-deploy-organizations-enrichment-worker.yaml @@ -0,0 +1,60 @@ +name: LF Staging Deploy Organizations Enrichment Worker + +on: + push: + branches: + - 'lf-staging/**' + - 'lf-staging-**' + paths: + - 'services/libs/**' + - 'services/apps/premium/organizations_enrichment_worker/**' + +env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} + CROWD_CLUSTER: ${{ secrets.LF_STAGING_CLUSTER_NAME }} + CROWD_ROLE_ARN: ${{ secrets.LF_STAGING_CLUSTER_ROLE_ARN }} + AWS_ACCESS_KEY_ID: ${{ secrets.LF_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.LF_AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.LF_AWS_REGION }} + SLACK_CHANNEL: deploys-lf-staging + SLACK_WEBHOOK: ${{ secrets.LF_STAGING_SLACK_CHANNEL_HOOK }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + outputs: + image: ${{ steps.image.outputs.IMAGE }} + defaults: + run: + shell: bash + + steps: + - name: Check out repository code + uses: actions/checkout@v2 + + - uses: ./.github/actions/build-docker-image + id: image-builder + with: + image: organizations-enrichment-worker + + - name: Set docker image output + id: image + run: echo "IMAGE=${{ steps.image-builder.outputs.image }}" >> $GITHUB_OUTPUT + + deploy-organizations-enrichment-worker: + needs: build-and-push + runs-on: ubuntu-latest + defaults: + run: + shell: bash + + steps: + - name: Check out repository code + uses: actions/checkout@v2 + + - uses: ./.github/actions/deploy-service + with: + service: organizations-enrichment-worker + image: ${{ needs.build-and-push.outputs.image }} + cluster: ${{ env.CROWD_CLUSTER }} diff --git a/.github/workflows/staging-deploy-organizations-enrichment-worker.yaml b/.github/workflows/staging-deploy-organizations-enrichment-worker.yaml new file mode 100644 index 0000000000..bda1bd1584 --- /dev/null +++ b/.github/workflows/staging-deploy-organizations-enrichment-worker.yaml @@ -0,0 +1,60 @@ +name: Staging Deploy Organizations Enrichment Worker + +on: + push: + branches: + - 'staging/**' + - 'staging-**' + paths: + - 'services/libs/**' + - 'services/apps/premium/organizations_enrichment_worker/**' + +env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} + CROWD_CLUSTER: ${{ secrets.STAGING_CLUSTER_NAME }} + CROWD_ROLE_ARN: ${{ secrets.STAGING_CLUSTER_ROLE_ARN }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + SLACK_CHANNEL: deploys-staging + SLACK_WEBHOOK: ${{ secrets.STAGING_SLACK_CHANNEL_HOOK }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + outputs: + image: ${{ steps.image.outputs.IMAGE }} + defaults: + run: + shell: bash + + steps: + - name: Check out repository code + uses: actions/checkout@v2 + + - uses: ./.github/actions/build-docker-image + id: image-builder + with: + image: organizations-enrichment-worker + + - name: Set docker image output + id: image + run: echo "IMAGE=${{ steps.image-builder.outputs.image }}" >> $GITHUB_OUTPUT + + deploy-organizations-enrichment-worker: + needs: build-and-push + runs-on: ubuntu-latest + defaults: + run: + shell: bash + + steps: + - name: Check out repository code + uses: actions/checkout@v2 + + - uses: ./.github/actions/deploy-service + with: + service: organizations-enrichment-worker + image: ${{ needs.build-and-push.outputs.image }} + cluster: ${{ env.CROWD_CLUSTER }} diff --git a/backend/src/api/plan/stripe/checkout.ts b/backend/src/api/plan/stripe/checkout.ts index e109902211..98cedf4a9d 100644 --- a/backend/src/api/plan/stripe/checkout.ts +++ b/backend/src/api/plan/stripe/checkout.ts @@ -1,4 +1,5 @@ import { Error400, Error403 } from '@crowd/common' +import { TenantPlans } from '@crowd/types' import { PLANS_CONFIG } from '../../../conf' import Plans from '../../../security/plans' import TenantService from '../../../services/tenantService' @@ -19,7 +20,7 @@ export default async (req, res) => { } if ( - currentTenant.plan !== Plans.values.essential && + currentTenant.plan !== TenantPlans.Essential && currentTenant.planStatus !== 'cancel_at_period_end' && currentTenant.planUserId !== currentUser.id ) { diff --git a/backend/src/api/plan/stripe/portal.ts b/backend/src/api/plan/stripe/portal.ts index 4506af4951..34ec6fd94f 100644 --- a/backend/src/api/plan/stripe/portal.ts +++ b/backend/src/api/plan/stripe/portal.ts @@ -1,6 +1,6 @@ import { Error400, Error403 } from '@crowd/common' +import { TenantPlans } from '@crowd/types' import { PLANS_CONFIG } from '../../../conf' -import Plans from '../../../security/plans' import TenantService from '../../../services/tenantService' import { tenantSubdomain } from '../../../services/tenantSubdomain' @@ -19,7 +19,7 @@ export default async (req, res) => { } if ( - currentTenant.plan !== Plans.values.essential && + currentTenant.plan !== TenantPlans.Essential && currentTenant.planStatus !== 'cancel_at_period_end' && currentTenant.planUserId !== currentUser.id ) { diff --git a/backend/src/api/premium/enrichment/memberEnrichBulk.ts b/backend/src/api/premium/enrichment/memberEnrichBulk.ts index 6485d93ccb..ae411b6db1 100644 --- a/backend/src/api/premium/enrichment/memberEnrichBulk.ts +++ b/backend/src/api/premium/enrichment/memberEnrichBulk.ts @@ -1,13 +1,12 @@ import { RedisCache } from '@crowd/redis' import { getServiceLogger } from '@crowd/logging' import { Error403 } from '@crowd/common' -import { FeatureFlag, FeatureFlagRedisKey } from '@crowd/types' +import { FeatureFlag, FeatureFlagRedisKey, PLAN_LIMITS } from '@crowd/types' import { getSecondsTillEndOfMonth } from '../../../utils/timing' import Permissions from '../../../security/permissions' import identifyTenant from '../../../segment/identifyTenant' import PermissionChecker from '../../../services/user/permissionChecker' import track from '../../../segment/track' -import { PLAN_LIMITS } from '../../../feature-flags/isFeatureEnabled' import SequelizeRepository from '../../../database/repositories/sequelizeRepository' import { getNodejsWorkerEmitter } from '@/serverless/utils/serviceSQS' diff --git a/backend/src/bin/scripts/unleash-init.ts b/backend/src/bin/scripts/unleash-init.ts index 1c498321d5..1ed22c4542 100644 --- a/backend/src/bin/scripts/unleash-init.ts +++ b/backend/src/bin/scripts/unleash-init.ts @@ -1,11 +1,9 @@ -import Sequelize, { QueryTypes } from 'sequelize' -import { getServiceLogger } from '@crowd/logging' import { generateUUIDv1 } from '@crowd/common' -import { FeatureFlag } from '@crowd/types' -import { UnleashContextField } from '../../types/unleashContext' +import { getServiceLogger } from '@crowd/logging' +import { FeatureFlag, PLAN_LIMITS, TenantPlans } from '@crowd/types' +import Sequelize, { QueryTypes } from 'sequelize' import { UNLEASH_CONFIG } from '../../conf' -import Plans from '../../security/plans' -import { PLAN_LIMITS } from '../../feature-flags/isFeatureEnabled' +import { UnleashContextField } from '../../types/unleashContext' /* eslint-disable no-console */ @@ -15,14 +13,14 @@ const constaintConfiguration = { [FeatureFlag.AUTOMATIONS]: [ [ { - values: [Plans.values.scale], + values: [TenantPlans.Scale], inverted: false, operator: 'IN', contextName: 'plan', caseInsensitive: false, }, { - value: PLAN_LIMITS[Plans.values.scale][FeatureFlag.AUTOMATIONS].toString(), + value: PLAN_LIMITS[TenantPlans.Scale][FeatureFlag.AUTOMATIONS].toString(), values: [], inverted: false, operator: 'NUM_LT', @@ -32,14 +30,14 @@ const constaintConfiguration = { ], [ { - values: [Plans.values.growth], + values: [TenantPlans.Growth], inverted: false, operator: 'IN', contextName: 'plan', caseInsensitive: false, }, { - value: PLAN_LIMITS[Plans.values.growth][FeatureFlag.AUTOMATIONS].toString(), + value: PLAN_LIMITS[TenantPlans.Growth][FeatureFlag.AUTOMATIONS].toString(), values: [], inverted: false, operator: 'NUM_LT', @@ -49,14 +47,14 @@ const constaintConfiguration = { ], [ { - values: [Plans.values.essential], + values: [TenantPlans.Essential], inverted: false, operator: 'IN', contextName: 'plan', caseInsensitive: false, }, { - value: PLAN_LIMITS[Plans.values.essential][FeatureFlag.AUTOMATIONS].toString(), + value: PLAN_LIMITS[TenantPlans.Essential][FeatureFlag.AUTOMATIONS].toString(), values: [], inverted: false, operator: 'NUM_LT', @@ -68,14 +66,14 @@ const constaintConfiguration = { [FeatureFlag.CSV_EXPORT]: [ [ { - values: [Plans.values.scale], + values: [TenantPlans.Scale], inverted: false, operator: 'IN', contextName: 'plan', caseInsensitive: false, }, { - value: PLAN_LIMITS[Plans.values.scale][FeatureFlag.CSV_EXPORT].toString(), + value: PLAN_LIMITS[TenantPlans.Scale][FeatureFlag.CSV_EXPORT].toString(), values: [], inverted: false, operator: 'NUM_LT', @@ -85,14 +83,14 @@ const constaintConfiguration = { ], [ { - values: [Plans.values.growth], + values: [TenantPlans.Growth], inverted: false, operator: 'IN', contextName: 'plan', caseInsensitive: false, }, { - value: PLAN_LIMITS[Plans.values.growth][FeatureFlag.CSV_EXPORT].toString(), + value: PLAN_LIMITS[TenantPlans.Growth][FeatureFlag.CSV_EXPORT].toString(), values: [], inverted: false, operator: 'NUM_LT', @@ -102,14 +100,14 @@ const constaintConfiguration = { ], [ { - values: [Plans.values.essential], + values: [TenantPlans.Essential], inverted: false, operator: 'IN', contextName: 'plan', caseInsensitive: false, }, { - value: PLAN_LIMITS[Plans.values.essential][FeatureFlag.CSV_EXPORT].toString(), + value: PLAN_LIMITS[TenantPlans.Essential][FeatureFlag.CSV_EXPORT].toString(), values: [], inverted: false, operator: 'NUM_LT', @@ -121,7 +119,7 @@ const constaintConfiguration = { [FeatureFlag.EAGLE_EYE]: [ [ { - values: [Plans.values.growth, Plans.values.eagleEye, Plans.values.scale], + values: [TenantPlans.Growth, TenantPlans.EagleEye, TenantPlans.Scale], inverted: false, operator: 'IN', contextName: 'plan', @@ -133,7 +131,7 @@ const constaintConfiguration = { [FeatureFlag.LINKEDIN]: [ [ { - values: [Plans.values.growth, Plans.values.scale], + values: [TenantPlans.Growth, TenantPlans.Scale], inverted: false, operator: 'IN', contextName: 'plan', @@ -144,7 +142,7 @@ const constaintConfiguration = { [FeatureFlag.HUBSPOT]: [ [ { - values: [Plans.values.scale], + values: [TenantPlans.Scale], inverted: false, operator: 'IN', contextName: 'plan', @@ -155,14 +153,14 @@ const constaintConfiguration = { [FeatureFlag.MEMBER_ENRICHMENT]: [ [ { - values: [Plans.values.scale], + values: [TenantPlans.Scale], inverted: false, operator: 'IN', contextName: 'plan', caseInsensitive: false, }, { - value: PLAN_LIMITS[Plans.values.scale][FeatureFlag.MEMBER_ENRICHMENT].toString(), + value: PLAN_LIMITS[TenantPlans.Scale][FeatureFlag.MEMBER_ENRICHMENT].toString(), values: [], inverted: false, operator: 'NUM_LT', @@ -172,14 +170,14 @@ const constaintConfiguration = { ], [ { - values: [Plans.values.growth], + values: [TenantPlans.Growth], inverted: false, operator: 'IN', contextName: 'plan', caseInsensitive: false, }, { - value: PLAN_LIMITS[Plans.values.growth][FeatureFlag.MEMBER_ENRICHMENT].toString(), + value: PLAN_LIMITS[TenantPlans.Growth][FeatureFlag.MEMBER_ENRICHMENT].toString(), values: [], inverted: false, operator: 'NUM_LT', @@ -191,14 +189,14 @@ const constaintConfiguration = { [FeatureFlag.ORGANIZATION_ENRICHMENT]: [ [ { - values: [Plans.values.scale], + values: [TenantPlans.Scale], inverted: false, operator: 'IN', contextName: 'plan', caseInsensitive: false, }, { - value: PLAN_LIMITS[Plans.values.scale][FeatureFlag.ORGANIZATION_ENRICHMENT].toString(), + value: PLAN_LIMITS[TenantPlans.Scale][FeatureFlag.ORGANIZATION_ENRICHMENT].toString(), values: [], inverted: false, operator: 'NUM_LT', @@ -208,14 +206,14 @@ const constaintConfiguration = { ], [ { - values: [Plans.values.growth], + values: [TenantPlans.Growth], inverted: false, operator: 'IN', contextName: 'plan', caseInsensitive: false, }, { - value: PLAN_LIMITS[Plans.values.growth][FeatureFlag.ORGANIZATION_ENRICHMENT].toString(), + value: PLAN_LIMITS[TenantPlans.Growth][FeatureFlag.ORGANIZATION_ENRICHMENT].toString(), values: [], inverted: false, operator: 'NUM_LT', @@ -230,7 +228,7 @@ const constaintConfiguration = { [FeatureFlag.TEMPORAL_MEMBERS_ENRICHMENT]: [ [ { - values: [Plans.values.scale, Plans.values.enterprise], + values: [TenantPlans.Scale, TenantPlans.Enterprise], inverted: false, operator: 'IN', contextName: 'plan', @@ -243,10 +241,10 @@ const constaintConfiguration = { [ { values: [ - Plans.values.essential, - Plans.values.growth, - Plans.values.scale, - Plans.values.enterprise, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.Scale, + TenantPlans.Enterprise, ], inverted: false, operator: 'IN', @@ -260,11 +258,11 @@ const constaintConfiguration = { [ { values: [ - Plans.values.scale, - Plans.values.eagleEye, - Plans.values.enterprise, - Plans.values.essential, - Plans.values.growth, + TenantPlans.Scale, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Essential, + TenantPlans.Growth, ], inverted: false, operator: 'IN', @@ -278,11 +276,28 @@ const constaintConfiguration = { [ { values: [ - Plans.values.scale, - Plans.values.eagleEye, - Plans.values.enterprise, - Plans.values.essential, - Plans.values.growth, + TenantPlans.Scale, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Essential, + TenantPlans.Growth, + ], + inverted: false, + operator: 'IN', + contextName: 'plan', + caseInsensitive: false, + }, + ], + ], + [FeatureFlag.TEMPORAL_ORGANIZATION_ENRICHMENT]: [ + [ + { + values: [ + TenantPlans.Scale, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Essential, + TenantPlans.Growth, ], inverted: false, operator: 'IN', diff --git a/backend/src/database/migrations/U1703748504__org-identities.sql b/backend/src/database/migrations/U1703748504__org-identities.sql new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/backend/src/database/migrations/U1703748504__org-identities.sql @@ -0,0 +1 @@ + diff --git a/backend/src/database/migrations/V1703748504__org-identities.sql b/backend/src/database/migrations/V1703748504__org-identities.sql new file mode 100644 index 0000000000..967bad1e5d --- /dev/null +++ b/backend/src/database/migrations/V1703748504__org-identities.sql @@ -0,0 +1,131 @@ +create table "organizationCacheIdentities" ( + id uuid not null, + name text not null, + website text null, + + primary key (id, name), + foreign key (id) references "organizationCaches" (id) +); + +create index "ix_organizationCacheIdentities_name" on "organizationCacheIdentities" (name); +create index "ix_organizationCacheIdentities_website" on "organizationCacheIdentities" (website); + +alter table "organizationCaches" + rename column name to "oldName"; + +alter table "organizationCaches" + rename column website to "oldWebsite"; + +alter table "organizationCaches" + alter column "oldName" drop not null, + alter column "oldWebsite" drop not null; + +-- fill organizationCacheIdentities table with existing data (for caches without website because for those we need a better logic) +insert into "organizationCacheIdentities"(id, name) +select id, "oldName" +from "organizationCaches" +where "oldWebsite" is null; + +-- fill organizationCacheIdentities table with caches that are the only one in the group by website +insert into "organizationCacheIdentities"(id, name, website) +with data as (select count(id) as ids, "oldWebsite" + from "organizationCaches" + where "oldWebsite" is not null + group by "oldWebsite" + having count(id) = 1) +select oc.id, oc."oldName", oc."oldWebsite" +from "organizationCaches" oc + inner join data d on oc."oldWebsite" = d."oldWebsite"; + +create table "organizationCacheLinks" ( + "organizationCacheId" uuid not null, + "organizationId" uuid not null, + + primary key ("organizationCacheId", "organizationId"), + foreign key ("organizationCacheId") references "organizationCaches" (id), + foreign key ("organizationId") references organizations (id) +); + +-- fill the new organizationCacheLinks table +-- first the ones with website match +insert into "organizationCacheLinks"("organizationCacheId", "organizationId") +select oc.id, o.id +from organizations o + inner join "organizationCaches" oc on oc."oldWebsite" = o.website +where o.website is not null + and not exists (select 1 from "organizationCacheLinks" where "organizationId" = o.id); + +-- then the ones with name match +insert into "organizationCacheLinks"("organizationCacheId", "organizationId") +select oc.id, o.id +from organizations o + inner join "organizationCaches" oc on oc."oldName" = o."displayName" +where o."displayName" is not null + and not exists (select 1 from "organizationCacheLinks" where "organizationId" = o.id); + +-- then the ones that are left +do +$$ + declare + cache_id uuid; + org organizations%rowtype; + begin + for org in select * + from organizations o + where not exists (select 1 from "organizationCacheLinks" where "organizationId" = o.id) + and o."displayName" is not null + and length(trim(o."displayName")) > 0 + loop + -- check if we already created a cache + if org.website is not null then + cache_id := (select id from "organizationCaches" where "oldWebsite" = org.website limit 1); + end if; + + if cache_id is null then + cache_id := (select id from "organizationCaches" where "oldName" = org."displayName" limit 1); + end if; + + if cache_id is null then + -- generate id + cache_id := (select uuid_generate_v1()); + -- insert organizationsCaches row + insert into "organizationCaches" (id, description, emails, "phoneNumbers", logo, tags, twitter, linkedin, crunchbase, employees, "revenueRange", "importHash", "createdAt", "updatedAt", location, github, "employeeCountByCountry", type, "geoLocation", size, ticker, headline, profiles, naics, address, industry, founded, "manuallyCreated", "oldName", "oldWebsite") + values (cache_id, org.description, org.emails, org."phoneNumbers", org.logo, org.tags, org.twitter, org.linkedin, org.crunchbase, org.employees, org."revenueRange", org."importHash", org."createdAt", org."createdAt", org.location, org.github, org."employeeCountByCountry", org.type, org."geoLocation", org.size, org.ticker, org.headline, org.profiles, org.naics, org.address, org.industry, org.founded, org."manuallyCreated", org."displayName", org.website); + -- insert organizationCacheIdentities row + insert into "organizationCacheIdentities"(id, name, website) + values (cache_id, org."displayName", org.website); + end if; + + -- update create link between organizations and organizationCaches tables + insert into "organizationCacheLinks"("organizationCacheId", "organizationId") values (cache_id, org.id); + end loop; + end; +$$; + +-- naics column in organizationCaches and organizations is of type jsonb[] +-- which is not really useful for us since we are not using array operators on it to query it anyway +-- it's also much harder to safely update/insert this using a raw query +-- so we are going to change both columns to just a regular jsonb that will contain an array of objects +alter table "organizationCaches" + add column new_naics jsonb; + +update "organizationCaches" +set new_naics = to_jsonb(naics); + +alter table "organizationCaches" + drop column naics; + +alter table "organizationCaches" + rename column new_naics to naics; + +alter table organizations + add column new_naics jsonb; + +update organizations +set new_naics = to_jsonb(naics); + +alter table organizations + drop column naics; + +alter table organizations + rename column new_naics to naics; \ No newline at end of file diff --git a/backend/src/database/models/organization.ts b/backend/src/database/models/organization.ts index 9dfae4b65a..2dab251328 100644 --- a/backend/src/database/models/organization.ts +++ b/backend/src/database/models/organization.ts @@ -108,7 +108,7 @@ export default (sequelize) => { comment: 'A range representing the size of the company.', }, naics: { - type: DataTypes.ARRAY(DataTypes.JSONB), + type: DataTypes.JSONB, allowNull: true, comment: 'industry classifications for a company according to NAICS', }, diff --git a/backend/src/database/models/organizationCache.ts b/backend/src/database/models/organizationCache.ts index 049400d403..440b6ef4a7 100644 --- a/backend/src/database/models/organizationCache.ts +++ b/backend/src/database/models/organizationCache.ts @@ -9,10 +9,6 @@ export default (sequelize) => { defaultValue: DataTypes.UUIDV4, primaryKey: true, }, - name: { - type: DataTypes.TEXT, - allowNull: false, - }, url: { type: DataTypes.TEXT, allowNull: true, @@ -75,10 +71,6 @@ export default (sequelize) => { type: DataTypes.TEXT, allowNull: true, }, - website: { - type: DataTypes.TEXT, - allowNull: true, - }, founded: { type: DataTypes.INTEGER, allowNull: true, @@ -93,7 +85,7 @@ export default (sequelize) => { comment: 'A range representing the size of the company.', }, naics: { - type: DataTypes.ARRAY(DataTypes.JSONB), + type: DataTypes.JSONB, allowNull: true, comment: 'industry classifications for a company according to NAICS', }, diff --git a/backend/src/database/models/tenant.ts b/backend/src/database/models/tenant.ts index b2cfb4c1cf..66c905108e 100644 --- a/backend/src/database/models/tenant.ts +++ b/backend/src/database/models/tenant.ts @@ -1,6 +1,4 @@ -import Plans from '../../security/plans' - -const plans = Plans.values +import { TenantPlans } from '@crowd/types' export default (sequelize, DataTypes) => { const tenant = sequelize.define( @@ -44,9 +42,17 @@ export default (sequelize, DataTypes) => { allowNull: true, validate: { notEmpty: true, - isIn: [[plans.essential, plans.growth, plans.eagleEye, plans.enterprise, plans.scale]], + isIn: [ + [ + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, + ], + ], }, - defaultValue: plans.essential, + defaultValue: TenantPlans.Essential, }, onboardedAt: { diff --git a/backend/src/database/repositories/__tests__/organizationCacheRepository.test.ts b/backend/src/database/repositories/__tests__/organizationCacheRepository.test.ts index 09d7e88d5b..3391685fbc 100644 --- a/backend/src/database/repositories/__tests__/organizationCacheRepository.test.ts +++ b/backend/src/database/repositories/__tests__/organizationCacheRepository.test.ts @@ -82,11 +82,14 @@ describe('OrganizationCacheCacheRepository tests', () => { const expectedorganizationCacheCreated = { id: organizationCacheCreated.id, ...toCreate, + names: [toCreate.name], importHash: null, createdAt: SequelizeTestUtils.getNowWithoutTime(), updatedAt: SequelizeTestUtils.getNowWithoutTime(), deletedAt: null, } + delete expectedorganizationCacheCreated.name + expect(organizationCacheCreated).toStrictEqual(expectedorganizationCacheCreated) }) @@ -120,11 +123,13 @@ describe('OrganizationCacheCacheRepository tests', () => { const expectedorganizationCacheFound = { id: organizationCacheCreated.id, ...toCreate, + names: [toCreate.name], importHash: null, createdAt: SequelizeTestUtils.getNowWithoutTime(), updatedAt: SequelizeTestUtils.getNowWithoutTime(), deletedAt: null, } + delete expectedorganizationCacheFound.name const organizationCacheById = await organizationCacheRepository.findById( organizationCacheCreated.id, mockIRepositoryOptions, @@ -165,11 +170,13 @@ describe('OrganizationCacheCacheRepository tests', () => { const expectedorganizationCacheFound = { id: organizationCacheCreated.id, ...toCreate, + names: [toCreate.name], importHash: null, createdAt: SequelizeTestUtils.getNowWithoutTime(), updatedAt: SequelizeTestUtils.getNowWithoutTime(), deletedAt: null, } + delete expectedorganizationCacheFound.name const organizationCacheById = await organizationCacheRepository.findByUrl( organizationCacheCreated.url, mockIRepositoryOptions, @@ -200,11 +207,14 @@ describe('OrganizationCacheCacheRepository tests', () => { const expectedorganizationCacheFound = { id: organizationCacheCreated.id, ...toCreate, + names: [toCreate.name], importHash: null, createdAt: SequelizeTestUtils.getNowWithoutTime(), updatedAt: SequelizeTestUtils.getNowWithoutTime(), deletedAt: null, } + delete expectedorganizationCacheFound.name + const organizationCacheById = await organizationCacheRepository.findByUrl( organizationCacheCreated.url, mock2, @@ -228,7 +238,7 @@ describe('OrganizationCacheCacheRepository tests', () => { const organizationCacheUpdated = await organizationCacheRepository.update( organizationCacheCreated.id, - { name: 'updated-organizationCache-name' }, + { importHash: 'test' }, mockIRepositoryOptions, ) @@ -239,12 +249,13 @@ describe('OrganizationCacheCacheRepository tests', () => { const organizationCacheExpected = { id: organizationCacheCreated.id, ...toCreate, - name: organizationCacheUpdated.name, - importHash: null, + importHash: 'test', + names: [toCreate.name], createdAt: organizationCacheCreated.createdAt, updatedAt: organizationCacheUpdated.updatedAt, deletedAt: null, } + delete organizationCacheExpected.name expect(organizationCacheUpdated).toStrictEqual(organizationCacheExpected) }) diff --git a/backend/src/database/repositories/__tests__/tenantRepository.test.ts b/backend/src/database/repositories/__tests__/tenantRepository.test.ts index 7e29fa2497..a8b17935c3 100644 --- a/backend/src/database/repositories/__tests__/tenantRepository.test.ts +++ b/backend/src/database/repositories/__tests__/tenantRepository.test.ts @@ -1,6 +1,7 @@ import TenantRepository from '../tenantRepository' import SequelizeTestUtils from '../../utils/sequelizeTestUtils' import Plans from '../../../security/plans' +import { TenantPlans } from '@crowd/types' const db = null @@ -20,12 +21,12 @@ describe('TenantRepository tests', () => { const ToCreatePLanForEssentialPlanTenantOnTrial = { name: 'essential tenant name', url: 'an-essential-tenant-name', - plan: Plans.values.essential, + plan: TenantPlans.Essential, } const ToCreatPlanForGrowthTenantOnTrial = { name: 'growth tenant name', url: 'a-growth-tenant-name', - plan: Plans.values.growth, + plan: TenantPlans.Growth, } const options = await SequelizeTestUtils.getTestIRepositoryOptions(db) await options.database.tenant.create(ToCreatePLanForEssentialPlanTenantOnTrial) @@ -56,7 +57,7 @@ describe('TenantRepository tests', () => { await options.database.tenant.create({ name: tenantName, url: 'a-tenant-name', - plan: Plans.values.essential, + plan: TenantPlans.Essential, }) // now generate function should return 'a-tenant-name-1' because it already exists diff --git a/backend/src/database/repositories/automationRepository.ts b/backend/src/database/repositories/automationRepository.ts index c46b549776..dacdc26557 100644 --- a/backend/src/database/repositories/automationRepository.ts +++ b/backend/src/database/repositories/automationRepository.ts @@ -3,11 +3,11 @@ import { AutomationSyncTrigger, FeatureFlag, IAutomationData, + PLAN_LIMITS, PageData, } from '@crowd/types' import Sequelize, { QueryTypes } from 'sequelize' import { Error404 } from '@crowd/common' -import { PLAN_LIMITS } from '@/feature-flags/isFeatureEnabled' import { AutomationCriteria } from '../../types/automationTypes' import { IRepositoryOptions } from './IRepositoryOptions' import AuditLogRepository from './auditLogRepository' diff --git a/backend/src/database/repositories/organizationCacheRepository.ts b/backend/src/database/repositories/organizationCacheRepository.ts index a85b6998c7..c4178d65f8 100644 --- a/backend/src/database/repositories/organizationCacheRepository.ts +++ b/backend/src/database/repositories/organizationCacheRepository.ts @@ -1,17 +1,43 @@ -import lodash from 'lodash' import { Error404 } from '@crowd/common' -import SequelizeRepository from './sequelizeRepository' -import AuditLogRepository from './auditLogRepository' +import lodash from 'lodash' +import { QueryTypes } from 'sequelize' import { IRepositoryOptions } from './IRepositoryOptions' +import AuditLogRepository from './auditLogRepository' +import SequelizeRepository from './sequelizeRepository' class OrganizationCacheRepository { + static async linkCacheAndOrganization( + cacheId: string, + organizationId: string, + options: IRepositoryOptions, + ): Promise { + const transaction = SequelizeRepository.getTransaction(options) + const seq = SequelizeRepository.getSequelize(options) + + await seq.query( + ` + insert into "organizationCacheLinks"("organizationCacheId", "organizationId") + values (:cacheId, :organizationId) + on conflict do nothing; + `, + { + replacements: { + cacheId, + organizationId, + }, + transaction, + type: QueryTypes.INSERT, + }, + ) + } + static async create(data, options: IRepositoryOptions) { const transaction = SequelizeRepository.getTransaction(options) + const seq = SequelizeRepository.getSequelize(options) const record = await options.database.organizationCache.create( { ...lodash.pick(data, [ - 'name', 'url', 'description', 'emails', @@ -25,7 +51,6 @@ class OrganizationCacheRepository { 'revenueRange', 'importHash', 'enriched', - 'website', 'github', 'location', 'employeeCountByCountry', @@ -47,13 +72,27 @@ class OrganizationCacheRepository { }, ) + await seq.query( + `insert into "organizationCacheIdentities"(id, name, website) values(:id, :name, :website)`, + { + replacements: { + id: record.id, + name: data.name, + website: data.website || null, + }, + type: QueryTypes.INSERT, + transaction, + }, + ) + await this._createAuditLog(AuditLogRepository.CREATE, record, data, options) return this.findById(record.id, options) } - static async update(id, data, options: IRepositoryOptions) { + static async update(id, data, options: IRepositoryOptions, nameToCreateIdentity?: string) { const transaction = SequelizeRepository.getTransaction(options) + const seq = SequelizeRepository.getSequelize(options) let record = await options.database.organizationCache.findOne({ where: { @@ -69,7 +108,6 @@ class OrganizationCacheRepository { record = await record.update( { ...lodash.pick(data, [ - 'name', 'url', 'description', 'emails', @@ -83,7 +121,6 @@ class OrganizationCacheRepository { 'revenueRange', 'importHash', 'enriched', - 'website', 'github', 'location', 'geoLocation', @@ -112,6 +149,35 @@ class OrganizationCacheRepository { throw new Error404() } + if (nameToCreateIdentity) { + await seq.query( + `insert into "organizationCacheIdentities" (id, name, website) values ($(id), $(name), $(website))`, + { + replacements: { + id, + name: nameToCreateIdentity, + website: data.website, + }, + type: QueryTypes.INSERT, + transaction, + }, + ) + } + + if (data.website) { + await seq.query( + `update "organizationCacheIdentities" set website = :website where id = :id and website is null`, + { + replacements: { + id, + website: data.website, + }, + type: QueryTypes.UPDATE, + transaction, + }, + ) + } + await this._createAuditLog(AuditLogRepository.UPDATE, record, data, options) return this.findById(record.id, options) @@ -153,6 +219,7 @@ class OrganizationCacheRepository { static async destroy(id, options: IRepositoryOptions, force = false) { const transaction = SequelizeRepository.getTransaction(options) + const seq = SequelizeRepository.getSequelize(options) const record = await options.database.organizationCache.findOne({ where: { @@ -165,6 +232,14 @@ class OrganizationCacheRepository { throw new Error404() } + await seq.query('delete from "organizationCacheIdentities" where id = :id', { + replacements: { + id, + type: QueryTypes.DELETE, + transaction, + }, + }) + await record.destroy({ transaction, force, @@ -175,6 +250,7 @@ class OrganizationCacheRepository { static async findById(id, options: IRepositoryOptions) { const transaction = SequelizeRepository.getTransaction(options) + const seq = SequelizeRepository.getSequelize(options) const include = [] @@ -189,7 +265,32 @@ class OrganizationCacheRepository { if (!record) { throw new Error404() } - const output = record.get({ plain: true }) + + const identityRows = await seq.query( + `select name, website from "organizationCacheIdentities" where id = :id`, + { + replacements: { + id, + }, + type: QueryTypes.SELECT, + transaction, + }, + ) + + const names = (identityRows as any[]).map((r) => r.name) + // we just need one website since every identity should have the same website for the same organization cache + const website = (identityRows as any[]) + .filter((r) => r.website !== null) + .map((r) => r.website) + .reduce((prev, curr) => { + if (prev === null) { + return curr + } + + return prev + }, null) + + const output = { ...record.get({ plain: true }), names, website } return output } @@ -204,35 +305,60 @@ class OrganizationCacheRepository { }, include, transaction, + attributes: ['id'], }) if (!record) { return undefined } - const output = record.get({ plain: true }) - return output + return this.findById(record.id, options) } - static async findByName(name, options: IRepositoryOptions) { + static async findByWebsite(website: string, options: IRepositoryOptions) { const transaction = SequelizeRepository.getTransaction(options) - const include = [] + const seq = SequelizeRepository.getSequelize(options) - const record = await options.database.organizationCache.findOne({ - where: { - name, + const result = await seq.query( + `select id from "organizationCacheIdentities" where website = :website`, + { + replacements: { + website, + }, + type: QueryTypes.SELECT, + transaction, }, - include, - transaction, - }) + ) - if (!record) { - return undefined + if (result.length === 1) { + return this.findById((result[0] as any).id, options) } - const output = record.get({ plain: true }) - return output + return undefined + } + + static async findByName(name: string, options: IRepositoryOptions) { + const transaction = SequelizeRepository.getTransaction(options) + + const seq = SequelizeRepository.getSequelize(options) + + const result = await seq.query( + `select id from "organizationCacheIdentities" where name = :name`, + { + replacements: { + name, + }, + type: QueryTypes.SELECT, + transaction, + }, + ) + + if (result.length === 1) { + return this.findById((result[0] as any).id, options) + } + + return undefined } static async _createAuditLog(action, record, data, options: IRepositoryOptions) { diff --git a/backend/src/database/repositories/tenantRepository.ts b/backend/src/database/repositories/tenantRepository.ts index bbc09b0c7f..f20c5b9942 100644 --- a/backend/src/database/repositories/tenantRepository.ts +++ b/backend/src/database/repositories/tenantRepository.ts @@ -1,14 +1,13 @@ +import { Error400, Error404, getCleanString } from '@crowd/common' +import { Edition, TenantPlans } from '@crowd/types' import lodash from 'lodash' import Sequelize, { QueryTypes } from 'sequelize' -import { getCleanString, Error400, Error404 } from '@crowd/common' -import { Edition } from '@crowd/types' -import SequelizeRepository from './sequelizeRepository' -import AuditLogRepository from './auditLogRepository' +import { API_CONFIG } from '../../conf' import SequelizeFilterUtils from '../utils/sequelizeFilterUtils' import { isUserInTenant } from '../utils/userTenantUtils' import { IRepositoryOptions } from './IRepositoryOptions' -import Plans from '../../security/plans' -import { API_CONFIG } from '../../conf' +import AuditLogRepository from './auditLogRepository' +import SequelizeRepository from './sequelizeRepository' const { Op } = Sequelize @@ -17,7 +16,6 @@ const forbiddenTenantUrls = ['www'] class TenantRepository { static async getPayingTenantIds(options: IRepositoryOptions): Promise<({ id: string } & {})[]> { const database = SequelizeRepository.getSequelize(options) - const plans = Plans.values const transaction = SequelizeRepository.getTransaction(options) const query = ` @@ -31,7 +29,7 @@ class TenantRepository { type: QueryTypes.SELECT, transaction, replacements: { - growth: plans.growth, + growth: TenantPlans.Growth, }, }) } @@ -70,7 +68,7 @@ class TenantRepository { 'integrationsRequired', 'importHash', ]), - plan: API_CONFIG.edition === Edition.LFX ? Plans.values.enterprise : null, + plan: API_CONFIG.edition === Edition.LFX ? TenantPlans.Enterprise : null, createdById: currentUser.id, updatedById: currentUser.id, }, diff --git a/backend/src/database/utils/sequelizeTestUtils.ts b/backend/src/database/utils/sequelizeTestUtils.ts index 949c0f2c5d..c08592c8d4 100644 --- a/backend/src/database/utils/sequelizeTestUtils.ts +++ b/backend/src/database/utils/sequelizeTestUtils.ts @@ -1,19 +1,18 @@ -import moment from 'moment' -import jwt from 'jsonwebtoken' -import bcrypt from 'bcrypt' import { getServiceLogger } from '@crowd/logging' import { getRedisClient } from '@crowd/redis' -import { SegmentStatus } from '@crowd/types' import { getTemporalClient } from '@crowd/temporal' +import { SegmentStatus, TenantPlans } from '@crowd/types' +import bcrypt from 'bcrypt' +import jwt from 'jsonwebtoken' +import moment from 'moment' +import { API_CONFIG, REDIS_CONFIG, TEMPORAL_CONFIG } from '../../conf' +import Roles from '../../security/roles' +import { IServiceOptions } from '../../services/IServiceOptions' import { databaseInit } from '../databaseConnection' import { IRepositoryOptions } from '../repositories/IRepositoryOptions' -import { IServiceOptions } from '../../services/IServiceOptions' -import Roles from '../../security/roles' -import UserRepository from '../repositories/userRepository' -import TenantRepository from '../repositories/tenantRepository' -import Plans from '../../security/plans' -import { API_CONFIG, REDIS_CONFIG, TEMPORAL_CONFIG } from '../../conf' import SettingsRepository from '../repositories/settingsRepository' +import TenantRepository from '../repositories/tenantRepository' +import UserRepository from '../repositories/userRepository' export default class SequelizeTestUtils { static async wipeDatabase(db) { @@ -59,7 +58,12 @@ export default class SequelizeTestUtils { return db } - static async getTestIServiceOptions(db, plan = Plans.values.essential, tenantName?, tenantUrl?) { + static async getTestIServiceOptions( + db, + plan: TenantPlans = TenantPlans.Essential, + tenantName?, + tenantUrl?, + ) { db = await this.getDatabase(db) const randomTenant = @@ -188,11 +192,11 @@ export default class SequelizeTestUtils { } as IRepositoryOptions } - static getRandomTestTenant(plan = Plans.values.essential) { + static getRandomTestTenant(plan = TenantPlans.Essential) { return this.getTenant(this.getRandomString('test-tenant'), this.getRandomString('url#'), plan) } - static getTenant(name, url, plan = Plans.values.essential) { + static getTenant(name, url, plan = TenantPlans.Essential) { return { name, url, diff --git a/backend/src/feature-flags/isFeatureEnabled.ts b/backend/src/feature-flags/isFeatureEnabled.ts index 05c89e5a93..6cd6c0ffe3 100644 --- a/backend/src/feature-flags/isFeatureEnabled.ts +++ b/backend/src/feature-flags/isFeatureEnabled.ts @@ -1,33 +1,7 @@ import { isFeatureEnabled } from '@crowd/feature-flags' import { FeatureFlag } from '@crowd/types' -import Plans from '../security/plans' import getFeatureFlagTenantContext from './getFeatureFlagTenantContext' -export const PLAN_LIMITS = { - [Plans.values.essential]: { - [FeatureFlag.AUTOMATIONS]: 2, - [FeatureFlag.CSV_EXPORT]: 2, - }, - [Plans.values.growth]: { - [FeatureFlag.AUTOMATIONS]: 10, - [FeatureFlag.CSV_EXPORT]: 10, - [FeatureFlag.MEMBER_ENRICHMENT]: 1000, - [FeatureFlag.ORGANIZATION_ENRICHMENT]: 200, - }, - [Plans.values.scale]: { - [FeatureFlag.AUTOMATIONS]: 20, - [FeatureFlag.CSV_EXPORT]: 20, - [FeatureFlag.MEMBER_ENRICHMENT]: Infinity, - [FeatureFlag.ORGANIZATION_ENRICHMENT]: Infinity, - }, - [Plans.values.enterprise]: { - [FeatureFlag.AUTOMATIONS]: Infinity, - [FeatureFlag.CSV_EXPORT]: Infinity, - [FeatureFlag.MEMBER_ENRICHMENT]: Infinity, - [FeatureFlag.ORGANIZATION_ENRICHMENT]: Infinity, - }, -} - export default async (featureFlag: FeatureFlag, req: any): Promise => isFeatureEnabled( featureFlag, diff --git a/backend/src/security/permissions.ts b/backend/src/security/permissions.ts index ed9f46cdb5..e108674ed1 100644 --- a/backend/src/security/permissions.ts +++ b/backend/src/security/permissions.ts @@ -1,10 +1,9 @@ +import { TenantPlans } from '@crowd/types' import Roles from './roles' -import Plans from './plans' import Storage from './storage' const storage = Storage.values const roles = Roles.values -const plans = Plans.values class Permissions { static get values() { @@ -13,132 +12,132 @@ class Permissions { id: 'tenantEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, tenantDestroy: { id: 'tenantDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, planEdit: { id: 'planEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, planRead: { id: 'planRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, userEdit: { id: 'userEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, userDestroy: { id: 'userDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, userCreate: { id: 'userCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, userImport: { id: 'userImport', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, userRead: { id: 'userRead', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, userAutocomplete: { id: 'userAutocomplete', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, auditLogRead: { id: 'auditLogRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, settingsRead: { id: 'settingsRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [storage.settingsBackgroundImages, storage.settingsLogos], }, @@ -146,11 +145,11 @@ class Permissions { id: 'settingsEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [storage.settingsBackgroundImages, storage.settingsLogos], }, @@ -158,11 +157,11 @@ class Permissions { id: 'memberAttributesRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -170,11 +169,11 @@ class Permissions { id: 'memberAttributesEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -182,11 +181,11 @@ class Permissions { id: 'memberAttributesDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -194,11 +193,11 @@ class Permissions { id: 'memberAttributesCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -206,22 +205,22 @@ class Permissions { id: 'memberImport', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, memberCreate: { id: 'memberCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -229,11 +228,11 @@ class Permissions { id: 'memberEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -241,11 +240,11 @@ class Permissions { id: 'memberDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -253,44 +252,44 @@ class Permissions { id: 'memberRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, memberAutocomplete: { id: 'memberAutocomplete', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, activityImport: { id: 'activityImport', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, activityCreate: { id: 'activityCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -298,11 +297,11 @@ class Permissions { id: 'activityEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -310,11 +309,11 @@ class Permissions { id: 'activityDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -322,88 +321,88 @@ class Permissions { id: 'activityRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, activityAutocomplete: { id: 'activityAutocomplete', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, automationCreate: { id: 'automationCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, automationUpdate: { id: 'automationUpdate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, automationDestroy: { id: 'automationDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, automationRead: { id: 'automationRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, tagImport: { id: 'tagImport', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, tagCreate: { id: 'tagCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -411,11 +410,11 @@ class Permissions { id: 'tagEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -423,11 +422,11 @@ class Permissions { id: 'tagDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -435,44 +434,44 @@ class Permissions { id: 'tagRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, tagAutocomplete: { id: 'tagAutocomplete', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, organizationImport: { id: 'organizationImport', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, organizationCreate: { id: 'organizationCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -480,11 +479,11 @@ class Permissions { id: 'organizationEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -492,11 +491,11 @@ class Permissions { id: 'organizationDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -504,44 +503,44 @@ class Permissions { id: 'organizationRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, organizationAutocomplete: { id: 'organizationAutocomplete', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, widgetImport: { id: 'widgetImport', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, widgetCreate: { id: 'widgetCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -549,11 +548,11 @@ class Permissions { id: 'widgetEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -561,11 +560,11 @@ class Permissions { id: 'widgetDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -573,44 +572,44 @@ class Permissions { id: 'widgetRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, widgetAutocomplete: { id: 'widgetAutocomplete', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, reportImport: { id: 'reportImport', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, reportCreate: { id: 'reportCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -618,11 +617,11 @@ class Permissions { id: 'reportEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -630,11 +629,11 @@ class Permissions { id: 'reportDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -642,33 +641,33 @@ class Permissions { id: 'reportRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, reportAutocomplete: { id: 'reportAutocomplete', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, integrationImport: { id: 'integrationImport', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, integrationControlLimit: { @@ -680,11 +679,11 @@ class Permissions { id: 'integrationCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -692,11 +691,11 @@ class Permissions { id: 'integrationEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -704,11 +703,11 @@ class Permissions { id: 'integrationDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -716,44 +715,44 @@ class Permissions { id: 'integrationRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, integrationAutocomplete: { id: 'integrationAutocomplete', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, microserviceImport: { id: 'microserviceImport', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, microserviceCreate: { id: 'microserviceCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -761,11 +760,11 @@ class Permissions { id: 'microserviceEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -773,11 +772,11 @@ class Permissions { id: 'microserviceDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -785,49 +784,54 @@ class Permissions { id: 'microserviceRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, microserviceAutocomplete: { id: 'microserviceAutocomplete', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, microserviceVariantFree: { id: 'microserviceVariantFree', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, microserviceVariantPremium: { id: 'microserviceVariantPremium', allowedRoles: [roles.admin, roles.readonly], - allowedPlans: [plans.growth, plans.eagleEye, plans.enterprise, plans.scale], + allowedPlans: [ + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, + ], }, conversationCreate: { id: 'conversationCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -835,11 +839,11 @@ class Permissions { id: 'conversationEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -847,11 +851,11 @@ class Permissions { id: 'conversationDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -859,99 +863,99 @@ class Permissions { id: 'conversationRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, eagleEyeActionCreate: { id: 'eagleEyeActionCreate', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.growth, - plans.essential, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Growth, + TenantPlans.Essential, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, eagleEyeActionDestroy: { id: 'eagleEyeActionDestroy', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.growth, - plans.essential, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Growth, + TenantPlans.Essential, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, eagleEyeContentCreate: { id: 'eagleEyeContentCreate', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.growth, - plans.essential, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Growth, + TenantPlans.Essential, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, eagleEyeContentRead: { id: 'eagleEyeContentRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.growth, - plans.essential, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Growth, + TenantPlans.Essential, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, eagleEyeContentSearch: { id: 'eagleEyeContentSearch', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.growth, - plans.essential, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Growth, + TenantPlans.Essential, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, eagleEyeContentEdit: { id: 'eagleEyeContentEdit', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.growth, - plans.essential, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Growth, + TenantPlans.Essential, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, taskImport: { id: 'taskImport', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, taskCreate: { id: 'taskCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -959,11 +963,11 @@ class Permissions { id: 'taskEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -971,11 +975,11 @@ class Permissions { id: 'taskDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -983,55 +987,55 @@ class Permissions { id: 'taskRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, taskAutocomplete: { id: 'taskAutocomplete', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, taskBatch: { id: 'taskBatch', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, noteImport: { id: 'noteImport', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, noteCreate: { id: 'noteCreate', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -1039,11 +1043,11 @@ class Permissions { id: 'noteEdit', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -1051,11 +1055,11 @@ class Permissions { id: 'noteDestroy', allowedRoles: [roles.admin], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], allowedStorage: [], }, @@ -1063,121 +1067,121 @@ class Permissions { id: 'noteRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, noteAutocomplete: { id: 'noteAutocomplete', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, quickstartGuideRead: { id: 'quickstartGuideRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, quickstartGuideSettingsUpdate: { id: 'quickstartGuideSettingsUpdate', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, segmentRead: { id: 'segmentRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, segmentCreate: { id: 'segmentCreate', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, segmentEdit: { id: 'segmentEdit', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, customViewCreate: { id: 'customViewCreate', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, customViewEdit: { id: 'customViewEdit', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, customViewDestroy: { id: 'customViewDestroy', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, customViewRead: { id: 'customViewRead', allowedRoles: [roles.admin, roles.readonly], allowedPlans: [ - plans.essential, - plans.growth, - plans.eagleEye, - plans.enterprise, - plans.scale, + TenantPlans.Essential, + TenantPlans.Growth, + TenantPlans.EagleEye, + TenantPlans.Enterprise, + TenantPlans.Scale, ], }, } diff --git a/backend/src/security/plans.ts b/backend/src/security/plans.ts index 83f3bcf40b..aa1d4af20f 100644 --- a/backend/src/security/plans.ts +++ b/backend/src/security/plans.ts @@ -1,28 +1,19 @@ +import { TenantPlans } from '@crowd/types' import { PLANS_CONFIG } from '../conf' class Plans { - static get values() { - return { - essential: 'Essential', - growth: 'Growth', - eagleEye: 'Eagle Eye', - scale: 'Scale', - enterprise: 'Enterprise', - } - } - static selectPlanByStripePriceId(stripePriceId) { const premiumStripePriceId = PLANS_CONFIG.stripePricePremium if (premiumStripePriceId === stripePriceId) { - return Plans.values.growth + return TenantPlans.Growth } - return Plans.values.essential + return TenantPlans.Essential } static selectStripePriceIdByPlan(plan) { - if (plan === Plans.values.growth) { + if (plan === TenantPlans.Growth) { return PLANS_CONFIG.stripePricePremium } @@ -64,7 +55,7 @@ class Plans { * because future charges might occur */ static allowTenantDestroy(plan, planStatus) { - if (plan === Plans.values.essential || plan === Plans.values.growth) { + if (plan === TenantPlans.Essential || plan === TenantPlans.Growth) { return true } diff --git a/backend/src/serverless/integrations/workers/stripeWebhookWorker.ts b/backend/src/serverless/integrations/workers/stripeWebhookWorker.ts index 4e64ceb42b..be48955cf5 100644 --- a/backend/src/serverless/integrations/workers/stripeWebhookWorker.ts +++ b/backend/src/serverless/integrations/workers/stripeWebhookWorker.ts @@ -1,13 +1,13 @@ import { Error404, timeout } from '@crowd/common' import { getServiceChildLogger } from '@crowd/logging' -import { getRedisClient, RedisPubSubEmitter } from '@crowd/redis' +import { RedisPubSubEmitter, getRedisClient } from '@crowd/redis' import { ApiWebsocketMessage } from '@crowd/types' import moment from 'moment' import { Stripe } from 'stripe' +import StripeService from '@/services/stripeService' import { getNodejsWorkerEmitter } from '@/serverless/utils/serviceSQS' import { PLANS_CONFIG, REDIS_CONFIG } from '../../../conf' import SequelizeRepository from '../../../database/repositories/sequelizeRepository' -import StripeService from '@/services/stripeService' const log = getServiceChildLogger('stripeWebhookWorker') diff --git a/backend/src/serverless/microservices/nodejs/bulk-enrichment/bulkOrganizationEnrichmentWorker.ts b/backend/src/serverless/microservices/nodejs/bulk-enrichment/bulkOrganizationEnrichmentWorker.ts index b94929e763..85c67e6f10 100644 --- a/backend/src/serverless/microservices/nodejs/bulk-enrichment/bulkOrganizationEnrichmentWorker.ts +++ b/backend/src/serverless/microservices/nodejs/bulk-enrichment/bulkOrganizationEnrichmentWorker.ts @@ -1,9 +1,8 @@ import { getRedisClient, RedisCache } from '@crowd/redis' -import { FeatureFlag, FeatureFlagRedisKey } from '@crowd/types' +import { FeatureFlag, FeatureFlagRedisKey, PLAN_LIMITS } from '@crowd/types' import { getSecondsTillEndOfMonth } from '../../../../utils/timing' import { ORGANIZATION_ENRICHMENT_CONFIG, REDIS_CONFIG } from '../../../../conf' import getUserContext from '../../../../database/utils/getUserContext' -import { PLAN_LIMITS } from '../../../../feature-flags/isFeatureEnabled' import OrganizationEnrichmentService from '../../../../services/premium/enrichment/organizationEnrichmentService' export async function BulkorganizationEnrichmentWorker( diff --git a/backend/src/services/__tests__/organizationService.test.ts b/backend/src/services/__tests__/organizationService.test.ts index 5843e402e1..3920262563 100644 --- a/backend/src/services/__tests__/organizationService.test.ts +++ b/backend/src/services/__tests__/organizationService.test.ts @@ -1,6 +1,5 @@ -import organizationCacheRepository from '../../database/repositories/organizationCacheRepository' +import { TenantPlans } from '@crowd/types' import SequelizeTestUtils from '../../database/utils/sequelizeTestUtils' -import Plans from '../../security/plans' import OrganizationService from '../organizationService' const db = null @@ -19,7 +18,7 @@ describe('OrganizationService tests', () => { it('Should create organization', async () => { const mockIServiceOptions = await SequelizeTestUtils.getTestIServiceOptions( db, - Plans.values.growth, + TenantPlans.Growth, ) const service = new OrganizationService(mockIServiceOptions) @@ -39,7 +38,7 @@ describe('OrganizationService tests', () => { it('Should throw an error when name is not sent', async () => { const mockIServiceOptions = await SequelizeTestUtils.getTestIServiceOptions( db, - Plans.values.growth, + TenantPlans.Growth, ) const service = new OrganizationService(mockIServiceOptions) diff --git a/backend/src/services/__tests__/tenantService.test.ts b/backend/src/services/__tests__/tenantService.test.ts index 9d45442d96..02e4534050 100644 --- a/backend/src/services/__tests__/tenantService.test.ts +++ b/backend/src/services/__tests__/tenantService.test.ts @@ -1,16 +1,14 @@ -import moment from 'moment' +import { generateUUIDv1 } from '@crowd/common' +import { getRedisClient } from '@crowd/redis' +import { MemberAttributeName, PlatformType } from '@crowd/types' +import { REDIS_CONFIG } from '../../conf' import SequelizeTestUtils from '../../database/utils/sequelizeTestUtils' -import TenantService from '../tenantService' -import MemberService from '../memberService' import { IServiceOptions } from '../IServiceOptions' -import MicroserviceService from '../microserviceService' -import { MemberAttributeName, PlatformType } from '@crowd/types' import MemberAttributeSettingsService from '../memberAttributeSettingsService' +import MemberService from '../memberService' +import MicroserviceService from '../microserviceService' import TaskService from '../taskService' -import Plans from '../../security/plans' -import { generateUUIDv1 } from '@crowd/common' -import { getRedisClient } from '@crowd/redis' -import { REDIS_CONFIG } from '../../conf' +import TenantService from '../tenantService' const db = null diff --git a/backend/src/services/organizationService.ts b/backend/src/services/organizationService.ts index 997df9969d..0216bb9e9d 100644 --- a/backend/src/services/organizationService.ts +++ b/backend/src/services/organizationService.ts @@ -1,4 +1,3 @@ -import { isEqual } from 'lodash' import { Error400, websiteNormalizer } from '@crowd/common' import { LoggerBase } from '@crowd/logging' import { @@ -8,8 +7,9 @@ import { OrganizationMergeSuggestionType, SyncMode, } from '@crowd/types' -import { IRepositoryOptions } from '@/database/repositories/IRepositoryOptions' +import { isEqual } from 'lodash' import getObjectWithoutKey from '@/utils/getObjectWithoutKey' +import { IRepositoryOptions } from '@/database/repositories/IRepositoryOptions' import MemberRepository from '../database/repositories/memberRepository' import { MergeActionState, @@ -27,8 +27,8 @@ import { keepPrimaryIfExists, mergeUniqueStringArrayItems, } from './helpers/mergeFunctions' -import SearchSyncService from './searchSyncService' import MemberOrganizationService from './memberOrganizationService' +import SearchSyncService from './searchSyncService' export default class OrganizationService extends LoggerBase { options: IServiceOptions @@ -402,17 +402,33 @@ export default class OrganizationService extends LoggerBase { const primaryIdentity = data.identities[0] const nameToCheckInCache = (data as any).name || primaryIdentity.name - // check cache existing by name - let cache = await organizationCacheRepository.findByName(nameToCheckInCache, { - ...this.options, - transaction, - }) - // Normalize the website URL if it exists if (data.website) { data.website = websiteNormalizer(data.website) } + // lets check if we have this organization in cache by website + let cache + let createCacheIdentity = false + if (data.website) { + cache = await organizationCacheRepository.findByWebsite(data.website, { + ...this.options, + transaction, + }) + + if (cache && !cache.names.includes(nameToCheckInCache)) { + createCacheIdentity = true + } + } + + // check cache existing by name + if (!cache) { + cache = await organizationCacheRepository.findByName(nameToCheckInCache, { + ...this.options, + transaction, + }) + } + // if cache exists, merge current data with cache data // if it doesn't exist, create it from incoming data if (cache) { @@ -443,10 +459,15 @@ export default class OrganizationService extends LoggerBase { } }) if (Object.keys(updateData).length > 0) { - await organizationCacheRepository.update(cache.id, updateData, { - ...this.options, - transaction, - }) + await organizationCacheRepository.update( + cache.id, + updateData, + { + ...this.options, + transaction, + }, + createCacheIdentity ? nameToCheckInCache : undefined, + ) cache = { ...cache, ...updateData } // Update the cached data with the new data } @@ -583,6 +604,11 @@ export default class OrganizationService extends LoggerBase { } } + await organizationCacheRepository.linkCacheAndOrganization(cache.id, record.id, { + ...this.options, + transaction, + }) + await SequelizeRepository.commitTransaction(transaction) if (syncOptions.doSync) { diff --git a/backend/src/services/premium/enrichment/organizationEnrichmentService.ts b/backend/src/services/premium/enrichment/organizationEnrichmentService.ts index 355edb359b..c7b33495e7 100644 --- a/backend/src/services/premium/enrichment/organizationEnrichmentService.ts +++ b/backend/src/services/premium/enrichment/organizationEnrichmentService.ts @@ -12,9 +12,9 @@ import { SyncMode, } from '@crowd/types' import { EnrichmentParams, IEnrichmentResponse } from '@crowd/types/premium' +import { renameKeys } from '@crowd/common' import { REDIS_CONFIG } from '../../../conf' import OrganizationRepository from '../../../database/repositories/organizationRepository' -import { renameKeys } from '../../../utils/renameKeys' import { IServiceOptions } from '../../IServiceOptions' import SequelizeRepository from '@/database/repositories/sequelizeRepository' import SearchSyncService from '@/services/searchSyncService' @@ -304,6 +304,7 @@ export default class OrganizationEnrichmentService extends LoggerBase { ): IOrganization { let data = renameKeys(pdlData, { summary: 'description', + display_name: 'displayName', employee_count_by_country: 'employeeCountByCountry', employee_count: 'employees', location: 'address', diff --git a/backend/src/services/slackCommandService.ts b/backend/src/services/slackCommandService.ts index 676ea39cf2..4fd6fc8773 100644 --- a/backend/src/services/slackCommandService.ts +++ b/backend/src/services/slackCommandService.ts @@ -1,8 +1,9 @@ /* eslint-disable no-case-declarations */ import { validateUUID as uuidValidate } from '@crowd/common' +import { TenantPlans } from '@crowd/types' import moment from 'moment' -import { Section, Message, SlackMessageDto, Divider } from 'slack-block-builder' -import { IS_DEV_ENV, IS_STAGING_ENV, IS_PROD_ENV } from '../conf' +import { Divider, Message, Section, SlackMessageDto } from 'slack-block-builder' +import { IS_DEV_ENV, IS_PROD_ENV, IS_STAGING_ENV } from '../conf' import TenantRepository from '../database/repositories/tenantRepository' import { SlackCommand, @@ -12,7 +13,6 @@ import { SlackParameterParseResult, } from '../types/slackTypes' import { IServiceOptions } from './IServiceOptions' -import Plans from '../security/plans' export default class SlackCommandService { private readonly commands: SlackCommandDefinition[] @@ -57,7 +57,7 @@ export default class SlackCommandService { required: true, description: 'Plan to set', type: SlackCommandParameterType.STRING, - allowedValues: [Plans.values.growth, Plans.values.essential], + allowedValues: [TenantPlans.Growth, TenantPlans.Essential], }, { name: 'trialEndsAt', diff --git a/backend/src/services/stripeService.ts b/backend/src/services/stripeService.ts index ce28fe0e87..e9e17699d0 100644 --- a/backend/src/services/stripeService.ts +++ b/backend/src/services/stripeService.ts @@ -1,6 +1,6 @@ import { Stripe } from 'stripe' +import { TenantPlans } from '@crowd/types' import { PLANS_CONFIG } from '@/conf' -import Plans from '@/security/plans' const stripe = new Stripe(PLANS_CONFIG.stripeSecretKey, { apiVersion: '2022-08-01', @@ -18,16 +18,16 @@ class StripeService { static getPlanFromProductId(productId: string) { if (productId === PLANS_CONFIG.stripeScalePlanProductId) { - return Plans.values.scale + return TenantPlans.Scale } if (productId === PLANS_CONFIG.stripeEssentialPlanProductId) { - return Plans.values.essential + return TenantPlans.Essential } if (productId === PLANS_CONFIG.stripeEagleEyePlanProductId) { - return Plans.values.eagleEye + return TenantPlans.EagleEye } if (productId === PLANS_CONFIG.stripeGrowthPlanProductId) { - return Plans.values.growth + return TenantPlans.Growth } return null } diff --git a/backend/src/services/tenantService.ts b/backend/src/services/tenantService.ts index d622499da3..821482f179 100644 --- a/backend/src/services/tenantService.ts +++ b/backend/src/services/tenantService.ts @@ -1,5 +1,5 @@ import { DEFAULT_MEMBER_ATTRIBUTES } from '@crowd/integrations' -import { SegmentData, SegmentStatus } from '@crowd/types' +import { SegmentData, SegmentStatus, TenantPlans } from '@crowd/types' import { Error400, Error404 } from '@crowd/common' import moment from 'moment/moment' import { TENANT_MODE } from '../conf/index' @@ -390,7 +390,7 @@ export default class TenantService { } async updatePlanToFree(planStripeCustomerId) { - return this.updatePlanStatus(planStripeCustomerId, Plans.values.essential, 'active') + return this.updatePlanStatus(planStripeCustomerId, TenantPlans.Essential, 'active') } async updatePlanStatus(planStripeCustomerId, plan, planStatus) { diff --git a/backend/src/services/user/__tests__/permissionChecker.test.ts b/backend/src/services/user/__tests__/permissionChecker.test.ts index 7b5f9e0b6a..40cb594f37 100644 --- a/backend/src/services/user/__tests__/permissionChecker.test.ts +++ b/backend/src/services/user/__tests__/permissionChecker.test.ts @@ -1,4 +1,4 @@ -import { PlatformType } from '@crowd/types' +import { PlatformType, TenantPlans } from '@crowd/types' import { Error403 } from '@crowd/common' import SequelizeTestUtils from '../../../database/utils/sequelizeTestUtils' import Plans from '../../../security/plans' @@ -18,8 +18,11 @@ describe('PermissionChecker tests', () => { describe('Integration protected fields', () => { it('Should throw an error when limitCount is passed', async () => { - for (const plan of Object.values(Plans.values)) { - const mockIServiceOptions = await SequelizeTestUtils.getTestIServiceOptions(db, plan) + for (const plan of Object.values(TenantPlans)) { + const mockIServiceOptions = await SequelizeTestUtils.getTestIServiceOptions( + db, + plan as TenantPlans, + ) const permissionChecker = new PermissionChecker(mockIServiceOptions) const data = { limitCount: 1, @@ -33,8 +36,11 @@ describe('PermissionChecker tests', () => { }) it('Should throw an error when limitCount is passed as 0', async () => { - for (const plan of Object.values(Plans.values)) { - const mockIServiceOptions = await SequelizeTestUtils.getTestIServiceOptions(db, plan) + for (const plan of Object.values(TenantPlans)) { + const mockIServiceOptions = await SequelizeTestUtils.getTestIServiceOptions( + db, + plan as TenantPlans, + ) const permissionChecker = new PermissionChecker(mockIServiceOptions) const data = { limitCount: 0, diff --git a/backend/src/services/user/permissionChecker.ts b/backend/src/services/user/permissionChecker.ts index ac5970d00d..89c1655a9b 100644 --- a/backend/src/services/user/permissionChecker.ts +++ b/backend/src/services/user/permissionChecker.ts @@ -1,11 +1,9 @@ import assert from 'assert' import { Error400, Error403 } from '@crowd/common' -import Plans from '../../security/plans' +import { TenantPlans } from '@crowd/types' import Permissions from '../../security/permissions' import EmailSender from '../emailSender' -const plans = Plans.values - /** * Checks the Permission of the User on a Tenant. */ @@ -164,7 +162,7 @@ export default class PermissionChecker { */ get currentTenantPlan() { if (!this.currentTenant || !this.currentTenant.plan) { - return plans.essential + return TenantPlans.Essential } return this.currentTenant.plan diff --git a/backend/src/services/user/userDestroyer.ts b/backend/src/services/user/userDestroyer.ts index 9c27048307..dfd6464c72 100644 --- a/backend/src/services/user/userDestroyer.ts +++ b/backend/src/services/user/userDestroyer.ts @@ -1,9 +1,9 @@ -import assert from 'assert' import { Error400 } from '@crowd/common' +import { TenantPlans } from '@crowd/types' +import assert from 'assert' import SequelizeRepository from '../../database/repositories/sequelizeRepository' -import UserRepository from '../../database/repositories/userRepository' import TenantUserRepository from '../../database/repositories/tenantUserRepository' -import Plans from '../../security/plans' +import UserRepository from '../../database/repositories/userRepository' import { IServiceOptions } from '../IServiceOptions' /** @@ -65,7 +65,7 @@ export default class UserDestroyer { async _isRemovingPlanUser() { const { currentTenant } = this.options - if (currentTenant.plan === Plans.values.essential) { + if (currentTenant.plan === TenantPlans.Essential) { return false } diff --git a/backend/src/services/user/userEditor.ts b/backend/src/services/user/userEditor.ts index d1dbe48ebb..5bddd9ac2d 100644 --- a/backend/src/services/user/userEditor.ts +++ b/backend/src/services/user/userEditor.ts @@ -1,10 +1,10 @@ -import assert from 'assert' import { Error400 } from '@crowd/common' -import Roles from '../../security/roles' +import { TenantPlans } from '@crowd/types' +import assert from 'assert' import SequelizeRepository from '../../database/repositories/sequelizeRepository' -import UserRepository from '../../database/repositories/userRepository' import TenantUserRepository from '../../database/repositories/tenantUserRepository' -import Plans from '../../security/plans' +import UserRepository from '../../database/repositories/userRepository' +import Roles from '../../security/roles' import { IServiceOptions } from '../IServiceOptions' /** @@ -86,7 +86,7 @@ export default class UserEditor { const { currentTenant } = this.options - if (currentTenant.plan === Plans.values.essential) { + if (currentTenant.plan === TenantPlans.Essential) { return false } diff --git a/backend/src/utils/renameKeys.ts b/backend/src/utils/renameKeys.ts deleted file mode 100644 index 0e39cdd4f0..0000000000 --- a/backend/src/utils/renameKeys.ts +++ /dev/null @@ -1,10 +0,0 @@ -type RemapT = { [index: string]: any } - -export const renameKeys = (obj: RemapT, fieldMap: RemapT): T => - Object.keys(obj).reduce( - (acc, key) => ({ - ...acc, - ...{ [fieldMap[key] || key]: obj[key] }, - }), - {} as T, - ) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92e8165818..1d38ad0ae8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1217,6 +1217,85 @@ importers: specifier: ^3.0.3 version: 3.1.0 + services/apps/premium/organizations_enrichment_worker: + dependencies: + '@crowd/archetype-standard': + specifier: file:../../../archetypes/standard + version: file:services/archetypes/standard(@swc/core@1.3.100)(@types/node@20.10.1)(typescript@5.3.2) + '@crowd/archetype-worker': + specifier: file:../../../archetypes/worker + version: file:services/archetypes/worker(@swc/core@1.3.100)(@types/node@20.10.1)(typescript@5.3.2) + '@crowd/common': + specifier: file:../../../libs/common + version: file:services/libs/common + '@crowd/common_services': + specifier: file:../../../libs/common_services + version: file:services/libs/common_services + '@crowd/database': + specifier: file:../../../libs/database + version: file:services/libs/database + '@crowd/feature-flags': + specifier: file:../../../libs/feature-flags + version: file:services/libs/feature-flags + '@crowd/logging': + specifier: file:../../../libs/logging + version: file:services/libs/logging + '@crowd/opensearch': + specifier: file:../../../libs/opensearch + version: file:services/libs/opensearch + '@crowd/redis': + specifier: file:../../../libs/redis + version: file:services/libs/redis + '@crowd/types': + specifier: file:../../../libs/types + version: file:services/libs/types + '@temporalio/client': + specifier: ~1.8.6 + version: 1.8.6 + '@temporalio/workflow': + specifier: ~1.8.6 + version: 1.8.6 + peopledatalabs: + specifier: ~6.1.5 + version: 6.1.5 + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@swc/core@1.3.100)(@types/node@20.10.1)(typescript@5.3.2) + tsconfig-paths: + specifier: ^4.2.0 + version: 4.2.0 + typescript: + specifier: ^5.2.2 + version: 5.3.2 + devDependencies: + '@types/node': + specifier: ^20.8.2 + version: 20.10.1 + '@types/uuid': + specifier: ~9.0.6 + version: 9.0.7 + '@typescript-eslint/eslint-plugin': + specifier: ^6.7.4 + version: 6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.54.0)(typescript@5.3.2) + '@typescript-eslint/parser': + specifier: ^6.7.4 + version: 6.13.1(eslint@8.54.0)(typescript@5.3.2) + eslint: + specifier: ^8.50.0 + version: 8.54.0 + eslint-config-prettier: + specifier: ^9.0.0 + version: 9.0.0(eslint@8.54.0) + eslint-plugin-prettier: + specifier: ^5.0.0 + version: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.54.0)(prettier@3.1.0) + nodemon: + specifier: ^3.0.1 + version: 3.0.1 + prettier: + specifier: ^3.0.3 + version: 3.1.0 + services/apps/profiles_worker: dependencies: '@crowd/archetype-standard': @@ -1768,6 +1847,9 @@ importers: '@crowd/archetype-standard': specifier: file:../../archetypes/standard version: file:services/archetypes/standard(@swc/core@1.3.100)(@types/node@20.10.1)(typescript@5.3.2) + '@crowd/common': + specifier: file:../../libs/common + version: file:services/libs/common '@crowd/database': specifier: file:../../libs/database version: file:services/libs/database @@ -17085,6 +17167,7 @@ packages: name: '@crowd/archetype-worker' dependencies: '@crowd/archetype-standard': file:services/archetypes/standard(@swc/core@1.3.100)(@types/node@20.10.1)(typescript@5.3.2) + '@crowd/common': file:services/libs/common '@crowd/database': file:services/libs/database '@crowd/temporal': file:services/libs/temporal '@opensearch-project/opensearch': 1.2.0 diff --git a/scripts/builders/organizations-enrichment-worker.sh b/scripts/builders/organizations-enrichment-worker.sh new file mode 100644 index 0000000000..4c78c9867c --- /dev/null +++ b/scripts/builders/organizations-enrichment-worker.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +DOCKERFILE="./services/docker/Dockerfile.organizations_enrichment_worker" +CONTEXT="../" +REPO="crowddotdev/organizations-enrichment-worker" \ No newline at end of file diff --git a/scripts/services/docker/Dockerfile.organizations_enrichment_worker b/scripts/services/docker/Dockerfile.organizations_enrichment_worker new file mode 100644 index 0000000000..f84b767090 --- /dev/null +++ b/scripts/services/docker/Dockerfile.organizations_enrichment_worker @@ -0,0 +1,18 @@ +FROM node:16-alpine as builder + +WORKDIR /usr/crowd/app +RUN corepack enable && apk add --update --no-cache python3 build-base && ln -sf python3 /usr/bin/python && python3 -m ensurepip && pip3 install --no-cache --upgrade pip setuptools + +COPY ./pnpm-workspace.yaml ./pnpm-lock.yaml ./ +COPY ./services ./services +RUN pnpm i --frozen-lockfile + +FROM node:16-bookworm-slim as runner + +WORKDIR /usr/crowd/app +RUN corepack enable && apt update && apt install -y ca-certificates --no-install-recommends && rm -rf /var/lib/apt/lists/* + +COPY --from=builder /usr/crowd/app/node_modules ./node_modules +COPY --from=builder /usr/crowd/app/services/libs ./services/libs +COPY --from=builder /usr/crowd/app/services/archetypes/ ./services/archetypes +COPY --from=builder /usr/crowd/app/services/apps/premium/organizations_enrichment_worker/ ./services/apps/premium/organizations_enrichment_worker diff --git a/scripts/services/docker/Dockerfile.organizations_enrichment_worker.dockerignore b/scripts/services/docker/Dockerfile.organizations_enrichment_worker.dockerignore new file mode 100644 index 0000000000..77d313162a --- /dev/null +++ b/scripts/services/docker/Dockerfile.organizations_enrichment_worker.dockerignore @@ -0,0 +1,22 @@ +**/.git +**/node_modules +**/venv* +**/.webpack +**/.serverless +**/.cubestore +**/.env +**/.env.* +**/.idea +**/.vscode +**/dist +backend/server-config/ +backend/util/ +backend/src/serverless/microservices/python +.vscode/ +.github/ +frontend/ +scripts/ +.flake8 +*.md +Makefile +backend/ \ No newline at end of file diff --git a/scripts/services/organizations-enrichment-worker.yaml b/scripts/services/organizations-enrichment-worker.yaml new file mode 100644 index 0000000000..2852807b35 --- /dev/null +++ b/scripts/services/organizations-enrichment-worker.yaml @@ -0,0 +1,63 @@ +version: '3.1' + +x-env-args: &env-args + DOCKER_BUILDKIT: 1 + NODE_ENV: docker + SERVICE: organizations-enrichment-worker + CROWD_TEMPORAL_TASKQUEUE: organizations-enrichment + SHELL: /bin/sh + +services: + organizations-enrichment-worker: + build: + context: ../../ + dockerfile: ./scripts/services/docker/Dockerfile.organizations_enrichment_worker + command: 'pnpm run start' + working_dir: /usr/crowd/app/services/apps/organizations_enrichment_worker + env_file: + - ../../backend/.env.dist.local + - ../../backend/.env.dist.composed + - ../../backend/.env.override.local + - ../../backend/.env.override.composed + environment: + <<: *env-args + restart: always + networks: + - crowd-bridge + + organizations-enrichment-worker-dev: + build: + context: ../../ + dockerfile: ./scripts/services/docker/Dockerfile.organizations_enrichment_worker + command: 'pnpm run dev' + working_dir: /usr/crowd/app/services/apps/premium/organizations_enrichment_worker + # user: '${USER_ID}:${GROUP_ID}' + env_file: + - ../../backend/.env.dist.local + - ../../backend/.env.dist.composed + - ../../backend/.env.override.local + - ../../backend/.env.override.composed + environment: + <<: *env-args + hostname: organizations-enrichment-worker + networks: + - crowd-bridge + volumes: + - ../../services/libs/alerting/src:/usr/crowd/app/services/libs/alerting/src + - ../../services/libs/common/src:/usr/crowd/app/services/libs/common/src + - ../../services/libs/common_services/src:/usr/crowd/app/services/libs/common_services/src + - ../../services/libs/conversations/src:/usr/crowd/app/services/libs/conversations/src + - ../../services/libs/database/src:/usr/crowd/app/services/libs/database/src + - ../../services/libs/integrations/src:/usr/crowd/app/services/libs/integrations/src + - ../../services/libs/ioc/src:/usr/crowd/app/services/libs/ioc/src + - ../../services/libs/logging/src:/usr/crowd/app/services/libs/logging/src + - ../../services/libs/opensearch/src:/usr/crowd/app/services/libs/opensearch/src + - ../../services/libs/redis/src:/usr/crowd/app/services/libs/redis/src + - ../../services/libs/sentiment/src:/usr/crowd/app/services/libs/sentiment/src + - ../../services/libs/sqs/src:/usr/crowd/app/services/libs/sqs/src + - ../../services/libs/types/src:/usr/crowd/app/services/libs/types/src + - ../../services/apps/premium/organizations_enrichment_worker/src:/usr/crowd/app/services/apps/premium/organizations_enrichment_worker/src + +networks: + crowd-bridge: + external: true diff --git a/services/apps/data_sink_worker/package.json b/services/apps/data_sink_worker/package.json index 1b057f2db2..6d806742d6 100644 --- a/services/apps/data_sink_worker/package.json +++ b/services/apps/data_sink_worker/package.json @@ -18,6 +18,7 @@ "script:restart-result": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true node -r tsconfig-paths/register -r ts-node/register src/bin/restart-result.ts", "script:process-results": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true node -r tsconfig-paths/register -r ts-node/register src/bin/process-results.ts", "script:map-tenant-members-to-org": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true node -r tsconfig-paths/register -r ts-node/register src/bin/map-tenant-members-to-org.ts", + "script:migrate-org-caches": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true node -r tsconfig-paths/register -r ts-node/register src/bin/migrate-org-cache-to-identities.ts", "script:map-member-to-org": "SERVICE=script TS_NODE_TRANSPILE_ONLY=true node -r tsconfig-paths/register -r ts-node/register src/bin/map-member-to-org.ts" }, "dependencies": { diff --git a/services/apps/data_sink_worker/src/bin/migrate-org-cache-to-identities.ts b/services/apps/data_sink_worker/src/bin/migrate-org-cache-to-identities.ts new file mode 100644 index 0000000000..ca6495dab1 --- /dev/null +++ b/services/apps/data_sink_worker/src/bin/migrate-org-cache-to-identities.ts @@ -0,0 +1,266 @@ +import { + DbConnection, + DbInstance, + DbTransaction, + RepositoryBase, + getDbConnection, + getDbInstance, +} from '@crowd/database' +import { getServiceLogger } from '@crowd/logging' +import { DB_CONFIG } from '../conf' +import { distinctBy } from '@crowd/common' + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +interface IOrgCacheToMerge { + website: string + ids: string[] +} + +async function getOrganizationCachesToMigrate(db: DbConnection): Promise { + const results = await db.any( + ` +select oc."oldWebsite", array_agg(oc.id) as ids +from "organizationCaches" oc +where oc."oldWebsite" is not null + and not exists (select 1 from "organizationCacheIdentities" oci where oci.website = oc."oldWebsite") +group by oc."oldWebsite" +limit 100; +`, + ) + + return results +} + +async function getOrganizationCaches(db: DbConnection, ids: string[]): Promise { + const results = await db.any( + ` +select * +from "organizationCaches" +where id in ($(ids:csv)) +`, + { ids }, + ) + + return results +} + +async function createOrgCacheIdentity( + db: DbTransaction, + dbInstance: DbInstance, + id: string, + identities: { name: string; website: string }[], +): Promise { + const prepared = identities.map((i) => { + return { ...i, id } + }) + + const query = dbInstance.helpers.insert( + prepared, + ['id', 'name', 'website'], + 'organizationCacheIdentities', + ) + await db.none(query) +} + +async function moveLinksToNewCacheId( + db: DbTransaction, + fromIds: string[], + toId: string, +): Promise { + const existingLinks = await db.any( + ` + select "organizationId", "organizationCacheId" from "organizationCacheLinks" + where "organizationCacheId" in ($(ids:csv)) + `, + { + ids: fromIds.concat([toId]), + }, + ) + + const toLinks = existingLinks.filter((l) => l.organizationCacheId === toId) + const toRemove: any[] = [] + const toMove: any[] = [] + for (const fromId of fromIds) { + const fromIdLinks = existingLinks.filter((l) => l.organizationCacheId === fromId) + for (const fromIdLink of fromIdLinks) { + // if we already have a link to the same organization we can just remove the old link + // otherwise we need to move the link to the new cache id + if (toLinks.find((l) => l.organizationId === fromIdLink.organizationId)) { + toRemove.push(fromIdLink) + } else { + toMove.push(fromIdLink) + } + } + } + + for (const link of toRemove) { + await db.none( + `delete from "organizationCacheLinks" where "organizationId" = $(organizationId) and "organizationCacheId" = $(organizationCacheId)`, + { + organizationId: link.organizationId, + organizationCacheId: link.organizationCacheId, + }, + ) + } + + for (const link of toMove) { + await db.none( + `update "organizationCacheLinks" set "organizationCacheId" = $(toId) where "organizationId" = $(organizationId) and "organizationCacheId" = $(organizationCacheId)`, + { + organizationId: link.organizationId, + organizationCacheId: link.organizationCacheId, + toId, + }, + ) + } +} + +async function removeCaches(db: DbTransaction, ids: string[]): Promise { + await db.none(`delete from "organizationCaches" where id in ($(ids:csv))`, { ids }) +} + +const columnsToIgnore = [ + 'id', + 'createdAt', + 'updatedAt', + 'deletedAt', + 'enriched', + 'lastEnrichedAt', + 'manuallyCreated', + 'oldName', + 'oldWebsite', +] + +async function updateOrganizationCacheData(dbInstance, db, id, data) { + const keys = Object.keys(data).filter((k) => !columnsToIgnore.includes(k)) + + if (keys.length !== 0) { + const dynamicColumnSet = new dbInstance.helpers.ColumnSet(keys, { + table: 'organizationCaches', + }) + + if (data.naics) { + data.naics = JSON.stringify(data.naics) + } + + const prepared = RepositoryBase.prepare(data, dynamicColumnSet) + const query = dbInstance.helpers.update(prepared, dynamicColumnSet) + + const condition = dbInstance.as.format('where id = $(id)', { id }) + const result = await db.result(`${query} ${condition}`) + + if (result.rowCount !== 1) { + throw new Error('Updated row count not equal to 1!') + } + } +} + +const log = getServiceLogger() + +async function processOrgCache( + db: DbConnection, + dbInstance: DbInstance, + result: IOrgCacheToMerge, +): Promise { + const ids = result.ids + + const caches = await getOrganizationCaches(db, ids) + + if (ids.length > 1) { + // need to merge all datapoints together + if (ids.length !== caches.length) { + throw new Error(`Did not find all org cache entries for these ids: [${ids.join(', ')}]`) + } + + // pick the most completed entry + let index = 0 + let max = 0 + for (let i = 0; i < caches.length; i++) { + const filledKeys = Object.keys(caches[i]).filter( + (k) => !columnsToIgnore.includes(k) && caches[i][k] !== null, + ) + + if (filledKeys.length > max) { + max = filledKeys.length + index = i + } + } + + const data = caches[index] + + const toUpdate: any = {} + for (let i = 0; i < caches.length; i++) { + if (i === index) { + continue + } + for (const key of Object.keys(data)) { + if (data[key] === null && caches[i][key] !== null) { + toUpdate[key] = caches[i][key] + } + } + } + + let identities = caches.map((c) => { + return { + name: c.oldName, + website: c.oldWebsite, + } + }) + + identities = distinctBy(identities, (i) => `${i.name}:${i.website}`) + + try { + await db.tx(async (tx) => { + await createOrgCacheIdentity(tx, dbInstance, data.id, identities) + if (Object.keys(toUpdate).length > 0) { + await updateOrganizationCacheData(dbInstance, tx, data.id, toUpdate) + } + const cacheIdsToRemove = caches.filter((c) => c.id !== data.id).map((c) => c.id) + await moveLinksToNewCacheId(tx, cacheIdsToRemove, data.id) + await removeCaches(tx, cacheIdsToRemove) + }) + } catch (err) { + log.error(err, { id: data.id }, 'Error while processing organization caches!') + throw err + } + } else if (ids.length === 1) { + if (caches.length !== 1) { + throw new Error(`Did not find org cache for id: ${ids[0]}`) + } + // no need to merge datapoints just extract identity into organizationCacheIdentities + const data = caches[0] + try { + await db.tx(async (tx) => { + await createOrgCacheIdentity(tx, dbInstance, data.id, [ + { name: data.oldName, website: data.oldWebsite }, + ]) + }) + } catch (err) { + log.error(err, { id: data.id }, 'Error while processing organization cache!') + throw err + } + } else { + throw new Error(`No ids found!`) + } +} + +setImmediate(async () => { + const db = await getDbConnection(DB_CONFIG()) + const dbInstance = getDbInstance() + + let results = await getOrganizationCachesToMigrate(db) + + let count = 0 + while (results.length > 0) { + for (const result of results) { + await processOrgCache(db, dbInstance, result) + count++ + log.info({ count }, `Processed org cache!`) + } + + results = await getOrganizationCachesToMigrate(db) + } + + process.exit(0) +}) diff --git a/services/apps/data_sink_worker/src/repo/organization.data.ts b/services/apps/data_sink_worker/src/repo/organization.data.ts index d209103917..ad274a27f2 100644 --- a/services/apps/data_sink_worker/src/repo/organization.data.ts +++ b/services/apps/data_sink_worker/src/repo/organization.data.ts @@ -49,7 +49,6 @@ export interface IDbInsertOrganizationData { export interface IDbInsertOrganizationCacheData { name: string - displayName?: string url: string | null description: string | null emails: string[] | null @@ -188,9 +187,28 @@ export function getUpdateOrganizationColumnSet(instance: DbInstance): DbColumnSe return updateOrganizationColumnSet } -export interface IDbCacheOrganization extends IDbOrganization { +export interface IDbCacheOrganization { + id: string + names: string[] + url: string | null + description: string | null + emails: string[] | null + logo: string | null + tags: string[] | null + github: IOrganizationSocial | null + twitter: IOrganizationSocial | null + linkedin: IOrganizationSocial | null + crunchbase: IOrganizationSocial | null + employees: number | null + location: string | null + website: string | null + type: string | null + size: string | null + headline: string | null + industry: string | null + founded: string | null + attributes: IAttributes | null enriched: boolean - name: string } let insertCacheOrganizationColumnSet: DbColumnSet @@ -200,7 +218,6 @@ export function getInsertCacheOrganizationColumnSet(instance: DbInstance): DbCol insertCacheOrganizationColumnSet = new instance.helpers.ColumnSet( [ 'id', - 'name', 'url', 'description', 'emails', @@ -212,7 +229,6 @@ export function getInsertCacheOrganizationColumnSet(instance: DbInstance): DbCol 'crunchbase', 'employees', 'location', - 'website', 'type', 'size', 'headline', @@ -239,7 +255,6 @@ export function getUpdateCacheOrganizationColumnSet(instance: DbInstance): DbCol updateCacheOrganizationColumnSet = new instance.helpers.ColumnSet( [ - 'name', 'url', 'description', 'emails', @@ -251,7 +266,6 @@ export function getUpdateCacheOrganizationColumnSet(instance: DbInstance): DbCol 'crunchbase', 'employees', 'location', - 'website', 'type', 'size', 'headline', diff --git a/services/apps/data_sink_worker/src/repo/organization.repo.ts b/services/apps/data_sink_worker/src/repo/organization.repo.ts index a0edcff222..435a1672d4 100644 --- a/services/apps/data_sink_worker/src/repo/organization.repo.ts +++ b/services/apps/data_sink_worker/src/repo/organization.repo.ts @@ -1,5 +1,12 @@ +import { generateUUIDv1 } from '@crowd/common' import { DbColumnSet, DbStore, RepositoryBase } from '@crowd/database' import { Logger } from '@crowd/logging' +import { + IOrganization, + IOrganizationIdSource, + IOrganizationIdentity, + SyncStatus, +} from '@crowd/types' import { IDbCacheOrganization, IDbInsertOrganizationCacheData, @@ -9,64 +16,86 @@ import { IDbUpdateOrganizationData, getInsertCacheOrganizationColumnSet, getInsertOrganizationColumnSet, - getUpdateCacheOrganizationColumnSet, - getUpdateOrganizationColumnSet, } from './organization.data' -import { generateUUIDv1 } from '@crowd/common' -import { - IOrganizationIdentity, - SyncStatus, - IOrganization, - IOrganizationIdSource, -} from '@crowd/types' export class OrganizationRepository extends RepositoryBase { private readonly insertCacheOrganizationColumnSet: DbColumnSet - private readonly updateCacheOrganizationColumnSet: DbColumnSet private readonly insertOrganizationColumnSet: DbColumnSet - private readonly updateOrganizationColumnSet: DbColumnSet constructor(dbStore: DbStore, parentLog: Logger) { super(dbStore, parentLog) this.insertCacheOrganizationColumnSet = getInsertCacheOrganizationColumnSet(this.dbInstance) - this.updateCacheOrganizationColumnSet = getUpdateCacheOrganizationColumnSet(this.dbInstance) - this.insertOrganizationColumnSet = getInsertOrganizationColumnSet(this.dbInstance) - this.updateOrganizationColumnSet = getUpdateOrganizationColumnSet(this.dbInstance) + } + + private static findCacheQuery = ` + with identities as ( + select oci.id, + array_agg(oci.name) as names, + max(oci.website) as website + from "organizationCacheIdentities" oci + group by oci.id + ) + select oc.id, + oc.url, + oc.description, + oc.emails, + oc.logo, + oc.tags, + oc.github, + oc.twitter, + oc.linkedin, + oc.crunchbase, + oc.employees, + oc.location, + oc.type, + oc.size, + oc.headline, + oc.industry, + oc.founded, + i.names, + i.website + from "organizationCacheIdentities" oci + inner join "organizationCaches" oc on oci.id = oc.id + inner join identities i on oci.id = i.id + ` + + public async findCacheByWebsite(website: string): Promise { + // limit 1 because multiple rows can have the same website in organizationCacheIdentities + const result = await this.db().oneOrNone( + `${OrganizationRepository.findCacheQuery} where oci.website = $(website) limit 1`, + { website }, + ) + + return result } public async findCacheByName(name: string): Promise { + // limit is not needed since only 1 row can have the same name in organizationCacheIdentities const result = await this.db().oneOrNone( - ` - select id, - name, - url, - description, - emails, - logo, - tags, - github, - twitter, - linkedin, - crunchbase, - employees, - location, - website, - type, - size, - headline, - industry, - founded - from "organizationCaches" - where name = $(name);`, + `${OrganizationRepository.findCacheQuery} where oci.name = $(name)`, { name }, ) return result } + public async linkCacheAndOrganization(cacheId: string, organizationId: string): Promise { + await this.db().none( + ` + insert into "organizationCacheLinks"("organizationCacheId", "organizationId") + values ($(cacheId), $(organizationId)) + on conflict do nothing; + `, + { + cacheId, + organizationId, + }, + ) + } + public async insertCache(data: IDbInsertOrganizationCacheData): Promise { const id = generateUUIDv1() const ts = new Date() @@ -82,14 +111,26 @@ export class OrganizationRepository extends RepositoryBase, + nameToCreateIdentity?: string, ): Promise { - const keys = Object.keys(data) + const keys = Object.keys(data).filter((k) => k !== 'website' && k !== 'name') keys.push('updatedAt') // construct custom column set const dynamicColumnSet = new this.dbInstance.helpers.ColumnSet(keys, { @@ -109,9 +150,32 @@ export class OrganizationRepository extends RepositoryBase 0) { - await this.repo.updateCache(cached.id, updateData) + await this.repo.updateCache( + cached.id, + updateData, + createCacheIdentity ? primaryIdentity.name : undefined, + ) cached = { ...cached, ...updateData } // Update the cached data with the new data } } else { @@ -125,7 +141,7 @@ export class OrganizationService extends LoggerBase { enriched: false, ...insertData, attributes: {}, - weakIdentities: data.weakIdentities, + names: [primaryIdentity.name], } } @@ -206,7 +222,7 @@ export class OrganizationService extends LoggerBase { await this.checkForStrongWeakIdentities(txRepo, tenantId, data) const payload = { - displayName: cached.name, + displayName: primaryIdentity.name, description: cached.description, emails: cached.emails, logo: cached.logo, @@ -224,7 +240,7 @@ export class OrganizationService extends LoggerBase { industry: cached.industry, founded: cached.founded, attributes, - weakIdentities: cached.weakIdentities, + weakIdentities: data.weakIdentities, } this.log.trace({ payload }, `Creating new organization!`) @@ -255,6 +271,8 @@ export class OrganizationService extends LoggerBase { } } + await txRepo.linkCacheAndOrganization(cached.id, id) + return id }) diff --git a/services/apps/premium/members_enrichment_worker/package-lock.json b/services/apps/premium/members_enrichment_worker/package-lock.json deleted file mode 100644 index 4dbbc56269..0000000000 --- a/services/apps/premium/members_enrichment_worker/package-lock.json +++ /dev/null @@ -1,5291 +0,0 @@ -{ - "name": "@crowd/members-enrichment-worker", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "@crowd/members-enrichment-worker", - "version": "1.0.0", - "dependencies": { - "@crowd/archetype-standard": "file:../../archetypes/standard", - "@crowd/archetype-worker": "file:../../archetypes/worker", - "@crowd/common": "file:../../libs/common", - "@crowd/feature-flags": "file:../../libs/feature-flags", - "@crowd/types": "file:../../libs/types", - "@temporalio/activity": "~1.8.6", - "@temporalio/client": "~1.8.6", - "@temporalio/workflow": "~1.8.6", - "axios": "~1.6.2", - "lodash": "~4.17.21", - "moment": "~2.29.4", - "ts-node": "^10.9.1", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.2.2" - }, - "devDependencies": { - "@types/node": "^20.8.2", - "@types/uuid": "~9.0.6", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", - "eslint": "^8.50.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.0", - "nodemon": "^3.0.1", - "prettier": "^3.0.3" - } - }, - "../../archetypes/standard": { - "name": "@crowd/archetype-standard", - "version": "1.0.0", - "dependencies": { - "@crowd/feature-flags": "file:../../libs/feature-flags", - "@crowd/integrations": "file:../../libs/integrations", - "@crowd/logging": "file:../../libs/logging", - "@crowd/redis": "file:../../libs/redis", - "@crowd/temporal": "file:../../libs/temporal", - "@crowd/tracing": "file:../../libs/tracing", - "@crowd/types": "file:../../libs/types", - "@temporalio/client": "~1.8.6", - "config": "^3.3.9", - "kafkajs": "~2.2.4", - "ts-node": "^10.9.1", - "tsconfig-paths": "^4.2.0" - }, - "devDependencies": { - "@types/config": "^3.3.1", - "@types/node": "^20.8.2", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", - "eslint": "^8.50.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.0", - "prettier": "^3.0.3", - "typescript": "^5.2.2" - } - }, - "../../archetypes/worker": { - "name": "@crowd/archetype-worker", - "version": "1.0.0", - "dependencies": { - "@crowd/archetype-standard": "file:../../archetypes/standard", - "@crowd/database": "file:../../libs/database", - "@crowd/temporal": "file:../../libs/temporal", - "@temporalio/client": "~1.8.6", - "@temporalio/worker": "~1.8.6", - "@temporalio/workflow": "~1.8.6", - "config": "^3.3.9", - "ts-node": "^10.9.1", - "tsconfig-paths": "^4.2.0" - }, - "devDependencies": { - "@types/config": "^3.3.1", - "@types/node": "^20.8.2", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", - "eslint": "^8.50.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.0", - "prettier": "^3.0.3", - "typescript": "^5.2.2" - } - }, - "../../libs/common": { - "name": "@crowd/common", - "version": "1.0.0", - "dependencies": { - "@crowd/logging": "file:../logging", - "@crowd/types": "file:../types", - "lodash.get": "~4.4.2", - "psl": "^1.9.0", - "uuid": "^9.0.0" - }, - "devDependencies": { - "@types/node": "^18.16.3", - "@typescript-eslint/eslint-plugin": "^5.59.2", - "@typescript-eslint/parser": "^5.59.2", - "eslint": "^8.39.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "^5.0.4" - } - }, - "../../libs/feature-flags": { - "name": "@crowd/feature-flags", - "version": "1.0.0", - "dependencies": { - "@crowd/common": "file:../common", - "@crowd/logging": "file:../logging", - "@crowd/redis": "file:../redis", - "@crowd/types": "file:../types", - "unleash-client": "^3.18.1" - }, - "devDependencies": { - "@types/node": "^18.16.3", - "@typescript-eslint/eslint-plugin": "^5.59.2", - "@typescript-eslint/parser": "^5.59.2", - "eslint": "^8.39.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "^5.0.4" - } - }, - "../../libs/types": { - "name": "@crowd/types", - "version": "1.0.0", - "devDependencies": { - "@types/node": "^18.16.3", - "@typescript-eslint/eslint-plugin": "^5.59.2", - "@typescript-eslint/parser": "^5.59.2", - "eslint": "^8.39.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "^5.0.4" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@crowd/archetype-standard": { - "resolved": "../../archetypes/standard", - "link": true - }, - "node_modules/@crowd/archetype-worker": { - "resolved": "../../archetypes/worker", - "link": true - }, - "node_modules/@crowd/common": { - "resolved": "../../libs/common", - "link": true - }, - "node_modules/@crowd/feature-flags": { - "resolved": "../../libs/feature-flags", - "link": true - }, - "node_modules/@crowd/types": { - "resolved": "../../libs/types", - "link": true - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", - "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@grpc/grpc-js": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.3.tgz", - "integrity": "sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog==", - "dependencies": { - "@grpc/proto-loader": "^0.7.0", - "@types/node": ">=12.12.47" - }, - "engines": { - "node": "^8.13.0 || >=10.10.0" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", - "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.4", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@opentelemetry/api": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", - "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@pkgr/utils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "node_modules/@temporalio/activity": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@temporalio/activity/-/activity-1.8.6.tgz", - "integrity": "sha512-xTPIWIR1mL51Ub45nWpix+ATzJz/8ZAp0cCp3qw+xnKD8pwd4z9L0b30lr3co5kaPlWhSesdhypQ7VwWEf7ZHA==", - "dependencies": { - "@temporalio/common": "1.8.6", - "abort-controller": "^3.0.0" - } - }, - "node_modules/@temporalio/client": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@temporalio/client/-/client-1.8.6.tgz", - "integrity": "sha512-lcDy9YeNrHU1thGpfsvxxHOLj8VKEbEthkAWQfyZghvvjQ3K7NHS7qRzYfB9ZbYnaus+ZiodSqnKTyqW9gsJ6A==", - "dependencies": { - "@grpc/grpc-js": "~1.7.3", - "@temporalio/common": "1.8.6", - "@temporalio/proto": "1.8.6", - "abort-controller": "^3.0.0", - "long": "^5.2.0", - "uuid": "^8.3.2" - } - }, - "node_modules/@temporalio/common": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@temporalio/common/-/common-1.8.6.tgz", - "integrity": "sha512-7Cu1ymGkgklJM5xoYgJbppM/K/Dmq6Lb9bQnJiSvp8sb/RxXz6DQbiWypl9iDmaocUyFzDDaB/brA6abIq9xOQ==", - "dependencies": { - "@opentelemetry/api": "^1.4.1", - "@temporalio/proto": "1.8.6", - "long": "^5.2.0", - "ms": "^3.0.0-canary.1", - "proto3-json-serializer": "^1.0.3", - "protobufjs": "^7.2.5" - } - }, - "node_modules/@temporalio/proto": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@temporalio/proto/-/proto-1.8.6.tgz", - "integrity": "sha512-32ADOGSGBRinikP/XRTmyPXlIRaWjg5oAn64zj5NJRESrQy6ifpXb8bYVoPK01K9NIvcCL8FewCCWRKxxNvKZg==", - "dependencies": { - "long": "^5.2.0", - "protobufjs": "^7.2.5" - } - }, - "node_modules/@temporalio/workflow": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@temporalio/workflow/-/workflow-1.8.6.tgz", - "integrity": "sha512-ltVIqjNyJ3HvkhdA8RvcID6ylKZK8Fggw4mOSNnpPXkhGiCxA0oXuidaZK+MW9lTJzbm5EDiOvt3YctchCWK0Q==", - "dependencies": { - "@temporalio/common": "1.8.6", - "@temporalio/proto": "1.8.6" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.9.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.2.tgz", - "integrity": "sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/semver": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", - "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", - "dev": true - }, - "node_modules/@types/uuid": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", - "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz", - "integrity": "sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.11.0", - "@typescript-eslint/type-utils": "6.11.0", - "@typescript-eslint/utils": "6.11.0", - "@typescript-eslint/visitor-keys": "6.11.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.11.0.tgz", - "integrity": "sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.11.0", - "@typescript-eslint/types": "6.11.0", - "@typescript-eslint/typescript-estree": "6.11.0", - "@typescript-eslint/visitor-keys": "6.11.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz", - "integrity": "sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.11.0", - "@typescript-eslint/visitor-keys": "6.11.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.11.0.tgz", - "integrity": "sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.11.0", - "@typescript-eslint/utils": "6.11.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.11.0.tgz", - "integrity": "sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.11.0.tgz", - "integrity": "sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.11.0", - "@typescript-eslint/visitor-keys": "6.11.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.11.0.tgz", - "integrity": "sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.11.0", - "@typescript-eslint/types": "6.11.0", - "@typescript-eslint/typescript-estree": "6.11.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.11.0.tgz", - "integrity": "sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.11.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", - "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.44" - }, - "engines": { - "node": ">= 5.10.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "dev": true, - "dependencies": { - "run-applescript": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dev": true, - "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", - "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.54.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "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.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", - "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", - "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "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" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "3.0.0-canary.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", - "integrity": "sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==", - "engines": { - "node": ">=12.13" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/nodemon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", - "integrity": "sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==", - "dev": true, - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^3.2.7", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/nodemon/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/nodemon/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/nodemon/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "dev": true, - "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", - "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/proto3-json-serializer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.1.tgz", - "integrity": "sha512-AwAuY4g9nxx0u52DnSMkqqgyLHaW/XaPLtaAo3y/ZCfeaQB/g4YDH4kb8Wc/mWzWvu0YjOznVnfn373MVZZrgw==", - "dependencies": { - "protobufjs": "^7.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/protobufjs": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", - "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-applescript/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "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" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/run-applescript/node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/run-applescript/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-applescript/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/run-applescript/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/run-applescript/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-applescript/node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", - "dev": true, - "dependencies": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "dependencies": { - "nopt": "~1.0.10" - }, - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", - "dev": true, - "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, - "@crowd/archetype-standard": { - "version": "file:../../archetypes/standard", - "requires": { - "@crowd/feature-flags": "file:../../libs/feature-flags", - "@crowd/integrations": "file:../../libs/integrations", - "@crowd/logging": "file:../../libs/logging", - "@crowd/redis": "file:../../libs/redis", - "@crowd/temporal": "file:../../libs/temporal", - "@crowd/tracing": "file:../../libs/tracing", - "@crowd/types": "file:../../libs/types", - "@temporalio/client": "~1.8.6", - "@types/config": "^3.3.1", - "@types/node": "^20.8.2", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", - "config": "^3.3.9", - "eslint": "^8.50.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.0", - "kafkajs": "~2.2.4", - "prettier": "^3.0.3", - "ts-node": "^10.9.1", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.2.2" - } - }, - "@crowd/archetype-worker": { - "version": "file:../../archetypes/worker", - "requires": { - "@crowd/archetype-standard": "file:../standard", - "@crowd/database": "file:../../libs/database", - "@crowd/temporal": "file:../../libs/temporal", - "@temporalio/client": "~1.8.6", - "@temporalio/worker": "~1.8.6", - "@temporalio/workflow": "~1.8.6", - "@types/config": "^3.3.1", - "@types/node": "^20.8.2", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", - "config": "^3.3.9", - "eslint": "^8.50.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.0", - "prettier": "^3.0.3", - "ts-node": "^10.9.1", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.2.2" - } - }, - "@crowd/common": { - "version": "file:../../libs/common", - "requires": { - "@crowd/logging": "file:../logging", - "@crowd/types": "file:../types", - "@types/node": "^18.16.3", - "@typescript-eslint/eslint-plugin": "^5.59.2", - "@typescript-eslint/parser": "^5.59.2", - "eslint": "^8.39.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^4.2.1", - "lodash.get": "~4.4.2", - "prettier": "^2.8.8", - "psl": "^1.9.0", - "typescript": "^5.0.4", - "uuid": "^9.0.0" - } - }, - "@crowd/feature-flags": { - "version": "file:../../libs/feature-flags", - "requires": { - "@crowd/common": "file:../common", - "@crowd/logging": "file:../logging", - "@crowd/redis": "file:../redis", - "@crowd/types": "file:../types", - "@types/node": "^18.16.3", - "@typescript-eslint/eslint-plugin": "^5.59.2", - "@typescript-eslint/parser": "^5.59.2", - "eslint": "^8.39.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "^5.0.4", - "unleash-client": "^3.18.1" - } - }, - "@crowd/types": { - "version": "file:../../libs/types", - "requires": { - "@types/node": "^18.16.3", - "@typescript-eslint/eslint-plugin": "^5.59.2", - "@typescript-eslint/parser": "^5.59.2", - "eslint": "^8.39.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "^5.0.4" - } - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - } - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } - }, - "@eslint/js": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", - "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", - "dev": true - }, - "@grpc/grpc-js": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.3.tgz", - "integrity": "sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog==", - "requires": { - "@grpc/proto-loader": "^0.7.0", - "@types/node": ">=12.12.47" - } - }, - "@grpc/proto-loader": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", - "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", - "requires": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.4", - "yargs": "^17.7.2" - } - }, - "@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true - }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@opentelemetry/api": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", - "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==" - }, - "@pkgr/utils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" - } - }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "requires": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "@temporalio/activity": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@temporalio/activity/-/activity-1.8.6.tgz", - "integrity": "sha512-xTPIWIR1mL51Ub45nWpix+ATzJz/8ZAp0cCp3qw+xnKD8pwd4z9L0b30lr3co5kaPlWhSesdhypQ7VwWEf7ZHA==", - "requires": { - "@temporalio/common": "1.8.6", - "abort-controller": "^3.0.0" - } - }, - "@temporalio/client": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@temporalio/client/-/client-1.8.6.tgz", - "integrity": "sha512-lcDy9YeNrHU1thGpfsvxxHOLj8VKEbEthkAWQfyZghvvjQ3K7NHS7qRzYfB9ZbYnaus+ZiodSqnKTyqW9gsJ6A==", - "requires": { - "@grpc/grpc-js": "~1.7.3", - "@temporalio/common": "1.8.6", - "@temporalio/proto": "1.8.6", - "abort-controller": "^3.0.0", - "long": "^5.2.0", - "uuid": "^8.3.2" - } - }, - "@temporalio/common": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@temporalio/common/-/common-1.8.6.tgz", - "integrity": "sha512-7Cu1ymGkgklJM5xoYgJbppM/K/Dmq6Lb9bQnJiSvp8sb/RxXz6DQbiWypl9iDmaocUyFzDDaB/brA6abIq9xOQ==", - "requires": { - "@opentelemetry/api": "^1.4.1", - "@temporalio/proto": "1.8.6", - "long": "^5.2.0", - "ms": "^3.0.0-canary.1", - "proto3-json-serializer": "^1.0.3", - "protobufjs": "^7.2.5" - } - }, - "@temporalio/proto": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@temporalio/proto/-/proto-1.8.6.tgz", - "integrity": "sha512-32ADOGSGBRinikP/XRTmyPXlIRaWjg5oAn64zj5NJRESrQy6ifpXb8bYVoPK01K9NIvcCL8FewCCWRKxxNvKZg==", - "requires": { - "long": "^5.2.0", - "protobufjs": "^7.2.5" - } - }, - "@temporalio/workflow": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@temporalio/workflow/-/workflow-1.8.6.tgz", - "integrity": "sha512-ltVIqjNyJ3HvkhdA8RvcID6ylKZK8Fggw4mOSNnpPXkhGiCxA0oXuidaZK+MW9lTJzbm5EDiOvt3YctchCWK0Q==", - "requires": { - "@temporalio/common": "1.8.6", - "@temporalio/proto": "1.8.6" - } - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" - }, - "@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" - }, - "@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "@types/node": { - "version": "20.9.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.2.tgz", - "integrity": "sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg==", - "requires": { - "undici-types": "~5.26.4" - } - }, - "@types/semver": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", - "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", - "dev": true - }, - "@types/uuid": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", - "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz", - "integrity": "sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.11.0", - "@typescript-eslint/type-utils": "6.11.0", - "@typescript-eslint/utils": "6.11.0", - "@typescript-eslint/visitor-keys": "6.11.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/parser": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.11.0.tgz", - "integrity": "sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "6.11.0", - "@typescript-eslint/types": "6.11.0", - "@typescript-eslint/typescript-estree": "6.11.0", - "@typescript-eslint/visitor-keys": "6.11.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz", - "integrity": "sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.11.0", - "@typescript-eslint/visitor-keys": "6.11.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.11.0.tgz", - "integrity": "sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "6.11.0", - "@typescript-eslint/utils": "6.11.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/types": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.11.0.tgz", - "integrity": "sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.11.0.tgz", - "integrity": "sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.11.0", - "@typescript-eslint/visitor-keys": "6.11.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/utils": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.11.0.tgz", - "integrity": "sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.11.0", - "@typescript-eslint/types": "6.11.0", - "@typescript-eslint/typescript-estree": "6.11.0", - "semver": "^7.5.4" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.11.0.tgz", - "integrity": "sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.11.0", - "eslint-visitor-keys": "^3.4.1" - } - }, - "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, - "acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==" - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", - "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==" - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", - "requires": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "requires": { - "big-integer": "^1.6.44" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "dev": true, - "requires": { - "run-applescript": "^5.0.0" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dev": true, - "requires": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - } - }, - "default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "requires": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - } - }, - "define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", - "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.54.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "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.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - } - }, - "eslint-config-prettier": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", - "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", - "dev": true, - "requires": {} - }, - "eslint-plugin-prettier": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", - "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" - } - }, - "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true - }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true - }, - "fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "requires": { - "@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" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "requires": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true - }, - "ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true - }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "requires": { - "is-docker": "^3.0.0" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - }, - "dependencies": { - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - } - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" - }, - "keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "requires": { - "json-buffer": "3.0.1" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" - }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, - "ms": { - "version": "3.0.0-canary.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-3.0.0-canary.1.tgz", - "integrity": "sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "nodemon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", - "integrity": "sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==", - "dev": true, - "requires": { - "chokidar": "^3.5.2", - "debug": "^3.2.7", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "requires": { - "path-key": "^4.0.0" - }, - "dependencies": { - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true - } - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "dev": true, - "requires": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - } - }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", - "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "proto3-json-serializer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.1.1.tgz", - "integrity": "sha512-AwAuY4g9nxx0u52DnSMkqqgyLHaW/XaPLtaAo3y/ZCfeaQB/g4YDH4kb8Wc/mWzWvu0YjOznVnfn373MVZZrgw==", - "requires": { - "protobufjs": "^7.0.0" - } - }, - "protobufjs": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", - "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dev": true, - "requires": { - "execa": "^5.0.0" - }, - "dependencies": { - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "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" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - } - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "requires": { - "semver": "^7.5.3" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" - }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", - "dev": true, - "requires": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "requires": { - "nopt": "~1.0.10" - } - }, - "ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", - "dev": true, - "requires": {} - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "requires": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==" - }, - "undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } - } -} diff --git a/services/apps/premium/organizations_enrichment_worker/.eslintrc.cjs b/services/apps/premium/organizations_enrichment_worker/.eslintrc.cjs new file mode 100644 index 0000000000..25ba4e0d66 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + parser: '@typescript-eslint/parser', + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + plugins: ['@typescript-eslint', 'prettier'], + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + env: { + es6: true, + node: true, + }, + rules: { + 'prettier/prettier': 'error', + '@typescript-eslint/explicit-module-boundary-types': 'off', + }, +} diff --git a/services/apps/premium/organizations_enrichment_worker/.prettierignore b/services/apps/premium/organizations_enrichment_worker/.prettierignore new file mode 100644 index 0000000000..2b24877309 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/.prettierignore @@ -0,0 +1,3 @@ +dist/ +.eslintrc.cjs +.prettierrc.cjs diff --git a/services/apps/premium/organizations_enrichment_worker/.prettierrc.cjs b/services/apps/premium/organizations_enrichment_worker/.prettierrc.cjs new file mode 100644 index 0000000000..a8ca459839 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/.prettierrc.cjs @@ -0,0 +1,7 @@ +module.exports = { + singleQuote: true, + arrowParens: 'always', + printWidth: 100, + trailingComma: 'all', + semi: false, +} diff --git a/services/apps/premium/organizations_enrichment_worker/package.json b/services/apps/premium/organizations_enrichment_worker/package.json new file mode 100644 index 0000000000..262e0bb9e2 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/package.json @@ -0,0 +1,45 @@ +{ + "name": "@crowd/organizations-enrichment-worker", + "version": "1.0.0", + "private": true, + "scripts": { + "start": "CROWD_TEMPORAL_TASKQUEUE=organizations-enrichment SERVICE=organizations-enrichment-worker TS_NODE_TRANSPILE_ONLY=true node -r tsconfig-paths/register -r ts-node/register src/main.ts", + "start:debug:local": "set -a && . ../../../../backend/.env.dist.local && . ../../../../backend/.env.override.local && set +a && CROWD_TEMPORAL_TASKQUEUE=organizations-enrichment SERVICE=organizations-enrichment-worker TS_NODE_TRANSPILE_ONLY=true LOG_LEVEL=trace node --inspect=0.0.0.0:9232 -r tsconfig-paths/register -r ts-node/register src/main.ts", + "start:debug": "CROWD_TEMPORAL_TASKQUEUE=organizations-enrichment SERVICE=organizations-enrichment-worker TS_NODE_TRANSPILE_ONLY=true LOG_LEVEL=trace node --inspect=0.0.0.0:9232 -r tsconfig-paths/register -r ts-node/register src/main.ts", + "dev:local": "./node_modules/.bin/nodemon --watch src --watch ../../../libs --ext ts --exec pnpm run start:debug:local", + "dev": "./node_modules/.bin/nodemon --watch src --watch ../../../libs --ext ts --exec pnpm run start:debug", + "lint": "./node_modules/.bin/eslint --ext .ts src --max-warnings=0", + "format": "./node_modules/.bin/prettier --write \"src/**/*.ts\"", + "format-check": "./node_modules/.bin/prettier --check .", + "tsc-check": "./node_modules/.bin/tsc --noEmit" + }, + "dependencies": { + "@crowd/archetype-standard": "file:../../../archetypes/standard", + "@crowd/archetype-worker": "file:../../../archetypes/worker", + "@crowd/common": "file:../../../libs/common", + "@crowd/database": "file:../../../libs/database", + "@crowd/logging": "file:../../../libs/logging", + "@crowd/redis": "file:../../../libs/redis", + "@crowd/feature-flags": "file:../../../libs/feature-flags", + "@crowd/common_services": "file:../../../libs/common_services", + "@crowd/opensearch": "file:../../../libs/opensearch", + "@crowd/types": "file:../../../libs/types", + "@temporalio/client": "~1.8.6", + "@temporalio/workflow": "~1.8.6", + "peopledatalabs": "~6.1.5", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.2.2" + }, + "devDependencies": { + "@types/node": "^20.8.2", + "@types/uuid": "~9.0.6", + "@typescript-eslint/eslint-plugin": "^6.7.4", + "@typescript-eslint/parser": "^6.7.4", + "eslint": "^8.50.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "nodemon": "^3.0.1", + "prettier": "^3.0.3" + } +} diff --git a/services/apps/premium/organizations_enrichment_worker/src/activities.ts b/services/apps/premium/organizations_enrichment_worker/src/activities.ts new file mode 100644 index 0000000000..dc13762485 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/src/activities.ts @@ -0,0 +1,19 @@ +import { + getTenantCredits, + incrementTenantCredits, + getTenantOrganizationsForEnrichment, + tryEnrichOrganization, + getApplicableTenants, + hasTenantOrganizationEnrichmentEnabled, + syncToOpensearch, +} from './activities/organizationEnrichment' + +export { + getApplicableTenants, + getTenantCredits, + incrementTenantCredits, + getTenantOrganizationsForEnrichment, + tryEnrichOrganization, + hasTenantOrganizationEnrichmentEnabled, + syncToOpensearch, +} diff --git a/services/apps/premium/organizations_enrichment_worker/src/activities/organizationEnrichment.ts b/services/apps/premium/organizations_enrichment_worker/src/activities/organizationEnrichment.ts new file mode 100644 index 0000000000..e9d5a9b933 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/src/activities/organizationEnrichment.ts @@ -0,0 +1,496 @@ +import { distinct, distinctBy, getSecondsTillEndOfMonth, renameKeys } from '@crowd/common' +import { Logger, getChildLogger } from '@crowd/logging' +import { RedisCache } from '@crowd/redis' +import { + FeatureFlag, + FeatureFlagRedisKey, + IEnrichableOrganization, + PLAN_LIMITS, + PlatformType, + TenantPlans, +} from '@crowd/types' +import { EnrichmentParams, IEnrichmentResponse } from '@crowd/types/premium' +import { svc } from '../main' +import { + IOrganizationData, + IOrganizationIdentity, + OrganizationRepository, +} from '../repos/organization.repo' +import { IPremiumTenantInfo, TenantRepository } from '../repos/tenant.repo' +import { isFeatureEnabled } from '@crowd/feature-flags' +import { OrganizationSyncService } from '@crowd/opensearch' + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +const syncOrganizations = new OrganizationSyncService( + svc.postgres.writer, + svc.opensearch, + svc.log, + { + edition: process.env['CROWD_EDITION'], + }, +) + +export async function syncToOpensearch(organizationId: string): Promise { + const log = getChildLogger(syncToOpensearch.name, svc.log, { + organizationId, + }) + + try { + await syncOrganizations.syncOrganizations([organizationId]) + } catch (err) { + log.error(err, 'Error while syncing organization to OpenSearch!') + throw new Error(err) + } + + return null +} + +export async function getApplicableTenants( + page: number, + lastId?: string, +): Promise { + const log = getChildLogger(getApplicableTenants.name, svc.log) + + log.debug('Getting applicable tenants!') + const repo = new TenantRepository(svc.postgres.reader, svc.log) + const tenants = await repo.getPremiumTenants(page, lastId) + + log.debug(`Got ${tenants.length} applicable tenants!`) + + return tenants +} + +export async function hasTenantOrganizationEnrichmentEnabled(tenantId: string): Promise { + const log = getChildLogger(hasTenantOrganizationEnrichmentEnabled.name, svc.log, { + tenantId, + }) + + const result = await isFeatureEnabled( + FeatureFlag.TEMPORAL_ORGANIZATION_ENRICHMENT, + async () => { + return { + tenantId, + } + }, + svc.unleash, + svc.redis, + 5 * 60, + tenantId, + ) + + log.debug({ tenantId, enabled: result }, 'Tenant organization enrichment enabled?') + return result +} + +/** + * Calculate how many credits the tenant has left for organization enrichment + * @param tenant + * @returns number of credits left for the tenant that can be used to enrich organizations + */ +export async function getTenantCredits(tenant: IPremiumTenantInfo): Promise { + const log = getChildLogger(getTenantCredits.name, svc.log, { tenantId: tenant.id }) + if (tenant.plan === TenantPlans.Growth) { + // need to check how many credits the tenant has left + const cache = new RedisCache(FeatureFlagRedisKey.ORGANIZATION_ENRICHMENT_COUNT, svc.redis, log) + const cached = await cache.get(tenant.id) + if (!cached) { + await cache.set(tenant.id, '0', getSecondsTillEndOfMonth()) + } + const usedCredits = parseInt(cached ?? '0', 10) + const remainingCredits = + PLAN_LIMITS[TenantPlans.Growth][FeatureFlag.ORGANIZATION_ENRICHMENT] - usedCredits + + log.debug({ tenantId: tenant.id }, `Tenant has ${remainingCredits} credits left.`) + return remainingCredits + } + + if ([TenantPlans.Enterprise, TenantPlans.Scale].includes(tenant.plan)) { + log.debug({ tenantId: tenant.id }, `Tenant has unlimited credits.`) + return -1 + } + + throw new Error(`Only premium tenant plans are supported - got ${tenant.plan}!`) +} + +/** + * Increment tenant organization enrichment credits by 1 + * @param tenantId + */ +export async function incrementTenantCredits(tenantId: string, plan: TenantPlans): Promise { + const log = getChildLogger(incrementTenantCredits.name, svc.log, { tenantId }) + + if (plan === TenantPlans.Growth) { + log.debug({ tenantId }, `Incrementing tenant credits.`) + const cache = new RedisCache(FeatureFlagRedisKey.ORGANIZATION_ENRICHMENT_COUNT, svc.redis, log) + + await cache.increment(tenantId, 1, getSecondsTillEndOfMonth()) + } +} + +/** + * Fetch organizations ids that need to be enriched + * This is those who have lastEnrichedAt set to null or they haven't been enriched in the last 3 months + * @param tenantId + * @param perPage + * @param lastId + * @returns + */ +export async function getTenantOrganizationsForEnrichment( + tenantId: string, + perPage: number, + lastId?: string, +): Promise { + const log = getChildLogger(getTenantOrganizationsForEnrichment.name, svc.log, { tenantId }) + const repo = new OrganizationRepository(svc.postgres.reader, svc.log) + const organizationIds = await repo.getTenantOrganizationsToEnrich(tenantId, perPage, lastId) + log.debug({ tenantId, nrOrgs: organizationIds.length }, 'Got organizations for enrichment!') + return organizationIds +} + +const ENRICHMENT_PLATFORM_PRIORITY = [ + PlatformType.GITHUB, + PlatformType.LINKEDIN, + PlatformType.TWITTER, +] + +/** + * Attempts organization enrichment. + * @param tenantId + * @param organizationId + * @returns true if organization was enriched, false otherwise + */ +export async function tryEnrichOrganization( + tenantId: string, + organizationId: string, +): Promise { + const log = getChildLogger(tryEnrichOrganization.name, svc.log, { + tenantId, + organizationId, + }) + + log.debug('Trying to enrich an organization!') + + const repo = new OrganizationRepository(svc.postgres.writer, log) + + const orgData = await repo.getOrganizationData(organizationId) + + if (!orgData) { + log.warn('Organization not found!') + return false + } + + const identityPlatforms = distinct(orgData.identities.map((i) => i.platform)) + + if (identityPlatforms.length > 0) { + const platformsForEnrichment = ENRICHMENT_PLATFORM_PRIORITY.filter((p) => + identityPlatforms.includes(p), + ) + + if (platformsForEnrichment.length > 0 || orgData.website) { + const platformToUseForEnrichment = platformsForEnrichment[0] + const identityForEnrichment = orgData.identities.find( + (i) => i.platform === platformToUseForEnrichment, + ) + + // we use website and identityForEnrichment?.name to enrich the organization + const enrichmentData = await getEnrichment( + { + website: orgData.website, + name: identityForEnrichment?.name, + }, + log, + ) + + if (enrichmentData) { + log.debug('Enrichment data found!') + const finalData = convertEnrichedDataToOrg(enrichmentData, orgData.identities) + finalData.weakIdentities = orgData.weakIdentities + await prepareIdentities(orgData.tenantId, orgData.id, finalData, repo) + + let website: string | undefined + let checkIfWebsiteIsTaken = false + if (orgData.website && finalData.website && orgData.website !== finalData.website) { + log.debug('Website changed!') + website = finalData.website + checkIfWebsiteIsTaken = true + } else if (orgData.website) { + website = orgData.website + } else if (finalData.website) { + log.debug('Website found!') + website = finalData.website + checkIfWebsiteIsTaken = true + } + + if ( + checkIfWebsiteIsTaken && + repo.anyOtherOrganizationWithTheSameWebsite(orgData.id, orgData.tenantId, orgData.website) + ) { + log.debug('Website is already taken!') + // we can't set this website in the database due to unique constraint but we can generate merge suggestions based on it + finalData.website = undefined + } + + await repo.transactionally(async (t) => { + await t.updateIdentities( + orgData.id, + orgData.tenantId, + orgData.identities, + finalData.identities, + ) + await t.updateOrganizationWithEnrichedData(orgData, finalData) + + if (website) { + log.debug({ website }, 'Generating merge suggestions!') + await t.generateMergeSuggestions(orgData.id, orgData.tenantId, website) + } + }) + + log.debug('Enrichment done!') + } else { + log.debug('No enrichment data found!') + await repo.markEnriched(organizationId) + } + + return true + } else { + log.warn('Organization does not have a website and no identities with enrichment platforms!') + } + } else { + log.warn('Organization has no activities or identities and therefore it will not be enriched!') + } + + return false +} + +async function prepareIdentities( + tenantId: string, + organizationId: string, + orgData: IOrganizationData, + repo: OrganizationRepository, +): Promise { + // find identities belonging to other orgs that match the ones in the orgData identities and weakIdentities + const allIdentities = orgData.identities.concat(orgData.weakIdentities) + let existingIdentities = await repo.findIdentities(tenantId, organizationId, allIdentities) + existingIdentities = distinctBy(existingIdentities, (i) => `${i.platform}:${i.name}`) + + const weakIdentities: IOrganizationIdentity[] = [] + const identities: IOrganizationIdentity[] = [] + + for (const identity of allIdentities) { + // check if any other org has this identity + if ( + existingIdentities.find((i) => i.platform === identity.platform && i.name === identity.name) + ) { + // this identity should be a weak one because some other org has this identity as well + weakIdentities.push(identity) + } else { + // this identity should be a strong one because no other org has this identity + identities.push(identity) + } + } + + // set it + orgData.identities = identities + orgData.weakIdentities = weakIdentities +} + +/** + * Fetch enrichment data from PDL Company API + * @param enrichmentInput - The object that contains organization enrichment attributes + * @returns the PDL company response + */ +async function getEnrichment( + { name, website, locality }: EnrichmentParams, + log: Logger, +): Promise { + const PDLJSModule = await import('peopledatalabs') + const PDLClient = new PDLJSModule.default({ + apiKey: process.env['CROWD_ORGANIZATION_ENRICHMENT_API_KEY'], + }) + let data: null | IEnrichmentResponse + try { + const payload: Partial = {} + + if (name) { + payload.name = name + } + + if (website) { + payload.website = website + } + + data = await PDLClient.company.enrichment(payload as EnrichmentParams) + + if (data.website === 'undefined.es') { + return null + } + + data.name = name + } catch (error) { + if (error.status === 404) { + log.debug({ name, website, locality }, 'PDL data not available!') + } else { + log.error(error, { name, website, locality }, 'Error while fetching PDL data!') + } + + return null + } + return data +} + +function convertEnrichedDataToOrg( + pdlData: Awaited, + existingIdentities: IOrganizationIdentity[], +): any { + let data = renameKeys(pdlData, { + summary: 'description', + employee_count_by_country: 'employeeCountByCountry', + employee_count: 'employees', + location: 'address', + tags: 'tags', + ultimate_parent: 'ultimateParent', + immediate_parent: 'immediateParent', + affiliated_profiles: 'affiliatedProfiles', + all_subsidiaries: 'allSubsidiaries', + alternative_domains: 'alternativeDomains', + alternative_names: 'alternativeNames', + average_employee_tenure: 'averageEmployeeTenure', + average_tenure_by_level: 'averageTenureByLevel', + average_tenure_by_role: 'averageTenureByRole', + direct_subsidiaries: 'directSubsidiaries', + employee_churn_rate: 'employeeChurnRate', + employee_count_by_month: 'employeeCountByMonth', + employee_growth_rate: 'employeeGrowthRate', + employee_count_by_month_by_level: 'employeeCountByMonthByLevel', + employee_count_by_month_by_role: 'employeeCountByMonthByRole', + gics_sector: 'gicsSector', + gross_additions_by_month: 'grossAdditionsByMonth', + gross_departures_by_month: 'grossDeparturesByMonth', + inferred_revenue: 'inferredRevenue', + }) + data = sanitize(data) + + data = enrichSocialNetworks(data, { + profiles: pdlData.profiles, + linkedin_id: pdlData.linkedin_id, + }) + + data.identities = existingIdentities + + if (data.github && data.github.handle) { + const identityExists = data.identities.find( + (i) => i.platform === PlatformType.GITHUB && i.name === data.github.handle, + ) + if (!identityExists) { + data.identities.push({ + name: data.github.handle, + platform: PlatformType.GITHUB, + url: data.github.url || `https://github.com/${data.github.handle}`, + }) + } + } + + if (data.twitter && data.twitter.handle) { + const identityExists = data.identities.find( + (i) => i.platform === PlatformType.TWITTER && i.name === data.twitter.handle, + ) + if (!identityExists) { + data.identities.push({ + name: data.twitter.handle, + platform: PlatformType.TWITTER, + url: data.twitter.url || `https://twitter.com/${data.twitter.handle}`, + }) + } + } + + if (data.linkedin && data.linkedin.handle) { + const identityExists = data.identities.find( + (i) => i.platform === PlatformType.LINKEDIN && i.name === data.linkedin.handle, + ) + if (!identityExists) { + data.identities.push({ + name: data.linkedin.handle, + platform: PlatformType.LINKEDIN, + url: data.linkedin.url || `https://linkedin.com/company/${data.linkedin.handle}`, + }) + } + } + + // Set displayName using the first identity or fallback to website + if (!data.displayName) { + const identity = data.identities[0] + data.displayName = identity ? identity.name : data.website + } + + return data +} + +function sanitize(data: any): any { + if (data.address) { + data.geoLocation = data.address.geo ?? null + delete data.address.geo + data.location = `${data.address.street_address ?? ''} ${data.address.address_line_2 ?? ''} ${ + data.address.name ?? '' + }` + } + if (data.employeeCountByCountry && !data.employees) { + const employees = Object.values(data.employeeCountByCountry).reduce( + (acc, size) => (acc as any) + size, + 0, + ) + Object.assign(data, { employees: employees || data.employees }) + } + if (data.description) { + let description = data.description[0].toUpperCase() + for (let char of data.description.slice(1)) { + if (description.length > 1 && description.slice(-2) === '. ') { + char = char.toUpperCase() + } + description += char + } + data.description = description + } + if (data.inferredRevenue) { + const revenueList = data.inferredRevenue + .replaceAll('$', '') + .split('-') + .map((x) => { + // billions --> millions conversion, if needed + const multiple = x.endsWith('B') ? 1000 : 1 + const value = parseInt(x, 10) * multiple + return value + }) + + data.revenueRange = { + min: revenueList[0], + max: revenueList[1], + } + } + + return data +} + +function enrichSocialNetworks( + data: IEnrichableOrganization, + socialNetworks: { + profiles: IEnrichmentResponse['profiles'] + linkedin_id: IEnrichmentResponse['linkedin_id'] + }, +): IEnrichableOrganization { + const socials = socialNetworks.profiles.reduce((acc, social) => { + const platform = social.split('.')[0] + const handle = social.split('/').splice(-1)[0] + if ( + Object.values(PlatformType).includes(platform as any) && + handle !== socialNetworks.linkedin_id + ) { + acc[platform] = { + handle, + [platform === PlatformType.TWITTER ? 'site' : 'url']: social, + } + } + return acc + }, {}) + return { ...data, ...socials } +} diff --git a/services/apps/premium/organizations_enrichment_worker/src/main.ts b/services/apps/premium/organizations_enrichment_worker/src/main.ts new file mode 100644 index 0000000000..d1e6bc4938 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/src/main.ts @@ -0,0 +1,35 @@ +import { Config } from '@crowd/archetype-standard' +import { ServiceWorker, Options } from '@crowd/archetype-worker' +import { scheduleOrganizationsEnrichment } from './schedules' + +const config: Config = { + envvars: ['CROWD_ORGANIZATION_ENRICHMENT_API_KEY'], + producer: { + enabled: false, + }, + temporal: { + enabled: true, + }, + redis: { + enabled: true, + }, +} + +const options: Options = { + postgres: { + enabled: true, + }, + opensearch: { + enabled: true, + }, +} + +export const svc = new ServiceWorker(config, options) + +setImmediate(async () => { + await svc.init() + + await scheduleOrganizationsEnrichment() + + await svc.start() +}) diff --git a/services/apps/premium/organizations_enrichment_worker/src/repos/organization.repo.ts b/services/apps/premium/organizations_enrichment_worker/src/repos/organization.repo.ts new file mode 100644 index 0000000000..7d2bc8d725 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/src/repos/organization.repo.ts @@ -0,0 +1,500 @@ +import { EDITION, singleOrDefault } from '@crowd/common' +import { DbStore, RepositoryBase } from '@crowd/database' +import { Logger } from '@crowd/logging' +import { Edition } from '@crowd/types' + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export class OrganizationRepository extends RepositoryBase { + constructor(dbStore: DbStore, parentLog: Logger) { + super(dbStore, parentLog) + } + + public async getTenantOrganizationsToEnrich( + tenantId: string, + perPage: number, + lastId?: string, + ): Promise { + const parameters: Record = { + tenantId, + } + + const conditions = [ + 'o."tenantId" = $(tenantId)', + 'o."deletedAt" is null', + `(o."lastEnrichedAt" is null or o."lastEnrichedAt" < now() - interval '3 months')`, + ] + + if (lastId) { + conditions.push('o.id > $(lastId)') + parameters.lastId = lastId + } + + if (EDITION === Edition.LFX) { + conditions.push('ad."activityCount" >= 3') + } else { + conditions.push(`ad."lastActive" > now() - interval '1 year'`) + + // this is guaranteed for now by the inner join just leaving it here for future reference + // conditions.push(`ad."activityCount" >= 1`) + } + + const query = ` + with activity_data as (select "organizationId", + count(id) as "activityCount", + max(timestamp) as "lastActive" + from activities + where "tenantId" = $(tenantId) + and "deletedAt" is null + group by "organizationId") + select o.id + from organizations o + inner join activity_data ad on ad."organizationId" = o.id + where ${conditions.join(' and ')} + order by o.id + limit ${perPage}; + ` + + const results = await this.db().any(query, parameters) + + return results.map((r) => r.id) + } + + public async getOrganizationData(organizationId: string): Promise { + return await this.db().oneOrNone( + ` + with identities as (select oi."organizationId", + json_agg(json_build_object( + 'platform', oi.platform, + 'name', oi.name, + 'url', oi.url + )) as "identities" + from "organizationIdentities" oi + where oi."organizationId" = $(organizationId) + group by oi."organizationId") + select o.id, + o.description, + o.emails, + o."phoneNumbers", + o.logo, + o.tags, + o.twitter, + o.linkedin, + o.crunchbase, + o.employees, + o."revenueRange", + o."tenantId", + o.location, + o.github, + o.website, + o."employeeCountByCountry", + o.type, + o."geoLocation", + o.size, + o.ticker, + o.headline, + o.profiles, + o.naics, + o.address, + o.industry, + o.founded, + o."displayName", + o."affiliatedProfiles", + o."allSubsidiaries", + o."alternativeDomains", + o."alternativeNames", + o."averageEmployeeTenure", + o."averageTenureByLevel", + o."averageTenureByRole", + o."directSubsidiaries", + o."employeeChurnRate", + o."employeeCountByMonth", + o."employeeGrowthRate", + o."employeeCountByMonthByLevel", + o."employeeCountByMonthByRole", + o."gicsSector", + o."grossAdditionsByMonth", + o."grossDeparturesByMonth", + o."ultimateParent", + o."immediateParent", + o."weakIdentities", + o."manuallyChangedFields", + i.identities + from organizations o + inner join identities i on o.id = i."organizationId" + where o.id = $(organizationId); + `, + { + organizationId, + }, + ) + } + + public async markEnriched(organizationId: string): Promise { + await this.db().none( + ` + update organizations + set "lastEnrichedAt" = now() + where id = $(organizationId) + `, + { + organizationId, + }, + ) + } + + public async updateIdentities( + organizationId: string, + tenantId: string, + existingIdentities: IOrganizationIdentity[], + newIdentities: IOrganizationIdentity[], + ): Promise { + // check which identities are new or updated + // according to the unique key one tenant can have only one identity per platform + const toCreate: IOrganizationIdentity[] = [] + const toUpdate: IOrganizationIdentity[] = [] + + for (const newIdentity of newIdentities) { + const existingIdentity = singleOrDefault( + existingIdentities, + (i) => i.platform === newIdentity.platform && i.name === newIdentity.name, + ) + + if (existingIdentity) { + // we are only updating the url if needed nothing else + if (existingIdentity.url !== newIdentity.url) { + toUpdate.push(newIdentity) + } + } else { + toCreate.push(newIdentity) + } + } + + const queries: string[] = [] + + if (toUpdate.length > 0) { + // generate bulk update query + const entries = toUpdate.map((i) => { + return { + name: i.name, + platform: i.platform, + organizationId, + tenantId, + } + }) + + const query = + this.dbInstance.helpers.update( + entries, + ['?organizationId', '?tenantId', '?platform', '?name', 'url'], + 'organizationIdentities', + ) + + ' where t."organizationId" = v."organizationId" and t.platform = v.platform and t.name = v.name and t."tenantId" = v."tenantId"' + + queries.push(query) + } + + if (toCreate.length > 0) { + // generate bulk insert query + const entries = toCreate.map((i) => { + return { + name: i.name, + platform: i.platform, + organizationId, + tenantId, + } + }) + + const query = + this.dbInstance.helpers.insert( + entries, + ['organizationId', 'tenantId', 'platform', 'name'], + 'organizationIdentities', + ) + ` on conflict do nothing` + + queries.push(query) + } + + if (queries.length > 0) { + await Promise.all(queries.map((q) => this.db().none(q))) + } + } + + public async findIdentities( + tenantId: string, + organizationId: string, + identities: IOrganizationIdentity[], + ): Promise { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const params: any = { + tenantId, + organizationId, + } + + const identityParams = identities + .map( + (identity) => + `(${this.dbInstance.as.text(identity.platform)}, ${this.dbInstance.as.text( + identity.name, + )})`, + ) + .join(', ') + + const results = await this.db().any( + ` + with input_identities (platform, name) as ( + values ${identityParams} + ) + select oi.url, i.platform, i.name + from "organizationIdentities" oi + inner join input_identities i on oi.platform = i.platform and oi.name = i.name + where oi."tenantId" = $(tenantId) and oi."organizationId" <> $(organizationId) +`, + params, + ) + + return results + } + + private static readonly ENRICHABLE_ORGANIZATION_FIELDS = [ + 'description', + 'emails', + 'phoneNumbers', + 'logo', + 'tags', + 'twitter', + 'linkedin', + 'crunchbase', + 'employees', + 'revenueRange', + 'location', + 'github', + 'website', + 'employeeCountByCountry', + 'type', + 'geoLocation', + 'size', + 'ticker', + 'headline', + 'profiles', + 'naics', + 'address', + 'industry', + 'founded', + 'affiliatedProfiles', + 'allSubsidiaries', + 'alternativeDomains', + 'alternativeNames', + 'averageEmployeeTenure', + 'averageTenureByLevel', + 'averageTenureByRole', + 'directSubsidiaries', + 'employeeChurnRate', + 'employeeCountByMonth', + 'employeeGrowthRate', + 'employeeCountByMonthByLevel', + 'employeeCountByMonthByRole', + 'gicsSector', + 'grossAdditionsByMonth', + 'grossDeparturesByMonth', + 'ultimateParent', + 'immediateParent', + ] + + public async updateOrganizationWithEnrichedData( + originalData: IOrganizationData, + enrichedData: any, + ): Promise { + const manuallyChangedFields = originalData.manuallyChangedFields || [] + + const toUpdate: Record = {} + + if ( + originalData.displayName === null || + (originalData.displayName.trim() === '' && enrichedData.displayName) + ) { + toUpdate.displayName = enrichedData.displayName + } + + for (const field of OrganizationRepository.ENRICHABLE_ORGANIZATION_FIELDS) { + if (manuallyChangedFields.includes(field)) { + continue + } + + if (field in enrichedData) { + const enrichedValue = enrichedData[field] + + // ignore null/undefined/empty string values + if ( + enrichedValue === null || + enrichedValue === undefined || + (typeof enrichedValue === 'string' && enrichedValue.trim() === '') + ) { + continue + } + + const existingValue = originalData[field] + + if (typeof enrichedValue === 'object') { + // compare stringified objects + if (JSON.stringify(enrichedValue) === JSON.stringify(existingValue)) { + continue + } + } else if (enrichedValue === existingValue) { + continue + } + + toUpdate[field] = enrichedValue + } + } + + const keysToUpdate = Object.keys(toUpdate) + if (keysToUpdate.length > 0) { + this.log.debug( + { organizationId: originalData.id }, + `Updating organization with enriched data! With ${keysToUpdate.length} fields`, + ) + + if (toUpdate.naics) { + toUpdate.naics = JSON.stringify(toUpdate.naics) + } + + // set lastEnrichedAt to now + keysToUpdate.push('lastEnrichedAt') + toUpdate.lastEnrichedAt = new Date() + + const query = this.dbInstance.helpers.update(toUpdate, keysToUpdate, 'organizations') + + const result = await this.db().result(`${query} where id = $(id)`, { + ...toUpdate, + id: originalData.id, + }) + + this.checkUpdateRowCount(result.rowCount, 1) + } else { + await this.markEnriched(originalData.id) + } + } + + public async anyOtherOrganizationWithTheSameWebsite( + organizationId: string, + tenantId: string, + website: string, + ): Promise { + const res = await this.db().oneOrNone( + `select 1 from organizations where + "tenantId" = $(tenantId) + and id <> $(organizationId) + and website = $(website)`, + { + tenantId, + organizationId, + website, + }, + ) + + if (res) { + return true + } + + return false + } + + public async generateMergeSuggestions( + organizationId: string, + tenantId: string, + website: string, + ): Promise { + await this.db().none( + ` + insert into "organizationToMerge"("organizationId", "toMergeId", "similarity", "createdAt", "updatedAt") + -- find all organization caches that have this website + with applicable_organization_caches as (select distinct id + from "organizationCacheIdentities" + where website = $(website)), + -- then find all organizations that are connected to that cache + -- and are in the correct tenant and are not the same as the organization we are enriching + applicable_organizations as (select o.id + from "organizationCacheLinks" ocl + inner join applicable_organization_caches aoc on ocl."organizationCacheId" = aoc.id + inner join organizations o on ocl."organizationId" = o.id + where o."tenantId" = $(tenantId) + and o.id <> $(organizationId)) + -- then again filter out the ones that are already in the toMerge and noMerge tables + -- and insert the rest into the toMerge table + select $(organizationId), ao.id, 0.9, now(), now() + from applicable_organizations ao + left join "organizationNoMerge" anm + on anm."organizationId" = $(organizationId) and anm."noMergeId" = ao.id + left join "organizationToMerge" atm + on atm."organizationId" = $(organizationId) and atm."toMergeId" = ao.id + where anm."noMergeId" is null + and atm."toMergeId" is null + -- just in case add this + on conflict do nothing; + `, + { + organizationId, + tenantId, + website, + }, + ) + } +} + +export interface IOrganizationIdentity { + platform: string + name: string + url: string | null +} + +export interface IOrganizationData { + id: string + tenantId: string + description: string | null + emails: string[] | null + phoneNumbers: string[] | null + logo: string | null + tags: string[] | null + twitter: unknown | null + linkedin: unknown | null + crunchbase: unknown | null + employees: number | null + revenueRange: unknown | null + location: string | null + github: unknown | null + website: string | null + employeeCountByCountry: unknown | null + type: string | null + geoLocation: string | null + size: string | null + ticker: string | null + headline: string | null + profiles: string[] | null + naics: unknown[] | null + address: unknown | null + industry: string | null + founded: number | null + displayName: string | null + affiliatedProfiles: string[] | null + allSubsidiaries: string[] | null + alternativeDomains: string[] | null + alternativeNames: string[] | null + averageEmployeeTenure: number | null + averageTenureByLevel: unknown | null + averageTenureByRole: unknown | null + directSubsidiaries: string[] | null + employeeChurnRate: unknown | null + employeeCountByMonth: unknown | null + employeeGrowthRate: unknown | null + employeeCountByMonthByLevel: unknown | null + employeeCountByMonthByRole: unknown | null + gicsSector: string | null + grossAdditionsByMonth: unknown | null + grossDeparturesByMonth: unknown | null + ultimateParent: string | null + immediateParent: string | null + manuallyChangedFields: string[] + identities: IOrganizationIdentity[] + weakIdentities: IOrganizationIdentity[] +} diff --git a/services/apps/premium/organizations_enrichment_worker/src/repos/tenant.repo.ts b/services/apps/premium/organizations_enrichment_worker/src/repos/tenant.repo.ts new file mode 100644 index 0000000000..d88cb4ad28 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/src/repos/tenant.repo.ts @@ -0,0 +1,31 @@ +import { TenantPlans } from '@crowd/types' +import { Logger } from '@crowd/logging' +import { DbStore, RepositoryBase } from '@crowd/database' + +export interface IPremiumTenantInfo { + id: string + plan: TenantPlans +} + +export class TenantRepository extends RepositoryBase { + constructor(dbStore: DbStore, parentLog: Logger) { + super(dbStore, parentLog) + } + + async getPremiumTenants(perPage: number, lastId?: string): Promise { + return await this.db().any( + ` + select id, + plan + from tenants + where plan in ($(plans:csv)) ${lastId ? 'and id > $(lastId)' : ''} + order by id + limit ${perPage}; + `, + { + plans: [TenantPlans.Enterprise, TenantPlans.Growth, TenantPlans.Scale], + lastId, + }, + ) + } +} diff --git a/services/apps/premium/organizations_enrichment_worker/src/schedules.ts b/services/apps/premium/organizations_enrichment_worker/src/schedules.ts new file mode 100644 index 0000000000..3d7430f82f --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/src/schedules.ts @@ -0,0 +1,3 @@ +import { scheduleOrganizationsEnrichment } from './schedules/organizationEnrichment' + +export { scheduleOrganizationsEnrichment } diff --git a/services/apps/premium/organizations_enrichment_worker/src/schedules/organizationEnrichment.ts b/services/apps/premium/organizations_enrichment_worker/src/schedules/organizationEnrichment.ts new file mode 100644 index 0000000000..e130972047 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/src/schedules/organizationEnrichment.ts @@ -0,0 +1,32 @@ +import { ScheduleAlreadyRunning, ScheduleOverlapPolicy } from '@temporalio/client' +import { svc } from '../main' +import { triggerTenantOrganizationEnrichment } from '../workflows' + +export const scheduleOrganizationsEnrichment = async () => { + try { + await svc.temporal.schedule.create({ + scheduleId: 'organizations-enrichment', + spec: { + // every hour (at minute 0) + cronExpressions: ['0 * * * *'], + }, + policies: { + overlap: ScheduleOverlapPolicy.BUFFER_ONE, + catchupWindow: '1 minute', + }, + action: { + type: 'startWorkflow', + workflowType: triggerTenantOrganizationEnrichment, + taskQueue: 'organizations-enrichment', + workflowExecutionTimeout: '5 minutes', + }, + }) + } catch (err) { + if (err instanceof ScheduleAlreadyRunning) { + svc.log.info('Schedule already registered in Temporal.') + svc.log.info('Configuration may have changed since. Please make sure they are in sync.') + } else { + throw new Error(err) + } + } +} diff --git a/services/apps/premium/organizations_enrichment_worker/src/workflows.ts b/services/apps/premium/organizations_enrichment_worker/src/workflows.ts new file mode 100644 index 0000000000..0d0a038717 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/src/workflows.ts @@ -0,0 +1,5 @@ +import { triggerTenantOrganizationEnrichment } from './workflows/triggerTenantOrganizationEnrichment' +import { enrichTenantOrganizations } from './workflows/entrichTenantOrganizations' +import { enrichOrganization } from './workflows/enrichOrganization' + +export { triggerTenantOrganizationEnrichment, enrichTenantOrganizations, enrichOrganization } diff --git a/services/apps/premium/organizations_enrichment_worker/src/workflows/enrichOrganization.ts b/services/apps/premium/organizations_enrichment_worker/src/workflows/enrichOrganization.ts new file mode 100644 index 0000000000..ab867b600c --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/src/workflows/enrichOrganization.ts @@ -0,0 +1,24 @@ +import { proxyActivities } from '@temporalio/workflow' +import * as activities from '../activities/organizationEnrichment' +import { TenantPlans } from '@crowd/types' + +const { tryEnrichOrganization, incrementTenantCredits, syncToOpensearch } = proxyActivities< + typeof activities +>({ + startToCloseTimeout: '75 seconds', +}) + +export interface IEnrichOrganizationInput { + organizationId: string + tenantId: string + plan: TenantPlans +} + +export async function enrichOrganization(input: IEnrichOrganizationInput): Promise { + const wasEnriched = await tryEnrichOrganization(input.tenantId, input.organizationId) + + if (wasEnriched) { + await syncToOpensearch(input.organizationId) + await incrementTenantCredits(input.tenantId, input.plan) + } +} diff --git a/services/apps/premium/organizations_enrichment_worker/src/workflows/entrichTenantOrganizations.ts b/services/apps/premium/organizations_enrichment_worker/src/workflows/entrichTenantOrganizations.ts new file mode 100644 index 0000000000..660a51dbd7 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/src/workflows/entrichTenantOrganizations.ts @@ -0,0 +1,80 @@ +import { IPremiumTenantInfo } from '../repos/tenant.repo' +import * as activities from '../activities/organizationEnrichment' +import { + ChildWorkflowCancellationType, + ParentClosePolicy, + proxyActivities, + startChild, +} from '@temporalio/workflow' +import { enrichOrganization } from './enrichOrganization' + +const { getTenantCredits, getTenantOrganizationsForEnrichment } = proxyActivities< + typeof activities +>({ + startToCloseTimeout: '75 seconds', +}) + +const MAX_ENRICHED_ORGANIZATIONS_PER_EXECUTION = 100 +const BATCH_SIZE = 10 + +export async function enrichTenantOrganizations(tenant: IPremiumTenantInfo): Promise { + // check how many credits the tenant has left + // this will be our limit (1 credit = 1 enriched organization) + const credits = await getTenantCredits(tenant) + + if (credits === 0) { + // we have no credits left on this tenant + return + } + + let remainingCredits = + credits === -1 + ? MAX_ENRICHED_ORGANIZATIONS_PER_EXECUTION + : Math.min(credits, MAX_ENRICHED_ORGANIZATIONS_PER_EXECUTION) + + let lastId: string | undefined + while (remainingCredits > 0) { + const batchSize = Math.min(remainingCredits, BATCH_SIZE) + // get organizations that should be enriched + const organizationIds = await getTenantOrganizationsForEnrichment(tenant.id, batchSize, lastId) + + if (organizationIds.length === 0) { + // no more organizations to enrich + return + } + + // trigger enrichment workflow + const promises = [] + for (const organizationId of organizationIds) { + promises.push( + startChild(enrichOrganization, { + workflowId: 'enrich-organization/' + organizationId, + cancellationType: ChildWorkflowCancellationType.ABANDON, + parentClosePolicy: ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON, + workflowExecutionTimeout: '15 minutes', + retry: { + backoffCoefficient: 2, + maximumAttempts: 10, + initialInterval: 2 * 1000, + maximumInterval: 30 * 1000, + }, + args: [ + { + organizationId, + tenantId: tenant.id, + plan: tenant.plan, + }, + ], + searchAttributes: { + TenantId: [tenant.id], + }, + }), + ) + remainingCredits-- + } + + await Promise.all(promises) + + lastId = organizationIds[organizationIds.length - 1] + } +} diff --git a/services/apps/premium/organizations_enrichment_worker/src/workflows/triggerTenantOrganizationEnrichment.ts b/services/apps/premium/organizations_enrichment_worker/src/workflows/triggerTenantOrganizationEnrichment.ts new file mode 100644 index 0000000000..f47b123cb0 --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/src/workflows/triggerTenantOrganizationEnrichment.ts @@ -0,0 +1,52 @@ +import { + ChildWorkflowCancellationType, + ParentClosePolicy, + proxyActivities, + startChild, +} from '@temporalio/workflow' +import * as activities from '../activities/organizationEnrichment' +import { enrichTenantOrganizations } from './entrichTenantOrganizations' + +const { getApplicableTenants, hasTenantOrganizationEnrichmentEnabled } = proxyActivities< + typeof activities +>({ + startToCloseTimeout: '75 seconds', +}) + +export async function triggerTenantOrganizationEnrichment(): Promise { + const perPage = 100 + // get tenants that are applicable for organization enrichment + let tenants = await getApplicableTenants(perPage) + while (tenants.length > 0) { + const tenantsEnabled = await Promise.all( + tenants.map((t) => hasTenantOrganizationEnrichmentEnabled(t.id)), + ) + const enabledTenants = tenants.filter((_, i) => tenantsEnabled[i]) + if (enabledTenants.length > 0) { + // trigger tenant organization enrichment for each tenant + await Promise.all( + enabledTenants.map((tenant) => { + return startChild(enrichTenantOrganizations, { + workflowId: 'tenant-organizations-enrichment/' + tenant.id, + cancellationType: ChildWorkflowCancellationType.ABANDON, + parentClosePolicy: ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON, + workflowExecutionTimeout: '15 minutes', + retry: { + backoffCoefficient: 2, + maximumAttempts: 10, + initialInterval: 2 * 1000, + maximumInterval: 30 * 1000, + }, + args: [tenant], + searchAttributes: { + TenantId: [tenant.id], + }, + }) + }), + ) + } + + // load next page of applicable tenants + tenants = await getApplicableTenants(perPage, tenants[tenants.length - 1].id) + } +} diff --git a/services/apps/premium/organizations_enrichment_worker/tsconfig.json b/services/apps/premium/organizations_enrichment_worker/tsconfig.json new file mode 100644 index 0000000000..bf5ba4932d --- /dev/null +++ b/services/apps/premium/organizations_enrichment_worker/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es2017", + "module": "Node16", + "lib": ["es6", "es7", "es2017", "es2017.object", "es2015.promise"], + "skipLibCheck": true, + "sourceMap": true, + "moduleResolution": "node16", + "experimentalDecorators": true, + "esModuleInterop": true, + "baseUrl": "./src", + "paths": { + "@crowd/*/premium": ["../../../../libs/*/src/premium"], + "@crowd/*": ["../../../../libs/*/src"], + "@crowd/archetype-*": ["../../../../archetypes/*/src"] + } + }, + "include": ["src/**/*"] +} diff --git a/services/archetypes/worker/package.json b/services/archetypes/worker/package.json index 0674fc3937..4a8c42edf2 100644 --- a/services/archetypes/worker/package.json +++ b/services/archetypes/worker/package.json @@ -12,6 +12,7 @@ "@crowd/archetype-standard": "file:../../archetypes/standard", "@crowd/database": "file:../../libs/database", "@crowd/temporal": "file:../../libs/temporal", + "@crowd/common": "file:../../libs/common", "@opensearch-project/opensearch": "^1.2.0", "@temporalio/client": "~1.8.6", "@temporalio/worker": "~1.8.6", diff --git a/services/archetypes/worker/src/index.ts b/services/archetypes/worker/src/index.ts index 1febedad7e..b0f7c5ce9a 100644 --- a/services/archetypes/worker/src/index.ts +++ b/services/archetypes/worker/src/index.ts @@ -6,6 +6,7 @@ import { Config, Service } from '@crowd/archetype-standard' import { getDbConnection, DbStore } from '@crowd/database' import { OpenSearchService } from '@crowd/opensearch' import { getDataConverter } from '@crowd/temporal' +import { IS_DEV_ENV, IS_TEST_ENV } from '@crowd/common' // List all required environment variables, grouped per "component". // They are in addition to the ones required by the "standard" archetype. @@ -25,12 +26,15 @@ const envvars = { 'CROWD_DB_PASSWORD', 'CROWD_DB_DATABASE', ], - opensearch: [ - 'CROWD_OPENSEARCH_AWS_REGION', - 'CROWD_OPENSEARCH_AWS_ACCESS_KEY_ID', - 'CROWD_OPENSEARCH_AWS_SECRET_ACCESS_KEY', - 'CROWD_OPENSEARCH_NODE', - ], + opensearch: + IS_DEV_ENV || IS_TEST_ENV + ? ['CROWD_OPENSEARCH_NODE'] + : [ + 'CROWD_OPENSEARCH_AWS_REGION', + 'CROWD_OPENSEARCH_AWS_ACCESS_KEY_ID', + 'CROWD_OPENSEARCH_AWS_SECRET_ACCESS_KEY', + 'CROWD_OPENSEARCH_NODE', + ], } /* diff --git a/services/libs/common/src/object.ts b/services/libs/common/src/object.ts index 07463c9101..23fd1a3202 100644 --- a/services/libs/common/src/object.ts +++ b/services/libs/common/src/object.ts @@ -34,3 +34,15 @@ export const mergeIgnoreUndefined = >(first: T return result } + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type RemapT = { [index: string]: any } + +export const renameKeys = (obj: RemapT, fieldMap: RemapT): T => + Object.keys(obj).reduce( + (acc, key) => ({ + ...acc, + ...{ [fieldMap[key] || key]: obj[key] }, + }), + {} as T, + ) diff --git a/services/libs/common/src/timing.ts b/services/libs/common/src/timing.ts index 2c68d7696f..fb79ed0af6 100644 --- a/services/libs/common/src/timing.ts +++ b/services/libs/common/src/timing.ts @@ -10,3 +10,11 @@ export const addSeconds = (date: Date, seconds: number): Date => { } export const EPOCH_DATE = new Date('1970-01-01T00:00:00+00:00') + +export const getSecondsTillEndOfMonth = () => { + const now = new Date() + const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59) // Set to last day of current month, at 23:59:59 + + const diffInSeconds = (endOfMonth.getTime() - now.getTime()) / 1000 + return Math.round(diffInSeconds) +} diff --git a/services/libs/types/src/enums/featureFlags.ts b/services/libs/types/src/enums/featureFlags.ts index 5c50c47ee2..0080ff5d2e 100644 --- a/services/libs/types/src/enums/featureFlags.ts +++ b/services/libs/types/src/enums/featureFlags.ts @@ -16,6 +16,7 @@ export enum FeatureFlag { // temporal TEMPORAL_MEMBERS_ENRICHMENT = 'temporal-members-enrichment', TEMPORAL_MEMBER_MERGE_SUGGESTIONS = 'temporal-member-merge-suggestions', + TEMPORAL_ORGANIZATION_ENRICHMENT = 'temporal-organization-enrichment', SERVE_PROFILES_OPENSEARCH = 'serve-profiles-opensearch', } diff --git a/services/libs/types/src/enums/tenants.ts b/services/libs/types/src/enums/tenants.ts index fefd01911d..9758d95821 100644 --- a/services/libs/types/src/enums/tenants.ts +++ b/services/libs/types/src/enums/tenants.ts @@ -1,7 +1,34 @@ +import { FeatureFlag } from './featureFlags' + export enum TenantPlans { Essential = 'Essential', Growth = 'Growth', EagleEye = 'Eagle Eye', Scale = 'Scale', - Enterprise = 'enterprise', + Enterprise = 'Enterprise', +} + +export const PLAN_LIMITS = { + [TenantPlans.Essential]: { + [FeatureFlag.AUTOMATIONS]: 2, + [FeatureFlag.CSV_EXPORT]: 2, + }, + [TenantPlans.Growth]: { + [FeatureFlag.AUTOMATIONS]: 10, + [FeatureFlag.CSV_EXPORT]: 10, + [FeatureFlag.MEMBER_ENRICHMENT]: 1000, + [FeatureFlag.ORGANIZATION_ENRICHMENT]: 200, + }, + [TenantPlans.Scale]: { + [FeatureFlag.AUTOMATIONS]: 20, + [FeatureFlag.CSV_EXPORT]: 20, + [FeatureFlag.MEMBER_ENRICHMENT]: Infinity, + [FeatureFlag.ORGANIZATION_ENRICHMENT]: Infinity, + }, + [TenantPlans.Enterprise]: { + [FeatureFlag.AUTOMATIONS]: Infinity, + [FeatureFlag.CSV_EXPORT]: Infinity, + [FeatureFlag.MEMBER_ENRICHMENT]: Infinity, + [FeatureFlag.ORGANIZATION_ENRICHMENT]: Infinity, + }, }