From 997c1f8ba8a3ef0675f027834b3bfcfe580af9c1 Mon Sep 17 00:00:00 2001 From: Sokratis Vidros Date: Mon, 23 Dec 2024 20:09:27 +0200 Subject: [PATCH 01/49] fix(api): Allow arbitrary variables on the payload namespace Allow arbitrary variables on the payload namespace after adding at least one variable in a previous control value. --- .../build-payload-schema.usecase.ts | 53 +-- .../src/app/workflows-v2/util/jsonToSchema.ts | 71 +--- apps/dashboard/package.json | 2 +- .../workflow-editor/steps/email/maily.tsx | 9 +- .../parseStepVariablesToLiquidVariables.ts | 1 - pnpm-lock.yaml | 315 ++++++++++++++---- 6 files changed, 293 insertions(+), 158 deletions(-) diff --git a/apps/api/src/app/workflows-v2/usecases/build-payload-schema/build-payload-schema.usecase.ts b/apps/api/src/app/workflows-v2/usecases/build-payload-schema/build-payload-schema.usecase.ts index 33f841a8f0f..bd997ef468d 100644 --- a/apps/api/src/app/workflows-v2/usecases/build-payload-schema/build-payload-schema.usecase.ts +++ b/apps/api/src/app/workflows-v2/usecases/build-payload-schema/build-payload-schema.usecase.ts @@ -5,7 +5,6 @@ import { Instrument, InstrumentUsecase } from '@novu/application-generic'; import { flattenObjectValues } from '../../util/utils'; import { pathsToObject } from '../../util/path-to-object'; import { extractLiquidTemplateVariables } from '../../util/template-parser/liquid-parser'; -import { convertJsonToSchemaWithDefaults, emptyJsonSchema } from '../../util/jsonToSchema'; import { BuildPayloadSchemaCommand } from './build-payload-schema.command'; import { transformMailyContentToLiquid } from '../generate-preview/transform-maily-content-to-liquid'; import { isStringTipTapNode } from '../../util/tip-tap.util'; @@ -16,26 +15,13 @@ export class BuildPayloadSchema { @InstrumentUsecase() async execute(command: BuildPayloadSchemaCommand): Promise { - const controlValues = await this.buildControlValues(command); + const controlValues = await this.getControlValues(command); + const sanitizedControlValues = await this.sanitizeControlValues(controlValues); - if (!controlValues.length) { - return emptyJsonSchema(); - } - - const templateVars = await this.processControlValues(controlValues); - if (templateVars.length === 0) { - return emptyJsonSchema(); - } - - const variablesExample = pathsToObject(templateVars, { - valuePrefix: '{{', - valueSuffix: '}}', - }).payload; - - return convertJsonToSchemaWithDefaults(variablesExample); + return this.buildVariablesSchema(sanitizedControlValues); } - private async buildControlValues(command: BuildPayloadSchemaCommand) { + private async getControlValues(command: BuildPayloadSchemaCommand) { let controlValues = command.controlValues ? [command.controlValues] : []; if (!controlValues.length) { @@ -56,15 +42,15 @@ export class BuildPayloadSchema { ).map((item) => item.controls); } - return controlValues; + return controlValues.flat(); } @Instrument() - private async processControlValues(controlValues: Record[]): Promise { + private async sanitizeControlValues(controlValues: Record[]): Promise { const allVariables: string[] = []; for (const controlValue of controlValues) { - const processedControlValue = await this.processControlValue(controlValue); + const processedControlValue = await this.sanitizeControlValue(controlValue); const controlValuesString = flattenObjectValues(processedControlValue).join(' '); const templateVariables = extractLiquidTemplateVariables(controlValuesString); allVariables.push(...templateVariables.validVariables.map((variable) => variable.name)); @@ -74,7 +60,7 @@ export class BuildPayloadSchema { } @Instrument() - private async processControlValue(controlValue: Record): Promise> { + private async sanitizeControlValue(controlValue: Record): Promise> { const processedValue: Record = {}; for (const [key, value] of Object.entries(controlValue)) { @@ -87,4 +73,27 @@ export class BuildPayloadSchema { return processedValue; } + + private async buildVariablesSchema(variables: string[]) { + const variablesObject = pathsToObject(variables, { + valuePrefix: '{{', + valueSuffix: '}}', + }).payload; + + const schema: JSONSchemaDto = { + type: 'object', + properties: {}, + required: [], + additionalProperties: true, + }; + + for (const [key, value] of Object.entries(variablesObject)) { + if (schema.properties && schema.required) { + schema.properties[key] = { type: 'string', default: value }; + schema.required.push(key); + } + } + + return schema; + } } diff --git a/apps/api/src/app/workflows-v2/util/jsonToSchema.ts b/apps/api/src/app/workflows-v2/util/jsonToSchema.ts index d3617cfd730..a65bc01a04e 100644 --- a/apps/api/src/app/workflows-v2/util/jsonToSchema.ts +++ b/apps/api/src/app/workflows-v2/util/jsonToSchema.ts @@ -1,5 +1,7 @@ import { JSONSchemaDefinition, JSONSchemaDto } from '@novu/shared'; +export { JSONSchemaDto } from '@novu/shared'; + export function emptyJsonSchema(): JSONSchemaDto { return { type: 'object', @@ -8,70 +10,7 @@ export function emptyJsonSchema(): JSONSchemaDto { }; } -export function convertJsonToSchemaWithDefaults(unknownObject?: Record) { - if (!unknownObject) { - return {}; - } - - return generateJsonSchema(unknownObject) as unknown as JSONSchemaDto; -} - -function generateJsonSchema(jsonObject: Record): JSONSchemaDto { - const schema: JSONSchemaDto = { - type: 'object', - properties: {}, - required: [], - }; - - for (const [key, value] of Object.entries(jsonObject)) { - if (schema.properties && schema.required) { - schema.properties[key] = determineSchemaType(value); - schema.required.push(key); - } - } - - return schema; -} - -function determineSchemaType(value: unknown): JSONSchemaDto { - if (value === null) { - return { type: 'null' }; - } - - if (Array.isArray(value)) { - return { - type: 'array', - items: value.length > 0 ? determineSchemaType(value[0]) : { type: 'null' }, - }; - } - - switch (typeof value) { - case 'string': - return { type: 'string', default: value }; - case 'number': - return { type: 'number', default: value }; - case 'boolean': - return { type: 'boolean', default: value }; - case 'object': - return { - type: 'object', - properties: Object.entries(value).reduce( - (acc, [key, val]) => { - acc[key] = determineSchemaType(val); - - return acc; - }, - {} as { [key: string]: JSONSchemaDto } - ), - required: Object.keys(value), - }; - - default: - return { type: 'null' }; - } -} - -function isMatchingJsonSchema(schema: JSONSchemaDefinition, obj?: Record | null): boolean { +export function isMatchingJsonSchema(schema: JSONSchemaDefinition, obj?: Record | null): boolean { // Ensure the schema is an object with properties if (!obj || !schema || typeof schema !== 'object' || schema.type !== 'object' || !schema.properties) { return false; // If schema is not structured or no properties are defined, assume match @@ -101,7 +40,7 @@ function isMatchingJsonSchema(schema: JSONSchemaDefinition, obj?: Record { +export function extractMinValuesFromSchema(schema: JSONSchemaDefinition): Record { const result = {}; if (typeof schema === 'object' && schema.type === 'object') { @@ -121,5 +60,3 @@ function extractMinValuesFromSchema(schema: JSONSchemaDefinition): Record { variableTriggerCharacter="{{" variables={({ query, editor, from }) => { const queryWithoutSuffix = query.replace(/}+$/, ''); + const filteredVariables: { name: string; required: boolean }[] = []; function addInlineVariable() { if (!query.endsWith('}}')) { @@ -121,9 +122,7 @@ export const Maily = (props: MailyProps) => { }); } - const filteredVariables: { name: string; required: boolean }[] = []; - - if (from === 'for') { + if (from === 'for-variable') { filteredVariables.push(...arrays, ...namespaces); if (namespaces.some((namespace) => queryWithoutSuffix.includes(namespace.name))) { filteredVariables.push({ name: queryWithoutSuffix, required: false }); @@ -142,7 +141,9 @@ export const Maily = (props: MailyProps) => { filteredVariables.push({ name: queryWithoutSuffix, required: false }); } - addInlineVariable(); + if (from === 'content-variable') { + addInlineVariable(); + } return dedupAndSortVariables(filteredVariables, queryWithoutSuffix); }} contentJson={field.value ? JSON.parse(field.value) : undefined} diff --git a/apps/dashboard/src/utils/parseStepVariablesToLiquidVariables.ts b/apps/dashboard/src/utils/parseStepVariablesToLiquidVariables.ts index 309e8676126..9131554009d 100644 --- a/apps/dashboard/src/utils/parseStepVariablesToLiquidVariables.ts +++ b/apps/dashboard/src/utils/parseStepVariablesToLiquidVariables.ts @@ -33,7 +33,6 @@ export function parseStepVariables(schema: JSONSchemaDefinition): ParsedVariable type: 'variable', label: path, }); - return; } if (!obj.properties) return; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b9909c723e7..bb461bc310e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -684,8 +684,8 @@ importers: specifier: ^1.2.1 version: 1.2.1 '@maily-to/core': - specifier: ^0.0.19 - version: 0.0.19(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2))(y-prosemirror@1.2.15(prosemirror-model@1.22.3)(prosemirror-state@1.4.3)(prosemirror-view@1.37.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20)) + specifier: ^0.0.20 + version: 0.0.20(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2))(y-prosemirror@1.2.15(prosemirror-model@1.22.3)(prosemirror-state@1.4.3)(prosemirror-view@1.37.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20)) '@novu/framework': specifier: workspace:* version: link:../../packages/framework @@ -1128,13 +1128,13 @@ importers: dependencies: '@babel/plugin-proposal-optional-chaining': specifier: ^7.20.7 - version: 7.21.0(@babel/core@7.25.2) + version: 7.21.0(@babel/core@7.22.11) '@babel/plugin-transform-react-display-name': specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.25.2) + version: 7.18.6(@babel/core@7.22.11) '@babel/plugin-transform-runtime': specifier: ^7.23.2 - version: 7.23.2(@babel/core@7.25.2) + version: 7.23.2(@babel/core@7.22.11) '@clerk/clerk-react': specifier: ^5.15.1 version: 5.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1459,13 +1459,13 @@ importers: version: 7.12.1 '@babel/preset-env': specifier: ^7.23.2 - version: 7.23.2(@babel/core@7.25.2) + version: 7.23.2(@babel/core@7.22.11) '@babel/preset-react': specifier: ^7.13.13 - version: 7.18.6(@babel/core@7.25.2) + version: 7.18.6(@babel/core@7.22.11) '@babel/preset-typescript': specifier: ^7.13.0 - version: 7.21.4(@babel/core@7.25.2) + version: 7.21.4(@babel/core@7.22.11) '@babel/runtime': specifier: ^7.20.13 version: 7.21.0 @@ -1510,13 +1510,13 @@ importers: version: 7.4.2 '@storybook/preset-create-react-app': specifier: ^7.4.2 - version: 7.4.2(joi3nebcpxx5275442gow2nj4e) + version: 7.4.2(ucmnrhmq4kewpo24xrp57f5r6y) '@storybook/react': specifier: ^7.4.2 version: 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) '@storybook/react-webpack5': specifier: ^7.4.2 - version: 7.4.2(@babel/core@7.25.2)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1) + version: 7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1) '@testing-library/jest-dom': specifier: ^4.2.4 version: 4.2.4 @@ -1546,13 +1546,13 @@ importers: version: 4.1.0(less@4.1.3)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) react-app-rewired: specifier: ^2.2.1 - version: 2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)) + version: 2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)) react-error-overlay: specifier: 6.0.11 version: 6.0.11 react-scripts: specifier: ^5.0.1 - version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) + version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) sinon: specifier: 9.2.4 version: 9.2.4 @@ -9904,8 +9904,8 @@ packages: resolution: {integrity: sha512-SaNFseFPSDQlOYM9JTyYY6wauMu6qJ8eExo+jssFyb20ZaVvxKX1eTb3Gm5aW/4aWuxn6nofU+02sCk51//wdw==} engines: {node: '>=10.0.0'} - '@maily-to/core@0.0.19': - resolution: {integrity: sha512-FppQl3Wb2iwxxKMBoHmaoCwYiIyVwZxygbEA7qCAaTiHFZjvZIvifA8toU5xvL5Z5SEUtjkLe9Q3UQUzSnS1aQ==} + '@maily-to/core@0.0.20': + resolution: {integrity: sha512-psHI1Hl42edpwnAYf9WzcVIpD/IIW2pO0aEw7gi3FITbgH6LUb9slKcbZGqZSh7DMz10hBo7WYwbTsO5ml8hGQ==} engines: {node: '>=18.0.0'} peerDependencies: react: ^18.3.1 @@ -38620,6 +38620,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + debug: 4.3.6(supports-color@8.1.1) + lodash.debounce: 4.0.8 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.3)': dependencies: '@babel/core': 7.24.3 @@ -39212,6 +39223,13 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.11) + '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -39404,6 +39422,11 @@ snapshots: '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.23.2)': dependencies: '@babel/core': 7.23.2 @@ -39524,6 +39547,11 @@ snapshots: '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.2)': dependencies: '@babel/core': 7.23.2 @@ -39539,6 +39567,11 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.3)': dependencies: '@babel/core': 7.24.3 @@ -39759,6 +39792,11 @@ snapshots: '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 @@ -40333,6 +40371,12 @@ snapshots: '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-flow': 7.22.5(@babel/core@7.21.4) + '@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.22.11) + '@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.23.2)': dependencies: '@babel/core': 7.23.2 @@ -40571,6 +40615,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-modules-commonjs@7.22.15(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.22.11) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-simple-access': 7.22.5 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-modules-commonjs@7.22.15(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -41141,9 +41194,9 @@ snapshots: '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-transform-react-display-name@7.18.6(@babel/core@7.25.2)': + '@babel/plugin-transform-react-display-name@7.18.6(@babel/core@7.22.11)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.22.11 '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.21.4)': @@ -41151,6 +41204,11 @@ snapshots: '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.23.2)': dependencies: '@babel/core': 7.23.2 @@ -41173,10 +41231,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-development@7.18.6(@babel/core@7.25.2)': + '@babel/plugin-transform-react-jsx-development@7.18.6(@babel/core@7.22.11)': dependencies: - '@babel/core': 7.25.2 - '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.25.2) + '@babel/core': 7.22.11 + '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.22.11) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.22.11) transitivePeerDependencies: - supports-color @@ -41242,17 +41307,28 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.21.0(@babel/core@7.25.2)': + '@babel/plugin-transform-react-jsx@7.21.0(@babel/core@7.22.11)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.22.11 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-module-imports': 7.24.7 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.25.2) + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.11) '@babel/types': 7.22.19 transitivePeerDependencies: - supports-color + '@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.11) + '@babel/types': 7.25.6 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.23.2)': dependencies: '@babel/core': 7.23.2 @@ -41286,6 +41362,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.22.11) + '@babel/types': 7.25.6 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.24.3)': dependencies: '@babel/core': 7.24.3 @@ -41314,12 +41401,18 @@ snapshots: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-react-pure-annotations@7.18.6(@babel/core@7.25.2)': + '@babel/plugin-transform-react-pure-annotations@7.18.6(@babel/core@7.22.11)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.22.11 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.23.2)': dependencies: '@babel/core': 7.23.2 @@ -41405,6 +41498,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-runtime@7.23.2(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.22.11) + babel-plugin-polyfill-corejs3: 0.8.5(@babel/core@7.22.11) + babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.22.11) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-runtime@7.23.2(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -41551,6 +41656,16 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-transform-typescript@7.21.3(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.22.11) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.22.11) + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-typescript@7.21.3(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -42144,6 +42259,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/preset-flow@7.22.15(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-validator-option': 7.24.8 + '@babel/plugin-transform-flow-strip-types': 7.25.2(@babel/core@7.22.11) + '@babel/preset-flow@7.22.15(@babel/core@7.23.2)': dependencies: '@babel/core': 7.23.2 @@ -42212,15 +42334,27 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/preset-react@7.18.6(@babel/core@7.25.2)': + '@babel/preset-react@7.18.6(@babel/core@7.22.11)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.22.11 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.25.2) - '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.25.2) - '@babel/plugin-transform-react-jsx-development': 7.18.6(@babel/core@7.25.2) - '@babel/plugin-transform-react-pure-annotations': 7.18.6(@babel/core@7.25.2) + '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.22.11) + '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.22.11) + '@babel/plugin-transform-react-jsx-development': 7.18.6(@babel/core@7.22.11) + '@babel/plugin-transform-react-pure-annotations': 7.18.6(@babel/core@7.22.11) + transitivePeerDependencies: + - supports-color + + '@babel/preset-react@7.22.15(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.22.15 + '@babel/plugin-transform-react-display-name': 7.22.5(@babel/core@7.22.11) + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.22.11) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.22.11) + '@babel/plugin-transform-react-pure-annotations': 7.22.5(@babel/core@7.22.11) transitivePeerDependencies: - supports-color @@ -42260,6 +42394,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/preset-typescript@7.21.4(@babel/core@7.22.11)': + dependencies: + '@babel/core': 7.22.11 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.22.15 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.11) + '@babel/plugin-transform-modules-commonjs': 7.22.15(@babel/core@7.22.11) + '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.22.11) + transitivePeerDependencies: + - supports-color + '@babel/preset-typescript@7.21.4(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -45984,7 +46129,7 @@ snapshots: transitivePeerDependencies: - debug - '@maily-to/core@0.0.19(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2))(y-prosemirror@1.2.15(prosemirror-model@1.22.3)(prosemirror-state@1.4.3)(prosemirror-view@1.37.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20))': + '@maily-to/core@0.0.20(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2))(y-prosemirror@1.2.15(prosemirror-model@1.22.3)(prosemirror-state@1.4.3)(prosemirror-view@1.37.0)(y-protocols@1.0.6(yjs@13.6.20))(yjs@13.6.20))': dependencies: '@radix-ui/react-dropdown-menu': 2.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-popover': 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -54803,7 +54948,7 @@ snapshots: get-port: 5.1.1 giget: 1.1.2 globby: 11.1.0 - jscodeshift: 0.14.0(@babel/preset-env@7.23.2(@babel/core@7.25.2)) + jscodeshift: 0.14.0(@babel/preset-env@7.23.2(@babel/core@7.22.11)) leven: 3.1.0 ora: 5.4.1 prettier: 2.8.8 @@ -55374,16 +55519,16 @@ snapshots: '@storybook/postinstall@7.4.2': {} - '@storybook/preset-create-react-app@7.4.2(joi3nebcpxx5275442gow2nj4e)': + '@storybook/preset-create-react-app@7.4.2(ucmnrhmq4kewpo24xrp57f5r6y)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.22.11 '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) '@storybook/types': 7.4.2 '@types/babel__core': 7.20.0 babel-plugin-react-docgen: 4.2.1 pnp-webpack-plugin: 1.7.0(typescript@5.6.2) - react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) + react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) semver: 7.5.4 transitivePeerDependencies: - '@types/webpack' @@ -55397,16 +55542,16 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/preset-react-webpack@7.4.2(@babel/core@7.23.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-hot-middleware@2.26.1)': + '@storybook/preset-react-webpack@7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)': dependencies: - '@babel/preset-flow': 7.22.15(@babel/core@7.23.2) - '@babel/preset-react': 7.22.15(@babel/core@7.23.2) - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + '@babel/preset-flow': 7.22.15(@babel/core@7.22.11) + '@babel/preset-react': 7.22.15(@babel/core@7.22.11) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) '@storybook/core-webpack': 7.4.2(encoding@0.1.13) '@storybook/docs-tools': 7.4.2(encoding@0.1.13) '@storybook/node-logger': 7.4.2 '@storybook/react': 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) '@types/node': 16.11.7 '@types/semver': 7.5.8 babel-plugin-add-react-displayname: 0.0.5 @@ -55416,9 +55561,9 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-refresh: 0.11.0 semver: 7.6.3 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) optionalDependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.22.11 typescript: 5.6.2 transitivePeerDependencies: - '@swc/core' @@ -55434,16 +55579,16 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/preset-react-webpack@7.4.2(@babel/core@7.25.2)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)': + '@storybook/preset-react-webpack@7.4.2(@babel/core@7.23.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-hot-middleware@2.26.1)': dependencies: - '@babel/preset-flow': 7.22.15(@babel/core@7.25.2) - '@babel/preset-react': 7.22.15(@babel/core@7.25.2) - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + '@babel/preset-flow': 7.22.15(@babel/core@7.23.2) + '@babel/preset-react': 7.22.15(@babel/core@7.23.2) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) '@storybook/core-webpack': 7.4.2(encoding@0.1.13) '@storybook/docs-tools': 7.4.2(encoding@0.1.13) '@storybook/node-logger': 7.4.2 '@storybook/react': 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) '@types/node': 16.11.7 '@types/semver': 7.5.8 babel-plugin-add-react-displayname: 0.0.5 @@ -55453,9 +55598,9 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-refresh: 0.11.0 semver: 7.6.3 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) optionalDependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.23.2 typescript: 5.6.2 transitivePeerDependencies: - '@swc/core' @@ -55658,16 +55803,16 @@ snapshots: - vite-plugin-glimmerx - webpack-sources - '@storybook/react-webpack5@7.4.2(@babel/core@7.23.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-hot-middleware@2.26.1)': + '@storybook/react-webpack5@7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)': dependencies: - '@storybook/builder-webpack5': 7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@storybook/preset-react-webpack': 7.4.2(@babel/core@7.23.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-hot-middleware@2.26.1) + '@storybook/builder-webpack5': 7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@storybook/preset-react-webpack': 7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1) '@storybook/react': 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) '@types/node': 16.11.7 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.22.11 typescript: 5.6.2 transitivePeerDependencies: - '@swc/core' @@ -55686,16 +55831,16 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/react-webpack5@7.4.2(@babel/core@7.25.2)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)': + '@storybook/react-webpack5@7.4.2(@babel/core@7.23.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-hot-middleware@2.26.1)': dependencies: - '@storybook/builder-webpack5': 7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@storybook/preset-react-webpack': 7.4.2(@babel/core@7.25.2)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1) + '@storybook/builder-webpack5': 7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@storybook/preset-react-webpack': 7.4.2(@babel/core@7.23.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-hot-middleware@2.26.1) '@storybook/react': 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) '@types/node': 16.11.7 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.23.2 typescript: 5.6.2 transitivePeerDependencies: - '@swc/core' @@ -60692,6 +60837,15 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.22.11): + dependencies: + '@babel/compat-data': 7.25.4 + '@babel/core': 7.22.11 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.22.11) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.3): dependencies: '@babel/compat-data': 7.25.4 @@ -64514,6 +64668,33 @@ snapshots: dependencies: eslint: 8.57.1 + eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2): + dependencies: + '@babel/core': 7.21.4 + '@babel/eslint-parser': 7.25.1(@babel/core@7.21.4)(eslint@9.9.1(jiti@1.21.6)) + '@rushstack/eslint-patch': 1.2.0 + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2) + '@typescript-eslint/parser': 5.62.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2) + babel-preset-react-app: 10.0.1 + confusing-browser-globals: 1.0.11 + eslint: 9.9.1(jiti@1.21.6) + eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) + eslint-plugin-jsx-a11y: 6.9.0(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-react: 7.35.0(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-react-hooks: 4.6.2(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-testing-library: 5.10.2(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2) + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - '@babel/plugin-syntax-flow' + - '@babel/plugin-transform-react-jsx' + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - jest + - supports-color + eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2): dependencies: '@babel/core': 7.21.4 @@ -64622,6 +64803,14 @@ snapshots: eslint: 8.57.1 ignore: 5.3.2 + eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(eslint@9.9.1(jiti@1.21.6)): + dependencies: + '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.22.11) + '@babel/plugin-transform-react-jsx': 7.25.2(@babel/core@7.22.11) + eslint: 9.9.1(jiti@1.21.6) + lodash: 4.17.21 + string-natural-compare: 3.0.1 + eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint@9.9.1(jiti@1.21.6)): dependencies: '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.25.2) @@ -69696,7 +69885,7 @@ snapshots: jsbn@0.1.1: {} - jscodeshift@0.14.0(@babel/preset-env@7.23.2(@babel/core@7.25.2)): + jscodeshift@0.14.0(@babel/preset-env@7.23.2(@babel/core@7.22.11)): dependencies: '@babel/core': 7.25.2 '@babel/parser': 7.25.6 @@ -69704,7 +69893,7 @@ snapshots: '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.25.2) '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.25.2) '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.25.2) - '@babel/preset-env': 7.23.2(@babel/core@7.25.2) + '@babel/preset-env': 7.23.2(@babel/core@7.22.11) '@babel/preset-flow': 7.24.7(@babel/core@7.25.2) '@babel/preset-typescript': 7.23.2(@babel/core@7.25.2) '@babel/register': 7.21.0(@babel/core@7.25.2) @@ -76343,9 +76532,9 @@ snapshots: regenerator-runtime: 0.13.11 whatwg-fetch: 3.6.2 - react-app-rewired@2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)): + react-app-rewired@2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)): dependencies: - react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) + react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) semver: 5.7.2 react-app-rewired@2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12)))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)): @@ -76780,7 +76969,7 @@ snapshots: transitivePeerDependencies: - supports-color - react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1): + react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1): dependencies: '@babel/core': 7.21.4 '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) @@ -76798,7 +76987,7 @@ snapshots: dotenv: 10.0.0 dotenv-expand: 5.1.0 eslint: 9.9.1(jiti@1.21.6) - eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) + eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) eslint-webpack-plugin: 3.2.0(eslint@9.9.1(jiti@1.21.6))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) file-loader: 6.2.0(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) fs-extra: 10.1.0 From feaf04c63988037055c15296c59ca18c8946109e Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 23 Dec 2024 21:18:31 +0200 Subject: [PATCH 02/49] wup: --- .../steps/email/email-subject.tsx | 238 +++++++++++++++++- 1 file changed, 226 insertions(+), 12 deletions(-) diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx index 29b3af8a1f6..e59e26c416c 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx @@ -1,5 +1,5 @@ -import { EditorView } from '@uiw/react-codemirror'; -import { useMemo } from 'react'; +import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; +import { useMemo, useState, useRef } from 'react'; import { useFormContext } from 'react-hook-form'; import { Editor } from '@/components/primitives/editor'; @@ -9,13 +9,212 @@ import { completions } from '@/utils/liquid-autocomplete'; import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables'; import { capitalize } from '@/utils/string'; import { autocompletion } from '@codemirror/autocomplete'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/primitives/popover'; +import { Button } from '@/components/primitives/button'; +import { Input } from '@/components/primitives/input'; const subjectKey = 'subject'; +interface VariablePopoverProps { + variable: string; + onClose: () => void; + onUpdate: (newValue: string) => void; +} + +const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) => { + const [value, setValue] = useState(variable); + + return ( + +
+
+

Edit Variable

+

Modify the variable name or select from available options.

+
+
+ setValue(e.target.value)} className="h-8" /> +
+ + +
+
+
+
+ ); +}; + +// Create a decoration for variable pills +const variablePillTheme = EditorView.baseTheme({ + '.cm-variable-pill': { + backgroundColor: 'rgb(255 255 255 / 0.5)', + color: 'rgb(55, 65, 81)', + border: '1px solid #e5e7eb', + borderRadius: '9999px', + padding: '2px 6px 2px 24px', + margin: '0 2px', + fontFamily: 'inherit', + display: 'inline-flex', + alignItems: 'center', + height: '24px', + lineHeight: '24px', + fontSize: '14px', + cursor: 'pointer', + position: 'relative', + }, + '.cm-variable-pill::before': { + content: '""', + position: 'absolute', + left: '6px', + top: '50%', + transform: 'translateY(-50%)', + width: '14px', + height: '14px', + backgroundColor: '#E11D48', + maskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, + maskRepeat: 'no-repeat', + maskPosition: 'center', + maskSize: 'contain', + WebkitMaskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, + WebkitMaskRepeat: 'no-repeat', + WebkitMaskPosition: 'center', + WebkitMaskSize: 'contain', + }, + '.cm-variable-pill.cm-dark': { + backgroundColor: '#FFD6EE', + color: '#AD74FF', + border: '1px solid #3D3D4D', + }, + '.cm-variable-pill .cm-bracket': { + display: 'none', + }, +}); + export const EmailSubject = () => { const { control } = useFormContext(); const { step } = useWorkflow(); const variables = useMemo(() => (step ? parseStepVariablesToLiquidVariables(step.variables) : []), [step]); + const [selectedVariable, setSelectedVariable] = useState<{ value: string; from: number; to: number } | null>(null); + + // Plugin to find and decorate variables + const createVariablePlugin = () => { + return ViewPlugin.fromClass( + class { + decorations: DecorationSet; + + constructor(view: EditorView) { + this.decorations = this.createDecorations(view); + } + + update(update: any) { + if (update.docChanged || update.viewportChanged) { + this.decorations = this.createDecorations(update.view); + } + } + + createDecorations(view: EditorView) { + const decorations: any[] = []; + const content = view.state.doc.toString(); + const variableRegex = /{{(.*?)}}/g; + let match; + + while ((match = variableRegex.exec(content)) !== null) { + const start = match.index; + const end = start + match[0].length; + const variableName = match[1].trim(); + + // Add bracket decoration for the opening {{ + decorations.push( + Decoration.mark({ + class: 'cm-bracket', + }).range(start, start + 2) + ); + + // Add the main variable decoration + decorations.push( + Decoration.mark({ + class: `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''}`, + attributes: { + 'data-variable': variableName, + 'data-start': start.toString(), + 'data-end': end.toString(), + }, + }).range(start, end) + ); + + // Add bracket decoration for the closing }} + decorations.push( + Decoration.mark({ + class: 'cm-bracket', + }).range(end - 2, end) + ); + } + + return Decoration.set(decorations, true); + } + }, + { + decorations: (v) => v.decorations, + provide: (plugin) => + EditorView.atomicRanges.of((view) => { + return view.plugin(plugin)?.decorations || Decoration.none; + }), + } + ); + }; + + const handleVariableClick = (e: MouseEvent, view: EditorView) => { + const target = e.target as HTMLElement; + if (target.classList.contains('cm-variable-pill')) { + const variable = target.getAttribute('data-variable'); + const start = parseInt(target.getAttribute('data-start') || '0'); + const end = parseInt(target.getAttribute('data-end') || '0'); + + if (variable) { + setSelectedVariable({ value: variable, from: start, to: end }); + } + } + }; + + const handleVariableUpdate = (newValue: string) => { + if (!selectedVariable) return; + + const { from, to } = selectedVariable; + const view = editorRef.current?.view; + if (!view) return; + + view.dispatch({ + changes: { + from, + to, + insert: `{{${newValue}}}`, + }, + }); + }; + + const editorRef = useRef(null); + + const extensions = useMemo( + () => [ + autocompletion({ override: [completions(variables)] }), + EditorView.lineWrapping, + variablePillTheme, + createVariablePlugin(), + EditorView.domEventHandlers({ + click: handleVariableClick, + }), + ], + [variables] + ); return ( { <> - field.onChange(val)} - /> +
+ field.onChange(val)} + /> + !open && setSelectedVariable(null)}> + +
+ + {selectedVariable && ( + setSelectedVariable(null)} + onUpdate={handleVariableUpdate} + /> + )} + +
From 163ad78042dbc0adcb02f4e1a572b7fa6e852cdf Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 23 Dec 2024 21:39:07 +0200 Subject: [PATCH 03/49] fix: items --- .../steps/email/email-subject.tsx | 316 +++++++++++++----- 1 file changed, 240 insertions(+), 76 deletions(-) diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx index e59e26c416c..d79873b139d 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx @@ -12,6 +12,7 @@ import { autocompletion } from '@codemirror/autocomplete'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/primitives/popover'; import { Button } from '@/components/primitives/button'; import { Input } from '@/components/primitives/input'; +import { GripVertical } from 'lucide-react'; const subjectKey = 'subject'; @@ -21,29 +22,157 @@ interface VariablePopoverProps { onUpdate: (newValue: string) => void; } +const TRANSFORMERS = [ + { label: 'Uppercase', value: 'upcase' }, + { label: 'Lowercase', value: 'downcase' }, + { label: 'Capitalize', value: 'capitalize' }, + { label: 'Strip HTML', value: 'strip_html' }, + { label: 'Strip Newlines', value: 'strip_newlines' }, + { label: 'Escape', value: 'escape' }, +] as const; + const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) => { - const [value, setValue] = useState(variable); + // Parse the variable to extract name, default value, and transformers + const [variableName, defaultValue = '', initialTransformers = []] = useMemo(() => { + const parts = variable.split('|').map((part) => part.trim()); + const [nameWithDefault, ...rest] = parts; + + // Handle default value + const defaultMatch = nameWithDefault.match(/default:\s*([^}]+)/); + const name = defaultMatch ? nameWithDefault.replace(/\s*\|\s*default:[^}]+/, '') : nameWithDefault; + const defaultVal = defaultMatch ? defaultMatch[1].trim() : ''; + + // Get all transformers + const transforms = rest.filter((part) => TRANSFORMERS.some((t) => t.value === part.trim())); + + console.log('Parsed variable:', { name, defaultVal, transforms, parts }); + return [name, defaultVal, transforms]; + }, [variable]); + + const [name, setName] = useState(variableName); + const [defaultVal, setDefaultVal] = useState(defaultValue); + const [transformers, setTransformers] = useState(initialTransformers); + + const handleTransformerToggle = (value: string) => { + setTransformers((current) => { + if (current.includes(value)) { + return current.filter((t) => t !== value); + } + return [...current, value]; + }); + }; + + const moveTransformer = (from: number, to: number) => { + setTransformers((current) => { + const newTransformers = [...current]; + const [removed] = newTransformers.splice(from, 1); + newTransformers.splice(to, 0, removed); + return newTransformers; + }); + }; + + const handleUpdate = () => { + let finalValue = name.trim(); + + if (defaultVal) { + finalValue += ` | default: ${defaultVal.trim()}`; + } + + // Add all active transformers in order + transformers.forEach((t) => { + finalValue += ` | ${t}`; + }); + + console.log('Updating with value:', finalValue); + onUpdate(finalValue); + }; return (

Edit Variable

-

Modify the variable name or select from available options.

+

Modify the variable name or add transformers.

-
- setValue(e.target.value)} className="h-8" /> +
+
+ + setName(e.target.value)} className="h-8" /> +
+ +
+ + setDefaultVal(e.target.value)} + className="h-8" + placeholder="Enter default value..." + /> +
+ +
+ +
+ {/* Selected transformers with drag handles */} + {transformers.length > 0 && ( +
+ {transformers.map((value, index) => { + const transformer = TRANSFORMERS.find((t) => t.value === value); + return ( +
{ + e.dataTransfer.setData('text/plain', index.toString()); + }} + onDragOver={(e) => { + e.preventDefault(); + }} + onDrop={(e) => { + e.preventDefault(); + const fromIndex = parseInt(e.dataTransfer.getData('text/plain')); + moveTransformer(fromIndex, index); + }} + > + + {transformer?.label} + +
+ ); + })} +
+ )} + + {/* Available transformers */} +
+ {TRANSFORMERS.filter((t) => !transformers.includes(t.value)).map(({ label, value }) => ( + + ))} +
+
+
+
-
@@ -104,6 +233,24 @@ export const EmailSubject = () => { const { step } = useWorkflow(); const variables = useMemo(() => (step ? parseStepVariablesToLiquidVariables(step.variables) : []), [step]); const [selectedVariable, setSelectedVariable] = useState<{ value: string; from: number; to: number } | null>(null); + const viewRef = useRef(null); + const fieldRef = useRef(null); + + const handleVariableClick = (e: MouseEvent, view: EditorView) => { + const target = e.target as HTMLElement; + const pill = target.closest('.cm-variable-pill'); + + if (pill instanceof HTMLElement) { + const variable = pill.getAttribute('data-variable'); + const start = parseInt(pill.getAttribute('data-start') || '0'); + const end = parseInt(pill.getAttribute('data-end') || '0'); + + if (variable) { + console.log('Selected variable:', { variable, start, end }); + setSelectedVariable({ value: variable, from: start, to: end }); + } + } + }; // Plugin to find and decorate variables const createVariablePlugin = () => { @@ -113,12 +260,16 @@ export const EmailSubject = () => { constructor(view: EditorView) { this.decorations = this.createDecorations(view); + viewRef.current = view; } update(update: any) { if (update.docChanged || update.viewportChanged) { this.decorations = this.createDecorations(update.view); } + if (update.view) { + viewRef.current = update.view; + } } createDecorations(view: EditorView) { @@ -132,13 +283,6 @@ export const EmailSubject = () => { const end = start + match[0].length; const variableName = match[1].trim(); - // Add bracket decoration for the opening {{ - decorations.push( - Decoration.mark({ - class: 'cm-bracket', - }).range(start, start + 2) - ); - // Add the main variable decoration decorations.push( Decoration.mark({ @@ -151,7 +295,12 @@ export const EmailSubject = () => { }).range(start, end) ); - // Add bracket decoration for the closing }} + // Add bracket decorations + decorations.push( + Decoration.mark({ + class: 'cm-bracket', + }).range(start, start + 2) + ); decorations.push( Decoration.mark({ class: 'cm-bracket', @@ -172,36 +321,40 @@ export const EmailSubject = () => { ); }; - const handleVariableClick = (e: MouseEvent, view: EditorView) => { - const target = e.target as HTMLElement; - if (target.classList.contains('cm-variable-pill')) { - const variable = target.getAttribute('data-variable'); - const start = parseInt(target.getAttribute('data-start') || '0'); - const end = parseInt(target.getAttribute('data-end') || '0'); - - if (variable) { - setSelectedVariable({ value: variable, from: start, to: end }); - } - } - }; - const handleVariableUpdate = (newValue: string) => { - if (!selectedVariable) return; + if (!selectedVariable || !viewRef.current || !fieldRef.current) { + console.log('Missing required refs:', { + selectedVariable, + viewRef: !!viewRef.current, + fieldRef: !!fieldRef.current, + }); + return; + } const { from, to } = selectedVariable; - const view = editorRef.current?.view; - if (!view) return; + const view = viewRef.current; + console.log('Updating variable:', { from, to, newValue }); + + // Create and dispatch the transaction + const changes = { + from, + to, + insert: `{{${newValue}}}`, + }; + console.log('Applying changes:', changes); view.dispatch({ - changes: { - from, - to, - insert: `{{${newValue}}}`, - }, + changes, + selection: { anchor: from + `{{${newValue}}}`.length }, }); - }; - const editorRef = useRef(null); + // Get the updated content and trigger form update + const updatedContent = view.state.doc.toString(); + console.log('Updated content:', updatedContent); + fieldRef.current.onChange(updatedContent); + + setSelectedVariable(null); + }; const extensions = useMemo( () => [ @@ -220,40 +373,51 @@ export const EmailSubject = () => { ( - <> - - -
- field.onChange(val)} - /> - !open && setSelectedVariable(null)}> - -
- - {selectedVariable && ( - setSelectedVariable(null)} - onUpdate={handleVariableUpdate} - /> - )} - -
- - - - - )} + render={({ field }) => { + // Store the field reference + fieldRef.current = field; + + return ( + <> + + +
+ field.onChange(val)} + /> + { + if (!open) { + setSelectedVariable(null); + } + }} + > + +
+ + {selectedVariable && ( + setSelectedVariable(null)} + onUpdate={handleVariableUpdate} + /> + )} + +
+ + + + + ); + }} /> ); }; From b2111ec31375bfc3df8d37225f6703cff9090f4c Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 23 Dec 2024 22:03:55 +0200 Subject: [PATCH 04/49] Update email-subject.tsx --- .../steps/email/email-subject.tsx | 174 +++++++++++------- 1 file changed, 111 insertions(+), 63 deletions(-) diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx index d79873b139d..47451258057 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx @@ -1,5 +1,5 @@ import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; -import { useMemo, useState, useRef } from 'react'; +import { useMemo, useState, useRef, useEffect, useCallback } from 'react'; import { useFormContext } from 'react-hook-form'; import { Editor } from '@/components/primitives/editor'; @@ -45,13 +45,58 @@ const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) // Get all transformers const transforms = rest.filter((part) => TRANSFORMERS.some((t) => t.value === part.trim())); - console.log('Parsed variable:', { name, defaultVal, transforms, parts }); return [name, defaultVal, transforms]; }, [variable]); const [name, setName] = useState(variableName); const [defaultVal, setDefaultVal] = useState(defaultValue); const [transformers, setTransformers] = useState(initialTransformers); + const updateTimeoutRef = useRef(); + const isUpdatingRef = useRef(false); + + // Debounced update function + const debouncedUpdate = useCallback( + (newName: string, newDefaultVal: string, newTransformers: string[]) => { + if (isUpdatingRef.current) return; + + if (updateTimeoutRef.current) { + clearTimeout(updateTimeoutRef.current); + } + + updateTimeoutRef.current = setTimeout(() => { + try { + isUpdatingRef.current = true; + + // Build the variable parts + const parts = [newName.trim()]; + + if (newDefaultVal) { + parts.push(`default: ${newDefaultVal.trim()}`); + } + + // Add transformers in order + parts.push(...newTransformers); + + // Join with proper spacing + const finalValue = parts.join(' | '); + onUpdate(finalValue); + } finally { + isUpdatingRef.current = false; + } + }, 300); + }, + [onUpdate] + ); + + // Update when values change + useEffect(() => { + debouncedUpdate(name, defaultVal, transformers); + return () => { + if (updateTimeoutRef.current) { + clearTimeout(updateTimeoutRef.current); + } + }; + }, [name, defaultVal, transformers, debouncedUpdate]); const handleTransformerToggle = (value: string) => { setTransformers((current) => { @@ -71,22 +116,6 @@ const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) }); }; - const handleUpdate = () => { - let finalValue = name.trim(); - - if (defaultVal) { - finalValue += ` | default: ${defaultVal.trim()}`; - } - - // Add all active transformers in order - transformers.forEach((t) => { - finalValue += ` | ${t}`; - }); - - console.log('Updating with value:', finalValue); - onUpdate(finalValue); - }; - return (
@@ -168,12 +197,9 @@ const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps)
-
+
-
@@ -235,12 +261,18 @@ export const EmailSubject = () => { const [selectedVariable, setSelectedVariable] = useState<{ value: string; from: number; to: number } | null>(null); const viewRef = useRef(null); const fieldRef = useRef(null); + const isUpdatingRef = useRef(false); + + const handleVariableClick = useCallback((e: MouseEvent, view: EditorView) => { + if (isUpdatingRef.current) return; - const handleVariableClick = (e: MouseEvent, view: EditorView) => { const target = e.target as HTMLElement; const pill = target.closest('.cm-variable-pill'); if (pill instanceof HTMLElement) { + e.preventDefault(); + e.stopPropagation(); + const variable = pill.getAttribute('data-variable'); const start = parseInt(pill.getAttribute('data-start') || '0'); const end = parseInt(pill.getAttribute('data-end') || '0'); @@ -250,10 +282,60 @@ export const EmailSubject = () => { setSelectedVariable({ value: variable, from: start, to: end }); } } - }; + }, []); + + const handleVariableUpdate = useCallback( + (newValue: string) => { + if (!selectedVariable || !viewRef.current || !fieldRef.current || isUpdatingRef.current) { + console.log('Skipping update:', { + selectedVariable, + viewRef: !!viewRef.current, + fieldRef: !!fieldRef.current, + isUpdating: isUpdatingRef.current, + }); + return; + } + + try { + isUpdatingRef.current = true; + const { from, to } = selectedVariable; + const view = viewRef.current; + + // Get the current content and ensure we're not duplicating + const currentContent = view.state.doc.toString(); + const beforeContent = currentContent.slice(0, from); + const afterContent = currentContent.slice(to); + + // Create the new variable text + const newVariableText = `{{${newValue}}}`; + + // Create and dispatch the transaction + const changes = { + from, + to, + insert: newVariableText, + }; + + view.dispatch({ + changes, + selection: { anchor: from + newVariableText.length }, + }); + + // Get the updated content and trigger form update + const updatedContent = view.state.doc.toString(); + fieldRef.current.onChange(updatedContent); + + // Update the selected variable with new bounds + setSelectedVariable((prev) => (prev ? { ...prev, value: newValue, to: from + newVariableText.length } : null)); + } finally { + isUpdatingRef.current = false; + } + }, + [selectedVariable] + ); // Plugin to find and decorate variables - const createVariablePlugin = () => { + const createVariablePlugin = useCallback(() => { return ViewPlugin.fromClass( class { decorations: DecorationSet; @@ -292,6 +374,7 @@ export const EmailSubject = () => { 'data-start': start.toString(), 'data-end': end.toString(), }, + inclusive: true, }).range(start, end) ); @@ -319,42 +402,7 @@ export const EmailSubject = () => { }), } ); - }; - - const handleVariableUpdate = (newValue: string) => { - if (!selectedVariable || !viewRef.current || !fieldRef.current) { - console.log('Missing required refs:', { - selectedVariable, - viewRef: !!viewRef.current, - fieldRef: !!fieldRef.current, - }); - return; - } - - const { from, to } = selectedVariable; - const view = viewRef.current; - console.log('Updating variable:', { from, to, newValue }); - - // Create and dispatch the transaction - const changes = { - from, - to, - insert: `{{${newValue}}}`, - }; - console.log('Applying changes:', changes); - - view.dispatch({ - changes, - selection: { anchor: from + `{{${newValue}}}`.length }, - }); - - // Get the updated content and trigger form update - const updatedContent = view.state.doc.toString(); - console.log('Updated content:', updatedContent); - fieldRef.current.onChange(updatedContent); - - setSelectedVariable(null); - }; + }, []); const extensions = useMemo( () => [ @@ -366,7 +414,7 @@ export const EmailSubject = () => { click: handleVariableClick, }), ], - [variables] + [variables, handleVariableClick, createVariablePlugin] ); return ( From 65733c83b42af530ba05ad82c686d1c9863d41ef Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 23 Dec 2024 22:10:18 +0200 Subject: [PATCH 05/49] fix: hello world --- .../steps/email/email-subject.tsx | 163 +++++++++--------- 1 file changed, 82 insertions(+), 81 deletions(-) diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx index 47451258057..2552f3462b2 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx @@ -1,5 +1,5 @@ import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; -import { useMemo, useState, useRef, useEffect, useCallback } from 'react'; +import { useMemo, useState, useRef, useEffect, useCallback, ChangeEvent } from 'react'; import { useFormContext } from 'react-hook-form'; import { Editor } from '@/components/primitives/editor'; @@ -11,7 +11,7 @@ import { capitalize } from '@/utils/string'; import { autocompletion } from '@codemirror/autocomplete'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/primitives/popover'; import { Button } from '@/components/primitives/button'; -import { Input } from '@/components/primitives/input'; +import { Input, InputField } from '@/components/primitives/input'; import { GripVertical } from 'lucide-react'; const subjectKey = 'subject'; @@ -117,90 +117,91 @@ const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) }; return ( - -
-
-

Edit Variable

-

Modify the variable name or add transformers.

+ +
+
+ + +
+ + + ) => setName(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+ + +
+ + + ) => setDefaultVal(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
-
-
- - setName(e.target.value)} className="h-8" /> -
- -
- - setDefaultVal(e.target.value)} - className="h-8" - placeholder="Enter default value..." - /> -
-
- -
- {/* Selected transformers with drag handles */} - {transformers.length > 0 && ( -
- {transformers.map((value, index) => { - const transformer = TRANSFORMERS.find((t) => t.value === value); - return ( -
{ - e.dataTransfer.setData('text/plain', index.toString()); - }} - onDragOver={(e) => { - e.preventDefault(); - }} - onDrop={(e) => { - e.preventDefault(); - const fromIndex = parseInt(e.dataTransfer.getData('text/plain')); - moveTransformer(fromIndex, index); - }} - > - - {transformer?.label} - -
- ); - })} -
- )} - - {/* Available transformers */} -
- {TRANSFORMERS.filter((t) => !transformers.includes(t.value)).map(({ label, value }) => ( - - ))} -
+ + {transformer?.label} + +
+ ); + })}
-
- -
- + )} + + {/* Available transformers */} +
+ {TRANSFORMERS.filter((t) => !transformers.includes(t.value)).map(({ label, value }) => ( + + ))}
From 624383a0daccf9bed0279ad1c7aeccee24d66948 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 23 Dec 2024 22:15:29 +0200 Subject: [PATCH 06/49] fix: re order --- .../steps/email/email-subject.tsx | 120 +++++++++++++----- 1 file changed, 88 insertions(+), 32 deletions(-) diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx index 2552f3462b2..bc8a6ab938e 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx @@ -1,6 +1,7 @@ import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; -import { useMemo, useState, useRef, useEffect, useCallback, ChangeEvent } from 'react'; +import { useMemo, useState, useRef, useEffect, useCallback, ChangeEvent, DragEvent } from 'react'; import { useFormContext } from 'react-hook-form'; +import { motion, AnimatePresence } from 'motion/react'; import { Editor } from '@/components/primitives/editor'; import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form'; @@ -53,6 +54,8 @@ const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) const [transformers, setTransformers] = useState(initialTransformers); const updateTimeoutRef = useRef(); const isUpdatingRef = useRef(false); + const [dragOverIndex, setDragOverIndex] = useState(null); + const [draggingItem, setDraggingItem] = useState(null); // Debounced update function const debouncedUpdate = useCallback( @@ -154,38 +157,91 @@ const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) {/* Selected transformers with drag handles */} {transformers.length > 0 && (
- {transformers.map((value, index) => { - const transformer = TRANSFORMERS.find((t) => t.value === value); - return ( -
{ - e.dataTransfer.setData('text/plain', index.toString()); - }} - onDragOver={(e) => { - e.preventDefault(); - }} - onDrop={(e) => { - e.preventDefault(); - const fromIndex = parseInt(e.dataTransfer.getData('text/plain')); - moveTransformer(fromIndex, index); - }} - > - - {transformer?.label} - -
- ); - })} + {dragOverIndex === index && ( + + )} + ) => { + e.dataTransfer.setData('text/plain', index.toString()); + setDraggingItem(index); + }} + onDragEnd={() => { + setDraggingItem(null); + setDragOverIndex(null); + }} + onDragOver={(e: DragEvent) => { + e.preventDefault(); + if (dragOverIndex !== index) { + setDragOverIndex(index); + } + }} + onDragLeave={(e: DragEvent) => { + const relatedTarget = e.relatedTarget as HTMLElement; + if (!e.currentTarget.contains(relatedTarget)) { + setDragOverIndex(null); + } + }} + onDrop={(e: DragEvent) => { + e.preventDefault(); + const fromIndex = parseInt(e.dataTransfer.getData('text/plain')); + if (fromIndex !== index) { + moveTransformer(fromIndex, index); + } + setDragOverIndex(null); + setDraggingItem(null); + }} + animate={draggingItem === index ? { scale: 1.02 } : { scale: 1 }} + whileHover={{ scale: 1.01 }} + transition={{ duration: 0.15 }} + > + + {transformer?.label} + + + {dragOverIndex === index + 1 && ( + + )} + + ); + })} +
)} From 75d2c7d598d5bf0b576b0764e4de2618cfd8defa Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 23 Dec 2024 22:26:51 +0200 Subject: [PATCH 07/49] fix: state --- .../steps/email/email-subject.tsx | 173 +++++++++++++----- 1 file changed, 123 insertions(+), 50 deletions(-) diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx index bc8a6ab938e..e426e477803 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx @@ -1,7 +1,8 @@ import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; -import { useMemo, useState, useRef, useEffect, useCallback, ChangeEvent, DragEvent } from 'react'; +import { useMemo, useState, useRef, useEffect, useCallback, ChangeEvent } from 'react'; import { useFormContext } from 'react-hook-form'; import { motion, AnimatePresence } from 'motion/react'; +import { Completion, CompletionContext } from '@codemirror/autocomplete'; import { Editor } from '@/components/primitives/editor'; import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form'; @@ -183,32 +184,28 @@ const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) className={`bg-secondary hover:bg-secondary/80 group flex cursor-move items-center gap-1.5 rounded px-1.5 py-0.5 text-sm ${ draggingItem === index ? 'ring-primary opacity-50 ring-2 ring-offset-1' : '' }`} - draggable - onDragStart={(e: DragEvent) => { - e.dataTransfer.setData('text/plain', index.toString()); - setDraggingItem(index); - }} + drag + dragConstraints={{ top: 0, right: 0, bottom: 0, left: 0 }} + dragElastic={0} + dragMomentum={false} + onDragStart={() => setDraggingItem(index)} onDragEnd={() => { setDraggingItem(null); setDragOverIndex(null); }} - onDragOver={(e: DragEvent) => { - e.preventDefault(); + onDragOver={() => { if (dragOverIndex !== index) { setDragOverIndex(index); } }} - onDragLeave={(e: DragEvent) => { - const relatedTarget = e.relatedTarget as HTMLElement; - if (!e.currentTarget.contains(relatedTarget)) { + onDragLeave={(e) => { + if (!e.currentTarget.contains(e.relatedTarget as Node)) { setDragOverIndex(null); } }} - onDrop={(e: DragEvent) => { - e.preventDefault(); - const fromIndex = parseInt(e.dataTransfer.getData('text/plain')); - if (fromIndex !== index) { - moveTransformer(fromIndex, index); + onDrop={() => { + if (draggingItem !== null && draggingItem !== index) { + moveTransformer(draggingItem, index); } setDragOverIndex(null); setDraggingItem(null); @@ -319,8 +316,9 @@ export const EmailSubject = () => { const viewRef = useRef(null); const fieldRef = useRef(null); const isUpdatingRef = useRef(false); + const lastCompletionRef = useRef<{ from: number; to: number } | null>(null); - const handleVariableClick = useCallback((e: MouseEvent, view: EditorView) => { + const handleVariableClick = useCallback((e: MouseEvent) => { if (isUpdatingRef.current) return; const target = e.target as HTMLElement; @@ -330,13 +328,17 @@ export const EmailSubject = () => { e.preventDefault(); e.stopPropagation(); + // Get the variable data const variable = pill.getAttribute('data-variable'); const start = parseInt(pill.getAttribute('data-start') || '0'); const end = parseInt(pill.getAttribute('data-end') || '0'); - if (variable) { - console.log('Selected variable:', { variable, start, end }); - setSelectedVariable({ value: variable, from: start, to: end }); + // Only update if we have valid data + if (variable && start && end) { + // Use requestAnimationFrame to ensure the popover opens after the click event is fully processed + requestAnimationFrame(() => { + setSelectedVariable({ value: variable, from: start, to: end }); + }); } } }, []); @@ -396,6 +398,7 @@ export const EmailSubject = () => { return ViewPlugin.fromClass( class { decorations: DecorationSet; + lastCursor: number = 0; constructor(view: EditorView) { this.decorations = this.createDecorations(view); @@ -403,7 +406,23 @@ export const EmailSubject = () => { } update(update: any) { - if (update.docChanged || update.viewportChanged) { + if (update.docChanged || update.viewportChanged || update.selectionSet) { + this.lastCursor = update.state.selection.main.head; + + // Check if we just completed a variable + const content = update.state.doc.toString(); + const pos = update.state.selection.main.head; + if (update.docChanged && content.slice(pos - 2, pos) === '}}') { + const start = content.lastIndexOf('{{', pos); + if (start !== -1) { + const variableContent = content.slice(start + 2, pos - 2).trim(); + if (variableContent) { + // Force immediate decoration + this.lastCursor = -1; + } + } + } + this.decorations = this.createDecorations(update.view); } if (update.view) { @@ -414,7 +433,7 @@ export const EmailSubject = () => { createDecorations(view: EditorView) { const decorations: any[] = []; const content = view.state.doc.toString(); - const variableRegex = /{{(.*?)}}/g; + const variableRegex = /{{([^{}]+)}}/g; let match; while ((match = variableRegex.exec(content)) !== null) { @@ -422,32 +441,46 @@ export const EmailSubject = () => { const end = start + match[0].length; const variableName = match[1].trim(); - // Add the main variable decoration - decorations.push( - Decoration.mark({ - class: `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''}`, - attributes: { - 'data-variable': variableName, - 'data-start': start.toString(), - 'data-end': end.toString(), - }, - inclusive: true, - }).range(start, end) - ); - - // Add bracket decorations - decorations.push( - Decoration.mark({ - class: 'cm-bracket', - }).range(start, start + 2) - ); - decorations.push( - Decoration.mark({ - class: 'cm-bracket', - }).range(end - 2, end) - ); + // Create pill if: + // 1. It's a complete variable with content + // 2. Cursor is not inside the variable + // 3. Or it was just completed via autocomplete + const isJustCompleted = + lastCompletionRef.current && + start === lastCompletionRef.current.from - 2 && + end === lastCompletionRef.current.to + 2; + + if (variableName && (this.lastCursor < start || this.lastCursor > end || isJustCompleted)) { + // Add the main variable decoration + decorations.push( + Decoration.mark({ + class: `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''}`, + attributes: { + 'data-variable': variableName, + 'data-start': start.toString(), + 'data-end': end.toString(), + }, + inclusive: true, + }).range(start, end) + ); + + // Add bracket decorations + decorations.push( + Decoration.mark({ + class: 'cm-bracket', + }).range(start, start + 2) + ); + decorations.push( + Decoration.mark({ + class: 'cm-bracket', + }).range(end - 2, end) + ); + } } + // Reset completion ref after processing + lastCompletionRef.current = null; + return Decoration.set(decorations, true); } }, @@ -461,17 +494,54 @@ export const EmailSubject = () => { ); }, []); + const completionSource = useCallback( + (context: CompletionContext) => { + const word = context.matchBefore(/\{\{([^}]*)/); + if (!word) return null; + + const options = completions(variables)(context); + if (!options) return null; + + return { + ...options, + apply: (view: EditorView, completion: Completion, from: number, to: number) => { + const text = completion.label; + lastCompletionRef.current = { from, to }; + + const content = view.state.doc.toString(); + const before = content.slice(Math.max(0, from - 2), from); + + if (before !== '{{') { + view.dispatch({ + changes: { from, to, insert: `{{${text}}}` }, + }); + } else { + view.dispatch({ + changes: { from, to, insert: `${text}}}` }, + }); + } + }, + }; + }, + [variables] + ); + const extensions = useMemo( () => [ - autocompletion({ override: [completions(variables)] }), + autocompletion({ + override: [completionSource], + closeOnBlur: false, + defaultKeymap: true, + activateOnTyping: true, + }), EditorView.lineWrapping, variablePillTheme, createVariablePlugin(), EditorView.domEventHandlers({ - click: handleVariableClick, + mousedown: handleVariableClick, }), ], - [variables, handleVariableClick, createVariablePlugin] + [variables, handleVariableClick, createVariablePlugin, completionSource] ); return ( @@ -501,7 +571,10 @@ export const EmailSubject = () => { open={!!selectedVariable} onOpenChange={(open) => { if (!open) { - setSelectedVariable(null); + // Use setTimeout to prevent immediate closing + setTimeout(() => { + setSelectedVariable(null); + }, 0); } }} > From faa410b08549fa179dbf9bd6884b6c28095e6890 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 23 Dec 2024 23:30:27 +0200 Subject: [PATCH 08/49] fix: refactor field editor --- .../primitives/field-editor/field-editor.tsx | 569 +++++++++++++++++ .../primitives/field-editor/index.ts | 1 + .../steps/email/email-subject.tsx | 598 +----------------- 3 files changed, 587 insertions(+), 581 deletions(-) create mode 100644 apps/dashboard/src/components/primitives/field-editor/field-editor.tsx create mode 100644 apps/dashboard/src/components/primitives/field-editor/index.ts diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx new file mode 100644 index 00000000000..174edf09d32 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -0,0 +1,569 @@ +import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; +import { useMemo, useState, useRef, useCallback, useEffect, ChangeEvent } from 'react'; +import { Completion, CompletionContext } from '@codemirror/autocomplete'; +import { motion, AnimatePresence } from 'motion/react'; + +import { Editor } from '@/components/primitives/editor'; +import { completions } from '@/utils/liquid-autocomplete'; +import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; +import { autocompletion } from '@codemirror/autocomplete'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/primitives/popover'; +import { Button } from '@/components/primitives/button'; +import { Input, InputField } from '@/components/primitives/input'; +import { GripVertical } from 'lucide-react'; +import { FormControl, FormItem } from '@/components/primitives/form/form'; + +const TRANSFORMERS = [ + { label: 'Uppercase', value: 'upcase' }, + { label: 'Lowercase', value: 'downcase' }, + { label: 'Capitalize', value: 'capitalize' }, + { label: 'Strip HTML', value: 'strip_html' }, + { label: 'Strip Newlines', value: 'strip_newlines' }, + { label: 'Escape', value: 'escape' }, +] as const; + +interface VariablePopoverProps { + variable: string; + onClose: () => void; + onUpdate: (newValue: string) => void; +} + +const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) => { + // Parse the variable to extract name, default value, and transformers + const [variableName, defaultValue = '', initialTransformers = []] = useMemo(() => { + const parts = variable.split('|').map((part) => part.trim()); + const [nameWithDefault, ...rest] = parts; + + // Handle default value + const defaultMatch = nameWithDefault.match(/default:\s*([^}]+)/); + const name = defaultMatch ? nameWithDefault.replace(/\s*\|\s*default:[^}]+/, '') : nameWithDefault; + const defaultVal = defaultMatch ? defaultMatch[1].trim() : ''; + + // Get all transformers + const transforms = rest.filter((part) => TRANSFORMERS.some((t) => t.value === part.trim())); + + return [name, defaultVal, transforms]; + }, [variable]); + + const [name, setName] = useState(variableName); + const [defaultVal, setDefaultVal] = useState(defaultValue); + const [transformers, setTransformers] = useState(initialTransformers); + const updateTimeoutRef = useRef(); + const isUpdatingRef = useRef(false); + const [dragOverIndex, setDragOverIndex] = useState(null); + const [draggingItem, setDraggingItem] = useState(null); + + // Debounced update function + const debouncedUpdate = useCallback( + (newName: string, newDefaultVal: string, newTransformers: string[]) => { + if (isUpdatingRef.current) return; + + if (updateTimeoutRef.current) { + clearTimeout(updateTimeoutRef.current); + } + + updateTimeoutRef.current = setTimeout(() => { + try { + isUpdatingRef.current = true; + + // Build the variable parts + const parts = [newName.trim()]; + + if (newDefaultVal) { + parts.push(`default: ${newDefaultVal.trim()}`); + } + + // Add transformers in order + parts.push(...newTransformers); + + // Join with proper spacing + const finalValue = parts.join(' | '); + onUpdate(finalValue); + } finally { + isUpdatingRef.current = false; + } + }, 300); + }, + [onUpdate] + ); + + // Update when values change + useEffect(() => { + debouncedUpdate(name, defaultVal, transformers); + return () => { + if (updateTimeoutRef.current) { + clearTimeout(updateTimeoutRef.current); + } + }; + }, [name, defaultVal, transformers, debouncedUpdate]); + + const handleTransformerToggle = (value: string) => { + setTransformers((current) => { + if (current.includes(value)) { + return current.filter((t) => t !== value); + } + return [...current, value]; + }); + }; + + const moveTransformer = (from: number, to: number) => { + setTransformers((current) => { + const newTransformers = [...current]; + const [removed] = newTransformers.splice(from, 1); + newTransformers.splice(to, 0, removed); + return newTransformers; + }); + }; + + return ( + +
+
+ + +
+ + + ) => setName(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+ + +
+ + + ) => setDefaultVal(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+
+ +
+ {/* Selected transformers with drag handles */} + {transformers.length > 0 && ( +
+ + {transformers.map((value, index) => { + const transformer = TRANSFORMERS.find((t) => t.value === value); + return ( + + {dragOverIndex === index && ( + + )} + setDraggingItem(index)} + onDragEnd={() => { + setDraggingItem(null); + setDragOverIndex(null); + }} + onDragOver={() => { + if (dragOverIndex !== index) { + setDragOverIndex(index); + } + }} + onDragLeave={(e) => { + if (!e.currentTarget.contains(e.relatedTarget as Node)) { + setDragOverIndex(null); + } + }} + onDrop={() => { + if (draggingItem !== null && draggingItem !== index) { + moveTransformer(draggingItem, index); + } + setDragOverIndex(null); + setDraggingItem(null); + }} + animate={draggingItem === index ? { scale: 1.02 } : { scale: 1 }} + whileHover={{ scale: 1.01 }} + transition={{ duration: 0.15 }} + > + + {transformer?.label} + + + {dragOverIndex === index + 1 && ( + + )} + + ); + })} + +
+ )} + + {/* Available transformers */} +
+ {TRANSFORMERS.filter((t) => !transformers.includes(t.value)).map(({ label, value }) => ( + + ))} +
+
+
+
+ ); +}; + +// Create a decoration for variable pills +const variablePillTheme = EditorView.baseTheme({ + '.cm-variable-pill': { + backgroundColor: 'rgb(255 255 255 / 0.5)', + color: 'rgb(55, 65, 81)', + border: '1px solid #e5e7eb', + borderRadius: '9999px', + padding: '2px 6px 2px 24px', + margin: '0 2px', + fontFamily: 'inherit', + display: 'inline-flex', + alignItems: 'center', + height: '24px', + lineHeight: '24px', + fontSize: '14px', + cursor: 'pointer', + position: 'relative', + }, + '.cm-variable-pill::before': { + content: '""', + position: 'absolute', + left: '6px', + top: '50%', + transform: 'translateY(-50%)', + width: '14px', + height: '14px', + backgroundColor: '#E11D48', + maskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, + maskRepeat: 'no-repeat', + maskPosition: 'center', + maskSize: 'contain', + WebkitMaskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, + WebkitMaskRepeat: 'no-repeat', + WebkitMaskPosition: 'center', + WebkitMaskSize: 'contain', + }, + '.cm-variable-pill.cm-dark': { + backgroundColor: '#FFD6EE', + color: '#AD74FF', + border: '1px solid #3D3D4D', + }, + '.cm-variable-pill .cm-bracket': { + display: 'none', + }, +}); + +type FieldEditorProps = { + value: string; + onChange: (value: string) => void; + variables: LiquidVariable[]; + placeholder?: string; + autoFocus?: boolean; + size?: 'default' | 'lg'; + fontFamily?: 'inherit'; + id?: string; +}; + +export const FieldEditor = ({ + value, + onChange, + variables, + placeholder, + autoFocus, + size = 'lg', + fontFamily = 'inherit', + id, +}: FieldEditorProps) => { + const [selectedVariable, setSelectedVariable] = useState<{ value: string; from: number; to: number } | null>(null); + const viewRef = useRef(null); + const isUpdatingRef = useRef(false); + const lastCompletionRef = useRef<{ from: number; to: number } | null>(null); + + const handleVariableClick = useCallback((e: MouseEvent) => { + if (isUpdatingRef.current) return; + + const target = e.target as HTMLElement; + const pill = target.closest('.cm-variable-pill'); + + if (pill instanceof HTMLElement) { + e.preventDefault(); + e.stopPropagation(); + + const variable = pill.getAttribute('data-variable'); + const start = parseInt(pill.getAttribute('data-start') || '0'); + const end = parseInt(pill.getAttribute('data-end') || '0'); + + if (variable && start && end) { + requestAnimationFrame(() => { + setSelectedVariable({ value: variable, from: start, to: end }); + }); + } + } + }, []); + + const handleVariableUpdate = useCallback( + (newValue: string) => { + if (!selectedVariable || !viewRef.current || isUpdatingRef.current) { + return; + } + + try { + isUpdatingRef.current = true; + const { from, to } = selectedVariable; + const view = viewRef.current; + + const currentContent = view.state.doc.toString(); + const beforeContent = currentContent.slice(0, from); + const afterContent = currentContent.slice(to); + + const newVariableText = `{{${newValue}}}`; + + const changes = { + from, + to, + insert: newVariableText, + }; + + view.dispatch({ + changes, + selection: { anchor: from + newVariableText.length }, + }); + + const updatedContent = view.state.doc.toString(); + onChange(updatedContent); + + setSelectedVariable((prev) => (prev ? { ...prev, value: newValue, to: from + newVariableText.length } : null)); + } finally { + isUpdatingRef.current = false; + } + }, + [selectedVariable, onChange] + ); + + const createVariablePlugin = useCallback(() => { + return ViewPlugin.fromClass( + class { + decorations: DecorationSet; + lastCursor: number = 0; + + constructor(view: EditorView) { + this.decorations = this.createDecorations(view); + viewRef.current = view; + } + + update(update: any) { + if (update.docChanged || update.viewportChanged || update.selectionSet) { + this.lastCursor = update.state.selection.main.head; + + const content = update.state.doc.toString(); + const pos = update.state.selection.main.head; + if (update.docChanged && content.slice(pos - 2, pos) === '}}') { + const start = content.lastIndexOf('{{', pos); + if (start !== -1) { + const variableContent = content.slice(start + 2, pos - 2).trim(); + if (variableContent) { + this.lastCursor = -1; + } + } + } + + this.decorations = this.createDecorations(update.view); + } + if (update.view) { + viewRef.current = update.view; + } + } + + createDecorations(view: EditorView) { + const decorations: any[] = []; + const content = view.state.doc.toString(); + const variableRegex = /{{([^{}]+)}}/g; + let match; + + while ((match = variableRegex.exec(content)) !== null) { + const start = match.index; + const end = start + match[0].length; + const variableName = match[1].trim(); + + const isJustCompleted = + lastCompletionRef.current && + start === lastCompletionRef.current.from - 2 && + end === lastCompletionRef.current.to + 2; + + if (variableName && (this.lastCursor < start || this.lastCursor > end || isJustCompleted)) { + decorations.push( + Decoration.mark({ + class: `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''}`, + attributes: { + 'data-variable': variableName, + 'data-start': start.toString(), + 'data-end': end.toString(), + }, + inclusive: true, + }).range(start, end) + ); + + decorations.push( + Decoration.mark({ + class: 'cm-bracket', + }).range(start, start + 2) + ); + decorations.push( + Decoration.mark({ + class: 'cm-bracket', + }).range(end - 2, end) + ); + } + } + + lastCompletionRef.current = null; + + return Decoration.set(decorations, true); + } + }, + { + decorations: (v) => v.decorations, + provide: (plugin) => + EditorView.atomicRanges.of((view) => { + return view.plugin(plugin)?.decorations || Decoration.none; + }), + } + ); + }, []); + + const completionSource = useCallback( + (context: CompletionContext) => { + const word = context.matchBefore(/\{\{([^}]*)/); + if (!word) return null; + + const options = completions(variables)(context); + if (!options) return null; + + return { + ...options, + apply: (view: EditorView, completion: Completion, from: number, to: number) => { + const text = completion.label; + lastCompletionRef.current = { from, to }; + + const content = view.state.doc.toString(); + const before = content.slice(Math.max(0, from - 2), from); + + if (before !== '{{') { + view.dispatch({ + changes: { from, to, insert: `{{${text}}}` }, + }); + } else { + view.dispatch({ + changes: { from, to, insert: `${text}}}` }, + }); + } + }, + }; + }, + [variables] + ); + + const extensions = useMemo( + () => [ + autocompletion({ + override: [completionSource], + closeOnBlur: false, + defaultKeymap: true, + activateOnTyping: true, + }), + EditorView.lineWrapping, + variablePillTheme, + createVariablePlugin(), + EditorView.domEventHandlers({ + mousedown: handleVariableClick, + }), + ], + [variables, handleVariableClick, createVariablePlugin, completionSource] + ); + + return ( +
+ + { + if (!open) { + setTimeout(() => { + setSelectedVariable(null); + }, 0); + } + }} + > + +
+ + {selectedVariable && ( + setSelectedVariable(null)} + onUpdate={handleVariableUpdate} + /> + )} + +
+ ); +}; diff --git a/apps/dashboard/src/components/primitives/field-editor/index.ts b/apps/dashboard/src/components/primitives/field-editor/index.ts new file mode 100644 index 00000000000..32bdcebcdf1 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/index.ts @@ -0,0 +1 @@ +export { FieldEditor } from './field-editor'; diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx index e426e477803..f0be9e476de 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx @@ -1,601 +1,37 @@ -import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; -import { useMemo, useState, useRef, useEffect, useCallback, ChangeEvent } from 'react'; +import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; -import { motion, AnimatePresence } from 'motion/react'; -import { Completion, CompletionContext } from '@codemirror/autocomplete'; - -import { Editor } from '@/components/primitives/editor'; import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form'; import { useWorkflow } from '@/components/workflow-editor/workflow-provider'; -import { completions } from '@/utils/liquid-autocomplete'; import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables'; import { capitalize } from '@/utils/string'; -import { autocompletion } from '@codemirror/autocomplete'; -import { Popover, PopoverContent, PopoverTrigger } from '@/components/primitives/popover'; -import { Button } from '@/components/primitives/button'; -import { Input, InputField } from '@/components/primitives/input'; -import { GripVertical } from 'lucide-react'; +import { FieldEditor } from '@/components/primitives/field-editor'; const subjectKey = 'subject'; -interface VariablePopoverProps { - variable: string; - onClose: () => void; - onUpdate: (newValue: string) => void; -} - -const TRANSFORMERS = [ - { label: 'Uppercase', value: 'upcase' }, - { label: 'Lowercase', value: 'downcase' }, - { label: 'Capitalize', value: 'capitalize' }, - { label: 'Strip HTML', value: 'strip_html' }, - { label: 'Strip Newlines', value: 'strip_newlines' }, - { label: 'Escape', value: 'escape' }, -] as const; - -const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) => { - // Parse the variable to extract name, default value, and transformers - const [variableName, defaultValue = '', initialTransformers = []] = useMemo(() => { - const parts = variable.split('|').map((part) => part.trim()); - const [nameWithDefault, ...rest] = parts; - - // Handle default value - const defaultMatch = nameWithDefault.match(/default:\s*([^}]+)/); - const name = defaultMatch ? nameWithDefault.replace(/\s*\|\s*default:[^}]+/, '') : nameWithDefault; - const defaultVal = defaultMatch ? defaultMatch[1].trim() : ''; - - // Get all transformers - const transforms = rest.filter((part) => TRANSFORMERS.some((t) => t.value === part.trim())); - - return [name, defaultVal, transforms]; - }, [variable]); - - const [name, setName] = useState(variableName); - const [defaultVal, setDefaultVal] = useState(defaultValue); - const [transformers, setTransformers] = useState(initialTransformers); - const updateTimeoutRef = useRef(); - const isUpdatingRef = useRef(false); - const [dragOverIndex, setDragOverIndex] = useState(null); - const [draggingItem, setDraggingItem] = useState(null); - - // Debounced update function - const debouncedUpdate = useCallback( - (newName: string, newDefaultVal: string, newTransformers: string[]) => { - if (isUpdatingRef.current) return; - - if (updateTimeoutRef.current) { - clearTimeout(updateTimeoutRef.current); - } - - updateTimeoutRef.current = setTimeout(() => { - try { - isUpdatingRef.current = true; - - // Build the variable parts - const parts = [newName.trim()]; - - if (newDefaultVal) { - parts.push(`default: ${newDefaultVal.trim()}`); - } - - // Add transformers in order - parts.push(...newTransformers); - - // Join with proper spacing - const finalValue = parts.join(' | '); - onUpdate(finalValue); - } finally { - isUpdatingRef.current = false; - } - }, 300); - }, - [onUpdate] - ); - - // Update when values change - useEffect(() => { - debouncedUpdate(name, defaultVal, transformers); - return () => { - if (updateTimeoutRef.current) { - clearTimeout(updateTimeoutRef.current); - } - }; - }, [name, defaultVal, transformers, debouncedUpdate]); - - const handleTransformerToggle = (value: string) => { - setTransformers((current) => { - if (current.includes(value)) { - return current.filter((t) => t !== value); - } - return [...current, value]; - }); - }; - - const moveTransformer = (from: number, to: number) => { - setTransformers((current) => { - const newTransformers = [...current]; - const [removed] = newTransformers.splice(from, 1); - newTransformers.splice(to, 0, removed); - return newTransformers; - }); - }; - - return ( - -
-
- - -
- - - ) => setName(e.target.value)} - className="h-7 text-sm" - /> - -
-
-
- - -
- - - ) => setDefaultVal(e.target.value)} - className="h-7 text-sm" - /> - -
-
-
-
- -
- {/* Selected transformers with drag handles */} - {transformers.length > 0 && ( -
- - {transformers.map((value, index) => { - const transformer = TRANSFORMERS.find((t) => t.value === value); - return ( - - {dragOverIndex === index && ( - - )} - setDraggingItem(index)} - onDragEnd={() => { - setDraggingItem(null); - setDragOverIndex(null); - }} - onDragOver={() => { - if (dragOverIndex !== index) { - setDragOverIndex(index); - } - }} - onDragLeave={(e) => { - if (!e.currentTarget.contains(e.relatedTarget as Node)) { - setDragOverIndex(null); - } - }} - onDrop={() => { - if (draggingItem !== null && draggingItem !== index) { - moveTransformer(draggingItem, index); - } - setDragOverIndex(null); - setDraggingItem(null); - }} - animate={draggingItem === index ? { scale: 1.02 } : { scale: 1 }} - whileHover={{ scale: 1.01 }} - transition={{ duration: 0.15 }} - > - - {transformer?.label} - - - {dragOverIndex === index + 1 && ( - - )} - - ); - })} - -
- )} - - {/* Available transformers */} -
- {TRANSFORMERS.filter((t) => !transformers.includes(t.value)).map(({ label, value }) => ( - - ))} -
-
-
-
- ); -}; - -// Create a decoration for variable pills -const variablePillTheme = EditorView.baseTheme({ - '.cm-variable-pill': { - backgroundColor: 'rgb(255 255 255 / 0.5)', - color: 'rgb(55, 65, 81)', - border: '1px solid #e5e7eb', - borderRadius: '9999px', - padding: '2px 6px 2px 24px', - margin: '0 2px', - fontFamily: 'inherit', - display: 'inline-flex', - alignItems: 'center', - height: '24px', - lineHeight: '24px', - fontSize: '14px', - cursor: 'pointer', - position: 'relative', - }, - '.cm-variable-pill::before': { - content: '""', - position: 'absolute', - left: '6px', - top: '50%', - transform: 'translateY(-50%)', - width: '14px', - height: '14px', - backgroundColor: '#E11D48', - maskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, - maskRepeat: 'no-repeat', - maskPosition: 'center', - maskSize: 'contain', - WebkitMaskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, - WebkitMaskRepeat: 'no-repeat', - WebkitMaskPosition: 'center', - WebkitMaskSize: 'contain', - }, - '.cm-variable-pill.cm-dark': { - backgroundColor: '#FFD6EE', - color: '#AD74FF', - border: '1px solid #3D3D4D', - }, - '.cm-variable-pill .cm-bracket': { - display: 'none', - }, -}); - export const EmailSubject = () => { const { control } = useFormContext(); const { step } = useWorkflow(); const variables = useMemo(() => (step ? parseStepVariablesToLiquidVariables(step.variables) : []), [step]); - const [selectedVariable, setSelectedVariable] = useState<{ value: string; from: number; to: number } | null>(null); - const viewRef = useRef(null); - const fieldRef = useRef(null); - const isUpdatingRef = useRef(false); - const lastCompletionRef = useRef<{ from: number; to: number } | null>(null); - - const handleVariableClick = useCallback((e: MouseEvent) => { - if (isUpdatingRef.current) return; - - const target = e.target as HTMLElement; - const pill = target.closest('.cm-variable-pill'); - - if (pill instanceof HTMLElement) { - e.preventDefault(); - e.stopPropagation(); - - // Get the variable data - const variable = pill.getAttribute('data-variable'); - const start = parseInt(pill.getAttribute('data-start') || '0'); - const end = parseInt(pill.getAttribute('data-end') || '0'); - - // Only update if we have valid data - if (variable && start && end) { - // Use requestAnimationFrame to ensure the popover opens after the click event is fully processed - requestAnimationFrame(() => { - setSelectedVariable({ value: variable, from: start, to: end }); - }); - } - } - }, []); - - const handleVariableUpdate = useCallback( - (newValue: string) => { - if (!selectedVariable || !viewRef.current || !fieldRef.current || isUpdatingRef.current) { - console.log('Skipping update:', { - selectedVariable, - viewRef: !!viewRef.current, - fieldRef: !!fieldRef.current, - isUpdating: isUpdatingRef.current, - }); - return; - } - - try { - isUpdatingRef.current = true; - const { from, to } = selectedVariable; - const view = viewRef.current; - - // Get the current content and ensure we're not duplicating - const currentContent = view.state.doc.toString(); - const beforeContent = currentContent.slice(0, from); - const afterContent = currentContent.slice(to); - - // Create the new variable text - const newVariableText = `{{${newValue}}}`; - - // Create and dispatch the transaction - const changes = { - from, - to, - insert: newVariableText, - }; - - view.dispatch({ - changes, - selection: { anchor: from + newVariableText.length }, - }); - - // Get the updated content and trigger form update - const updatedContent = view.state.doc.toString(); - fieldRef.current.onChange(updatedContent); - - // Update the selected variable with new bounds - setSelectedVariable((prev) => (prev ? { ...prev, value: newValue, to: from + newVariableText.length } : null)); - } finally { - isUpdatingRef.current = false; - } - }, - [selectedVariable] - ); - - // Plugin to find and decorate variables - const createVariablePlugin = useCallback(() => { - return ViewPlugin.fromClass( - class { - decorations: DecorationSet; - lastCursor: number = 0; - - constructor(view: EditorView) { - this.decorations = this.createDecorations(view); - viewRef.current = view; - } - - update(update: any) { - if (update.docChanged || update.viewportChanged || update.selectionSet) { - this.lastCursor = update.state.selection.main.head; - - // Check if we just completed a variable - const content = update.state.doc.toString(); - const pos = update.state.selection.main.head; - if (update.docChanged && content.slice(pos - 2, pos) === '}}') { - const start = content.lastIndexOf('{{', pos); - if (start !== -1) { - const variableContent = content.slice(start + 2, pos - 2).trim(); - if (variableContent) { - // Force immediate decoration - this.lastCursor = -1; - } - } - } - - this.decorations = this.createDecorations(update.view); - } - if (update.view) { - viewRef.current = update.view; - } - } - - createDecorations(view: EditorView) { - const decorations: any[] = []; - const content = view.state.doc.toString(); - const variableRegex = /{{([^{}]+)}}/g; - let match; - - while ((match = variableRegex.exec(content)) !== null) { - const start = match.index; - const end = start + match[0].length; - const variableName = match[1].trim(); - - // Create pill if: - // 1. It's a complete variable with content - // 2. Cursor is not inside the variable - // 3. Or it was just completed via autocomplete - const isJustCompleted = - lastCompletionRef.current && - start === lastCompletionRef.current.from - 2 && - end === lastCompletionRef.current.to + 2; - - if (variableName && (this.lastCursor < start || this.lastCursor > end || isJustCompleted)) { - // Add the main variable decoration - decorations.push( - Decoration.mark({ - class: `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''}`, - attributes: { - 'data-variable': variableName, - 'data-start': start.toString(), - 'data-end': end.toString(), - }, - inclusive: true, - }).range(start, end) - ); - - // Add bracket decorations - decorations.push( - Decoration.mark({ - class: 'cm-bracket', - }).range(start, start + 2) - ); - decorations.push( - Decoration.mark({ - class: 'cm-bracket', - }).range(end - 2, end) - ); - } - } - - // Reset completion ref after processing - lastCompletionRef.current = null; - - return Decoration.set(decorations, true); - } - }, - { - decorations: (v) => v.decorations, - provide: (plugin) => - EditorView.atomicRanges.of((view) => { - return view.plugin(plugin)?.decorations || Decoration.none; - }), - } - ); - }, []); - - const completionSource = useCallback( - (context: CompletionContext) => { - const word = context.matchBefore(/\{\{([^}]*)/); - if (!word) return null; - - const options = completions(variables)(context); - if (!options) return null; - - return { - ...options, - apply: (view: EditorView, completion: Completion, from: number, to: number) => { - const text = completion.label; - lastCompletionRef.current = { from, to }; - - const content = view.state.doc.toString(); - const before = content.slice(Math.max(0, from - 2), from); - - if (before !== '{{') { - view.dispatch({ - changes: { from, to, insert: `{{${text}}}` }, - }); - } else { - view.dispatch({ - changes: { from, to, insert: `${text}}}` }, - }); - } - }, - }; - }, - [variables] - ); - - const extensions = useMemo( - () => [ - autocompletion({ - override: [completionSource], - closeOnBlur: false, - defaultKeymap: true, - activateOnTyping: true, - }), - EditorView.lineWrapping, - variablePillTheme, - createVariablePlugin(), - EditorView.domEventHandlers({ - mousedown: handleVariableClick, - }), - ], - [variables, handleVariableClick, createVariablePlugin, completionSource] - ); return ( { - // Store the field reference - fieldRef.current = field; - - return ( - <> - - -
- field.onChange(val)} - /> - { - if (!open) { - // Use setTimeout to prevent immediate closing - setTimeout(() => { - setSelectedVariable(null); - }, 0); - } - }} - > - -
- - {selectedVariable && ( - setSelectedVariable(null)} - onUpdate={handleVariableUpdate} - /> - )} - -
- - - - - ); - }} + render={({ field }) => ( + + + + + + + )} /> ); }; From 96acb701856b549f704998c21d3683f798d6cf37 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Tue, 24 Dec 2024 00:22:24 +0200 Subject: [PATCH 09/49] j --- .../primitives/field-editor/field-editor.tsx | 399 +----------------- .../field-editor/variable-plugin.ts | 98 +++++ .../field-editor/variable-popover.tsx | 251 +++++++++++ .../primitives/field-editor/variable-theme.ts | 46 ++ .../steps/in-app/in-app-body.tsx | 26 +- .../steps/in-app/in-app-subject.tsx | 13 +- .../components/workflow-editor/url-input.tsx | 11 +- 7 files changed, 416 insertions(+), 428 deletions(-) create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-theme.ts diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index 174edf09d32..278ad76cb73 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -1,307 +1,15 @@ -import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; -import { useMemo, useState, useRef, useCallback, useEffect, ChangeEvent } from 'react'; +import { EditorView } from '@uiw/react-codemirror'; +import { useMemo, useState, useRef, useCallback } from 'react'; import { Completion, CompletionContext } from '@codemirror/autocomplete'; -import { motion, AnimatePresence } from 'motion/react'; import { Editor } from '@/components/primitives/editor'; import { completions } from '@/utils/liquid-autocomplete'; import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; import { autocompletion } from '@codemirror/autocomplete'; -import { Popover, PopoverContent, PopoverTrigger } from '@/components/primitives/popover'; -import { Button } from '@/components/primitives/button'; -import { Input, InputField } from '@/components/primitives/input'; -import { GripVertical } from 'lucide-react'; -import { FormControl, FormItem } from '@/components/primitives/form/form'; - -const TRANSFORMERS = [ - { label: 'Uppercase', value: 'upcase' }, - { label: 'Lowercase', value: 'downcase' }, - { label: 'Capitalize', value: 'capitalize' }, - { label: 'Strip HTML', value: 'strip_html' }, - { label: 'Strip Newlines', value: 'strip_newlines' }, - { label: 'Escape', value: 'escape' }, -] as const; - -interface VariablePopoverProps { - variable: string; - onClose: () => void; - onUpdate: (newValue: string) => void; -} - -const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) => { - // Parse the variable to extract name, default value, and transformers - const [variableName, defaultValue = '', initialTransformers = []] = useMemo(() => { - const parts = variable.split('|').map((part) => part.trim()); - const [nameWithDefault, ...rest] = parts; - - // Handle default value - const defaultMatch = nameWithDefault.match(/default:\s*([^}]+)/); - const name = defaultMatch ? nameWithDefault.replace(/\s*\|\s*default:[^}]+/, '') : nameWithDefault; - const defaultVal = defaultMatch ? defaultMatch[1].trim() : ''; - - // Get all transformers - const transforms = rest.filter((part) => TRANSFORMERS.some((t) => t.value === part.trim())); - - return [name, defaultVal, transforms]; - }, [variable]); - - const [name, setName] = useState(variableName); - const [defaultVal, setDefaultVal] = useState(defaultValue); - const [transformers, setTransformers] = useState(initialTransformers); - const updateTimeoutRef = useRef(); - const isUpdatingRef = useRef(false); - const [dragOverIndex, setDragOverIndex] = useState(null); - const [draggingItem, setDraggingItem] = useState(null); - - // Debounced update function - const debouncedUpdate = useCallback( - (newName: string, newDefaultVal: string, newTransformers: string[]) => { - if (isUpdatingRef.current) return; - - if (updateTimeoutRef.current) { - clearTimeout(updateTimeoutRef.current); - } - - updateTimeoutRef.current = setTimeout(() => { - try { - isUpdatingRef.current = true; - - // Build the variable parts - const parts = [newName.trim()]; - - if (newDefaultVal) { - parts.push(`default: ${newDefaultVal.trim()}`); - } - - // Add transformers in order - parts.push(...newTransformers); - - // Join with proper spacing - const finalValue = parts.join(' | '); - onUpdate(finalValue); - } finally { - isUpdatingRef.current = false; - } - }, 300); - }, - [onUpdate] - ); - - // Update when values change - useEffect(() => { - debouncedUpdate(name, defaultVal, transformers); - return () => { - if (updateTimeoutRef.current) { - clearTimeout(updateTimeoutRef.current); - } - }; - }, [name, defaultVal, transformers, debouncedUpdate]); - - const handleTransformerToggle = (value: string) => { - setTransformers((current) => { - if (current.includes(value)) { - return current.filter((t) => t !== value); - } - return [...current, value]; - }); - }; - - const moveTransformer = (from: number, to: number) => { - setTransformers((current) => { - const newTransformers = [...current]; - const [removed] = newTransformers.splice(from, 1); - newTransformers.splice(to, 0, removed); - return newTransformers; - }); - }; - - return ( - -
-
- - -
- - - ) => setName(e.target.value)} - className="h-7 text-sm" - /> - -
-
-
- - -
- - - ) => setDefaultVal(e.target.value)} - className="h-7 text-sm" - /> - -
-
-
-
- -
- {/* Selected transformers with drag handles */} - {transformers.length > 0 && ( -
- - {transformers.map((value, index) => { - const transformer = TRANSFORMERS.find((t) => t.value === value); - return ( - - {dragOverIndex === index && ( - - )} - setDraggingItem(index)} - onDragEnd={() => { - setDraggingItem(null); - setDragOverIndex(null); - }} - onDragOver={() => { - if (dragOverIndex !== index) { - setDragOverIndex(index); - } - }} - onDragLeave={(e) => { - if (!e.currentTarget.contains(e.relatedTarget as Node)) { - setDragOverIndex(null); - } - }} - onDrop={() => { - if (draggingItem !== null && draggingItem !== index) { - moveTransformer(draggingItem, index); - } - setDragOverIndex(null); - setDraggingItem(null); - }} - animate={draggingItem === index ? { scale: 1.02 } : { scale: 1 }} - whileHover={{ scale: 1.01 }} - transition={{ duration: 0.15 }} - > - - {transformer?.label} - - - {dragOverIndex === index + 1 && ( - - )} - - ); - })} - -
- )} - - {/* Available transformers */} -
- {TRANSFORMERS.filter((t) => !transformers.includes(t.value)).map(({ label, value }) => ( - - ))} -
-
-
-
- ); -}; - -// Create a decoration for variable pills -const variablePillTheme = EditorView.baseTheme({ - '.cm-variable-pill': { - backgroundColor: 'rgb(255 255 255 / 0.5)', - color: 'rgb(55, 65, 81)', - border: '1px solid #e5e7eb', - borderRadius: '9999px', - padding: '2px 6px 2px 24px', - margin: '0 2px', - fontFamily: 'inherit', - display: 'inline-flex', - alignItems: 'center', - height: '24px', - lineHeight: '24px', - fontSize: '14px', - cursor: 'pointer', - position: 'relative', - }, - '.cm-variable-pill::before': { - content: '""', - position: 'absolute', - left: '6px', - top: '50%', - transform: 'translateY(-50%)', - width: '14px', - height: '14px', - backgroundColor: '#E11D48', - maskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, - maskRepeat: 'no-repeat', - maskPosition: 'center', - maskSize: 'contain', - WebkitMaskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, - WebkitMaskRepeat: 'no-repeat', - WebkitMaskPosition: 'center', - WebkitMaskSize: 'contain', - }, - '.cm-variable-pill.cm-dark': { - backgroundColor: '#FFD6EE', - color: '#AD74FF', - border: '1px solid #3D3D4D', - }, - '.cm-variable-pill .cm-bracket': { - display: 'none', - }, -}); +import { Popover, PopoverTrigger } from '@/components/primitives/popover'; +import { VariablePopover } from './variable-popover'; +import { createVariablePlugin } from './variable-plugin'; +import { variablePillTheme } from './variable-theme'; type FieldEditorProps = { value: string; @@ -390,97 +98,6 @@ export const FieldEditor = ({ [selectedVariable, onChange] ); - const createVariablePlugin = useCallback(() => { - return ViewPlugin.fromClass( - class { - decorations: DecorationSet; - lastCursor: number = 0; - - constructor(view: EditorView) { - this.decorations = this.createDecorations(view); - viewRef.current = view; - } - - update(update: any) { - if (update.docChanged || update.viewportChanged || update.selectionSet) { - this.lastCursor = update.state.selection.main.head; - - const content = update.state.doc.toString(); - const pos = update.state.selection.main.head; - if (update.docChanged && content.slice(pos - 2, pos) === '}}') { - const start = content.lastIndexOf('{{', pos); - if (start !== -1) { - const variableContent = content.slice(start + 2, pos - 2).trim(); - if (variableContent) { - this.lastCursor = -1; - } - } - } - - this.decorations = this.createDecorations(update.view); - } - if (update.view) { - viewRef.current = update.view; - } - } - - createDecorations(view: EditorView) { - const decorations: any[] = []; - const content = view.state.doc.toString(); - const variableRegex = /{{([^{}]+)}}/g; - let match; - - while ((match = variableRegex.exec(content)) !== null) { - const start = match.index; - const end = start + match[0].length; - const variableName = match[1].trim(); - - const isJustCompleted = - lastCompletionRef.current && - start === lastCompletionRef.current.from - 2 && - end === lastCompletionRef.current.to + 2; - - if (variableName && (this.lastCursor < start || this.lastCursor > end || isJustCompleted)) { - decorations.push( - Decoration.mark({ - class: `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''}`, - attributes: { - 'data-variable': variableName, - 'data-start': start.toString(), - 'data-end': end.toString(), - }, - inclusive: true, - }).range(start, end) - ); - - decorations.push( - Decoration.mark({ - class: 'cm-bracket', - }).range(start, start + 2) - ); - decorations.push( - Decoration.mark({ - class: 'cm-bracket', - }).range(end - 2, end) - ); - } - } - - lastCompletionRef.current = null; - - return Decoration.set(decorations, true); - } - }, - { - decorations: (v) => v.decorations, - provide: (plugin) => - EditorView.atomicRanges.of((view) => { - return view.plugin(plugin)?.decorations || Decoration.none; - }), - } - ); - }, []); - const completionSource = useCallback( (context: CompletionContext) => { const word = context.matchBefore(/\{\{([^}]*)/); @@ -523,12 +140,12 @@ export const FieldEditor = ({ }), EditorView.lineWrapping, variablePillTheme, - createVariablePlugin(), + createVariablePlugin({ viewRef, lastCompletionRef }), EditorView.domEventHandlers({ mousedown: handleVariableClick, }), ], - [variables, handleVariableClick, createVariablePlugin, completionSource] + [variables, handleVariableClick, completionSource] ); return ( diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts new file mode 100644 index 00000000000..396fdedb5fe --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts @@ -0,0 +1,98 @@ +import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; +import { MutableRefObject } from 'react'; + +interface PluginState { + viewRef: MutableRefObject; + lastCompletionRef: MutableRefObject<{ from: number; to: number } | null>; +} + +export function createVariablePlugin({ viewRef, lastCompletionRef }: PluginState) { + return ViewPlugin.fromClass( + class { + decorations: DecorationSet; + lastCursor: number = 0; + + constructor(view: EditorView) { + this.decorations = this.createDecorations(view); + viewRef.current = view; + } + + update(update: any) { + if (update.docChanged || update.viewportChanged || update.selectionSet) { + this.lastCursor = update.state.selection.main.head; + + const content = update.state.doc.toString(); + const pos = update.state.selection.main.head; + if (update.docChanged && content.slice(pos - 2, pos) === '}}') { + const start = content.lastIndexOf('{{', pos); + if (start !== -1) { + const variableContent = content.slice(start + 2, pos - 2).trim(); + if (variableContent) { + this.lastCursor = -1; + } + } + } + + this.decorations = this.createDecorations(update.view); + } + if (update.view) { + viewRef.current = update.view; + } + } + + createDecorations(view: EditorView) { + const decorations: any[] = []; + const content = view.state.doc.toString(); + const variableRegex = /{{([^{}]+)}}/g; + let match; + + while ((match = variableRegex.exec(content)) !== null) { + const start = match.index; + const end = start + match[0].length; + const variableName = match[1].trim(); + + const isJustCompleted = + lastCompletionRef.current && + start === lastCompletionRef.current.from - 2 && + end === lastCompletionRef.current.to + 2; + + if (variableName) { + decorations.push( + Decoration.mark({ + class: `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''}`, + attributes: { + 'data-variable': variableName, + 'data-start': start.toString(), + 'data-end': end.toString(), + }, + inclusive: true, + }).range(start, end) + ); + + decorations.push( + Decoration.mark({ + class: 'cm-bracket', + }).range(start, start + 2) + ); + decorations.push( + Decoration.mark({ + class: 'cm-bracket', + }).range(end - 2, end) + ); + } + } + + lastCompletionRef.current = null; + + return Decoration.set(decorations, true); + } + }, + { + decorations: (v) => v.decorations, + provide: (plugin) => + EditorView.atomicRanges.of((view) => { + return view.plugin(plugin)?.decorations || Decoration.none; + }), + } + ); +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx new file mode 100644 index 00000000000..309f3fe9f88 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx @@ -0,0 +1,251 @@ +import { useMemo, useState, useRef, useCallback, useEffect, ChangeEvent } from 'react'; +import { motion, AnimatePresence } from 'motion/react'; +import { PopoverContent } from '@/components/primitives/popover'; +import { Button } from '@/components/primitives/button'; +import { Input, InputField } from '@/components/primitives/input'; +import { GripVertical } from 'lucide-react'; +import { FormControl, FormItem } from '@/components/primitives/form/form'; + +const TRANSFORMERS = [ + { label: 'Uppercase', value: 'upcase' }, + { label: 'Lowercase', value: 'downcase' }, + { label: 'Capitalize', value: 'capitalize' }, + { label: 'Strip HTML', value: 'strip_html' }, + { label: 'Strip Newlines', value: 'strip_newlines' }, + { label: 'Escape', value: 'escape' }, +] as const; + +interface VariablePopoverProps { + variable: string; + onClose: () => void; + onUpdate: (newValue: string) => void; +} + +export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) => { + // Parse the variable to extract name, default value, and transformers + const [variableName, defaultValue = '', initialTransformers = []] = useMemo(() => { + const parts = variable.split('|').map((part) => part.trim()); + const [nameWithDefault, ...rest] = parts; + + // Handle default value + const defaultMatch = nameWithDefault.match(/default:\s*([^}]+)/); + const name = defaultMatch ? nameWithDefault.replace(/\s*\|\s*default:[^}]+/, '') : nameWithDefault; + const defaultVal = defaultMatch ? defaultMatch[1].trim() : ''; + + // Get all transformers + const transforms = rest.filter((part) => TRANSFORMERS.some((t) => t.value === part.trim())); + + return [name, defaultVal, transforms]; + }, [variable]); + + const [name, setName] = useState(variableName); + const [defaultVal, setDefaultVal] = useState(defaultValue); + const [transformers, setTransformers] = useState(initialTransformers); + const updateTimeoutRef = useRef(); + const isUpdatingRef = useRef(false); + const [dragOverIndex, setDragOverIndex] = useState(null); + const [draggingItem, setDraggingItem] = useState(null); + + // Debounced update function + const debouncedUpdate = useCallback( + (newName: string, newDefaultVal: string, newTransformers: string[]) => { + if (isUpdatingRef.current) return; + + if (updateTimeoutRef.current) { + clearTimeout(updateTimeoutRef.current); + } + + updateTimeoutRef.current = setTimeout(() => { + try { + isUpdatingRef.current = true; + + // Build the variable parts + const parts = [newName.trim()]; + + if (newDefaultVal) { + parts.push(`default: ${newDefaultVal.trim()}`); + } + + // Add transformers in order + parts.push(...newTransformers); + + // Join with proper spacing + const finalValue = parts.join(' | '); + onUpdate(finalValue); + } finally { + isUpdatingRef.current = false; + } + }, 300); + }, + [onUpdate] + ); + + // Update when values change + useEffect(() => { + debouncedUpdate(name, defaultVal, transformers); + return () => { + if (updateTimeoutRef.current) { + clearTimeout(updateTimeoutRef.current); + } + }; + }, [name, defaultVal, transformers, debouncedUpdate]); + + const handleTransformerToggle = (value: string) => { + setTransformers((current) => { + if (current.includes(value)) { + return current.filter((t) => t !== value); + } + return [...current, value]; + }); + }; + + const moveTransformer = (from: number, to: number) => { + setTransformers((current) => { + const newTransformers = [...current]; + const [removed] = newTransformers.splice(from, 1); + newTransformers.splice(to, 0, removed); + return newTransformers; + }); + }; + + return ( + +
+
+ + +
+ + + ) => setName(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+ + +
+ + + ) => setDefaultVal(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+
+ +
+ {/* Selected transformers with drag handles */} + {transformers.length > 0 && ( +
+ + {transformers.map((value, index) => { + const transformer = TRANSFORMERS.find((t) => t.value === value); + return ( + + {dragOverIndex === index && ( + + )} + setDraggingItem(index)} + onDragEnd={() => { + setDraggingItem(null); + setDragOverIndex(null); + }} + onDragOver={() => { + if (dragOverIndex !== index) { + setDragOverIndex(index); + } + }} + onDragLeave={(e) => { + if (!e.currentTarget.contains(e.relatedTarget as Node)) { + setDragOverIndex(null); + } + }} + onDrop={() => { + if (draggingItem !== null && draggingItem !== index) { + moveTransformer(draggingItem, index); + } + setDragOverIndex(null); + setDraggingItem(null); + }} + animate={draggingItem === index ? { scale: 1.02 } : { scale: 1 }} + whileHover={{ scale: 1.01 }} + transition={{ duration: 0.15 }} + > + + {transformer?.label} + + + {dragOverIndex === index + 1 && ( + + )} + + ); + })} + +
+ )} + + {/* Available transformers */} +
+ {TRANSFORMERS.filter((t) => !transformers.includes(t.value)).map(({ label, value }) => ( + + ))} +
+
+
+
+ ); +}; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts b/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts new file mode 100644 index 00000000000..e20155c6683 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts @@ -0,0 +1,46 @@ +import { EditorView } from '@uiw/react-codemirror'; + +export const variablePillTheme = EditorView.baseTheme({ + '.cm-variable-pill': { + backgroundColor: 'rgb(255 255 255 / 0.5)', + color: 'rgb(55, 65, 81)', + border: '1px solid #e5e7eb', + borderRadius: '9999px', + padding: '2px 6px 2px 24px', + margin: '0 2px', + fontFamily: 'inherit', + display: 'inline-flex', + alignItems: 'center', + height: '24px', + lineHeight: '24px', + fontSize: '14px', + cursor: 'pointer', + position: 'relative', + }, + '.cm-variable-pill::before': { + content: '""', + position: 'absolute', + left: '6px', + top: '50%', + transform: 'translateY(-50%)', + width: '14px', + height: '14px', + backgroundColor: '#E11D48', + maskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, + maskRepeat: 'no-repeat', + maskPosition: 'center', + maskSize: 'contain', + WebkitMaskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, + WebkitMaskRepeat: 'no-repeat', + WebkitMaskPosition: 'center', + WebkitMaskSize: 'contain', + }, + '.cm-variable-pill.cm-dark': { + backgroundColor: '#FFD6EE', + color: '#AD74FF', + border: '1px solid #3D3D4D', + }, + '.cm-variable-pill .cm-bracket': { + display: 'none', + }, +}); diff --git a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx index 27b863d2297..eba9890f2c7 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx @@ -1,30 +1,19 @@ -import { EditorView } from '@uiw/react-codemirror'; import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; -import { Editor } from '@/components/primitives/editor'; import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form'; -import { InputField } from '@/components/primitives/input'; import { useWorkflow } from '@/components/workflow-editor/workflow-provider'; -import { completions } from '@/utils/liquid-autocomplete'; import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables'; import { capitalize } from '@/utils/string'; -import { autocompletion } from '@codemirror/autocomplete'; +import { FieldEditor } from '@/components/primitives/field-editor'; +import { InputField } from '../../../primitives/input'; const bodyKey = 'body'; -const basicSetup = { - defaultKeymap: true, -}; - export const InAppBody = () => { const { control } = useFormContext(); const { step } = useWorkflow(); const variables = useMemo(() => (step ? parseStepVariablesToLiquidVariables(step.variables) : []), [step]); - const extensions = useMemo( - () => [autocompletion({ override: [completions(variables)] }), EditorView.lineWrapping], - [variables] - ); return ( { render={({ field }) => ( - - + diff --git a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-subject.tsx index 9000a5c0e6f..410dc9cfcf3 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-subject.tsx @@ -1,15 +1,12 @@ -import { EditorView } from '@uiw/react-codemirror'; import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; -import { Editor } from '@/components/primitives/editor'; import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form'; import { InputField } from '@/components/primitives/input'; import { useWorkflow } from '@/components/workflow-editor/workflow-provider'; -import { completions } from '@/utils/liquid-autocomplete'; import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables'; import { capitalize } from '@/utils/string'; -import { autocompletion } from '@codemirror/autocomplete'; +import { FieldEditor } from '@/components/primitives/field-editor'; const subjectKey = 'subject'; @@ -17,10 +14,6 @@ export const InAppSubject = () => { const { control } = useFormContext(); const { step } = useWorkflow(); const variables = useMemo(() => (step ? parseStepVariablesToLiquidVariables(step.variables) : []), [step]); - const extensions = useMemo( - () => [autocompletion({ override: [completions(variables)] }), EditorView.lineWrapping], - [variables] - ); return ( { - diff --git a/apps/dashboard/src/components/workflow-editor/url-input.tsx b/apps/dashboard/src/components/workflow-editor/url-input.tsx index 1e767ecadca..569ca69fee5 100644 --- a/apps/dashboard/src/components/workflow-editor/url-input.tsx +++ b/apps/dashboard/src/components/workflow-editor/url-input.tsx @@ -1,14 +1,11 @@ -import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; -import { Editor } from '@/components/primitives/editor'; import { FormControl, FormField, FormItem, FormMessagePure } from '@/components/primitives/form/form'; import { Input, InputFieldProps, InputFieldPure, InputProps } from '@/components/primitives/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; -import { completions } from '@/utils/liquid-autocomplete'; import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; -import { autocompletion } from '@codemirror/autocomplete'; import { useSaveForm } from '@/components/workflow-editor/steps/save-form-context'; +import { FieldEditor } from '@/components/primitives/field-editor'; type URLInputProps = Omit & { options: string[]; @@ -34,7 +31,6 @@ export const URLInput = ({ const url = getFieldState(`${urlKey}`); const target = getFieldState(`${targetKey}`); const error = url.error || target.error; - const extensions = useMemo(() => [autocompletion({ override: [completions(variables)] })], [variables]); return (
@@ -48,13 +44,12 @@ export const URLInput = ({ {asEditor ? ( - ) : ( From 80ed1f3e9cd67dd8d403911bbd9a73011746ca8c Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Tue, 24 Dec 2024 00:28:05 +0200 Subject: [PATCH 10/49] fix: update other field --- .../primitives/field-editor/field-editor.tsx | 2 +- .../workflow-editor/steps/base/base-body.tsx | 22 +++++-------------- .../steps/base/base-subject.tsx | 13 +++-------- .../steps/email/email-subject.tsx | 1 + 4 files changed, 10 insertions(+), 28 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index 278ad76cb73..6c242572105 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -28,7 +28,7 @@ export const FieldEditor = ({ variables, placeholder, autoFocus, - size = 'lg', + size = 'default', fontFamily = 'inherit', id, }: FieldEditorProps) => { diff --git a/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx b/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx index ba345ba6c0c..64861646b28 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx @@ -1,30 +1,19 @@ -import { EditorView } from '@uiw/react-codemirror'; import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; -import { Editor } from '@/components/primitives/editor'; import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form'; import { InputField } from '@/components/primitives/input'; import { useWorkflow } from '@/components/workflow-editor/workflow-provider'; -import { completions } from '@/utils/liquid-autocomplete'; import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables'; import { capitalize } from '@/utils/string'; -import { autocompletion } from '@codemirror/autocomplete'; +import { FieldEditor } from '@/components/primitives/field-editor'; const bodyKey = 'body'; -const basicSetup = { - defaultKeymap: true, -}; - export const BaseBody = () => { const { control } = useFormContext(); const { step } = useWorkflow(); const variables = useMemo(() => (step ? parseStepVariablesToLiquidVariables(step.variables) : []), [step]); - const extensions = useMemo( - () => [autocompletion({ override: [completions(variables)] }), EditorView.lineWrapping], - [variables] - ); return ( { render={({ field }) => ( - - + diff --git a/apps/dashboard/src/components/workflow-editor/steps/base/base-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/base/base-subject.tsx index e71107874ab..efb658b7187 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/base/base-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/base/base-subject.tsx @@ -1,15 +1,12 @@ -import { EditorView } from '@uiw/react-codemirror'; import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; -import { Editor } from '@/components/primitives/editor'; import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form'; import { InputField } from '@/components/primitives/input'; import { useWorkflow } from '@/components/workflow-editor/workflow-provider'; -import { completions } from '@/utils/liquid-autocomplete'; import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables'; import { capitalize } from '@/utils/string'; -import { autocompletion } from '@codemirror/autocomplete'; +import { FieldEditor } from '@/components/primitives/field-editor'; const subjectKey = 'subject'; @@ -17,10 +14,6 @@ export const BaseSubject = () => { const { control } = useFormContext(); const { step } = useWorkflow(); const variables = useMemo(() => (step ? parseStepVariablesToLiquidVariables(step.variables) : []), [step]); - const extensions = useMemo( - () => [autocompletion({ override: [completions(variables)] }), EditorView.lineWrapping], - [variables] - ); return ( { - diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx index f0be9e476de..42e1243bc7c 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx @@ -21,6 +21,7 @@ export const EmailSubject = () => { Date: Tue, 24 Dec 2024 00:30:32 +0200 Subject: [PATCH 11/49] fix: refactor --- .../primitives/field-editor/field-editor.tsx | 2 +- .../steps/controls/text-widget.tsx | 18 ++++++------------ .../steps/digest/digest-key.tsx | 12 ++++-------- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index 6c242572105..d164796167f 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -51,7 +51,7 @@ export const FieldEditor = ({ const start = parseInt(pill.getAttribute('data-start') || '0'); const end = parseInt(pill.getAttribute('data-end') || '0'); - if (variable && start && end) { + if (variable && end) { requestAnimationFrame(() => { setSelectedVariable({ value: variable, from: start, to: end }); }); diff --git a/apps/dashboard/src/components/workflow-editor/steps/controls/text-widget.tsx b/apps/dashboard/src/components/workflow-editor/steps/controls/text-widget.tsx index 2bcd47b4231..ae6761e03f0 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/controls/text-widget.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/controls/text-widget.tsx @@ -1,16 +1,13 @@ -import { Editor } from '@/components/primitives/editor'; import { FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/primitives/form/form'; import { Input, InputField } from '@/components/primitives/input'; import { useWorkflow } from '@/components/workflow-editor/workflow-provider'; -import { completions } from '@/utils/liquid-autocomplete'; import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables'; import { capitalize } from '@/utils/string'; -import { autocompletion } from '@codemirror/autocomplete'; import { type WidgetProps } from '@rjsf/utils'; -import { EditorView } from '@uiw/react-codemirror'; import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; import { getFieldName } from './template-utils'; +import { FieldEditor } from '@/components/primitives/field-editor'; export function TextWidget(props: WidgetProps) { const { label, readonly, disabled, id, required } = props; @@ -20,10 +17,6 @@ export function TextWidget(props: WidgetProps) { const extractedName = useMemo(() => getFieldName(id), [id]); const isNumberType = useMemo(() => props.schema.type === 'number', [props.schema.type]); - const extensions = useMemo( - () => [autocompletion({ override: [completions(variables)] }), EditorView.lineWrapping], - [variables] - ); return ( ) : ( - )} diff --git a/apps/dashboard/src/components/workflow-editor/steps/digest/digest-key.tsx b/apps/dashboard/src/components/workflow-editor/steps/digest/digest-key.tsx index 823e265afe3..fed357a9786 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/digest/digest-key.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/digest/digest-key.tsx @@ -1,20 +1,17 @@ import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; -import { autocompletion } from '@codemirror/autocomplete'; import { FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/primitives/form/form'; import { InputFieldPure } from '@/components/primitives/input'; import { Code2 } from '@/components/icons/code-2'; -import { Editor } from '@/components/primitives/editor'; +import { FieldEditor } from '@/components/primitives/field-editor'; import { useWorkflow } from '@/components/workflow-editor/workflow-provider'; import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables'; -import { completions } from '@/utils/liquid-autocomplete'; export const DigestKey = () => { const { control } = useFormContext(); const { step } = useWorkflow(); const variables = useMemo(() => (step ? parseStepVariablesToLiquidVariables(step.variables) : []), [step]); - const extensions = useMemo(() => [autocompletion({ override: [completions(variables)] })], [variables]); return ( { subscriberId - From 25141c8b9191f74ac0466a5a8f362267fa3e117c Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Tue, 24 Dec 2024 00:32:15 +0200 Subject: [PATCH 12/49] fix: item --- .../src/components/primitives/field-editor/field-editor.tsx | 5 +---- .../components/primitives/field-editor/variable-plugin.ts | 5 ----- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index d164796167f..e65f0ce2319 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -70,10 +70,6 @@ export const FieldEditor = ({ const { from, to } = selectedVariable; const view = viewRef.current; - const currentContent = view.state.doc.toString(); - const beforeContent = currentContent.slice(0, from); - const afterContent = currentContent.slice(to); - const newVariableText = `{{${newValue}}}`; const changes = { @@ -88,6 +84,7 @@ export const FieldEditor = ({ }); const updatedContent = view.state.doc.toString(); + onChange(updatedContent); setSelectedVariable((prev) => (prev ? { ...prev, value: newValue, to: from + newVariableText.length } : null)); diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts index 396fdedb5fe..02d6051cfc2 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts @@ -51,11 +51,6 @@ export function createVariablePlugin({ viewRef, lastCompletionRef }: PluginState const end = start + match[0].length; const variableName = match[1].trim(); - const isJustCompleted = - lastCompletionRef.current && - start === lastCompletionRef.current.from - 2 && - end === lastCompletionRef.current.to + 2; - if (variableName) { decorations.push( Decoration.mark({ From c32cd7e3e6679d7006ef6466260883ea38a39692 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Tue, 24 Dec 2024 00:46:17 +0200 Subject: [PATCH 13/49] fix: --- .../primitives/field-editor/field-editor.tsx | 27 ++++++------ .../field-editor/variable-plugin.ts | 42 ++++++++++++++++++- .../primitives/field-editor/variable-theme.ts | 19 +++++++++ .../workflow-editor/steps/base/base-body.tsx | 24 ++++++----- 4 files changed, 88 insertions(+), 24 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index e65f0ce2319..f9d07377780 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -114,11 +114,11 @@ export const FieldEditor = ({ if (before !== '{{') { view.dispatch({ - changes: { from, to, insert: `{{${text}}}` }, + changes: { from, to, insert: `{{${text}}} ` }, }); } else { view.dispatch({ - changes: { from, to, insert: `${text}}}` }, + changes: { from, to, insert: `${text}}} ` }, }); } }, @@ -147,16 +147,19 @@ export const FieldEditor = ({ return (
- +
+ +
{ diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts index 02d6051cfc2..0660f6fbd13 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts @@ -1,4 +1,4 @@ -import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; +import { EditorView, ViewPlugin, Decoration, DecorationSet, WidgetType } from '@uiw/react-codemirror'; import { MutableRefObject } from 'react'; interface PluginState { @@ -6,6 +6,31 @@ interface PluginState { lastCompletionRef: MutableRefObject<{ from: number; to: number } | null>; } +class CursorWidget extends WidgetType { + toDOM() { + const dom = document.createElement('span'); + dom.className = 'cm-cursor-target'; + dom.style.display = 'inline-block'; + dom.style.width = '4px'; + dom.style.height = '1em'; + dom.textContent = '\u200B'; // Zero-width space to ensure cursor visibility + return dom; + } + + eq(other: CursorWidget) { + return true; + } + updateDOM(dom: HTMLElement) { + return true; + } + get estimatedHeight() { + return -1; + } + ignoreEvent() { + return false; + } +} + export function createVariablePlugin({ viewRef, lastCompletionRef }: PluginState) { return ViewPlugin.fromClass( class { @@ -29,6 +54,13 @@ export function createVariablePlugin({ viewRef, lastCompletionRef }: PluginState const variableContent = content.slice(start + 2, pos - 2).trim(); if (variableContent) { this.lastCursor = -1; + // Add a space after the variable if there isn't one + if (pos === content.length || content[pos] !== ' ') { + update.view.dispatch({ + changes: { from: pos, insert: ' ' }, + selection: { anchor: pos + 1 }, + }); + } } } } @@ -74,6 +106,14 @@ export function createVariablePlugin({ viewRef, lastCompletionRef }: PluginState class: 'cm-bracket', }).range(end - 2, end) ); + + // Add a widget after each variable pill + decorations.push( + Decoration.widget({ + widget: new CursorWidget(), + side: 1, + }).range(end) + ); } } diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts b/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts index e20155c6683..6f410aebd47 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts @@ -43,4 +43,23 @@ export const variablePillTheme = EditorView.baseTheme({ '.cm-variable-pill .cm-bracket': { display: 'none', }, + '.cm-cursor-target': { + display: 'inline-block', + width: '8px', + minWidth: '8px', + height: '24px', + cursor: 'text', + verticalAlign: 'middle', + margin: '0 1px', + }, + '.cm-content': { + minHeight: '100%', + display: 'flex', + flexDirection: 'column', + }, + '.cm-line': { + minHeight: '32px', + display: 'flex', + alignItems: 'center', + }, }); diff --git a/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx b/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx index 64861646b28..91b86e5350c 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx @@ -22,17 +22,19 @@ export const BaseBody = () => { render={({ field }) => ( - - + +
+ +
{`This supports markdown and variables, type {{ for more.`} From 2c3e6a951cf57a3105290541dd130338c00bc1f0 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Tue, 24 Dec 2024 01:02:50 +0200 Subject: [PATCH 14/49] fix: cursor --- .../field-editor/variable-plugin.ts | 35 +------------------ .../primitives/field-editor/variable-theme.ts | 10 +----- 2 files changed, 2 insertions(+), 43 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts index 0660f6fbd13..b57692cf54e 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts @@ -1,4 +1,4 @@ -import { EditorView, ViewPlugin, Decoration, DecorationSet, WidgetType } from '@uiw/react-codemirror'; +import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; import { MutableRefObject } from 'react'; interface PluginState { @@ -6,31 +6,6 @@ interface PluginState { lastCompletionRef: MutableRefObject<{ from: number; to: number } | null>; } -class CursorWidget extends WidgetType { - toDOM() { - const dom = document.createElement('span'); - dom.className = 'cm-cursor-target'; - dom.style.display = 'inline-block'; - dom.style.width = '4px'; - dom.style.height = '1em'; - dom.textContent = '\u200B'; // Zero-width space to ensure cursor visibility - return dom; - } - - eq(other: CursorWidget) { - return true; - } - updateDOM(dom: HTMLElement) { - return true; - } - get estimatedHeight() { - return -1; - } - ignoreEvent() { - return false; - } -} - export function createVariablePlugin({ viewRef, lastCompletionRef }: PluginState) { return ViewPlugin.fromClass( class { @@ -106,14 +81,6 @@ export function createVariablePlugin({ viewRef, lastCompletionRef }: PluginState class: 'cm-bracket', }).range(end - 2, end) ); - - // Add a widget after each variable pill - decorations.push( - Decoration.widget({ - widget: new CursorWidget(), - side: 1, - }).range(end) - ); } } diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts b/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts index 6f410aebd47..8d5e674d0cc 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts @@ -16,6 +16,7 @@ export const variablePillTheme = EditorView.baseTheme({ fontSize: '14px', cursor: 'pointer', position: 'relative', + marginRight: '0px', }, '.cm-variable-pill::before': { content: '""', @@ -43,15 +44,6 @@ export const variablePillTheme = EditorView.baseTheme({ '.cm-variable-pill .cm-bracket': { display: 'none', }, - '.cm-cursor-target': { - display: 'inline-block', - width: '8px', - minWidth: '8px', - height: '24px', - cursor: 'text', - verticalAlign: 'middle', - margin: '0 1px', - }, '.cm-content': { minHeight: '100%', display: 'flex', From bf9324a9f6967141637633f6df0e0216defbc9e4 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Tue, 24 Dec 2024 01:07:52 +0200 Subject: [PATCH 15/49] Update field-editor.tsx --- .../primitives/field-editor/field-editor.tsx | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index f9d07377780..3f5d9618462 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -147,19 +147,17 @@ export const FieldEditor = ({ return (
-
- -
+ { From fefc2ccd1939369d13fcbdad656c11239860a240 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Tue, 24 Dec 2024 12:24:31 +0200 Subject: [PATCH 16/49] fix: a --- .../primitives/field-editor/field-editor.tsx | 30 ++---- .../field-editor/variable-plugin.ts | 102 +++++++++++++----- .../primitives/field-editor/variable-theme.ts | 17 +++ 3 files changed, 99 insertions(+), 50 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index 3f5d9618462..22e2c015f04 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -37,26 +37,11 @@ export const FieldEditor = ({ const isUpdatingRef = useRef(false); const lastCompletionRef = useRef<{ from: number; to: number } | null>(null); - const handleVariableClick = useCallback((e: MouseEvent) => { + const handleVariableSelect = useCallback((value: string, from: number, to: number) => { if (isUpdatingRef.current) return; - - const target = e.target as HTMLElement; - const pill = target.closest('.cm-variable-pill'); - - if (pill instanceof HTMLElement) { - e.preventDefault(); - e.stopPropagation(); - - const variable = pill.getAttribute('data-variable'); - const start = parseInt(pill.getAttribute('data-start') || '0'); - const end = parseInt(pill.getAttribute('data-end') || '0'); - - if (variable && end) { - requestAnimationFrame(() => { - setSelectedVariable({ value: variable, from: start, to: end }); - }); - } - } + requestAnimationFrame(() => { + setSelectedVariable({ value, from, to }); + }); }, []); const handleVariableUpdate = useCallback( @@ -137,12 +122,9 @@ export const FieldEditor = ({ }), EditorView.lineWrapping, variablePillTheme, - createVariablePlugin({ viewRef, lastCompletionRef }), - EditorView.domEventHandlers({ - mousedown: handleVariableClick, - }), + createVariablePlugin({ viewRef, lastCompletionRef, onSelect: handleVariableSelect }), ], - [variables, handleVariableClick, completionSource] + [variables, completionSource, handleVariableSelect] ); return ( diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts index b57692cf54e..abd8a040c77 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts @@ -1,12 +1,72 @@ -import { EditorView, ViewPlugin, Decoration, DecorationSet } from '@uiw/react-codemirror'; +import { EditorView, ViewPlugin, Decoration, DecorationSet, WidgetType } from '@uiw/react-codemirror'; import { MutableRefObject } from 'react'; interface PluginState { viewRef: MutableRefObject; lastCompletionRef: MutableRefObject<{ from: number; to: number } | null>; + onSelect?: (value: string, from: number, to: number) => void; } -export function createVariablePlugin({ viewRef, lastCompletionRef }: PluginState) { +class VariablePillWidget extends WidgetType { + constructor( + private variableName: string, + private fullVariableName: string, + private start: number, + private end: number, + private hasModifiers: boolean, + private onSelect?: (value: string, from: number, to: number) => void + ) { + super(); + } + + toDOM() { + const span = document.createElement('span'); + const pillClass = `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''} ${this.hasModifiers ? 'has-modifiers' : ''}`; + span.className = pillClass; + span.setAttribute('data-variable', this.fullVariableName); + span.setAttribute('data-start', this.start.toString()); + span.setAttribute('data-end', this.end.toString()); + span.setAttribute('data-display', this.variableName); + span.textContent = this.variableName; + + const handleClick = (e: MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + setTimeout(() => { + this.onSelect?.(this.fullVariableName, this.start, this.end); + }, 0); + }; + + span.addEventListener('mousedown', handleClick); + (span as any)._variableClickHandler = handleClick; + + return span; + } + + eq(other: VariablePillWidget) { + return ( + other.variableName === this.variableName && + other.fullVariableName === this.fullVariableName && + other.start === this.start && + other.end === this.end && + other.hasModifiers === this.hasModifiers + ); + } + + destroy(dom: HTMLElement) { + if ((dom as any)._variableClickHandler) { + dom.removeEventListener('mousedown', (dom as any)._variableClickHandler); + delete (dom as any)._variableClickHandler; + } + } + + ignoreEvent() { + return false; + } +} + +export function createVariablePlugin({ viewRef, lastCompletionRef, onSelect }: PluginState) { return ViewPlugin.fromClass( class { decorations: DecorationSet; @@ -23,17 +83,19 @@ export function createVariablePlugin({ viewRef, lastCompletionRef }: PluginState const content = update.state.doc.toString(); const pos = update.state.selection.main.head; + if (update.docChanged && content.slice(pos - 2, pos) === '}}') { const start = content.lastIndexOf('{{', pos); if (start !== -1) { const variableContent = content.slice(start + 2, pos - 2).trim(); if (variableContent) { this.lastCursor = -1; - // Add a space after the variable if there isn't one if (pos === content.length || content[pos] !== ' ') { - update.view.dispatch({ - changes: { from: pos, insert: ' ' }, - selection: { anchor: pos + 1 }, + requestAnimationFrame(() => { + update.view.dispatch({ + changes: { from: pos, insert: ' ' }, + selection: { anchor: pos + 1 }, + }); }); } } @@ -56,36 +118,24 @@ export function createVariablePlugin({ viewRef, lastCompletionRef }: PluginState while ((match = variableRegex.exec(content)) !== null) { const start = match.index; const end = start + match[0].length; - const variableName = match[1].trim(); + const fullVariableName = match[1].trim(); + + const parts = fullVariableName.split('|').map((part) => part.trim()); + const variableName = parts[0]; + const hasModifiers = parts.length > 1; if (variableName) { decorations.push( - Decoration.mark({ - class: `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''}`, - attributes: { - 'data-variable': variableName, - 'data-start': start.toString(), - 'data-end': end.toString(), - }, + Decoration.replace({ + widget: new VariablePillWidget(variableName, fullVariableName, start, end, hasModifiers, onSelect), inclusive: true, + side: 1, }).range(start, end) ); - - decorations.push( - Decoration.mark({ - class: 'cm-bracket', - }).range(start, start + 2) - ); - decorations.push( - Decoration.mark({ - class: 'cm-bracket', - }).range(end - 2, end) - ); } } lastCompletionRef.current = null; - return Decoration.set(decorations, true); } }, diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts b/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts index 8d5e674d0cc..d1e1e4780c6 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts @@ -18,6 +18,9 @@ export const variablePillTheme = EditorView.baseTheme({ position: 'relative', marginRight: '0px', }, + '.cm-variable-pill.has-modifiers': { + paddingRight: '16px', + }, '.cm-variable-pill::before': { content: '""', position: 'absolute', @@ -36,11 +39,25 @@ export const variablePillTheme = EditorView.baseTheme({ WebkitMaskPosition: 'center', WebkitMaskSize: 'contain', }, + '.cm-variable-pill.has-modifiers::after': { + content: '""', + position: 'absolute', + right: '6px', + top: '50%', + transform: 'translateY(-50%)', + width: '4px', + height: '4px', + backgroundColor: '#E11D48', + borderRadius: '50%', + }, '.cm-variable-pill.cm-dark': { backgroundColor: '#FFD6EE', color: '#AD74FF', border: '1px solid #3D3D4D', }, + '.cm-variable-pill.cm-dark.has-modifiers::after': { + backgroundColor: '#AD74FF', + }, '.cm-variable-pill .cm-bracket': { display: 'none', }, From ec924ed2cb073411d7fec287e86b1f02e46c8b0a Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 11:31:48 +0200 Subject: [PATCH 17/49] fix: open prs --- .../field-editor/variable-popover.tsx | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx index 309f3fe9f88..a3650e7e6d0 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx @@ -5,6 +5,9 @@ import { Button } from '@/components/primitives/button'; import { Input, InputField } from '@/components/primitives/input'; import { GripVertical } from 'lucide-react'; import { FormControl, FormItem } from '@/components/primitives/form/form'; +import { RiDeleteBin2Line } from 'react-icons/ri'; +import { Code2 } from '../../icons/code-2'; +import { Separator } from '../separator'; const TRANSFORMERS = [ { label: 'Uppercase', value: 'upcase' }, @@ -109,14 +112,27 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover }; return ( - + +
+
+
+ CONFIGURE VARIABLE +
+ + +
+
-
+
-
- - +
+ + + + ) => setName(e.target.value)} @@ -128,9 +144,9 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover -
- - +
+ + ) => setDefaultVal(e.target.value)} @@ -142,7 +158,9 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover
-
+ + +
{/* Selected transformers with drag handles */} {transformers.length > 0 && (
From e717bc82a7b208bfacc7afacaa63c891f92b9d6c Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 14:44:32 +0200 Subject: [PATCH 18/49] fix: design --- .../field-editor/variable-popover.tsx | 393 +++++++++++------- 1 file changed, 237 insertions(+), 156 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx index a3650e7e6d0..018c83f6dc3 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx @@ -5,18 +5,31 @@ import { Button } from '@/components/primitives/button'; import { Input, InputField } from '@/components/primitives/input'; import { GripVertical } from 'lucide-react'; import { FormControl, FormItem } from '@/components/primitives/form/form'; -import { RiDeleteBin2Line } from 'react-icons/ri'; +import { RiCloseLine, RiDeleteBin2Line } from 'react-icons/ri'; import { Code2 } from '../../icons/code-2'; import { Separator } from '../separator'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; -const TRANSFORMERS = [ +interface Transformer { + label: string; + value: string; + hasParam?: boolean; +} + +const TRANSFORMERS: Transformer[] = [ { label: 'Uppercase', value: 'upcase' }, { label: 'Lowercase', value: 'downcase' }, { label: 'Capitalize', value: 'capitalize' }, { label: 'Strip HTML', value: 'strip_html' }, { label: 'Strip Newlines', value: 'strip_newlines' }, { label: 'Escape', value: 'escape' }, -] as const; + { label: 'Minus', value: 'minus', hasParam: true }, +]; + +interface TransformerWithParam { + value: string; + param?: string; +} interface VariablePopoverProps { variable: string; @@ -25,25 +38,43 @@ interface VariablePopoverProps { } export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) => { + const containerRef = useRef(null); // Parse the variable to extract name, default value, and transformers - const [variableName, defaultValue = '', initialTransformers = []] = useMemo(() => { + const { parsedName, parsedDefaultValue, parsedTransformers } = useMemo(() => { + if (!variable) { + return { parsedName: '', parsedDefaultValue: '', parsedTransformers: [] }; + } + const parts = variable.split('|').map((part) => part.trim()); const [nameWithDefault, ...rest] = parts; // Handle default value - const defaultMatch = nameWithDefault.match(/default:\s*([^}]+)/); - const name = defaultMatch ? nameWithDefault.replace(/\s*\|\s*default:[^}]+/, '') : nameWithDefault; + const defaultMatch = nameWithDefault?.match(/default:\s*([^}]+)/); + const name = defaultMatch ? nameWithDefault.replace(/\s*\|\s*default:[^}]+/, '') : nameWithDefault || ''; const defaultVal = defaultMatch ? defaultMatch[1].trim() : ''; - // Get all transformers - const transforms = rest.filter((part) => TRANSFORMERS.some((t) => t.value === part.trim())); + // Get all transformers with their parameters + const transforms = + rest?.reduce((acc, part) => { + const [transformerValue, param] = part.split(':').map((p) => p.trim()); + if (TRANSFORMERS.some((t) => t.value === transformerValue)) { + acc.push({ value: transformerValue, ...(param ? { param } : {}) }); + } + return acc; + }, []) || []; - return [name, defaultVal, transforms]; + return { + parsedName: name, + parsedDefaultValue: defaultVal, + parsedTransformers: transforms, + }; }, [variable]); - const [name, setName] = useState(variableName); - const [defaultVal, setDefaultVal] = useState(defaultValue); - const [transformers, setTransformers] = useState(initialTransformers); + const [name, setName] = useState(parsedName); + const [defaultVal, setDefaultVal] = useState(parsedDefaultValue); + const [transformers, setTransformers] = useState(parsedTransformers); + const [open, setOpen] = useState(false); + const [value, setValue] = useState(''); const updateTimeoutRef = useRef(); const isUpdatingRef = useRef(false); const [dragOverIndex, setDragOverIndex] = useState(null); @@ -51,7 +82,7 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover // Debounced update function const debouncedUpdate = useCallback( - (newName: string, newDefaultVal: string, newTransformers: string[]) => { + (newName: string, newDefaultVal: string, newTransformers: TransformerWithParam[]) => { if (isUpdatingRef.current) return; if (updateTimeoutRef.current) { @@ -69,8 +100,8 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover parts.push(`default: ${newDefaultVal.trim()}`); } - // Add transformers in order - parts.push(...newTransformers); + // Add transformers in order with their parameters + parts.push(...newTransformers.map((t) => (t.param ? `${t.value}: ${t.param}` : t.value))); // Join with proper spacing const finalValue = parts.join(' | '); @@ -95,10 +126,10 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover const handleTransformerToggle = (value: string) => { setTransformers((current) => { - if (current.includes(value)) { - return current.filter((t) => t !== value); + if (current.some((t) => t.value === value)) { + return current.filter((t) => t.value !== value); } - return [...current, value]; + return [...current, { value }]; }); }; @@ -111,156 +142,206 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover }); }; + const handleParamChange = (index: number, param: string) => { + setTransformers((current) => { + const newTransformers = [...current]; + newTransformers[index] = { ...newTransformers[index], param }; + return newTransformers; + }); + }; + return ( -
-
-
- CONFIGURE VARIABLE -
- - -
-
-
-
- - -
- - - +
+
+
+
+ CONFIGURE VARIABLE +
- ) => setName(e.target.value)} - className="h-7 text-sm" - /> - -
- - - - -
- - - ) => setDefaultVal(e.target.value)} - className="h-7 text-sm" - /> - -
-
-
+ +
+
+
+ + +
+ + + + ) => setName(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+ + +
+ + + ) => setDefaultVal(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+
- + -
- {/* Selected transformers with drag handles */} - {transformers.length > 0 && ( -
- - {transformers.map((value, index) => { - const transformer = TRANSFORMERS.find((t) => t.value === value); - return ( - - {dragOverIndex === index && ( - +
+ + +
+ + +
+
+
+ + {/* Selected transformers with drag handles */} + {transformers.length > 0 && ( +
+ + {transformers.map((transformer, index) => { + const transformerDef = TRANSFORMERS.find((t) => t.value === transformer.value); + return ( setDraggingItem(index)} - onDragEnd={() => { - setDraggingItem(null); - setDragOverIndex(null); - }} - onDragOver={() => { - if (dragOverIndex !== index) { - setDragOverIndex(index); - } - }} - onDragLeave={(e) => { - if (!e.currentTarget.contains(e.relatedTarget as Node)) { - setDragOverIndex(null); - } + key={transformer.value + index} + className="relative mb-1" + layout + layoutId={transformer.value + index} + transition={{ + layout: { duration: 0.2, ease: 'easeOut' }, + type: 'spring', + stiffness: 500, + damping: 30, }} - onDrop={() => { - if (draggingItem !== null && draggingItem !== index) { - moveTransformer(draggingItem, index); - } - setDragOverIndex(null); - setDraggingItem(null); - }} - animate={draggingItem === index ? { scale: 1.02 } : { scale: 1 }} - whileHover={{ scale: 1.01 }} - transition={{ duration: 0.15 }} > - - {transformer?.label} - - - {dragOverIndex === index + 1 && ( + {dragOverIndex === index && ( + + )} - )} - - ); - })} - -
- )} + className={`group flex cursor-move items-center gap-1.5 ${ + draggingItem === index ? 'ring-primary opacity-50 ring-2 ring-offset-1' : '' + }`} + drag="y" + dragDirectionLock + dragConstraints={{ top: 0, bottom: 0 }} + dragElastic={0.1} + dragMomentum={false} + onDragStart={() => setDraggingItem(index)} + onDragEnd={(e, info) => { + if (dragOverIndex !== null && draggingItem !== null && draggingItem !== dragOverIndex) { + moveTransformer(draggingItem, dragOverIndex); + } + setDraggingItem(null); + setDragOverIndex(null); + }} + onDrag={(e, info) => { + const elements = document.elementsFromPoint(info.point.x, info.point.y); + const droppableElement = elements.find( + (el) => el.classList.contains('group') && !el.classList.contains('opacity-50') + ); - {/* Available transformers */} -
- {TRANSFORMERS.filter((t) => !transformers.includes(t.value)).map(({ label, value }) => ( - - ))} + if (droppableElement) { + const index = parseInt(droppableElement.getAttribute('data-index') || '-1'); + if (index !== -1 && dragOverIndex !== index) { + setDragOverIndex(index); + } + } else { + setDragOverIndex(null); + } + }} + data-index={index} + animate={draggingItem === index ? { scale: 1.02, zIndex: 50 } : { scale: 1, zIndex: 1 }} + whileHover={{ scale: 1.01 }} + transition={{ + duration: 0.15, + ease: [0.32, 0.72, 0, 1], + }} + > + +
+
+
{transformerDef?.label}
+ {transformerDef?.hasParam && ( + handleParamChange(index, e.target.value)} + className="border-stroke-soft ml-1 h-[24px] min-w-[60px] flex-1 border-l pl-1 text-xs" + placeholder="Parameter..." + /> + )} +
+
+ + + {dragOverIndex === index + 1 && ( + + )} + + ); + })} + +
+ )}
From e56a5c698ba618d99958e8fa2beb58759f55c8cc Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 16:40:49 +0200 Subject: [PATCH 19/49] fix: --- apps/dashboard/public/images/code.svg | 20 + .../field-editor/variable-popover.tsx | 571 ++++++++++++++++-- .../primitives/field-editor/variable-theme.ts | 34 +- 3 files changed, 563 insertions(+), 62 deletions(-) create mode 100644 apps/dashboard/public/images/code.svg diff --git a/apps/dashboard/public/images/code.svg b/apps/dashboard/public/images/code.svg new file mode 100644 index 00000000000..cd537a11e50 --- /dev/null +++ b/apps/dashboard/public/images/code.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx index 018c83f6dc3..cc42caa1c7b 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx @@ -5,30 +5,298 @@ import { Button } from '@/components/primitives/button'; import { Input, InputField } from '@/components/primitives/input'; import { GripVertical } from 'lucide-react'; import { FormControl, FormItem } from '@/components/primitives/form/form'; -import { RiCloseLine, RiDeleteBin2Line } from 'react-icons/ri'; +import { RiCloseLine } from 'react-icons/ri'; import { Code2 } from '../../icons/code-2'; import { Separator } from '../separator'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; +import { Switch } from '@/components/primitives/switch'; interface Transformer { label: string; value: string; hasParam?: boolean; + description?: string; + example?: string; + params?: { + placeholder: string; + description?: string; + type?: 'string' | 'number'; + }[]; } const TRANSFORMERS: Transformer[] = [ - { label: 'Uppercase', value: 'upcase' }, - { label: 'Lowercase', value: 'downcase' }, - { label: 'Capitalize', value: 'capitalize' }, - { label: 'Strip HTML', value: 'strip_html' }, - { label: 'Strip Newlines', value: 'strip_newlines' }, - { label: 'Escape', value: 'escape' }, - { label: 'Minus', value: 'minus', hasParam: true }, + // Text Transformations + { + label: 'Uppercase', + value: 'upcase', + description: 'Convert text to uppercase', + example: '"hello" | upcase → HELLO', + }, + { + label: 'Lowercase', + value: 'downcase', + description: 'Convert text to lowercase', + example: '"HELLO" | downcase → hello', + }, + { + label: 'Capitalize', + value: 'capitalize', + description: 'Capitalize the first character', + example: '"hello" | capitalize → Hello', + }, + { + label: 'Strip HTML', + value: 'strip_html', + description: 'Remove all HTML tags from text', + example: '"

text

" | strip_html → text', + }, + { + label: 'Strip Newlines', + value: 'strip_newlines', + description: 'Remove all newline characters', + example: '"hello\\nworld" | strip_newlines → hello world', + }, + { + label: 'Escape', + value: 'escape', + description: 'Escape special characters', + example: '"" | escape → <hello>', + }, + { + label: 'Truncate', + value: 'truncate', + hasParam: true, + description: 'Truncate text to specified length', + example: '"hello world" | truncate: 5 → hello...', + params: [{ placeholder: 'Length (e.g. 20)', type: 'number' }], + }, + { + label: 'Truncate Words', + value: 'truncatewords', + hasParam: true, + description: 'Truncate text to specified number of words', + example: '"hello world and more" | truncatewords: 2 → hello world...', + params: [{ placeholder: 'Word count', type: 'number' }], + }, + { + label: 'Replace', + value: 'replace', + hasParam: true, + description: 'Replace all occurrences of a string', + example: '"Hello Hello" | replace: "Hello", "Hi" → Hi Hi', + params: [ + { placeholder: 'Search text', type: 'string' }, + { placeholder: 'Replace with', type: 'string' }, + ], + }, + { + label: 'Replace First', + value: 'replace_first', + hasParam: true, + description: 'Replace first occurrence of a string', + example: '"Hello Hello" | replace_first: "Hello", "Hi" → Hi Hello', + params: [ + { placeholder: 'Search text', type: 'string' }, + { placeholder: 'Replace with', type: 'string' }, + ], + }, + { + label: 'Remove', + value: 'remove', + hasParam: true, + description: 'Remove all occurrences of a string', + example: '"Hello Hello" | remove: "ello" → H H', + params: [{ placeholder: 'Text to remove', type: 'string' }], + }, + { + label: 'Remove First', + value: 'remove_first', + hasParam: true, + description: 'Remove first occurrence of a string', + example: '"Hello Hello" | remove_first: "ello" → H Hello', + params: [{ placeholder: 'Text to remove', type: 'string' }], + }, + { + label: 'Append', + value: 'append', + hasParam: true, + description: 'Add text to the end', + example: '"Hello" | append: " World" → Hello World', + params: [{ placeholder: 'Text to append', type: 'string' }], + }, + { + label: 'Prepend', + value: 'prepend', + hasParam: true, + description: 'Add text to the beginning', + example: '"World" | prepend: "Hello " → Hello World', + params: [{ placeholder: 'Text to prepend', type: 'string' }], + }, + { + label: 'Slice', + value: 'slice', + hasParam: true, + description: 'Extract a substring by position', + example: '"hello" | slice: 0, 2 → he', + params: [ + { placeholder: 'Start index', type: 'number' }, + { placeholder: 'Length (optional)', type: 'number' }, + ], + }, + + // Number Operations + { + label: 'Plus', + value: 'plus', + hasParam: true, + description: 'Add a number', + example: '5 | plus: 3 → 8', + params: [{ placeholder: 'Number to add', type: 'number' }], + }, + { + label: 'Minus', + value: 'minus', + hasParam: true, + description: 'Subtract a number', + example: '5 | minus: 3 → 2', + params: [{ placeholder: 'Number to subtract', type: 'number' }], + }, + { + label: 'Times', + value: 'times', + hasParam: true, + description: 'Multiply by a number', + example: '5 | times: 3 → 15', + params: [{ placeholder: 'Number to multiply by', type: 'number' }], + }, + { + label: 'Divided By', + value: 'divided_by', + hasParam: true, + description: 'Divide by a number', + example: '10 | divided_by: 2 → 5', + params: [{ placeholder: 'Number to divide by', type: 'number' }], + }, + { + label: 'Round', + value: 'round', + hasParam: true, + description: 'Round to specified decimal places', + example: '4.5678 | round: 2 → 4.57', + params: [{ placeholder: 'Decimal places', type: 'number' }], + }, + { + label: 'Floor', + value: 'floor', + description: 'Round down to nearest integer', + example: '4.6 | floor → 4', + }, + { + label: 'Ceil', + value: 'ceil', + description: 'Round up to nearest integer', + example: '4.3 | ceil → 5', + }, + { + label: 'Abs', + value: 'abs', + description: 'Get absolute value', + example: '-5 | abs → 5', + }, + + // Data Formatting + { + label: 'Date Format', + value: 'date', + hasParam: true, + description: 'Format a date using strftime format', + example: '"2024-01-20" | date: "%B %d, %Y" → January 20, 2024', + params: [{ placeholder: 'Format (e.g. "%Y-%m-%d")', description: 'strftime format', type: 'string' }], + }, + { + label: 'Default', + value: 'default', + hasParam: true, + description: 'Use default value if input is empty', + example: '"" | default: "N/A" → N/A', + params: [{ placeholder: 'Default value', type: 'string' }], + }, + { + label: 'JSON', + value: 'json', + description: 'Convert object to JSON string', + example: '{name: "John"} | json → {"name":"John"}', + }, + { + label: 'Size', + value: 'size', + description: 'Get length of string or array', + example: '"hello" | size → 5', + }, + { + label: 'Join', + value: 'join', + hasParam: true, + description: 'Join array elements with separator', + example: '["a","b"] | join: ", " → a, b', + params: [{ placeholder: 'Separator (e.g. ", ")', type: 'string' }], + }, + { + label: 'Split', + value: 'split', + hasParam: true, + description: 'Split string into array', + example: '"a,b" | split: "," → ["a","b"]', + params: [{ placeholder: 'Delimiter', type: 'string' }], + }, + { + label: 'First', + value: 'first', + description: 'Get first element of array', + example: '[1,2,3] | first → 1', + }, + { + label: 'Last', + value: 'last', + description: 'Get last element of array', + example: '[1,2,3] | last → 3', + }, + { + label: 'Map', + value: 'map', + hasParam: true, + description: 'Extract property from each item in array', + example: 'users | map: "name" → ["John", "Jane"]', + params: [{ placeholder: 'Property name', type: 'string' }], + }, + { + label: 'Where', + value: 'where', + hasParam: true, + description: 'Filter array by property value', + example: 'users | where: "active", true → [activeUsers]', + params: [ + { placeholder: 'Property name', type: 'string' }, + { placeholder: 'Value to match', type: 'string' }, + ], + }, + { + label: 'URL Encode', + value: 'url_encode', + description: 'Encode string for use in URL', + example: '"hello world" | url_encode → hello%20world', + }, + { + label: 'URL Decode', + value: 'url_decode', + description: 'Decode URL-encoded string', + example: '"hello%20world" | url_decode → hello world', + }, ]; interface TransformerWithParam { value: string; - param?: string; + params?: string[]; } interface VariablePopoverProps { @@ -37,8 +305,26 @@ interface VariablePopoverProps { onUpdate: (newValue: string) => void; } +const TransformerItem = ({ transformer }: { transformer: Transformer }) => { + return ( +
+
+ {transformer.label}{' '} +
+ {transformer.description} + + {transformer.example && ( +
+ {transformer.example} +
+ )} +
+ ); +}; + export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) => { const containerRef = useRef(null); + const searchInputRef = useRef(null); // Parse the variable to extract name, default value, and transformers const { parsedName, parsedDefaultValue, parsedTransformers } = useMemo(() => { if (!variable) { @@ -51,14 +337,23 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover // Handle default value const defaultMatch = nameWithDefault?.match(/default:\s*([^}]+)/); const name = defaultMatch ? nameWithDefault.replace(/\s*\|\s*default:[^}]+/, '') : nameWithDefault || ''; - const defaultVal = defaultMatch ? defaultMatch[1].trim() : ''; + const defaultVal = defaultMatch ? defaultMatch[1].trim().replace(/^["']|["']$/g, '') : ''; // Get all transformers with their parameters const transforms = rest?.reduce((acc, part) => { - const [transformerValue, param] = part.split(':').map((p) => p.trim()); + const [transformerValue, ...paramValues] = part.split(':').map((p) => p.trim()); if (TRANSFORMERS.some((t) => t.value === transformerValue)) { - acc.push({ value: transformerValue, ...(param ? { param } : {}) }); + // Parse parameters, handling quoted values and commas + const params = + paramValues.length > 0 + ? paramValues + .join(':') + .split(',') + .map((param) => param.trim().replace(/^['"]|['"]$/g, '')) + : undefined; + + acc.push({ value: transformerValue, ...(params ? { params } : {}) }); } return acc; }, []) || []; @@ -73,12 +368,19 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover const [name, setName] = useState(parsedName); const [defaultVal, setDefaultVal] = useState(parsedDefaultValue); const [transformers, setTransformers] = useState(parsedTransformers); - const [open, setOpen] = useState(false); - const [value, setValue] = useState(''); const updateTimeoutRef = useRef(); const isUpdatingRef = useRef(false); const [dragOverIndex, setDragOverIndex] = useState(null); const [draggingItem, setDraggingItem] = useState(null); + const [showRawLiquid, setShowRawLiquid] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + + const formatParamValue = (param: string, type?: 'string' | 'number') => { + if (type === 'number') { + return param; + } + return `'${param}'`; + }; // Debounced update function const debouncedUpdate = useCallback( @@ -97,11 +399,19 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover const parts = [newName.trim()]; if (newDefaultVal) { - parts.push(`default: ${newDefaultVal.trim()}`); + parts.push(`default: "${newDefaultVal.trim()}"`); } // Add transformers in order with their parameters - parts.push(...newTransformers.map((t) => (t.param ? `${t.value}: ${t.param}` : t.value))); + parts.push( + ...newTransformers.map((t) => { + const transformerDef = TRANSFORMERS.find((def) => def.value === t.value); + if (!t.params?.length) return t.value; + return `${t.value}: ${t.params + .map((param, index) => formatParamValue(param, transformerDef?.params?.[index]?.type)) + .join(', ')}`; + }) + ); // Join with proper spacing const finalValue = parts.join(' | '); @@ -142,14 +452,103 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover }); }; - const handleParamChange = (index: number, param: string) => { + const handleParamChange = (index: number, params: string[]) => { setTransformers((current) => { const newTransformers = [...current]; - newTransformers[index] = { ...newTransformers[index], param }; + const transformerDef = TRANSFORMERS.find((def) => def.value === newTransformers[index].value); + + // Format params based on their types + const formattedParams = params.map((param, paramIndex) => { + const paramType = transformerDef?.params?.[paramIndex]?.type; + if (paramType === 'number') { + // Remove any non-numeric characters except decimal point + const numericValue = param.replace(/[^\d.-]/g, ''); + // Return empty string if not a valid number + return isNaN(Number(numericValue)) ? '' : numericValue; + } + return param; + }); + + newTransformers[index] = { ...newTransformers[index], params: formattedParams }; return newTransformers; }); }; + const handleRawLiquidChange = (value: string) => { + // Remove {{ and }} and trim + const content = value.replace(/^\{\{\s*|\s*\}\}$/g, '').trim(); + + // Split by pipe and trim each part + const parts = content.split('|').map((part) => part.trim()); + + // First part is the name + const newName = parts[0]; + let newDefaultVal = ''; + const newTransformers: TransformerWithParam[] = []; + + // Process each part after the name + parts.slice(1).forEach((part) => { + if (part.startsWith('default:')) { + // Extract default value, handling quotes + newDefaultVal = part + .replace('default:', '') + .trim() + .replace(/^["']|["']$/g, ''); + } else { + // Handle transformers + const [transformerValue, ...paramValues] = part.split(':').map((p) => p.trim()); + if (TRANSFORMERS.some((t) => t.value === transformerValue)) { + const transformerDef = TRANSFORMERS.find((t) => t.value === transformerValue); + + // Parse parameters, handling quoted values and types + const params = + paramValues.length > 0 + ? paramValues + .join(':') + .split(',') + .map((param, index) => { + const paramType = transformerDef?.params?.[index]?.type; + const cleanParam = param.trim().replace(/^['"]|['"]$/g, ''); + + if (paramType === 'number') { + // For number type, remove any non-numeric characters + const numericValue = cleanParam.replace(/[^\d.-]/g, ''); + return isNaN(Number(numericValue)) ? '' : numericValue; + } + return cleanParam; + }) + : undefined; + + newTransformers.push({ + value: transformerValue, + ...(params ? { params } : {}), + }); + } + } + }); + + setName(newName); + setDefaultVal(newDefaultVal); + setTransformers(newTransformers); + }; + + // Filter function for transformers + const getFilteredTransformers = useCallback( + (query: string) => { + const normalizedQuery = query.toLowerCase(); + return TRANSFORMERS.filter((transformer) => { + if (transformers.some((t) => t.value === transformer.value)) return false; + + return ( + transformer.label.toLowerCase().includes(normalizedQuery) || + transformer.description?.toLowerCase().includes(normalizedQuery) || + transformer.value.toLowerCase().includes(normalizedQuery) + ); + }); + }, + [transformers] + ); + return (
@@ -158,10 +557,6 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover
CONFIGURE VARIABLE
- -
@@ -195,6 +590,44 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover
+ + +
+ + +
+
+
+ {showRawLiquid && ( + + +
+ + 0 + ? ' | ' + + transformers + .map((t) => { + const transformerDef = TRANSFORMERS.find((def) => def.value === t.value); + if (!t.params?.length) return t.value; + return `${t.value}: ${t.params + .map((param, index) => + formatParamValue(param, transformerDef?.params?.[index]?.type) + ) + .join(', ')}`; + }) + .join(' | ') + : '' + } }}`} + onChange={(e) => handleRawLiquidChange(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+ )}
@@ -209,25 +642,64 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover onValueChange={(value) => { if (value) { handleTransformerToggle(value); + setSearchQuery(''); } }} > - - {TRANSFORMERS.filter((transformer) => !transformers.some((t) => t.value === transformer.value)) - .length === 0 ? ( -
All modifiers have been added
- ) : ( - TRANSFORMERS.filter( - (transformer) => !transformers.some((t) => t.value === transformer.value) - ).map((transformer) => ( - - {transformer.label} - - )) - )} + { + e.preventDefault(); + searchInputRef.current?.focus(); + }} + className="max-h-[400px]" + align="start" + > +
+ + { + e.stopPropagation(); + setSearchQuery(e.target.value); + }} + className="h-7 text-sm" + placeholder="Search modifiers..." + autoFocus + onKeyDown={(e) => { + e.stopPropagation(); + if (e.key === 'Escape') { + setSearchQuery(''); + } + }} + /> + +
+
+ {getFilteredTransformers(searchQuery).length === 0 ? ( +
+ + {searchQuery ? 'No modifiers found' : 'All modifiers have been added'} + + {searchQuery && Try searching for different terms} +
+ ) : ( + <> + {getFilteredTransformers(searchQuery).map((transformer) => ( + + + + ))} + + )} +
@@ -243,7 +715,7 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover return (
-
{transformerDef?.label}
- {transformerDef?.hasParam && ( - handleParamChange(index, e.target.value)} - className="border-stroke-soft ml-1 h-[24px] min-w-[60px] flex-1 border-l pl-1 text-xs" - placeholder="Parameter..." - /> +
{transformerDef?.label}
+ {transformerDef?.hasParam && transformerDef.params && ( +
+ {transformerDef.params.map((param, paramIndex) => ( + { + const newParams = [...(transformer.params || [])]; + newParams[paramIndex] = e.target.value; + handleParamChange(index, newParams); + }} + className="border-stroke-soft ml-1 h-[20px] w-[calc(100%-8px)] border-l pl-1 text-xs" + placeholder={param.placeholder} + title={param.description} + /> + ))} +
)}
diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts b/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts index d1e1e4780c6..482dc1a2ac2 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts @@ -2,39 +2,39 @@ import { EditorView } from '@uiw/react-codemirror'; export const variablePillTheme = EditorView.baseTheme({ '.cm-variable-pill': { - backgroundColor: 'rgb(255 255 255 / 0.5)', - color: 'rgb(55, 65, 81)', - border: '1px solid #e5e7eb', + backgroundColor: 'hsl(var(--bg-weak))', + color: 'hsl(var(--text-sub))', + border: '1px solid hsl(var(--stroke-soft))', borderRadius: '9999px', - padding: '2px 6px 2px 24px', - margin: '0 2px', + padding: '2px 6px 2px 18px', + margin: '0 4px', fontFamily: 'inherit', display: 'inline-flex', alignItems: 'center', - height: '24px', - lineHeight: '24px', - fontSize: '14px', + height: '16px', + lineHeight: '16px', + fontSize: '12px', cursor: 'pointer', position: 'relative', marginRight: '0px', }, '.cm-variable-pill.has-modifiers': { - paddingRight: '16px', + paddingRight: '12px', }, '.cm-variable-pill::before': { content: '""', position: 'absolute', - left: '6px', + left: '4px', top: '50%', transform: 'translateY(-50%)', - width: '14px', - height: '14px', - backgroundColor: '#E11D48', - maskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, + width: '12px', + height: '12px', + backgroundColor: 'hsl(var(--feature-base))', + maskImage: `url("/images/code.svg")`, maskRepeat: 'no-repeat', maskPosition: 'center', maskSize: 'contain', - WebkitMaskImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'%3E%3C/path%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'%3E%3C/path%3E%3C/svg%3E")`, + WebkitMaskImage: `url("/images/code.svg")`, WebkitMaskRepeat: 'no-repeat', WebkitMaskPosition: 'center', WebkitMaskSize: 'contain', @@ -42,12 +42,12 @@ export const variablePillTheme = EditorView.baseTheme({ '.cm-variable-pill.has-modifiers::after': { content: '""', position: 'absolute', - right: '6px', + right: '5px', top: '50%', transform: 'translateY(-50%)', width: '4px', height: '4px', - backgroundColor: '#E11D48', + backgroundColor: 'hsl(var(--feature-base))', borderRadius: '50%', }, '.cm-variable-pill.cm-dark': { From c83af5c0e2979febabfddf3ececf0148ab6cf29b Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 16:51:04 +0200 Subject: [PATCH 20/49] Update variable-popover.tsx --- .../src/components/primitives/field-editor/variable-popover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx index cc42caa1c7b..ddb5da94cde 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx @@ -777,7 +777,7 @@ export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopover
-
{transformerDef?.label}
+
{transformerDef?.label}
{transformerDef?.hasParam && transformerDef.params && (
{transformerDef.params.map((param, paramIndex) => ( From 93c1d462094fd9740481bfb035bcf281386d1a28 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 17:53:33 +0200 Subject: [PATCH 21/49] fix: it worked --- .../primitives/field-editor/field-editor.tsx | 11 +- .../field-editor/variable-plugin.ts | 53 +- .../field-editor/variable-popover.tsx | 831 ------------------ .../components/draggable-transformer.tsx | 123 +++ .../components/transformer-item.tsx | 22 + .../components/transformer-list.tsx | 49 ++ .../variable-popover/constants.ts | 269 ++++++ .../hooks/use-transformer-manager.ts | 92 ++ .../hooks/use-variable-parser.ts | 72 ++ .../field-editor/variable-popover/index.ts | 2 + .../field-editor/variable-popover/index.tsx | 246 ++++++ .../field-editor/variable-popover/types.ts | 23 + .../field-editor/variable-popover/utils.ts | 39 + .../variable-popover/variable-popover.tsx | 251 ++++++ 14 files changed, 1242 insertions(+), 841 deletions(-) delete mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-list.tsx create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/constants.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/index.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/index.tsx create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index 22e2c015f04..e2a6edee5d9 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -7,9 +7,9 @@ import { completions } from '@/utils/liquid-autocomplete'; import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; import { autocompletion } from '@codemirror/autocomplete'; import { Popover, PopoverTrigger } from '@/components/primitives/popover'; -import { VariablePopover } from './variable-popover'; import { createVariablePlugin } from './variable-plugin'; import { variablePillTheme } from './variable-theme'; +import { VariablePopover } from './variable-popover'; type FieldEditorProps = { value: string; @@ -55,11 +55,16 @@ export const FieldEditor = ({ const { from, to } = selectedVariable; const view = viewRef.current; - const newVariableText = `{{${newValue}}}`; + const hasLiquidSyntax = newValue.match(/^\{\{.*\}\}$/); + const newVariableText = hasLiquidSyntax ? newValue : `{{${newValue}}}`; + + const currentContent = view.state.doc.toString(); + const afterCursor = currentContent.slice(to).trim(); + const hasClosingBrackets = afterCursor.startsWith('}}'); const changes = { from, - to, + to: hasClosingBrackets ? to + 2 : to, insert: newVariableText, }; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts index abd8a040c77..cf18eff9d22 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts @@ -21,7 +21,9 @@ class VariablePillWidget extends WidgetType { toDOM() { const span = document.createElement('span'); - const pillClass = `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''} ${this.hasModifiers ? 'has-modifiers' : ''}`; + const pillClass = `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''} ${ + this.hasModifiers ? 'has-modifiers' : '' + }`; span.className = pillClass; span.setAttribute('data-variable', this.fullVariableName); span.setAttribute('data-start', this.start.toString()); @@ -71,6 +73,7 @@ export function createVariablePlugin({ viewRef, lastCompletionRef, onSelect }: P class { decorations: DecorationSet; lastCursor: number = 0; + isTypingVariable: boolean = false; constructor(view: EditorView) { this.decorations = this.createDecorations(view); @@ -79,11 +82,41 @@ export function createVariablePlugin({ viewRef, lastCompletionRef, onSelect }: P update(update: any) { if (update.docChanged || update.viewportChanged || update.selectionSet) { - this.lastCursor = update.state.selection.main.head; - - const content = update.state.doc.toString(); const pos = update.state.selection.main.head; + const content = update.state.doc.toString(); + + // Check if we're currently typing a variable + const beforeCursor = content.slice(0, pos); + const afterCursor = content.slice(pos); + const lastOpenBrackets = beforeCursor.lastIndexOf('{{'); + const nextCloseBrackets = afterCursor.indexOf('}}'); + + // We're typing a variable if we have {{ before the cursor but no }} after it + this.isTypingVariable = + lastOpenBrackets !== -1 && + (nextCloseBrackets === -1 || beforeCursor.indexOf('}}', lastOpenBrackets) === -1); + + // Handle backspace inside a variable + if (update.docChanged && update.changes.desc === 'input.delete.backward') { + if (lastOpenBrackets !== -1 && nextCloseBrackets !== -1) { + const variableContent = content.slice(lastOpenBrackets + 2, pos).trim(); + // If we're deleting the last character of the variable name, remove the entire variable + if (!variableContent) { + requestAnimationFrame(() => { + update.view.dispatch({ + changes: { + from: lastOpenBrackets, + to: pos + nextCloseBrackets + 2, + insert: '', + }, + }); + }); + return; + } + } + } + // Handle variable completion if (update.docChanged && content.slice(pos - 2, pos) === '}}') { const start = content.lastIndexOf('{{', pos); if (start !== -1) { @@ -112,14 +145,20 @@ export function createVariablePlugin({ viewRef, lastCompletionRef, onSelect }: P createDecorations(view: EditorView) { const decorations: any[] = []; const content = view.state.doc.toString(); + const pos = view.state.selection.main.head; const variableRegex = /{{([^{}]+)}}/g; let match; while ((match = variableRegex.exec(content)) !== null) { const start = match.index; const end = start + match[0].length; - const fullVariableName = match[1].trim(); + // Don't create a pill if we're currently editing this variable + if (this.isTypingVariable && pos > start && pos < end) { + continue; + } + + const fullVariableName = match[1].trim(); const parts = fullVariableName.split('|').map((part) => part.trim()); const variableName = parts[0]; const hasModifiers = parts.length > 1; @@ -128,8 +167,8 @@ export function createVariablePlugin({ viewRef, lastCompletionRef, onSelect }: P decorations.push( Decoration.replace({ widget: new VariablePillWidget(variableName, fullVariableName, start, end, hasModifiers, onSelect), - inclusive: true, - side: 1, + inclusive: false, + side: -1, }).range(start, end) ); } diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx deleted file mode 100644 index ddb5da94cde..00000000000 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover.tsx +++ /dev/null @@ -1,831 +0,0 @@ -import { useMemo, useState, useRef, useCallback, useEffect, ChangeEvent } from 'react'; -import { motion, AnimatePresence } from 'motion/react'; -import { PopoverContent } from '@/components/primitives/popover'; -import { Button } from '@/components/primitives/button'; -import { Input, InputField } from '@/components/primitives/input'; -import { GripVertical } from 'lucide-react'; -import { FormControl, FormItem } from '@/components/primitives/form/form'; -import { RiCloseLine } from 'react-icons/ri'; -import { Code2 } from '../../icons/code-2'; -import { Separator } from '../separator'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; -import { Switch } from '@/components/primitives/switch'; - -interface Transformer { - label: string; - value: string; - hasParam?: boolean; - description?: string; - example?: string; - params?: { - placeholder: string; - description?: string; - type?: 'string' | 'number'; - }[]; -} - -const TRANSFORMERS: Transformer[] = [ - // Text Transformations - { - label: 'Uppercase', - value: 'upcase', - description: 'Convert text to uppercase', - example: '"hello" | upcase → HELLO', - }, - { - label: 'Lowercase', - value: 'downcase', - description: 'Convert text to lowercase', - example: '"HELLO" | downcase → hello', - }, - { - label: 'Capitalize', - value: 'capitalize', - description: 'Capitalize the first character', - example: '"hello" | capitalize → Hello', - }, - { - label: 'Strip HTML', - value: 'strip_html', - description: 'Remove all HTML tags from text', - example: '"

text

" | strip_html → text', - }, - { - label: 'Strip Newlines', - value: 'strip_newlines', - description: 'Remove all newline characters', - example: '"hello\\nworld" | strip_newlines → hello world', - }, - { - label: 'Escape', - value: 'escape', - description: 'Escape special characters', - example: '"" | escape → <hello>', - }, - { - label: 'Truncate', - value: 'truncate', - hasParam: true, - description: 'Truncate text to specified length', - example: '"hello world" | truncate: 5 → hello...', - params: [{ placeholder: 'Length (e.g. 20)', type: 'number' }], - }, - { - label: 'Truncate Words', - value: 'truncatewords', - hasParam: true, - description: 'Truncate text to specified number of words', - example: '"hello world and more" | truncatewords: 2 → hello world...', - params: [{ placeholder: 'Word count', type: 'number' }], - }, - { - label: 'Replace', - value: 'replace', - hasParam: true, - description: 'Replace all occurrences of a string', - example: '"Hello Hello" | replace: "Hello", "Hi" → Hi Hi', - params: [ - { placeholder: 'Search text', type: 'string' }, - { placeholder: 'Replace with', type: 'string' }, - ], - }, - { - label: 'Replace First', - value: 'replace_first', - hasParam: true, - description: 'Replace first occurrence of a string', - example: '"Hello Hello" | replace_first: "Hello", "Hi" → Hi Hello', - params: [ - { placeholder: 'Search text', type: 'string' }, - { placeholder: 'Replace with', type: 'string' }, - ], - }, - { - label: 'Remove', - value: 'remove', - hasParam: true, - description: 'Remove all occurrences of a string', - example: '"Hello Hello" | remove: "ello" → H H', - params: [{ placeholder: 'Text to remove', type: 'string' }], - }, - { - label: 'Remove First', - value: 'remove_first', - hasParam: true, - description: 'Remove first occurrence of a string', - example: '"Hello Hello" | remove_first: "ello" → H Hello', - params: [{ placeholder: 'Text to remove', type: 'string' }], - }, - { - label: 'Append', - value: 'append', - hasParam: true, - description: 'Add text to the end', - example: '"Hello" | append: " World" → Hello World', - params: [{ placeholder: 'Text to append', type: 'string' }], - }, - { - label: 'Prepend', - value: 'prepend', - hasParam: true, - description: 'Add text to the beginning', - example: '"World" | prepend: "Hello " → Hello World', - params: [{ placeholder: 'Text to prepend', type: 'string' }], - }, - { - label: 'Slice', - value: 'slice', - hasParam: true, - description: 'Extract a substring by position', - example: '"hello" | slice: 0, 2 → he', - params: [ - { placeholder: 'Start index', type: 'number' }, - { placeholder: 'Length (optional)', type: 'number' }, - ], - }, - - // Number Operations - { - label: 'Plus', - value: 'plus', - hasParam: true, - description: 'Add a number', - example: '5 | plus: 3 → 8', - params: [{ placeholder: 'Number to add', type: 'number' }], - }, - { - label: 'Minus', - value: 'minus', - hasParam: true, - description: 'Subtract a number', - example: '5 | minus: 3 → 2', - params: [{ placeholder: 'Number to subtract', type: 'number' }], - }, - { - label: 'Times', - value: 'times', - hasParam: true, - description: 'Multiply by a number', - example: '5 | times: 3 → 15', - params: [{ placeholder: 'Number to multiply by', type: 'number' }], - }, - { - label: 'Divided By', - value: 'divided_by', - hasParam: true, - description: 'Divide by a number', - example: '10 | divided_by: 2 → 5', - params: [{ placeholder: 'Number to divide by', type: 'number' }], - }, - { - label: 'Round', - value: 'round', - hasParam: true, - description: 'Round to specified decimal places', - example: '4.5678 | round: 2 → 4.57', - params: [{ placeholder: 'Decimal places', type: 'number' }], - }, - { - label: 'Floor', - value: 'floor', - description: 'Round down to nearest integer', - example: '4.6 | floor → 4', - }, - { - label: 'Ceil', - value: 'ceil', - description: 'Round up to nearest integer', - example: '4.3 | ceil → 5', - }, - { - label: 'Abs', - value: 'abs', - description: 'Get absolute value', - example: '-5 | abs → 5', - }, - - // Data Formatting - { - label: 'Date Format', - value: 'date', - hasParam: true, - description: 'Format a date using strftime format', - example: '"2024-01-20" | date: "%B %d, %Y" → January 20, 2024', - params: [{ placeholder: 'Format (e.g. "%Y-%m-%d")', description: 'strftime format', type: 'string' }], - }, - { - label: 'Default', - value: 'default', - hasParam: true, - description: 'Use default value if input is empty', - example: '"" | default: "N/A" → N/A', - params: [{ placeholder: 'Default value', type: 'string' }], - }, - { - label: 'JSON', - value: 'json', - description: 'Convert object to JSON string', - example: '{name: "John"} | json → {"name":"John"}', - }, - { - label: 'Size', - value: 'size', - description: 'Get length of string or array', - example: '"hello" | size → 5', - }, - { - label: 'Join', - value: 'join', - hasParam: true, - description: 'Join array elements with separator', - example: '["a","b"] | join: ", " → a, b', - params: [{ placeholder: 'Separator (e.g. ", ")', type: 'string' }], - }, - { - label: 'Split', - value: 'split', - hasParam: true, - description: 'Split string into array', - example: '"a,b" | split: "," → ["a","b"]', - params: [{ placeholder: 'Delimiter', type: 'string' }], - }, - { - label: 'First', - value: 'first', - description: 'Get first element of array', - example: '[1,2,3] | first → 1', - }, - { - label: 'Last', - value: 'last', - description: 'Get last element of array', - example: '[1,2,3] | last → 3', - }, - { - label: 'Map', - value: 'map', - hasParam: true, - description: 'Extract property from each item in array', - example: 'users | map: "name" → ["John", "Jane"]', - params: [{ placeholder: 'Property name', type: 'string' }], - }, - { - label: 'Where', - value: 'where', - hasParam: true, - description: 'Filter array by property value', - example: 'users | where: "active", true → [activeUsers]', - params: [ - { placeholder: 'Property name', type: 'string' }, - { placeholder: 'Value to match', type: 'string' }, - ], - }, - { - label: 'URL Encode', - value: 'url_encode', - description: 'Encode string for use in URL', - example: '"hello world" | url_encode → hello%20world', - }, - { - label: 'URL Decode', - value: 'url_decode', - description: 'Decode URL-encoded string', - example: '"hello%20world" | url_decode → hello world', - }, -]; - -interface TransformerWithParam { - value: string; - params?: string[]; -} - -interface VariablePopoverProps { - variable: string; - onClose: () => void; - onUpdate: (newValue: string) => void; -} - -const TransformerItem = ({ transformer }: { transformer: Transformer }) => { - return ( -
-
- {transformer.label}{' '} -
- {transformer.description} - - {transformer.example && ( -
- {transformer.example} -
- )} -
- ); -}; - -export const VariablePopover = ({ variable, onClose, onUpdate }: VariablePopoverProps) => { - const containerRef = useRef(null); - const searchInputRef = useRef(null); - // Parse the variable to extract name, default value, and transformers - const { parsedName, parsedDefaultValue, parsedTransformers } = useMemo(() => { - if (!variable) { - return { parsedName: '', parsedDefaultValue: '', parsedTransformers: [] }; - } - - const parts = variable.split('|').map((part) => part.trim()); - const [nameWithDefault, ...rest] = parts; - - // Handle default value - const defaultMatch = nameWithDefault?.match(/default:\s*([^}]+)/); - const name = defaultMatch ? nameWithDefault.replace(/\s*\|\s*default:[^}]+/, '') : nameWithDefault || ''; - const defaultVal = defaultMatch ? defaultMatch[1].trim().replace(/^["']|["']$/g, '') : ''; - - // Get all transformers with their parameters - const transforms = - rest?.reduce((acc, part) => { - const [transformerValue, ...paramValues] = part.split(':').map((p) => p.trim()); - if (TRANSFORMERS.some((t) => t.value === transformerValue)) { - // Parse parameters, handling quoted values and commas - const params = - paramValues.length > 0 - ? paramValues - .join(':') - .split(',') - .map((param) => param.trim().replace(/^['"]|['"]$/g, '')) - : undefined; - - acc.push({ value: transformerValue, ...(params ? { params } : {}) }); - } - return acc; - }, []) || []; - - return { - parsedName: name, - parsedDefaultValue: defaultVal, - parsedTransformers: transforms, - }; - }, [variable]); - - const [name, setName] = useState(parsedName); - const [defaultVal, setDefaultVal] = useState(parsedDefaultValue); - const [transformers, setTransformers] = useState(parsedTransformers); - const updateTimeoutRef = useRef(); - const isUpdatingRef = useRef(false); - const [dragOverIndex, setDragOverIndex] = useState(null); - const [draggingItem, setDraggingItem] = useState(null); - const [showRawLiquid, setShowRawLiquid] = useState(false); - const [searchQuery, setSearchQuery] = useState(''); - - const formatParamValue = (param: string, type?: 'string' | 'number') => { - if (type === 'number') { - return param; - } - return `'${param}'`; - }; - - // Debounced update function - const debouncedUpdate = useCallback( - (newName: string, newDefaultVal: string, newTransformers: TransformerWithParam[]) => { - if (isUpdatingRef.current) return; - - if (updateTimeoutRef.current) { - clearTimeout(updateTimeoutRef.current); - } - - updateTimeoutRef.current = setTimeout(() => { - try { - isUpdatingRef.current = true; - - // Build the variable parts - const parts = [newName.trim()]; - - if (newDefaultVal) { - parts.push(`default: "${newDefaultVal.trim()}"`); - } - - // Add transformers in order with their parameters - parts.push( - ...newTransformers.map((t) => { - const transformerDef = TRANSFORMERS.find((def) => def.value === t.value); - if (!t.params?.length) return t.value; - return `${t.value}: ${t.params - .map((param, index) => formatParamValue(param, transformerDef?.params?.[index]?.type)) - .join(', ')}`; - }) - ); - - // Join with proper spacing - const finalValue = parts.join(' | '); - onUpdate(finalValue); - } finally { - isUpdatingRef.current = false; - } - }, 300); - }, - [onUpdate] - ); - - // Update when values change - useEffect(() => { - debouncedUpdate(name, defaultVal, transformers); - return () => { - if (updateTimeoutRef.current) { - clearTimeout(updateTimeoutRef.current); - } - }; - }, [name, defaultVal, transformers, debouncedUpdate]); - - const handleTransformerToggle = (value: string) => { - setTransformers((current) => { - if (current.some((t) => t.value === value)) { - return current.filter((t) => t.value !== value); - } - return [...current, { value }]; - }); - }; - - const moveTransformer = (from: number, to: number) => { - setTransformers((current) => { - const newTransformers = [...current]; - const [removed] = newTransformers.splice(from, 1); - newTransformers.splice(to, 0, removed); - return newTransformers; - }); - }; - - const handleParamChange = (index: number, params: string[]) => { - setTransformers((current) => { - const newTransformers = [...current]; - const transformerDef = TRANSFORMERS.find((def) => def.value === newTransformers[index].value); - - // Format params based on their types - const formattedParams = params.map((param, paramIndex) => { - const paramType = transformerDef?.params?.[paramIndex]?.type; - if (paramType === 'number') { - // Remove any non-numeric characters except decimal point - const numericValue = param.replace(/[^\d.-]/g, ''); - // Return empty string if not a valid number - return isNaN(Number(numericValue)) ? '' : numericValue; - } - return param; - }); - - newTransformers[index] = { ...newTransformers[index], params: formattedParams }; - return newTransformers; - }); - }; - - const handleRawLiquidChange = (value: string) => { - // Remove {{ and }} and trim - const content = value.replace(/^\{\{\s*|\s*\}\}$/g, '').trim(); - - // Split by pipe and trim each part - const parts = content.split('|').map((part) => part.trim()); - - // First part is the name - const newName = parts[0]; - let newDefaultVal = ''; - const newTransformers: TransformerWithParam[] = []; - - // Process each part after the name - parts.slice(1).forEach((part) => { - if (part.startsWith('default:')) { - // Extract default value, handling quotes - newDefaultVal = part - .replace('default:', '') - .trim() - .replace(/^["']|["']$/g, ''); - } else { - // Handle transformers - const [transformerValue, ...paramValues] = part.split(':').map((p) => p.trim()); - if (TRANSFORMERS.some((t) => t.value === transformerValue)) { - const transformerDef = TRANSFORMERS.find((t) => t.value === transformerValue); - - // Parse parameters, handling quoted values and types - const params = - paramValues.length > 0 - ? paramValues - .join(':') - .split(',') - .map((param, index) => { - const paramType = transformerDef?.params?.[index]?.type; - const cleanParam = param.trim().replace(/^['"]|['"]$/g, ''); - - if (paramType === 'number') { - // For number type, remove any non-numeric characters - const numericValue = cleanParam.replace(/[^\d.-]/g, ''); - return isNaN(Number(numericValue)) ? '' : numericValue; - } - return cleanParam; - }) - : undefined; - - newTransformers.push({ - value: transformerValue, - ...(params ? { params } : {}), - }); - } - } - }); - - setName(newName); - setDefaultVal(newDefaultVal); - setTransformers(newTransformers); - }; - - // Filter function for transformers - const getFilteredTransformers = useCallback( - (query: string) => { - const normalizedQuery = query.toLowerCase(); - return TRANSFORMERS.filter((transformer) => { - if (transformers.some((t) => t.value === transformer.value)) return false; - - return ( - transformer.label.toLowerCase().includes(normalizedQuery) || - transformer.description?.toLowerCase().includes(normalizedQuery) || - transformer.value.toLowerCase().includes(normalizedQuery) - ); - }); - }, - [transformers] - ); - - return ( - -
-
-
-
- CONFIGURE VARIABLE -
-
-
-
-
- - -
- - - - ) => setName(e.target.value)} - className="h-7 text-sm" - /> - -
-
-
- - -
- - - ) => setDefaultVal(e.target.value)} - className="h-7 text-sm" - /> - -
-
-
- - -
- - -
-
-
- {showRawLiquid && ( - - -
- - 0 - ? ' | ' + - transformers - .map((t) => { - const transformerDef = TRANSFORMERS.find((def) => def.value === t.value); - if (!t.params?.length) return t.value; - return `${t.value}: ${t.params - .map((param, index) => - formatParamValue(param, transformerDef?.params?.[index]?.type) - ) - .join(', ')}`; - }) - .join(' | ') - : '' - } }}`} - onChange={(e) => handleRawLiquidChange(e.target.value)} - className="h-7 text-sm" - /> - -
-
-
- )} -
- - - -
- - -
- - { - e.stopPropagation(); - setSearchQuery(e.target.value); - }} - className="h-7 text-sm" - placeholder="Search modifiers..." - autoFocus - onKeyDown={(e) => { - e.stopPropagation(); - if (e.key === 'Escape') { - setSearchQuery(''); - } - }} - /> - -
-
- {getFilteredTransformers(searchQuery).length === 0 ? ( -
- - {searchQuery ? 'No modifiers found' : 'All modifiers have been added'} - - {searchQuery && Try searching for different terms} -
- ) : ( - <> - {getFilteredTransformers(searchQuery).map((transformer) => ( - - - - ))} - - )} -
- - -
- - - - {/* Selected transformers with drag handles */} - {transformers.length > 0 && ( -
- - {transformers.map((transformer, index) => { - const transformerDef = TRANSFORMERS.find((t) => t.value === transformer.value); - return ( - - {dragOverIndex === index && ( - - )} - setDraggingItem(index)} - onDragEnd={(e, info) => { - if (dragOverIndex !== null && draggingItem !== null && draggingItem !== dragOverIndex) { - moveTransformer(draggingItem, dragOverIndex); - } - setDraggingItem(null); - setDragOverIndex(null); - }} - onDrag={(e, info) => { - const elements = document.elementsFromPoint(info.point.x, info.point.y); - const droppableElement = elements.find( - (el) => el.classList.contains('group') && !el.classList.contains('opacity-50') - ); - - if (droppableElement) { - const index = parseInt(droppableElement.getAttribute('data-index') || '-1'); - if (index !== -1 && dragOverIndex !== index) { - setDragOverIndex(index); - } - } else { - setDragOverIndex(null); - } - }} - data-index={index} - animate={draggingItem === index ? { scale: 1.02, zIndex: 50 } : { scale: 1, zIndex: 1 }} - transition={{ - duration: 0.15, - ease: [0.32, 0.72, 0, 1], - }} - > - -
-
-
{transformerDef?.label}
- {transformerDef?.hasParam && transformerDef.params && ( -
- {transformerDef.params.map((param, paramIndex) => ( - { - const newParams = [...(transformer.params || [])]; - newParams[paramIndex] = e.target.value; - handleParamChange(index, newParams); - }} - className="border-stroke-soft ml-1 h-[20px] w-[calc(100%-8px)] border-l pl-1 text-xs" - placeholder={param.placeholder} - title={param.description} - /> - ))} -
- )} -
-
- -
- {dragOverIndex === index + 1 && ( - - )} - - ); - })} -
-
- )} -
-
-
- - ); -}; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx new file mode 100644 index 00000000000..582c9e7e8a1 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx @@ -0,0 +1,123 @@ +import { motion } from 'motion/react'; +import { GripVertical } from 'lucide-react'; +import { RiCloseLine } from 'react-icons/ri'; +import { Button } from '@/components/primitives/button'; +import { Input } from '@/components/primitives/input'; +import { TRANSFORMERS } from '../constants'; +import { TransformerWithParam } from '../types'; + +interface DraggableTransformerProps { + transformer: TransformerWithParam; + index: number; + isLast: boolean; + draggingItem: number | null; + dragOverIndex: number | null; + onDragStart: (index: number) => void; + onDragEnd: () => void; + onDrag: (e: any, info: any) => void; + onRemove: (value: string) => void; + onParamChange: (index: number, params: string[]) => void; +} + +export function DraggableTransformer({ + transformer, + index, + isLast, + draggingItem, + dragOverIndex, + onDragStart, + onDragEnd, + onDrag, + onRemove, + onParamChange, +}: DraggableTransformerProps) { + const transformerDef = TRANSFORMERS.find((t) => t.value === transformer.value); + + return ( + + {dragOverIndex === index && ( + + )} + onDragStart(index)} + onDragEnd={onDragEnd} + onDrag={onDrag} + data-index={index} + animate={draggingItem === index ? { scale: 1.02, zIndex: 50 } : { scale: 1, zIndex: 1 }} + transition={{ + duration: 0.15, + ease: [0.32, 0.72, 0, 1], + }} + > + +
+
+
{transformerDef?.label}
+ {transformerDef?.hasParam && transformerDef.params && ( +
+ {transformerDef.params.map((param, paramIndex) => ( + { + const newParams = [...(transformer.params || [])]; + newParams[paramIndex] = e.target.value; + onParamChange(index, newParams); + }} + className="border-stroke-soft ml-1 h-[20px] w-[calc(100%-8px)] border-l pl-1 text-xs" + placeholder={param.placeholder} + title={param.description} + /> + ))} +
+ )} +
+
+ +
+ {dragOverIndex === index + 1 && ( + + )} + + ); +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx new file mode 100644 index 00000000000..f800f92ed05 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx @@ -0,0 +1,22 @@ +import { Transformer } from '../types'; + +interface TransformerItemProps { + transformer: Transformer; +} + +export function TransformerItem({ transformer }: TransformerItemProps) { + return ( +
+
+ {transformer.label} +
+ {transformer.description} + + {transformer.example && ( +
+ {transformer.example} +
+ )} +
+ ); +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-list.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-list.tsx new file mode 100644 index 00000000000..46f0240a87c --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-list.tsx @@ -0,0 +1,49 @@ +import { AnimatePresence } from 'motion/react'; +import { DraggableTransformer } from './draggable-transformer'; +import { TransformerWithParam } from '../types'; + +interface TransformerListProps { + transformers: TransformerWithParam[]; + dragOverIndex: number | null; + draggingItem: number | null; + onDragStart: (index: number) => void; + onDragEnd: () => void; + onDrag: (e: any, info: any) => void; + onRemove: (value: string) => void; + onParamChange: (index: number, params: string[]) => void; +} + +export function TransformerList({ + transformers, + dragOverIndex, + draggingItem, + onDragStart, + onDragEnd, + onDrag, + onRemove, + onParamChange, +}: TransformerListProps) { + if (transformers.length === 0) return null; + + return ( +
+ + {transformers.map((transformer, index) => ( + + ))} + +
+ ); +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/constants.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/constants.ts new file mode 100644 index 00000000000..1023eee6153 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/constants.ts @@ -0,0 +1,269 @@ +import { Transformer } from './types'; + +export const TRANSFORMERS: Transformer[] = [ + // Text Transformations + { + label: 'Uppercase', + value: 'upcase', + description: 'Convert text to uppercase', + example: '"hello" | upcase → HELLO', + }, + { + label: 'Lowercase', + value: 'downcase', + description: 'Convert text to lowercase', + example: '"HELLO" | downcase → hello', + }, + { + label: 'Capitalize', + value: 'capitalize', + description: 'Capitalize the first character', + example: '"hello" | capitalize → Hello', + }, + { + label: 'Strip HTML', + value: 'strip_html', + description: 'Remove all HTML tags from text', + example: '"

text

" | strip_html → text', + }, + { + label: 'Strip Newlines', + value: 'strip_newlines', + description: 'Remove all newline characters', + example: '"hello\\nworld" | strip_newlines → hello world', + }, + { + label: 'Escape', + value: 'escape', + description: 'Escape special characters', + example: '"" | escape → <hello>', + }, + { + label: 'Truncate', + value: 'truncate', + hasParam: true, + description: 'Truncate text to specified length', + example: '"hello world" | truncate: 5 → hello...', + params: [{ placeholder: 'Length (e.g. 20)', type: 'number' }], + }, + { + label: 'Truncate Words', + value: 'truncatewords', + hasParam: true, + description: 'Truncate text to specified number of words', + example: '"hello world and more" | truncatewords: 2 → hello world...', + params: [{ placeholder: 'Word count', type: 'number' }], + }, + { + label: 'Replace', + value: 'replace', + hasParam: true, + description: 'Replace all occurrences of a string', + example: '"Hello Hello" | replace: "Hello", "Hi" → Hi Hi', + params: [ + { placeholder: 'Search text', type: 'string' }, + { placeholder: 'Replace with', type: 'string' }, + ], + }, + { + label: 'Replace First', + value: 'replace_first', + hasParam: true, + description: 'Replace first occurrence of a string', + example: '"Hello Hello" | replace_first: "Hello", "Hi" → Hi Hello', + params: [ + { placeholder: 'Search text', type: 'string' }, + { placeholder: 'Replace with', type: 'string' }, + ], + }, + { + label: 'Remove', + value: 'remove', + hasParam: true, + description: 'Remove all occurrences of a string', + example: '"Hello Hello" | remove: "ello" → H H', + params: [{ placeholder: 'Text to remove', type: 'string' }], + }, + { + label: 'Remove First', + value: 'remove_first', + hasParam: true, + description: 'Remove first occurrence of a string', + example: '"Hello Hello" | remove_first: "ello" → H Hello', + params: [{ placeholder: 'Text to remove', type: 'string' }], + }, + { + label: 'Append', + value: 'append', + hasParam: true, + description: 'Add text to the end', + example: '"Hello" | append: " World" → Hello World', + params: [{ placeholder: 'Text to append', type: 'string' }], + }, + { + label: 'Prepend', + value: 'prepend', + hasParam: true, + description: 'Add text to the beginning', + example: '"World" | prepend: "Hello " → Hello World', + params: [{ placeholder: 'Text to prepend', type: 'string' }], + }, + { + label: 'Slice', + value: 'slice', + hasParam: true, + description: 'Extract a substring by position', + example: '"hello" | slice: 0, 2 → he', + params: [ + { placeholder: 'Start index', type: 'number' }, + { placeholder: 'Length (optional)', type: 'number' }, + ], + }, + // Number Operations + { + label: 'Plus', + value: 'plus', + hasParam: true, + description: 'Add a number', + example: '5 | plus: 3 → 8', + params: [{ placeholder: 'Number to add', type: 'number' }], + }, + { + label: 'Minus', + value: 'minus', + hasParam: true, + description: 'Subtract a number', + example: '5 | minus: 3 → 2', + params: [{ placeholder: 'Number to subtract', type: 'number' }], + }, + { + label: 'Times', + value: 'times', + hasParam: true, + description: 'Multiply by a number', + example: '5 | times: 3 → 15', + params: [{ placeholder: 'Number to multiply by', type: 'number' }], + }, + { + label: 'Divided By', + value: 'divided_by', + hasParam: true, + description: 'Divide by a number', + example: '10 | divided_by: 2 → 5', + params: [{ placeholder: 'Number to divide by', type: 'number' }], + }, + { + label: 'Round', + value: 'round', + hasParam: true, + description: 'Round to specified decimal places', + example: '4.5678 | round: 2 → 4.57', + params: [{ placeholder: 'Decimal places', type: 'number' }], + }, + { + label: 'Floor', + value: 'floor', + description: 'Round down to nearest integer', + example: '4.6 | floor → 4', + }, + { + label: 'Ceil', + value: 'ceil', + description: 'Round up to nearest integer', + example: '4.3 | ceil → 5', + }, + { + label: 'Abs', + value: 'abs', + description: 'Get absolute value', + example: '-5 | abs → 5', + }, + // Data Formatting + { + label: 'Date Format', + value: 'date', + hasParam: true, + description: 'Format a date using strftime format', + example: '"2024-01-20" | date: "%B %d, %Y" → January 20, 2024', + params: [{ placeholder: 'Format (e.g. "%Y-%m-%d")', description: 'strftime format', type: 'string' }], + }, + { + label: 'Default', + value: 'default', + hasParam: true, + description: 'Use default value if input is empty', + example: '"" | default: "N/A" → N/A', + params: [{ placeholder: 'Default value', type: 'string' }], + }, + { + label: 'JSON', + value: 'json', + description: 'Convert object to JSON string', + example: '{name: "John"} | json → {"name":"John"}', + }, + { + label: 'Size', + value: 'size', + description: 'Get length of string or array', + example: '"hello" | size → 5', + }, + { + label: 'Join', + value: 'join', + hasParam: true, + description: 'Join array elements with separator', + example: '["a","b"] | join: ", " → a, b', + params: [{ placeholder: 'Separator (e.g. ", ")', type: 'string' }], + }, + { + label: 'Split', + value: 'split', + hasParam: true, + description: 'Split string into array', + example: '"a,b" | split: "," → ["a","b"]', + params: [{ placeholder: 'Delimiter', type: 'string' }], + }, + { + label: 'First', + value: 'first', + description: 'Get first element of array', + example: '[1,2,3] | first → 1', + }, + { + label: 'Last', + value: 'last', + description: 'Get last element of array', + example: '[1,2,3] | last → 3', + }, + { + label: 'Map', + value: 'map', + hasParam: true, + description: 'Extract property from each item in array', + example: 'users | map: "name" → ["John", "Jane"]', + params: [{ placeholder: 'Property name', type: 'string' }], + }, + { + label: 'Where', + value: 'where', + hasParam: true, + description: 'Filter array by property value', + example: 'users | where: "active", true → [activeUsers]', + params: [ + { placeholder: 'Property name', type: 'string' }, + { placeholder: 'Value to match', type: 'string' }, + ], + }, + { + label: 'URL Encode', + value: 'url_encode', + description: 'Encode string for use in URL', + example: '"hello world" | url_encode → hello%20world', + }, + { + label: 'URL Decode', + value: 'url_decode', + description: 'Decode URL-encoded string', + example: '"hello%20world" | url_decode → hello world', + }, +]; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts new file mode 100644 index 00000000000..cb9acbbb68e --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts @@ -0,0 +1,92 @@ +import { useState, useCallback } from 'react'; +import { TransformerWithParam } from '../types'; +import { TRANSFORMERS } from '../constants'; + +interface UseTransformerManagerProps { + initialTransformers: TransformerWithParam[]; + onUpdate: (transformers: TransformerWithParam[]) => void; +} + +export function useTransformerManager({ initialTransformers, onUpdate }: UseTransformerManagerProps) { + const [transformers, setTransformers] = useState(initialTransformers); + const [dragOverIndex, setDragOverIndex] = useState(null); + const [draggingItem, setDraggingItem] = useState(null); + + const handleTransformerToggle = useCallback( + (value: string) => { + setTransformers((current) => { + const newTransformers = current.some((t) => t.value === value) + ? current.filter((t) => t.value !== value) + : [...current, { value }]; + onUpdate(newTransformers); + return newTransformers; + }); + }, + [onUpdate] + ); + + const moveTransformer = useCallback( + (from: number, to: number) => { + setTransformers((current) => { + const newTransformers = [...current]; + const [removed] = newTransformers.splice(from, 1); + newTransformers.splice(to, 0, removed); + onUpdate(newTransformers); + return newTransformers; + }); + }, + [onUpdate] + ); + + const handleParamChange = useCallback( + (index: number, params: string[]) => { + setTransformers((current) => { + const newTransformers = [...current]; + const transformerDef = TRANSFORMERS.find((def) => def.value === newTransformers[index].value); + + // Format params based on their types + const formattedParams = params.map((param, paramIndex) => { + const paramType = transformerDef?.params?.[paramIndex]?.type; + if (paramType === 'number') { + const numericValue = param.replace(/[^\d.-]/g, ''); + return isNaN(Number(numericValue)) ? '' : numericValue; + } + return param; + }); + + newTransformers[index] = { ...newTransformers[index], params: formattedParams }; + onUpdate(newTransformers); + return newTransformers; + }); + }, + [onUpdate] + ); + + const getFilteredTransformers = useCallback( + (query: string) => { + const normalizedQuery = query.toLowerCase(); + return TRANSFORMERS.filter((transformer) => { + if (transformers.some((t) => t.value === transformer.value)) return false; + + return ( + transformer.label.toLowerCase().includes(normalizedQuery) || + transformer.description?.toLowerCase().includes(normalizedQuery) || + transformer.value.toLowerCase().includes(normalizedQuery) + ); + }); + }, + [transformers] + ); + + return { + transformers, + dragOverIndex, + draggingItem, + setDragOverIndex, + setDraggingItem, + handleTransformerToggle, + moveTransformer, + handleParamChange, + getFilteredTransformers, + }; +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts new file mode 100644 index 00000000000..b78d17a2574 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts @@ -0,0 +1,72 @@ +import { useMemo } from 'react'; +import { TransformerWithParam } from '../types'; +import { TRANSFORMERS } from '../constants'; + +export function useVariableParser(variable: string) { + return useMemo(() => { + if (!variable) { + return { parsedName: '', parsedDefaultValue: '', parsedTransformers: [] }; + } + + const parts = variable.split('|').map((part) => part.trim()); + const [nameWithDefault, ...restParts] = parts; + + // Handle default value - check both in nameWithDefault and rest parts + let name = nameWithDefault || ''; + let defaultVal = ''; + + // Function to extract default value from a part + const extractDefault = (part: string) => { + const defaultMatch = + part.match(/default:\s*'((?:[^'\\]|\\.)*)'/) || + part.match(/default:\s*"((?:[^"\\]|\\.)*)"/) || + part.match(/default:\s*([^}\s]+)/); + + if (defaultMatch) { + defaultVal = defaultMatch[1].replace(/\\\\'/g, "\\'").replace(/\\'/g, "'"); + return true; + } + return false; + }; + + // First check if default is in the nameWithDefault part + if (nameWithDefault?.includes('default:')) { + name = nameWithDefault.replace(/\s*\|\s*default:[^}]+/, '').trim(); + extractDefault(nameWithDefault); + } + + // Also check rest parts for default value and filter out default parts + const transformerParts = restParts.filter((part) => { + if (part.startsWith('default:')) { + extractDefault(part); + return false; + } + return true; + }); + + // Get all transformers with their parameters + const transforms = + transformerParts.reduce((acc, part) => { + const [transformerValue, ...paramValues] = part.split(':').map((p) => p.trim()); + if (TRANSFORMERS.some((t) => t.value === transformerValue)) { + // Parse parameters, handling quoted values and commas + const params = + paramValues.length > 0 + ? paramValues + .join(':') + .split(',') + .map((param) => param.trim().replace(/^['"]|["']$/g, '')) + : undefined; + + acc.push({ value: transformerValue, ...(params ? { params } : {}) }); + } + return acc; + }, []) || []; + + return { + parsedName: name, + parsedDefaultValue: defaultVal, + parsedTransformers: transforms, + }; + }, [variable]); +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/index.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/index.ts new file mode 100644 index 00000000000..66e1f1d93e7 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/index.ts @@ -0,0 +1,2 @@ +export * from './variable-popover'; +export * from './types'; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/index.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/index.tsx new file mode 100644 index 00000000000..613a2e26716 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/index.tsx @@ -0,0 +1,246 @@ +import { useState, useRef } from 'react'; +import { PopoverContent } from '@/components/primitives/popover'; +import { Input, InputField } from '@/components/primitives/input'; +import { FormControl, FormItem } from '@/components/primitives/form/form'; +import { Switch } from '@/components/primitives/switch'; +import { Code2 } from '../../../icons/code-2'; +import { Separator } from '../../separator'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; +import { TransformerItem } from './components/transformer-item'; +import { TransformerList } from './components/transformer-list'; +import { useVariableParser } from './hooks/use-variable-parser'; +import { useTransformerManager } from './hooks/use-transformer-manager'; +import { formatLiquidVariable } from './utils'; +import type { VariablePopoverProps } from './types'; + +export function VariablePopover({ variable, onClose, onUpdate }: VariablePopoverProps) { + const searchInputRef = useRef(null); + const { parsedName, parsedDefaultValue, parsedTransformers } = useVariableParser(variable); + const [name, setName] = useState(parsedName); + const [defaultVal, setDefaultVal] = useState(parsedDefaultValue); + const [showRawLiquid, setShowRawLiquid] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + + const { + transformers, + dragOverIndex, + draggingItem, + setDragOverIndex, + setDraggingItem, + handleTransformerToggle, + moveTransformer, + handleParamChange, + getFilteredTransformers, + } = useTransformerManager({ + initialTransformers: parsedTransformers, + onUpdate: (newTransformers) => { + onUpdate(formatLiquidVariable(name, defaultVal, newTransformers)); + }, + }); + + const handleNameChange = (newName: string) => { + setName(newName); + onUpdate(formatLiquidVariable(newName, defaultVal, transformers)); + }; + + const handleDefaultValueChange = (newDefaultVal: string) => { + setDefaultVal(newDefaultVal); + onUpdate(formatLiquidVariable(name, newDefaultVal, transformers)); + }; + + const handleRawLiquidChange = (value: string) => { + // Remove {{ and }} and trim + const content = value.replace(/^\{\{\s*|\s*\}\}$/g, '').trim(); + + // Split by pipe and trim each part + const parts = content.split('|').map((part) => part.trim()); + + // First part is the name + const newName = parts[0]; + setName(newName); + + // Process each part after the name + parts.slice(1).forEach((part) => { + if (part.startsWith('default:')) { + // Extract default value, handling quotes + const newDefaultVal = part + .replace('default:', '') + .trim() + .replace(/^["']|["']$/g, ''); + setDefaultVal(newDefaultVal); + } + }); + }; + + return ( + +
+
+
+
+ CONFIGURE VARIABLE +
+
+
+
+
+ + +
+ + + + handleNameChange(e.target.value)} className="h-7 text-sm" /> + +
+
+
+ + +
+ + + handleDefaultValueChange(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+ + +
+ + +
+
+
+ {showRawLiquid && ( + + +
+ + handleRawLiquidChange(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+ )} +
+ + + +
+ + +
+ + { + e.stopPropagation(); + setSearchQuery(e.target.value); + }} + className="h-7 text-sm" + placeholder="Search modifiers..." + autoFocus + onKeyDown={(e) => { + e.stopPropagation(); + if (e.key === 'Escape') { + setSearchQuery(''); + } + }} + /> + +
+
+ {getFilteredTransformers(searchQuery).length === 0 ? ( +
+ + {searchQuery ? 'No modifiers found' : 'All modifiers have been added'} + + {searchQuery && Try searching for different terms} +
+ ) : ( + getFilteredTransformers(searchQuery).map((transformer) => ( + + + + )) + )} +
+ + +
+ + + + { + if (dragOverIndex !== null && draggingItem !== null && draggingItem !== dragOverIndex) { + moveTransformer(draggingItem, dragOverIndex); + } + setDraggingItem(null); + setDragOverIndex(null); + }} + onDrag={(e, info) => { + const elements = document.elementsFromPoint(info.point.x, info.point.y); + const droppableElement = elements.find( + (el) => el.classList.contains('group') && !el.classList.contains('opacity-50') + ); + + if (droppableElement) { + const index = parseInt(droppableElement.getAttribute('data-index') || '-1'); + if (index !== -1 && dragOverIndex !== index) { + setDragOverIndex(index); + } + } else { + setDragOverIndex(null); + } + }} + onRemove={handleTransformerToggle} + onParamChange={handleParamChange} + /> +
+
+
+ + ); +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts new file mode 100644 index 00000000000..87c70c9ac75 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts @@ -0,0 +1,23 @@ +export interface Transformer { + label: string; + value: string; + hasParam?: boolean; + description?: string; + example?: string; + params?: { + placeholder: string; + description?: string; + type?: 'string' | 'number'; + }[]; +} + +export interface TransformerWithParam { + value: string; + params?: string[]; +} + +export interface VariablePopoverProps { + variable: string; + onClose: () => void; + onUpdate: (newValue: string) => void; +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts new file mode 100644 index 00000000000..99a9f7adf97 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts @@ -0,0 +1,39 @@ +import { TRANSFORMERS } from './constants'; + +function escapeString(str: string): string { + return str.replace(/'/g, "\\'"); +} + +export function formatParamValue(param: string, type?: 'string' | 'number') { + if (type === 'number') { + return param; + } + return `'${escapeString(param)}'`; +} + +export function formatLiquidVariable( + name: string, + defaultValue: string, + transformers: { value: string; params?: string[] }[] +) { + const parts = [name.trim()]; + + if (defaultValue) { + parts.push(`default: '${escapeString(defaultValue.trim())}'`); + } + + parts.push( + ...transformers.map((t) => { + if (!t.params?.length) return t.value; + + const transformerDef = TRANSFORMERS.find((def) => def.value === t.value); + const formattedParams = t.params.map((param, index) => + formatParamValue(param, transformerDef?.params?.[index]?.type) + ); + + return `${t.value}: ${formattedParams.join(', ')}`; + }) + ); + + return parts.join(' | '); +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx new file mode 100644 index 00000000000..e5a2c239590 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx @@ -0,0 +1,251 @@ +import { useState, useRef } from 'react'; +import { PopoverContent } from '@/components/primitives/popover'; +import { Input, InputField } from '@/components/primitives/input'; +import { FormControl, FormItem } from '@/components/primitives/form/form'; +import { Code2 } from '@/components/icons/code-2'; +import { Separator } from '@/components/primitives/separator'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; +import { Switch } from '@/components/primitives/switch'; +import { TransformerItem } from './components/transformer-item'; +import { TransformerList } from './components/transformer-list'; +import { useVariableParser } from './hooks/use-variable-parser'; +import { useTransformerManager } from './hooks/use-transformer-manager'; +import { formatLiquidVariable } from './utils'; +import type { VariablePopoverProps } from './types'; + +export function VariablePopover({ variable, onClose, onUpdate }: VariablePopoverProps) { + const searchInputRef = useRef(null); + const { parsedName, parsedDefaultValue, parsedTransformers } = useVariableParser(variable); + const [name, setName] = useState(parsedName); + const [defaultVal, setDefaultVal] = useState(parsedDefaultValue); + const [showRawLiquid, setShowRawLiquid] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + + const { + transformers, + dragOverIndex, + draggingItem, + setDragOverIndex, + setDraggingItem, + handleTransformerToggle, + moveTransformer, + handleParamChange, + getFilteredTransformers, + } = useTransformerManager({ + initialTransformers: parsedTransformers, + onUpdate: (newTransformers) => { + onUpdate(formatLiquidVariable(name, defaultVal, newTransformers)); + }, + }); + + const handleNameChange = (newName: string) => { + setName(newName); + onUpdate(formatLiquidVariable(newName, defaultVal, transformers)); + }; + + const handleDefaultValueChange = (newDefaultVal: string) => { + setDefaultVal(newDefaultVal); + onUpdate(formatLiquidVariable(name, newDefaultVal, transformers)); + }; + + const handleRawLiquidChange = (value: string) => { + // Remove {{ and }} and trim if they exist, but don't require them + const content = value.replace(/^\{\{?\s*|\s*\}?\}$/g, '').trim(); + + // Split by pipe and trim each part + const parts = content.split('|').map((part) => part.trim()); + + // First part is the name + const newName = parts[0]; + setName(newName); + + // Process each part after the name + parts.slice(1).forEach((part) => { + if (part.startsWith('default:')) { + // Extract default value, handling quotes and escaped quotes + const defaultMatch = + part.match(/default:\s*'((?:[^'\\]|\\.)*)'/) || + part.match(/default:\s*"((?:[^"\\]|\\.)*)"/) || + part.match(/default:\s*([^}\s]+)/); + + if (defaultMatch) { + // Handle CodeMirror's escaping by removing one level of escaping + const newDefaultVal = defaultMatch[1].replace(/\\\\'/g, "\\'").replace(/\\'/g, "'"); + setDefaultVal(newDefaultVal); + } + } + }); + }; + + return ( + +
+
+
+
+ CONFIGURE VARIABLE +
+
+
+
+
+ + +
+ + + + handleNameChange(e.target.value)} className="h-7 text-sm" /> + +
+
+
+ + +
+ + + handleDefaultValueChange(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+ + +
+ + +
+
+
+ {showRawLiquid && ( + + +
+ + handleRawLiquidChange(e.target.value)} + className="h-7 text-sm" + /> + +
+
+
+ )} +
+ + + +
+ + +
+ + { + e.stopPropagation(); + setSearchQuery(e.target.value); + }} + className="h-7 text-sm" + placeholder="Search modifiers..." + autoFocus + onKeyDown={(e) => { + e.stopPropagation(); + if (e.key === 'Escape') { + setSearchQuery(''); + } + }} + /> + +
+
+ {getFilteredTransformers(searchQuery).length === 0 ? ( +
+ + {searchQuery ? 'No modifiers found' : 'All modifiers have been added'} + + {searchQuery && Try searching for different terms} +
+ ) : ( + getFilteredTransformers(searchQuery).map((transformer) => ( + + + + )) + )} +
+ + +
+ + + + { + if (dragOverIndex !== null && draggingItem !== null && draggingItem !== dragOverIndex) { + moveTransformer(draggingItem, dragOverIndex); + } + setDraggingItem(null); + setDragOverIndex(null); + }} + onDrag={(e, info) => { + const elements = document.elementsFromPoint(info.point.x, info.point.y); + const droppableElement = elements.find( + (el) => el.classList.contains('group') && !el.classList.contains('opacity-50') + ); + + if (droppableElement) { + const index = parseInt(droppableElement.getAttribute('data-index') || '-1'); + if (index !== -1 && dragOverIndex !== index) { + setDragOverIndex(index); + } + } else { + setDragOverIndex(null); + } + }} + onRemove={handleTransformerToggle} + onParamChange={handleParamChange} + /> +
+
+
+ + ); +} From 50a25c9f71b7cd0762cdead9dcc24388129c5c57 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 18:01:11 +0200 Subject: [PATCH 22/49] fix: refactor --- .../primitives/field-editor/field-editor.tsx | 2 +- .../field-editor/variable-plugin.ts | 189 ------------------ .../field-editor/variable-plugin/constants.ts | 4 + .../field-editor/variable-plugin/index.ts | 33 +++ .../variable-plugin/plugin-view.ts | 78 ++++++++ .../field-editor/variable-plugin/types.ts | 16 ++ .../field-editor/variable-plugin/utils.ts | 75 +++++++ .../variable-plugin/variable-pill-widget.ts | 65 ++++++ .../{ => variable-plugin}/variable-theme.ts | 0 9 files changed, 272 insertions(+), 190 deletions(-) delete mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-plugin/constants.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-plugin/index.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-plugin/types.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts create mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts rename apps/dashboard/src/components/primitives/field-editor/{ => variable-plugin}/variable-theme.ts (100%) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index e2a6edee5d9..8df8b4f39ce 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -8,8 +8,8 @@ import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; import { autocompletion } from '@codemirror/autocomplete'; import { Popover, PopoverTrigger } from '@/components/primitives/popover'; import { createVariablePlugin } from './variable-plugin'; -import { variablePillTheme } from './variable-theme'; import { VariablePopover } from './variable-popover'; +import { variablePillTheme } from './variable-plugin/variable-theme'; type FieldEditorProps = { value: string; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts deleted file mode 100644 index cf18eff9d22..00000000000 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { EditorView, ViewPlugin, Decoration, DecorationSet, WidgetType } from '@uiw/react-codemirror'; -import { MutableRefObject } from 'react'; - -interface PluginState { - viewRef: MutableRefObject; - lastCompletionRef: MutableRefObject<{ from: number; to: number } | null>; - onSelect?: (value: string, from: number, to: number) => void; -} - -class VariablePillWidget extends WidgetType { - constructor( - private variableName: string, - private fullVariableName: string, - private start: number, - private end: number, - private hasModifiers: boolean, - private onSelect?: (value: string, from: number, to: number) => void - ) { - super(); - } - - toDOM() { - const span = document.createElement('span'); - const pillClass = `cm-variable-pill ${document.documentElement.classList.contains('dark') ? 'cm-dark' : ''} ${ - this.hasModifiers ? 'has-modifiers' : '' - }`; - span.className = pillClass; - span.setAttribute('data-variable', this.fullVariableName); - span.setAttribute('data-start', this.start.toString()); - span.setAttribute('data-end', this.end.toString()); - span.setAttribute('data-display', this.variableName); - span.textContent = this.variableName; - - const handleClick = (e: MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - - setTimeout(() => { - this.onSelect?.(this.fullVariableName, this.start, this.end); - }, 0); - }; - - span.addEventListener('mousedown', handleClick); - (span as any)._variableClickHandler = handleClick; - - return span; - } - - eq(other: VariablePillWidget) { - return ( - other.variableName === this.variableName && - other.fullVariableName === this.fullVariableName && - other.start === this.start && - other.end === this.end && - other.hasModifiers === this.hasModifiers - ); - } - - destroy(dom: HTMLElement) { - if ((dom as any)._variableClickHandler) { - dom.removeEventListener('mousedown', (dom as any)._variableClickHandler); - delete (dom as any)._variableClickHandler; - } - } - - ignoreEvent() { - return false; - } -} - -export function createVariablePlugin({ viewRef, lastCompletionRef, onSelect }: PluginState) { - return ViewPlugin.fromClass( - class { - decorations: DecorationSet; - lastCursor: number = 0; - isTypingVariable: boolean = false; - - constructor(view: EditorView) { - this.decorations = this.createDecorations(view); - viewRef.current = view; - } - - update(update: any) { - if (update.docChanged || update.viewportChanged || update.selectionSet) { - const pos = update.state.selection.main.head; - const content = update.state.doc.toString(); - - // Check if we're currently typing a variable - const beforeCursor = content.slice(0, pos); - const afterCursor = content.slice(pos); - const lastOpenBrackets = beforeCursor.lastIndexOf('{{'); - const nextCloseBrackets = afterCursor.indexOf('}}'); - - // We're typing a variable if we have {{ before the cursor but no }} after it - this.isTypingVariable = - lastOpenBrackets !== -1 && - (nextCloseBrackets === -1 || beforeCursor.indexOf('}}', lastOpenBrackets) === -1); - - // Handle backspace inside a variable - if (update.docChanged && update.changes.desc === 'input.delete.backward') { - if (lastOpenBrackets !== -1 && nextCloseBrackets !== -1) { - const variableContent = content.slice(lastOpenBrackets + 2, pos).trim(); - // If we're deleting the last character of the variable name, remove the entire variable - if (!variableContent) { - requestAnimationFrame(() => { - update.view.dispatch({ - changes: { - from: lastOpenBrackets, - to: pos + nextCloseBrackets + 2, - insert: '', - }, - }); - }); - return; - } - } - } - - // Handle variable completion - if (update.docChanged && content.slice(pos - 2, pos) === '}}') { - const start = content.lastIndexOf('{{', pos); - if (start !== -1) { - const variableContent = content.slice(start + 2, pos - 2).trim(); - if (variableContent) { - this.lastCursor = -1; - if (pos === content.length || content[pos] !== ' ') { - requestAnimationFrame(() => { - update.view.dispatch({ - changes: { from: pos, insert: ' ' }, - selection: { anchor: pos + 1 }, - }); - }); - } - } - } - } - - this.decorations = this.createDecorations(update.view); - } - if (update.view) { - viewRef.current = update.view; - } - } - - createDecorations(view: EditorView) { - const decorations: any[] = []; - const content = view.state.doc.toString(); - const pos = view.state.selection.main.head; - const variableRegex = /{{([^{}]+)}}/g; - let match; - - while ((match = variableRegex.exec(content)) !== null) { - const start = match.index; - const end = start + match[0].length; - - // Don't create a pill if we're currently editing this variable - if (this.isTypingVariable && pos > start && pos < end) { - continue; - } - - const fullVariableName = match[1].trim(); - const parts = fullVariableName.split('|').map((part) => part.trim()); - const variableName = parts[0]; - const hasModifiers = parts.length > 1; - - if (variableName) { - decorations.push( - Decoration.replace({ - widget: new VariablePillWidget(variableName, fullVariableName, start, end, hasModifiers, onSelect), - inclusive: false, - side: -1, - }).range(start, end) - ); - } - } - - lastCompletionRef.current = null; - return Decoration.set(decorations, true); - } - }, - { - decorations: (v) => v.decorations, - provide: (plugin) => - EditorView.atomicRanges.of((view) => { - return view.plugin(plugin)?.decorations || Decoration.none; - }), - } - ); -} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/constants.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/constants.ts new file mode 100644 index 00000000000..a7b7932a0c5 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/constants.ts @@ -0,0 +1,4 @@ +export const VARIABLE_REGEX = /{{([^{}]+)}}/g; +export const DARK_MODE_CLASS = 'dark'; +export const VARIABLE_PILL_CLASS = 'cm-variable-pill'; +export const MODIFIERS_CLASS = 'has-modifiers'; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/index.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/index.ts new file mode 100644 index 00000000000..9acf888b26f --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/index.ts @@ -0,0 +1,33 @@ +import { EditorView, ViewPlugin, Decoration } from '@uiw/react-codemirror'; +import type { PluginState } from './types'; +import { VariablePluginView } from './plugin-view'; + +export function createVariablePlugin({ viewRef, lastCompletionRef, onSelect }: PluginState) { + return ViewPlugin.fromClass( + class { + private view: VariablePluginView; + + constructor(view: EditorView) { + this.view = new VariablePluginView(view, viewRef, lastCompletionRef, onSelect); + } + + update(update: any) { + this.view.update(update); + } + + get decorations() { + return this.view.decorations; + } + }, + { + decorations: (v) => v.decorations, + provide: (plugin) => + EditorView.atomicRanges.of((view) => { + return view.plugin(plugin)?.decorations || Decoration.none; + }), + } + ); +} + +export * from './types'; +export * from './constants'; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts new file mode 100644 index 00000000000..4fe57d8eec9 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts @@ -0,0 +1,78 @@ +import { EditorView, Decoration, DecorationSet } from '@uiw/react-codemirror'; +import { MutableRefObject } from 'react'; +import { VARIABLE_REGEX } from './constants'; +import { VariablePillWidget } from './variable-pill-widget'; +import { handleVariableBackspace, handleVariableCompletion, isTypingVariable, parseVariable } from './utils'; + +export class VariablePluginView { + decorations: DecorationSet; + lastCursor: number = 0; + isTypingVariable: boolean = false; + + constructor( + private view: EditorView, + private viewRef: MutableRefObject, + private lastCompletionRef: MutableRefObject<{ from: number; to: number } | null>, + private onSelect?: (value: string, from: number, to: number) => void + ) { + this.decorations = this.createDecorations(view); + viewRef.current = view; + } + + update(update: any) { + if (update.docChanged || update.viewportChanged || update.selectionSet) { + const pos = update.state.selection.main.head; + const content = update.state.doc.toString(); + + this.isTypingVariable = isTypingVariable(content, pos); + + // Handle backspace inside a variable + if (update.docChanged && update.changes.desc === 'input.delete.backward') { + if (handleVariableBackspace(update.view, pos, content)) { + return; + } + } + + // Handle variable completion + if (update.docChanged) { + handleVariableCompletion(update.view, pos, content); + } + + this.decorations = this.createDecorations(update.view); + } + + if (update.view) { + this.viewRef.current = update.view; + } + } + + createDecorations(view: EditorView) { + const decorations: any[] = []; + const content = view.state.doc.toString(); + const pos = view.state.selection.main.head; + let match; + + while ((match = VARIABLE_REGEX.exec(content)) !== null) { + const { fullVariableName, variableName, start, end, hasModifiers } = parseVariable(match); + + // Don't create a pill if we're currently editing this variable + if (this.isTypingVariable && pos > start && pos < end) { + continue; + } + + if (variableName) { + decorations.push( + Decoration.replace({ + widget: new VariablePillWidget(variableName, fullVariableName, start, end, hasModifiers, this.onSelect), + inclusive: false, + side: -1, + }).range(start, end) + ); + } + } + + this.lastCompletionRef.current = null; + + return Decoration.set(decorations, true); + } +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/types.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/types.ts new file mode 100644 index 00000000000..a43bafdd01b --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/types.ts @@ -0,0 +1,16 @@ +import { EditorView } from '@uiw/react-codemirror'; +import { MutableRefObject } from 'react'; + +export interface PluginState { + viewRef: MutableRefObject; + lastCompletionRef: MutableRefObject<{ from: number; to: number } | null>; + onSelect?: (value: string, from: number, to: number) => void; +} + +export interface VariableMatch { + fullVariableName: string; + variableName: string; + start: number; + end: number; + hasModifiers: boolean; +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts new file mode 100644 index 00000000000..ea3c8450842 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts @@ -0,0 +1,75 @@ +import { EditorView } from '@uiw/react-codemirror'; +import type { VariableMatch } from './types'; + +export function parseVariable(match: RegExpExecArray): VariableMatch { + const start = match.index; + const end = start + match[0].length; + const fullVariableName = match[1].trim(); + const parts = fullVariableName.split('|').map((part) => part.trim()); + const variableName = parts[0]; + const hasModifiers = parts.length > 1; + + return { + fullVariableName, + variableName, + start, + end, + hasModifiers, + }; +} + +export function handleVariableBackspace(view: EditorView, pos: number, content: string): boolean { + const lastOpenBrackets = content.lastIndexOf('{{', pos); + const nextCloseBrackets = content.indexOf('}}', pos); + + if (lastOpenBrackets !== -1 && nextCloseBrackets !== -1) { + const variableContent = content.slice(lastOpenBrackets + 2, pos).trim(); + if (!variableContent) { + requestAnimationFrame(() => { + view.dispatch({ + changes: { + from: lastOpenBrackets, + to: pos + nextCloseBrackets + 2, + insert: '', + }, + }); + }); + + return true; + } + } + + return false; +} + +export function handleVariableCompletion(view: EditorView, pos: number, content: string): boolean { + if (content.slice(pos - 2, pos) === '}}') { + const start = content.lastIndexOf('{{', pos); + if (start !== -1) { + const variableContent = content.slice(start + 2, pos - 2).trim(); + if (variableContent) { + if (pos === content.length || content[pos] !== ' ') { + requestAnimationFrame(() => { + view.dispatch({ + changes: { from: pos, insert: ' ' }, + selection: { anchor: pos + 1 }, + }); + }); + } + + return true; + } + } + } + + return false; +} + +export function isTypingVariable(content: string, pos: number): boolean { + const beforeCursor = content.slice(0, pos); + const afterCursor = content.slice(pos); + const lastOpenBrackets = beforeCursor.lastIndexOf('{{'); + const nextCloseBrackets = afterCursor.indexOf('}}'); + + return lastOpenBrackets !== -1 && (nextCloseBrackets === -1 || beforeCursor.indexOf('}}', lastOpenBrackets) === -1); +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts new file mode 100644 index 00000000000..c531e5571fa --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts @@ -0,0 +1,65 @@ +import { WidgetType } from '@uiw/react-codemirror'; +import { DARK_MODE_CLASS, VARIABLE_PILL_CLASS, MODIFIERS_CLASS } from './constants'; + +export class VariablePillWidget extends WidgetType { + constructor( + private variableName: string, + private fullVariableName: string, + private start: number, + private end: number, + private hasModifiers: boolean, + private onSelect?: (value: string, from: number, to: number) => void + ) { + super(); + } + + toDOM() { + const span = document.createElement('span'); + const isDarkMode = document.documentElement.classList.contains(DARK_MODE_CLASS); + const pillClass = `${VARIABLE_PILL_CLASS} ${isDarkMode ? 'cm-dark' : ''} ${ + this.hasModifiers ? MODIFIERS_CLASS : '' + }`; + + span.className = pillClass; + span.setAttribute('data-variable', this.fullVariableName); + span.setAttribute('data-start', this.start.toString()); + span.setAttribute('data-end', this.end.toString()); + span.setAttribute('data-display', this.variableName); + span.textContent = this.variableName; + + const handleClick = (e: MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + setTimeout(() => { + this.onSelect?.(this.fullVariableName, this.start, this.end); + }, 0); + }; + + span.addEventListener('mousedown', handleClick); + (span as any)._variableClickHandler = handleClick; + + return span; + } + + eq(other: VariablePillWidget) { + return ( + other.variableName === this.variableName && + other.fullVariableName === this.fullVariableName && + other.start === this.start && + other.end === this.end && + other.hasModifiers === this.hasModifiers + ); + } + + destroy(dom: HTMLElement) { + if ((dom as any)._variableClickHandler) { + dom.removeEventListener('mousedown', (dom as any)._variableClickHandler); + delete (dom as any)._variableClickHandler; + } + } + + ignoreEvent() { + return false; + } +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-theme.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts similarity index 100% rename from apps/dashboard/src/components/primitives/field-editor/variable-theme.ts rename to apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts From 74b7ba4a9d98c9d9c49faf0f34d201c31e38e533 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 18:28:40 +0200 Subject: [PATCH 23/49] fix: done --- .../variable-plugin/variable-theme.ts | 7 +- .../components/transformer-item.tsx | 22 ++-- .../variable-popover/variable-popover.tsx | 110 ++++++++---------- .../src/utils/liquid-autocomplete.ts | 55 ++++++++- 4 files changed, 112 insertions(+), 82 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts index 482dc1a2ac2..1a7f00b206e 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts @@ -65,10 +65,7 @@ export const variablePillTheme = EditorView.baseTheme({ minHeight: '100%', display: 'flex', flexDirection: 'column', - }, - '.cm-line': { - minHeight: '32px', - display: 'flex', - alignItems: 'center', + whiteSpace: 'pre-wrap', + wordBreak: 'break-word', }, }); diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx index f800f92ed05..237431bf135 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx @@ -6,17 +6,19 @@ interface TransformerItemProps { export function TransformerItem({ transformer }: TransformerItemProps) { return ( -
-
- {transformer.label} -
- {transformer.description} - - {transformer.example && ( -
- {transformer.example} +
+
+
+ {transformer.label}
- )} +

{transformer.description}

+ + {transformer.example && ( + + {transformer.example} + + )} +
); } diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx index e5a2c239590..7e43c4756e6 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx @@ -1,17 +1,19 @@ import { useState, useRef } from 'react'; -import { PopoverContent } from '@/components/primitives/popover'; +import { PopoverContent, Popover, PopoverTrigger } from '@/components/primitives/popover'; import { Input, InputField } from '@/components/primitives/input'; import { FormControl, FormItem } from '@/components/primitives/form/form'; import { Code2 } from '@/components/icons/code-2'; import { Separator } from '@/components/primitives/separator'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; import { Switch } from '@/components/primitives/switch'; +import { Button } from '@/components/primitives/button'; import { TransformerItem } from './components/transformer-item'; import { TransformerList } from './components/transformer-list'; import { useVariableParser } from './hooks/use-variable-parser'; import { useTransformerManager } from './hooks/use-transformer-manager'; import { formatLiquidVariable } from './utils'; import type { VariablePopoverProps } from './types'; +import { Command, CommandInput, CommandList, CommandGroup, CommandItem } from '@/components/primitives/command'; +import { WandIcon } from 'lucide-react'; export function VariablePopover({ variable, onClose, onUpdate }: VariablePopoverProps) { const searchInputRef = useRef(null); @@ -20,6 +22,7 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover const [defaultVal, setDefaultVal] = useState(parsedDefaultValue); const [showRawLiquid, setShowRawLiquid] = useState(false); const [searchQuery, setSearchQuery] = useState(''); + const [isModifierPopoverOpen, setIsModifierPopoverOpen] = useState(false); const { transformers, @@ -146,69 +149,52 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover
- + + + + + +
+ { - e.stopPropagation(); - setSearchQuery(e.target.value); - }} - className="h-7 text-sm" + onValueChange={setSearchQuery} placeholder="Search modifiers..." - autoFocus - onKeyDown={(e) => { - e.stopPropagation(); - if (e.key === 'Escape') { - setSearchQuery(''); - } - }} + className="h-7 text-sm" /> - -
-
- {getFilteredTransformers(searchQuery).length === 0 ? ( -
- - {searchQuery ? 'No modifiers found' : 'All modifiers have been added'} - - {searchQuery && Try searching for different terms} -
- ) : ( - getFilteredTransformers(searchQuery).map((transformer) => ( - - - - )) - )} -
- - +
+ + {getFilteredTransformers(searchQuery).length === 0 ? ( +
+ + {searchQuery ? 'No modifiers found' : 'All modifiers have been added'} + + {searchQuery && Try searching for different terms} +
+ ) : ( + + {getFilteredTransformers(searchQuery).map((transformer) => ( + { + handleTransformerToggle(transformer.value); + setSearchQuery(''); + setIsModifierPopoverOpen(false); + }} + > + + + ))} + + )} +
+ + +
diff --git a/apps/dashboard/src/utils/liquid-autocomplete.ts b/apps/dashboard/src/utils/liquid-autocomplete.ts index 9aa3a137130..073a616c848 100644 --- a/apps/dashboard/src/utils/liquid-autocomplete.ts +++ b/apps/dashboard/src/utils/liquid-autocomplete.ts @@ -1,6 +1,12 @@ import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; import { CompletionContext, CompletionResult } from '@codemirror/autocomplete'; +interface CompletionOption { + label: string; + type: string; + boost?: number; +} + const filters = [ // Math filters { label: 'plus', type: 'function' }, @@ -89,6 +95,13 @@ const filters = [ { label: 'to_integer', type: 'function' }, ]; +function createPayloadVariable(input: string): LiquidVariable { + return { + label: `payload.${input}`, + type: 'variable', + }; +} + export const completions = (variables: LiquidVariable[]) => (context: CompletionContext): CompletionResult | null => { @@ -131,24 +144,56 @@ export const completions = } } - // If no pipe (|) is present, suggest variables + // Handle variable suggestions const word = context.matchBefore(/[\w.]+/); // Match variable names only if (!word && insideBraces.trim() === '') { return { from: pos, - options: variables.map((v) => ({ + options: [ + ...variables.map((v) => ({ + label: v.label, + type: 'variable', + })), + ], + }; + } + + // Special handling for payload namespace + if (word && word.text.startsWith('payload.')) { + const payloadPath = word.text.slice(8); // Remove 'payload.' prefix + const existingVariables = variables.filter((v) => v.label.startsWith('payload.')); + + const suggestions: CompletionOption[] = existingVariables + .filter((v) => v.label.toLowerCase().includes(payloadPath.toLowerCase())) + .map((v) => ({ label: v.label, type: 'variable', - })), + })); + + // Always add the current input as a suggestion for new variables + if (payloadPath && !existingVariables.some((v) => v.label === `payload.${payloadPath}`)) { + suggestions.unshift({ + label: `payload.${payloadPath}`, + type: 'variable', + boost: 99, // Higher boost to show at top + }); + } + + return { + from: word.from, + to: word.to ?? pos, + options: suggestions, }; } - // Suggest variables if typing a valid variable name + // Regular variable suggestions if (word) { + const matchingVariables = variables.filter((v) => v.label.toLowerCase().includes(word.text.toLowerCase())); + return { from: word.from, to: word.to ?? pos, - options: variables.map((v) => ({ + options: matchingVariables.map((v) => ({ label: v.label, type: 'variable', })), From b378aac731517dd426f1d20973e5e9affb01369d Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 18:35:20 +0200 Subject: [PATCH 24/49] fix: add some personality --- .cspell.json | 3 +- .../components/transformer-item.tsx | 9 +- .../variable-popover/constants.ts | 68 ++--- .../variable-popover/variable-popover.tsx | 237 ------------------ 4 files changed, 42 insertions(+), 275 deletions(-) delete mode 100644 apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx diff --git a/.cspell.json b/.cspell.json index 0ae84732f00..9d0d9be2bb1 100644 --- a/.cspell.json +++ b/.cspell.json @@ -821,6 +821,7 @@ "tsconfig.json", "unreadRead", "websockets", - "apps/dashboard/src/components/header-navigation/customer-support-button.tsx" + "apps/dashboard/src/components/header-navigation/customer-support-button.tsx", + "apps/dashboard/src/components/primitives/field-editor/variable-popover/constants.ts" ] } diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx index 237431bf135..12c4ddd233a 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx @@ -1,4 +1,5 @@ import { Transformer } from '../types'; +import TruncatedText from '@/components/truncated-text'; interface TransformerItemProps { transformer: Transformer; @@ -14,9 +15,11 @@ export function TransformerItem({ transformer }: TransformerItemProps) {

{transformer.description}

{transformer.example && ( - - {transformer.example} - + + + {transformer.example} + + )}
diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/constants.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/constants.ts index 1023eee6153..ca1a6e5f069 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/constants.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/constants.ts @@ -6,44 +6,44 @@ export const TRANSFORMERS: Transformer[] = [ label: 'Uppercase', value: 'upcase', description: 'Convert text to uppercase', - example: '"hello" | upcase → HELLO', + example: '"coffee" | upcase → COFFEE', }, { label: 'Lowercase', value: 'downcase', description: 'Convert text to lowercase', - example: '"HELLO" | downcase → hello', + example: '"PIZZA TIME!" | downcase → pizza time!', }, { label: 'Capitalize', value: 'capitalize', description: 'Capitalize the first character', - example: '"hello" | capitalize → Hello', + example: '"awesome sauce" | capitalize → Awesome sauce', }, { label: 'Strip HTML', value: 'strip_html', description: 'Remove all HTML tags from text', - example: '"

text

" | strip_html → text', + example: '"
🌟 sparkles 🌟
" | strip_html → 🌟 sparkles 🌟', }, { label: 'Strip Newlines', value: 'strip_newlines', description: 'Remove all newline characters', - example: '"hello\\nworld" | strip_newlines → hello world', + example: '"dear friend,\\nhow are you?" | strip_newlines → dear friend, how are you?', }, { label: 'Escape', value: 'escape', description: 'Escape special characters', - example: '"" | escape → <hello>', + example: '"mario" | escape → <super>mario</super>', }, { label: 'Truncate', value: 'truncate', hasParam: true, description: 'Truncate text to specified length', - example: '"hello world" | truncate: 5 → hello...', + example: '"supercalifragilisticexpialidocious" | truncate: 10 → supercali...', params: [{ placeholder: 'Length (e.g. 20)', type: 'number' }], }, { @@ -51,7 +51,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'truncatewords', hasParam: true, description: 'Truncate text to specified number of words', - example: '"hello world and more" | truncatewords: 2 → hello world...', + example: '"to infinity and beyond!" | truncatewords: 2 → to infinity...', params: [{ placeholder: 'Word count', type: 'number' }], }, { @@ -59,7 +59,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'replace', hasParam: true, description: 'Replace all occurrences of a string', - example: '"Hello Hello" | replace: "Hello", "Hi" → Hi Hi', + example: '"potato potato" | replace: "potato", "🥔" → 🥔 🥔', params: [ { placeholder: 'Search text', type: 'string' }, { placeholder: 'Replace with', type: 'string' }, @@ -70,7 +70,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'replace_first', hasParam: true, description: 'Replace first occurrence of a string', - example: '"Hello Hello" | replace_first: "Hello", "Hi" → Hi Hello', + example: '"bug bug" | replace_first: "bug", "🐛" → 🐛 bug', params: [ { placeholder: 'Search text', type: 'string' }, { placeholder: 'Replace with', type: 'string' }, @@ -81,7 +81,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'remove', hasParam: true, description: 'Remove all occurrences of a string', - example: '"Hello Hello" | remove: "ello" → H H', + example: '"banana banana" | remove: "ana" → bn bn', params: [{ placeholder: 'Text to remove', type: 'string' }], }, { @@ -89,7 +89,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'remove_first', hasParam: true, description: 'Remove first occurrence of a string', - example: '"Hello Hello" | remove_first: "ello" → H Hello', + example: '"yada yada" | remove_first: "yada" → yada', params: [{ placeholder: 'Text to remove', type: 'string' }], }, { @@ -97,7 +97,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'append', hasParam: true, description: 'Add text to the end', - example: '"Hello" | append: " World" → Hello World', + example: '"Party" | append: " 🎉" → Party 🎉', params: [{ placeholder: 'Text to append', type: 'string' }], }, { @@ -105,7 +105,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'prepend', hasParam: true, description: 'Add text to the beginning', - example: '"World" | prepend: "Hello " → Hello World', + example: '"World" | prepend: "🌍 " → 🌍 World', params: [{ placeholder: 'Text to prepend', type: 'string' }], }, { @@ -113,7 +113,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'slice', hasParam: true, description: 'Extract a substring by position', - example: '"hello" | slice: 0, 2 → he', + example: '"rainbow" | slice: 0, 3 → rai', params: [ { placeholder: 'Start index', type: 'number' }, { placeholder: 'Length (optional)', type: 'number' }, @@ -125,7 +125,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'plus', hasParam: true, description: 'Add a number', - example: '5 | plus: 3 → 8', + example: '99 | plus: 1 → 100', params: [{ placeholder: 'Number to add', type: 'number' }], }, { @@ -133,7 +133,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'minus', hasParam: true, description: 'Subtract a number', - example: '5 | minus: 3 → 2', + example: '42 | minus: 0 → 42', params: [{ placeholder: 'Number to subtract', type: 'number' }], }, { @@ -141,7 +141,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'times', hasParam: true, description: 'Multiply by a number', - example: '5 | times: 3 → 15', + example: '7 | times: 7 → 49', params: [{ placeholder: 'Number to multiply by', type: 'number' }], }, { @@ -149,7 +149,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'divided_by', hasParam: true, description: 'Divide by a number', - example: '10 | divided_by: 2 → 5', + example: '42 | divided_by: 2 → 21', params: [{ placeholder: 'Number to divide by', type: 'number' }], }, { @@ -157,26 +157,26 @@ export const TRANSFORMERS: Transformer[] = [ value: 'round', hasParam: true, description: 'Round to specified decimal places', - example: '4.5678 | round: 2 → 4.57', + example: '3.14159 | round: 2 → 3.14', params: [{ placeholder: 'Decimal places', type: 'number' }], }, { label: 'Floor', value: 'floor', description: 'Round down to nearest integer', - example: '4.6 | floor → 4', + example: '9.99 | floor → 9', }, { label: 'Ceil', value: 'ceil', description: 'Round up to nearest integer', - example: '4.3 | ceil → 5', + example: '9.01 | ceil → 10', }, { label: 'Abs', value: 'abs', description: 'Get absolute value', - example: '-5 | abs → 5', + example: '-42 | abs → 42', }, // Data Formatting { @@ -192,27 +192,27 @@ export const TRANSFORMERS: Transformer[] = [ value: 'default', hasParam: true, description: 'Use default value if input is empty', - example: '"" | default: "N/A" → N/A', + example: '"" | default: "¯\\_(ツ)_/¯" → ¯\\_(ツ)_/¯', params: [{ placeholder: 'Default value', type: 'string' }], }, { label: 'JSON', value: 'json', description: 'Convert object to JSON string', - example: '{name: "John"} | json → {"name":"John"}', + example: '{mood: "happy"} | json → {"mood":"happy"}', }, { label: 'Size', value: 'size', description: 'Get length of string or array', - example: '"hello" | size → 5', + example: '"supercalifragilisticexpialidocious" | size → 34', }, { label: 'Join', value: 'join', hasParam: true, description: 'Join array elements with separator', - example: '["a","b"] | join: ", " → a, b', + example: '["🌟","✨","💫"] | join: " " → 🌟 ✨ 💫', params: [{ placeholder: 'Separator (e.g. ", ")', type: 'string' }], }, { @@ -220,27 +220,27 @@ export const TRANSFORMERS: Transformer[] = [ value: 'split', hasParam: true, description: 'Split string into array', - example: '"a,b" | split: "," → ["a","b"]', + example: '"rock,paper,scissors" | split: "," → ["rock","paper","scissors"]', params: [{ placeholder: 'Delimiter', type: 'string' }], }, { label: 'First', value: 'first', description: 'Get first element of array', - example: '[1,2,3] | first → 1', + example: '["🥇","🥈","🥉"] | first → 🥇', }, { label: 'Last', value: 'last', description: 'Get last element of array', - example: '[1,2,3] | last → 3', + example: '["🥇","🥈","🥉"] | last → 🥉', }, { label: 'Map', value: 'map', hasParam: true, description: 'Extract property from each item in array', - example: 'users | map: "name" → ["John", "Jane"]', + example: 'superheroes | map: "power" → ["flight", "strength", "speed"]', params: [{ placeholder: 'Property name', type: 'string' }], }, { @@ -248,7 +248,7 @@ export const TRANSFORMERS: Transformer[] = [ value: 'where', hasParam: true, description: 'Filter array by property value', - example: 'users | where: "active", true → [activeUsers]', + example: 'tasks | where: "status", "done" → [completedTasks]', params: [ { placeholder: 'Property name', type: 'string' }, { placeholder: 'Value to match', type: 'string' }, @@ -258,12 +258,12 @@ export const TRANSFORMERS: Transformer[] = [ label: 'URL Encode', value: 'url_encode', description: 'Encode string for use in URL', - example: '"hello world" | url_encode → hello%20world', + example: '"space & special chars!" | url_encode → space%20%26%20special%20chars%21', }, { label: 'URL Decode', value: 'url_decode', description: 'Decode URL-encoded string', - example: '"hello%20world" | url_decode → hello world', + example: '"fun%20%26%20games" | url_decode → fun & games', }, ]; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx deleted file mode 100644 index 7e43c4756e6..00000000000 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx +++ /dev/null @@ -1,237 +0,0 @@ -import { useState, useRef } from 'react'; -import { PopoverContent, Popover, PopoverTrigger } from '@/components/primitives/popover'; -import { Input, InputField } from '@/components/primitives/input'; -import { FormControl, FormItem } from '@/components/primitives/form/form'; -import { Code2 } from '@/components/icons/code-2'; -import { Separator } from '@/components/primitives/separator'; -import { Switch } from '@/components/primitives/switch'; -import { Button } from '@/components/primitives/button'; -import { TransformerItem } from './components/transformer-item'; -import { TransformerList } from './components/transformer-list'; -import { useVariableParser } from './hooks/use-variable-parser'; -import { useTransformerManager } from './hooks/use-transformer-manager'; -import { formatLiquidVariable } from './utils'; -import type { VariablePopoverProps } from './types'; -import { Command, CommandInput, CommandList, CommandGroup, CommandItem } from '@/components/primitives/command'; -import { WandIcon } from 'lucide-react'; - -export function VariablePopover({ variable, onClose, onUpdate }: VariablePopoverProps) { - const searchInputRef = useRef(null); - const { parsedName, parsedDefaultValue, parsedTransformers } = useVariableParser(variable); - const [name, setName] = useState(parsedName); - const [defaultVal, setDefaultVal] = useState(parsedDefaultValue); - const [showRawLiquid, setShowRawLiquid] = useState(false); - const [searchQuery, setSearchQuery] = useState(''); - const [isModifierPopoverOpen, setIsModifierPopoverOpen] = useState(false); - - const { - transformers, - dragOverIndex, - draggingItem, - setDragOverIndex, - setDraggingItem, - handleTransformerToggle, - moveTransformer, - handleParamChange, - getFilteredTransformers, - } = useTransformerManager({ - initialTransformers: parsedTransformers, - onUpdate: (newTransformers) => { - onUpdate(formatLiquidVariable(name, defaultVal, newTransformers)); - }, - }); - - const handleNameChange = (newName: string) => { - setName(newName); - onUpdate(formatLiquidVariable(newName, defaultVal, transformers)); - }; - - const handleDefaultValueChange = (newDefaultVal: string) => { - setDefaultVal(newDefaultVal); - onUpdate(formatLiquidVariable(name, newDefaultVal, transformers)); - }; - - const handleRawLiquidChange = (value: string) => { - // Remove {{ and }} and trim if they exist, but don't require them - const content = value.replace(/^\{\{?\s*|\s*\}?\}$/g, '').trim(); - - // Split by pipe and trim each part - const parts = content.split('|').map((part) => part.trim()); - - // First part is the name - const newName = parts[0]; - setName(newName); - - // Process each part after the name - parts.slice(1).forEach((part) => { - if (part.startsWith('default:')) { - // Extract default value, handling quotes and escaped quotes - const defaultMatch = - part.match(/default:\s*'((?:[^'\\]|\\.)*)'/) || - part.match(/default:\s*"((?:[^"\\]|\\.)*)"/) || - part.match(/default:\s*([^}\s]+)/); - - if (defaultMatch) { - // Handle CodeMirror's escaping by removing one level of escaping - const newDefaultVal = defaultMatch[1].replace(/\\\\'/g, "\\'").replace(/\\'/g, "'"); - setDefaultVal(newDefaultVal); - } - } - }); - }; - - return ( - -
-
-
-
- CONFIGURE VARIABLE -
-
-
-
-
- - -
- - - - handleNameChange(e.target.value)} className="h-7 text-sm" /> - -
-
-
- - -
- - - handleDefaultValueChange(e.target.value)} - className="h-7 text-sm" - /> - -
-
-
- - -
- - -
-
-
- {showRawLiquid && ( - - -
- - handleRawLiquidChange(e.target.value)} - className="h-7 text-sm" - /> - -
-
-
- )} -
- - - -
- - -
- - - - - - - -
- -
- - {getFilteredTransformers(searchQuery).length === 0 ? ( -
- - {searchQuery ? 'No modifiers found' : 'All modifiers have been added'} - - {searchQuery && Try searching for different terms} -
- ) : ( - - {getFilteredTransformers(searchQuery).map((transformer) => ( - { - handleTransformerToggle(transformer.value); - setSearchQuery(''); - setIsModifierPopoverOpen(false); - }} - > - - - ))} - - )} -
-
-
-
-
-
-
- - { - if (dragOverIndex !== null && draggingItem !== null && draggingItem !== dragOverIndex) { - moveTransformer(draggingItem, dragOverIndex); - } - setDraggingItem(null); - setDragOverIndex(null); - }} - onDrag={(e, info) => { - const elements = document.elementsFromPoint(info.point.x, info.point.y); - const droppableElement = elements.find( - (el) => el.classList.contains('group') && !el.classList.contains('opacity-50') - ); - - if (droppableElement) { - const index = parseInt(droppableElement.getAttribute('data-index') || '-1'); - if (index !== -1 && dragOverIndex !== index) { - setDragOverIndex(index); - } - } else { - setDragOverIndex(null); - } - }} - onRemove={handleTransformerToggle} - onParamChange={handleParamChange} - /> -
-
-
-
- ); -} From c0f0c3e2d13bbc6ab303ea0e775e1ee3e0148ade Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 18:38:45 +0200 Subject: [PATCH 25/49] fix: view --- .../field-editor/variable-plugin/plugin-view.ts | 2 +- .../{index.tsx => variable-popover.tsx} | 13 ++++++++++++- .../workflow-editor/steps/base/base-body.tsx | 1 - 3 files changed, 13 insertions(+), 3 deletions(-) rename apps/dashboard/src/components/primitives/field-editor/variable-popover/{index.tsx => variable-popover.tsx} (96%) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts index 4fe57d8eec9..afa948af0c2 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts @@ -10,7 +10,7 @@ export class VariablePluginView { isTypingVariable: boolean = false; constructor( - private view: EditorView, + view: EditorView, private viewRef: MutableRefObject, private lastCompletionRef: MutableRefObject<{ from: number; to: number } | null>, private onSelect?: (value: string, from: number, to: number) => void diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/index.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx similarity index 96% rename from apps/dashboard/src/components/primitives/field-editor/variable-popover/index.tsx rename to apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx index 613a2e26716..c7723e4e375 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/index.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx @@ -1,4 +1,4 @@ -import { useState, useRef } from 'react'; +import { useState, useRef, useEffect } from 'react'; import { PopoverContent } from '@/components/primitives/popover'; import { Input, InputField } from '@/components/primitives/input'; import { FormControl, FormItem } from '@/components/primitives/form/form'; @@ -21,6 +21,17 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover const [showRawLiquid, setShowRawLiquid] = useState(false); const [searchQuery, setSearchQuery] = useState(''); + useEffect(() => { + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + onClose(); + } + }; + + document.addEventListener('keydown', handleEscape); + return () => document.removeEventListener('keydown', handleEscape); + }, [onClose]); + const { transformers, dragOverIndex, diff --git a/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx b/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx index 5b37f46bd32..0165f33538d 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx @@ -28,7 +28,6 @@ export const BaseBody = () => { fontFamily="inherit" placeholder={capitalize(field.name)} id={field.name} - ref={field.ref} value={field.value} onChange={field.onChange} variables={variables} From 48b448c8fa8fa74dc48a83038ed9eb3deef2b63d Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 18:41:36 +0200 Subject: [PATCH 26/49] fix: popover --- .../primitives/field-editor/variable-popover/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts index 99a9f7adf97..ff932ed8998 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts @@ -35,5 +35,5 @@ export function formatLiquidVariable( }) ); - return parts.join(' | '); + return `{{${parts.join(' | ')}}}`; } From 62c48c21119b46846654129062e3c0e7aaba29af Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 18:47:33 +0200 Subject: [PATCH 27/49] fixes: asdas --- .../field-editor/variable-plugin/utils.ts | 19 ++++++++++++++----- .../field-editor/variable-popover/utils.ts | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts index ea3c8450842..e5884ba410e 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts @@ -23,15 +23,23 @@ export function handleVariableBackspace(view: EditorView, pos: number, content: const nextCloseBrackets = content.indexOf('}}', pos); if (lastOpenBrackets !== -1 && nextCloseBrackets !== -1) { - const variableContent = content.slice(lastOpenBrackets + 2, pos).trim(); - if (!variableContent) { + const isAtStartOrInside = pos >= lastOpenBrackets && pos <= lastOpenBrackets + 2; + const isBeforeVariable = pos === lastOpenBrackets; + const isAfterVariable = pos === nextCloseBrackets + 3 && content[nextCloseBrackets + 2] === ' '; + + if (isAtStartOrInside || isBeforeVariable || isAfterVariable) { requestAnimationFrame(() => { + const deleteEnd = nextCloseBrackets + 2; + const hasSpaceAfter = content[deleteEnd] === ' '; + const hasSpaceBefore = isBeforeVariable && content[lastOpenBrackets - 1] === ' '; + view.dispatch({ changes: { - from: lastOpenBrackets, - to: pos + nextCloseBrackets + 2, + from: hasSpaceBefore ? lastOpenBrackets - 1 : lastOpenBrackets, + to: hasSpaceAfter ? deleteEnd + 1 : deleteEnd, insert: '', }, + selection: { anchor: hasSpaceBefore ? lastOpenBrackets - 1 : lastOpenBrackets }, }); }); @@ -48,7 +56,8 @@ export function handleVariableCompletion(view: EditorView, pos: number, content: if (start !== -1) { const variableContent = content.slice(start + 2, pos - 2).trim(); if (variableContent) { - if (pos === content.length || content[pos] !== ' ') { + const needsSpace = pos < content.length && content[pos] !== ' ' && content[pos] !== '\n'; + if (needsSpace) { requestAnimationFrame(() => { view.dispatch({ changes: { from: pos, insert: ' ' }, diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts index ff932ed8998..99a9f7adf97 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts @@ -35,5 +35,5 @@ export function formatLiquidVariable( }) ); - return `{{${parts.join(' | ')}}}`; + return parts.join(' | '); } From ecc8a8cd08edb2edb17ae0e99a62ad426f2250bb Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 18:55:08 +0200 Subject: [PATCH 28/49] fix: reusability --- .../src/utils/liquid-autocomplete.ts | 256 ++++++------------ 1 file changed, 83 insertions(+), 173 deletions(-) diff --git a/apps/dashboard/src/utils/liquid-autocomplete.ts b/apps/dashboard/src/utils/liquid-autocomplete.ts index 073a616c848..c6991e56d5c 100644 --- a/apps/dashboard/src/utils/liquid-autocomplete.ts +++ b/apps/dashboard/src/utils/liquid-autocomplete.ts @@ -1,5 +1,6 @@ import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; import { CompletionContext, CompletionResult } from '@codemirror/autocomplete'; +import { TRANSFORMERS } from '@/components/primitives/field-editor/variable-popover/constants'; interface CompletionOption { label: string; @@ -7,98 +8,79 @@ interface CompletionOption { boost?: number; } -const filters = [ - // Math filters - { label: 'plus', type: 'function' }, - { label: 'minus', type: 'function' }, - { label: 'modulo', type: 'function' }, - { label: 'times', type: 'function' }, - { label: 'floor', type: 'function' }, - { label: 'ceil', type: 'function' }, - { label: 'round', type: 'function' }, - { label: 'divided_by', type: 'function' }, - { label: 'abs', type: 'function' }, - { label: 'at_least', type: 'function' }, - { label: 'at_most', type: 'function' }, - - // String filters - { label: 'append', type: 'function' }, - { label: 'prepend', type: 'function' }, - { label: 'capitalize', type: 'function' }, - { label: 'upcase', type: 'function' }, - { label: 'downcase', type: 'function' }, - { label: 'strip', type: 'function' }, - { label: 'lstrip', type: 'function' }, - { label: 'rstrip', type: 'function' }, - { label: 'strip_newlines', type: 'function' }, - { label: 'split', type: 'function' }, - { label: 'replace', type: 'function' }, - { label: 'replace_first', type: 'function' }, - { label: 'replace_last', type: 'function' }, - { label: 'remove', type: 'function' }, - { label: 'remove_first', type: 'function' }, - { label: 'truncate', type: 'function' }, - { label: 'truncatewords', type: 'function' }, - { label: 'normalize_whitespace', type: 'function' }, - { label: 'number_of_words', type: 'function' }, - { label: 'array_to_sentence_string', type: 'function' }, - - // HTML/URI filters - { label: 'escape', type: 'function' }, - { label: 'escape_once', type: 'function' }, - { label: 'url_encode', type: 'function' }, - { label: 'url_decode', type: 'function' }, - { label: 'strip_html', type: 'function' }, - { label: 'newline_to_br', type: 'function' }, - { label: 'xml_escape', type: 'function' }, - { label: 'cgi_escape', type: 'function' }, - { label: 'uri_escape', type: 'function' }, - { label: 'slugify', type: 'function' }, - - // Array filters - { label: 'slice', type: 'function' }, - { label: 'map', type: 'function' }, - { label: 'sort', type: 'function' }, - { label: 'sort_natural', type: 'function' }, - { label: 'uniq', type: 'function' }, - { label: 'where', type: 'function' }, - { label: 'where_exp', type: 'function' }, - { label: 'group_by', type: 'function' }, - { label: 'group_by_exp', type: 'function' }, - { label: 'find', type: 'function' }, - { label: 'find_exp', type: 'function' }, - { label: 'first', type: 'function' }, - { label: 'last', type: 'function' }, - { label: 'join', type: 'function' }, - { label: 'reverse', type: 'function' }, - { label: 'concat', type: 'function' }, - { label: 'compact', type: 'function' }, - { label: 'size', type: 'function' }, - { label: 'push', type: 'function' }, - { label: 'pop', type: 'function' }, - { label: 'shift', type: 'function' }, - { label: 'unshift', type: 'function' }, - - // Date filters - { label: 'date', type: 'function' }, - { label: 'date_to_xmlschema', type: 'function' }, - { label: 'date_to_rfc822', type: 'function' }, - { label: 'date_to_string', type: 'function' }, - { label: 'date_to_long_string', type: 'function' }, - - // Misc filters - { label: 'default', type: 'function' }, - { label: 'json', type: 'function' }, - { label: 'jsonify', type: 'function' }, - { label: 'inspect', type: 'function' }, - { label: 'raw', type: 'function' }, - { label: 'to_integer', type: 'function' }, -]; - -function createPayloadVariable(input: string): LiquidVariable { +function isInsideLiquidBlock(beforeCursor: string): boolean { + const lastOpenBrace = beforeCursor.lastIndexOf('{{'); + const lastCloseBrace = beforeCursor.lastIndexOf('}}'); + + return lastOpenBrace !== -1 && lastOpenBrace > lastCloseBrace; +} + +function getContentAfterPipe(content: string): string | null { + const pipeIndex = content.lastIndexOf('|'); + if (pipeIndex === -1) return null; + + return content.slice(pipeIndex + 1).trimStart(); +} + +function getFilterCompletions(afterPipe: string): CompletionOption[] { + return TRANSFORMERS.filter((f) => f.label.toLowerCase().startsWith(afterPipe.toLowerCase())).map((f) => ({ + label: f.value, + type: 'function', + })); +} + +function getVariableCompletions( + word: { text: string; from: number; to?: number } | null, + pos: number, + variables: LiquidVariable[] +): CompletionResult | null { + if (!word && !variables.length) return null; + + if (word?.text.startsWith('payload.')) { + const payloadPath = word.text.slice(8); + const existingVariables = variables.filter((v) => v.label.startsWith('payload.')); + + const suggestions: CompletionOption[] = existingVariables + .filter((v) => v.label.toLowerCase().includes(payloadPath.toLowerCase())) + .map((v) => ({ + label: v.label, + type: 'variable', + })); + + if (payloadPath && !existingVariables.some((v) => v.label === `payload.${payloadPath}`)) { + suggestions.unshift({ + label: `payload.${payloadPath}`, + type: 'variable', + boost: 99, + }); + } + + return { + from: word.from, + to: word.to ?? pos, + options: suggestions, + }; + } + + if (word) { + const matchingVariables = variables.filter((v) => v.label.toLowerCase().includes(word.text.toLowerCase())); + + return { + from: word.from, + to: word.to ?? pos, + options: matchingVariables.map((v) => ({ + label: v.label, + type: 'variable', + })), + }; + } + return { - label: `payload.${input}`, - type: 'variable', + from: pos, + options: variables.map((v) => ({ + label: v.label, + type: 'variable', + })), }; } @@ -106,99 +88,27 @@ export const completions = (variables: LiquidVariable[]) => (context: CompletionContext): CompletionResult | null => { const { state, pos } = context; - const beforeCursor = state.sliceDoc(0, pos); - // Determine whether we're inside a {{ ... }} block - const lastOpenBrace = beforeCursor.lastIndexOf('{{'); - const lastCloseBrace = beforeCursor.lastIndexOf('}}'); - - if (lastOpenBrace === -1 || lastOpenBrace < lastCloseBrace) { - // Not inside a {{ ... }} block + if (!isInsideLiquidBlock(beforeCursor)) { return null; } - // Get the content inside the braces up to the cursor position + const lastOpenBrace = beforeCursor.lastIndexOf('{{'); const insideBraces = state.sliceDoc(lastOpenBrace + 2, pos); + const afterPipe = getContentAfterPipe(insideBraces); - // Detect the position of the last `|` relative to the cursor - const pipeIndex = insideBraces.lastIndexOf('|'); - - if (pipeIndex !== -1 && pos > lastOpenBrace + 2 + pipeIndex) { - // Cursor is after the pipe (`|`) - const afterPipe = insideBraces.slice(pipeIndex + 1).trimStart(); - - // Filter the list of filters based on the user's input - const matchingFilters = filters.filter((f) => f.label.toLowerCase().startsWith(afterPipe.toLowerCase())); - - // Suggest filters if content after the pipe is incomplete - if (/^[\w.]*$/.test(afterPipe)) { - return { - from: pos - afterPipe.length, // Start from where the filter name starts - to: pos, // Extend to the current cursor position - options: matchingFilters.map((f) => ({ - label: f.label, - type: 'function', - })), - }; - } - } - - // Handle variable suggestions - const word = context.matchBefore(/[\w.]+/); // Match variable names only - if (!word && insideBraces.trim() === '') { - return { - from: pos, - options: [ - ...variables.map((v) => ({ - label: v.label, - type: 'variable', - })), - ], - }; - } - - // Special handling for payload namespace - if (word && word.text.startsWith('payload.')) { - const payloadPath = word.text.slice(8); // Remove 'payload.' prefix - const existingVariables = variables.filter((v) => v.label.startsWith('payload.')); - - const suggestions: CompletionOption[] = existingVariables - .filter((v) => v.label.toLowerCase().includes(payloadPath.toLowerCase())) - .map((v) => ({ - label: v.label, - type: 'variable', - })); - - // Always add the current input as a suggestion for new variables - if (payloadPath && !existingVariables.some((v) => v.label === `payload.${payloadPath}`)) { - suggestions.unshift({ - label: `payload.${payloadPath}`, - type: 'variable', - boost: 99, // Higher boost to show at top - }); - } + if (afterPipe !== null) { + const filterCompletions = getFilterCompletions(afterPipe); return { - from: word.from, - to: word.to ?? pos, - options: suggestions, + from: pos - afterPipe.length, + to: pos, + options: filterCompletions, }; } - // Regular variable suggestions - if (word) { - const matchingVariables = variables.filter((v) => v.label.toLowerCase().includes(word.text.toLowerCase())); - - return { - from: word.from, - to: word.to ?? pos, - options: matchingVariables.map((v) => ({ - label: v.label, - type: 'variable', - })), - }; - } + const word = context.matchBefore(/[\w.]+/); - return null; // No suggestions in other cases + return getVariableCompletions(word, pos, variables); }; From fb0901f60fb7daaccf577b739dd1d4af1bf9a812 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 19:02:38 +0200 Subject: [PATCH 29/49] fix: refactor --- .../primitives/field-editor/field-editor.tsx | 192 +++++++++++------- 1 file changed, 113 insertions(+), 79 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index 8df8b4f39ce..0d3e9b1479c 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -1,15 +1,26 @@ import { EditorView } from '@uiw/react-codemirror'; -import { useMemo, useState, useRef, useCallback } from 'react'; import { Completion, CompletionContext } from '@codemirror/autocomplete'; +import { autocompletion } from '@codemirror/autocomplete'; import { Editor } from '@/components/primitives/editor'; import { completions } from '@/utils/liquid-autocomplete'; import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; -import { autocompletion } from '@codemirror/autocomplete'; import { Popover, PopoverTrigger } from '@/components/primitives/popover'; import { createVariablePlugin } from './variable-plugin'; import { VariablePopover } from './variable-popover'; import { variablePillTheme } from './variable-plugin/variable-theme'; +import { useCallback, useMemo, useRef, useState, Dispatch, SetStateAction } from 'react'; + +type SelectedVariable = { + value: string; + from: number; + to: number; +} | null; + +type CompletionRange = { + from: number; + to: number; +} | null; type FieldEditorProps = { value: string; @@ -22,7 +33,7 @@ type FieldEditorProps = { id?: string; }; -export const FieldEditor = ({ +export function FieldEditor({ value, onChange, variables, @@ -31,11 +42,76 @@ export const FieldEditor = ({ size = 'default', fontFamily = 'inherit', id, -}: FieldEditorProps) => { - const [selectedVariable, setSelectedVariable] = useState<{ value: string; from: number; to: number } | null>(null); +}: FieldEditorProps) { const viewRef = useRef(null); + const lastCompletionRef = useRef(null); + + const { selectedVariable, setSelectedVariable, handleVariableSelect, isUpdatingRef } = useVariableSelection(); + + const handleVariableUpdate = useVariableUpdate( + selectedVariable, + viewRef, + isUpdatingRef, + onChange, + setSelectedVariable + ); + + const completionSource = useCompletionSource(variables, lastCompletionRef); + + const extensions = useMemo( + () => [ + autocompletion({ + override: [completionSource], + closeOnBlur: false, + defaultKeymap: true, + activateOnTyping: true, + }), + EditorView.lineWrapping, + variablePillTheme, + createVariablePlugin({ viewRef, lastCompletionRef, onSelect: handleVariableSelect }), + ], + [variables, completionSource, handleVariableSelect] + ); + + return ( +
+ + { + if (!open) { + setTimeout(() => setSelectedVariable(null), 0); + } + }} + > + +
+ + {selectedVariable && ( + setSelectedVariable(null)} + onUpdate={handleVariableUpdate} + /> + )} + +
+ ); +} + +function useVariableSelection() { + const [selectedVariable, setSelectedVariable] = useState(null); const isUpdatingRef = useRef(false); - const lastCompletionRef = useRef<{ from: number; to: number } | null>(null); const handleVariableSelect = useCallback((value: string, from: number, to: number) => { if (isUpdatingRef.current) return; @@ -44,11 +120,24 @@ export const FieldEditor = ({ }); }, []); - const handleVariableUpdate = useCallback( + return { + selectedVariable, + setSelectedVariable, + handleVariableSelect, + isUpdatingRef, + }; +} + +function useVariableUpdate( + selectedVariable: SelectedVariable, + viewRef: React.RefObject, + isUpdatingRef: React.MutableRefObject, + onChange: (value: string) => void, + setSelectedVariable: Dispatch> +) { + return useCallback( (newValue: string) => { - if (!selectedVariable || !viewRef.current || isUpdatingRef.current) { - return; - } + if (!selectedVariable || !viewRef.current || isUpdatingRef.current) return; try { isUpdatingRef.current = true; @@ -73,19 +162,21 @@ export const FieldEditor = ({ selection: { anchor: from + newVariableText.length }, }); - const updatedContent = view.state.doc.toString(); + onChange(view.state.doc.toString()); - onChange(updatedContent); - - setSelectedVariable((prev) => (prev ? { ...prev, value: newValue, to: from + newVariableText.length } : null)); + setSelectedVariable((prev: SelectedVariable) => + prev ? { ...prev, value: newValue, to: from + newVariableText.length } : null + ); } finally { isUpdatingRef.current = false; } }, - [selectedVariable, onChange] + [selectedVariable, onChange, viewRef, isUpdatingRef, setSelectedVariable] ); +} - const completionSource = useCallback( +function useCompletionSource(variables: LiquidVariable[], lastCompletionRef: React.MutableRefObject) { + return useCallback( (context: CompletionContext) => { const word = context.matchBefore(/\{\{([^}]*)/); if (!word) return null; @@ -101,71 +192,14 @@ export const FieldEditor = ({ const content = view.state.doc.toString(); const before = content.slice(Math.max(0, from - 2), from); + const insert = before !== '{{' ? `{{${text}}} ` : `${text}}} `; - if (before !== '{{') { - view.dispatch({ - changes: { from, to, insert: `{{${text}}} ` }, - }); - } else { - view.dispatch({ - changes: { from, to, insert: `${text}}} ` }, - }); - } + view.dispatch({ + changes: { from, to, insert }, + }); }, }; }, - [variables] - ); - - const extensions = useMemo( - () => [ - autocompletion({ - override: [completionSource], - closeOnBlur: false, - defaultKeymap: true, - activateOnTyping: true, - }), - EditorView.lineWrapping, - variablePillTheme, - createVariablePlugin({ viewRef, lastCompletionRef, onSelect: handleVariableSelect }), - ], - [variables, completionSource, handleVariableSelect] - ); - - return ( -
- - { - if (!open) { - setTimeout(() => { - setSelectedVariable(null); - }, 0); - } - }} - > - -
- - {selectedVariable && ( - setSelectedVariable(null)} - onUpdate={handleVariableUpdate} - /> - )} - -
+ [variables, lastCompletionRef] ); -}; +} From 95f249b5c63927b647de2792039b2df2bc00fcd8 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 19:35:46 +0200 Subject: [PATCH 30/49] feat: add comments --- .../primitives/field-editor/field-editor.tsx | 27 +++++++++++++++++ .../variable-plugin/plugin-view.ts | 5 ++-- .../field-editor/variable-plugin/utils.ts | 29 +++++++++++++++++-- .../variable-plugin/variable-pill-widget.ts | 27 +++++++++++++---- .../variable-plugin/variable-theme.ts | 11 ++----- .../variable-popover/variable-popover.tsx | 2 +- 6 files changed, 83 insertions(+), 18 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index 0d3e9b1479c..4688dd43a90 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -109,12 +109,20 @@ export function FieldEditor({ ); } +/** + * Manages the state and selection of Liquid variables in the editor. + * + * This hook handles the complex state management needed when a user selects a variable in the editor: + * 1. Tracks which variable is currently selected + * 2. Prevents recursive updates when variables are being modified + */ function useVariableSelection() { const [selectedVariable, setSelectedVariable] = useState(null); const isUpdatingRef = useRef(false); const handleVariableSelect = useCallback((value: string, from: number, to: number) => { if (isUpdatingRef.current) return; + requestAnimationFrame(() => { setSelectedVariable({ value, from, to }); }); @@ -128,6 +136,21 @@ function useVariableSelection() { }; } +/** + * Handles the logic of updating Liquid variables in the editor while maintaining proper syntax. + * + * This hook manages several critical aspects of variable editing: + * 1. Ensures proper Liquid syntax ({{...}}) is maintained when updating variables + * 2. Handles edge cases like existing closing brackets + * 3. Maintains cursor position after updates + * 4. Prevents recursive updates during the edit process + * + * The update process: + * 1. Checks if the new value already has Liquid syntax + * 2. Detects if there are existing closing brackets after the cursor + * 3. Calculates the correct range to replace + * 4. Updates the editor state while maintaining proper cursor position + */ function useVariableUpdate( selectedVariable: SelectedVariable, viewRef: React.RefObject, @@ -178,6 +201,7 @@ function useVariableUpdate( function useCompletionSource(variables: LiquidVariable[], lastCompletionRef: React.MutableRefObject) { return useCallback( (context: CompletionContext) => { + // Match text that starts with {{ and capture everything after it until the cursor position const word = context.matchBefore(/\{\{([^}]*)/); if (!word) return null; @@ -190,6 +214,9 @@ function useCompletionSource(variables: LiquidVariable[], lastCompletionRef: Rea const text = completion.label; lastCompletionRef.current = { from, to }; + // Handle liquid variable syntax ({{variable}}) + // If we're not already inside liquid brackets, wrap the completion with {{}} + // If we're inside liquid brackets (detected by checking previous chars), just insert the variable name const content = view.state.doc.toString(); const before = content.slice(Math.max(0, from - 2), from); const insert = before !== '{{' ? `{{${text}}} ` : `${text}}} `; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts index afa948af0c2..315e902bc24 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/plugin-view.ts @@ -33,7 +33,6 @@ export class VariablePluginView { } } - // Handle variable completion if (update.docChanged) { handleVariableCompletion(update.view, pos, content); } @@ -52,10 +51,12 @@ export class VariablePluginView { const pos = view.state.selection.main.head; let match; + // Iterate through all variable matches in the content and add the pills while ((match = VARIABLE_REGEX.exec(content)) !== null) { const { fullVariableName, variableName, start, end, hasModifiers } = parseVariable(match); - // Don't create a pill if we're currently editing this variable + // Skip creating pills for variables that are currently being edited + // This allows users to modify variables without the pill getting in the way if (this.isTypingVariable && pos > start && pos < end) { continue; } diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts index e5884ba410e..cff0946f7f4 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/utils.ts @@ -1,6 +1,24 @@ import { EditorView } from '@uiw/react-codemirror'; import type { VariableMatch } from './types'; +/** + * Parses a variable match from the editor's content into structured data. + * This function is crucial for the variable pill system as it: + * 1. Extracts the position and content of variables like {{ subscriber.name | uppercase }} + * 2. Separates the base variable name from its modifiers (filters after |) + * 3. Provides the necessary information for rendering variable pills in the editor + * + * @example + * Input match for "{{ subscriber.name | uppercase }}" + * Returns: + * { + * fullVariableName: "subscriber.name | uppercase", + * variableName: "subscriber.name", + * start: [match start index], + * end: [match end index], + * hasModifiers: true + * } + */ export function parseVariable(match: RegExpExecArray): VariableMatch { const start = match.index; const end = start + match[0].length; @@ -18,7 +36,12 @@ export function parseVariable(match: RegExpExecArray): VariableMatch { }; } +/** + * Handles backspace behavior for variables in the editor. + * When backspace is pressed near or within a variable, it removes the entire variable including its brackets. + */ export function handleVariableBackspace(view: EditorView, pos: number, content: string): boolean { + // Find the boundaries of the potential variable surrounding the cursor const lastOpenBrackets = content.lastIndexOf('{{', pos); const nextCloseBrackets = content.indexOf('}}', pos); @@ -33,21 +56,23 @@ export function handleVariableBackspace(view: EditorView, pos: number, content: const hasSpaceAfter = content[deleteEnd] === ' '; const hasSpaceBefore = isBeforeVariable && content[lastOpenBrackets - 1] === ' '; + // Dispatch a change to remove the variable and any surrounding spaces view.dispatch({ changes: { from: hasSpaceBefore ? lastOpenBrackets - 1 : lastOpenBrackets, to: hasSpaceAfter ? deleteEnd + 1 : deleteEnd, insert: '', }, + // Place cursor where the variable started selection: { anchor: hasSpaceBefore ? lastOpenBrackets - 1 : lastOpenBrackets }, }); }); - return true; + return true; // Indicate that we handled the backspace } } - return false; + return false; // Let CodeMirror handle the backspace normally } export function handleVariableCompletion(view: EditorView, pos: number, content: string): boolean { diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts index c531e5571fa..e2c088e9db4 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts @@ -1,5 +1,5 @@ import { WidgetType } from '@uiw/react-codemirror'; -import { DARK_MODE_CLASS, VARIABLE_PILL_CLASS, MODIFIERS_CLASS } from './constants'; +import { VARIABLE_PILL_CLASS, MODIFIERS_CLASS } from './constants'; export class VariablePillWidget extends WidgetType { constructor( @@ -15,22 +15,27 @@ export class VariablePillWidget extends WidgetType { toDOM() { const span = document.createElement('span'); - const isDarkMode = document.documentElement.classList.contains(DARK_MODE_CLASS); - const pillClass = `${VARIABLE_PILL_CLASS} ${isDarkMode ? 'cm-dark' : ''} ${ - this.hasModifiers ? MODIFIERS_CLASS : '' - }`; + const pillClass = `${VARIABLE_PILL_CLASS} ${this.hasModifiers ? MODIFIERS_CLASS : ''}`; span.className = pillClass; + + // Stores the complete variable expression including any filters/transformers span.setAttribute('data-variable', this.fullVariableName); + span.setAttribute('data-start', this.start.toString()); span.setAttribute('data-end', this.end.toString()); + + // Contains the clean variable name shown to the user span.setAttribute('data-display', this.variableName); + span.textContent = this.variableName; const handleClick = (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); + // setTimeout is used to defer the selection until after CodeMirror's own click handling + // This prevents race conditions where our selection might be immediately cleared by the editor setTimeout(() => { this.onSelect?.(this.fullVariableName, this.start, this.end); }, 0); @@ -42,6 +47,10 @@ export class VariablePillWidget extends WidgetType { return span; } + /** + * Determines if two VariablePillWidget instances are equal by comparing all their properties. + * Used by CodeMirror to optimize re-rendering. + */ eq(other: VariablePillWidget) { return ( other.variableName === this.variableName && @@ -52,6 +61,10 @@ export class VariablePillWidget extends WidgetType { ); } + /** + * Cleanup method called when the widget is being removed from the editor. + * Removes event listeners to prevent memory leaks. + */ destroy(dom: HTMLElement) { if ((dom as any)._variableClickHandler) { dom.removeEventListener('mousedown', (dom as any)._variableClickHandler); @@ -59,6 +72,10 @@ export class VariablePillWidget extends WidgetType { } } + /** + * Controls whether CodeMirror should handle events on this widget. + * Returns false to allow events to propagate normally. + */ ignoreEvent() { return false; } diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts index 1a7f00b206e..7c6b8b56922 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts @@ -50,14 +50,6 @@ export const variablePillTheme = EditorView.baseTheme({ backgroundColor: 'hsl(var(--feature-base))', borderRadius: '50%', }, - '.cm-variable-pill.cm-dark': { - backgroundColor: '#FFD6EE', - color: '#AD74FF', - border: '1px solid #3D3D4D', - }, - '.cm-variable-pill.cm-dark.has-modifiers::after': { - backgroundColor: '#AD74FF', - }, '.cm-variable-pill .cm-bracket': { display: 'none', }, @@ -68,4 +60,7 @@ export const variablePillTheme = EditorView.baseTheme({ whiteSpace: 'pre-wrap', wordBreak: 'break-word', }, + '.cm-line': { + lineHeight: '19px !important', + }, }); diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx index c7723e4e375..61fe0354e9a 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx @@ -231,7 +231,7 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover setDraggingItem(null); setDragOverIndex(null); }} - onDrag={(e, info) => { + onDrag={(_, info) => { const elements = document.elementsFromPoint(info.point.x, info.point.y); const droppableElement = elements.find( (el) => el.classList.contains('group') && !el.classList.contains('opacity-50') From 92f915f83f7b4dc9286fbdaefa4c99647c41fd61 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 19:37:12 +0200 Subject: [PATCH 31/49] fix: types --- .../primitives/field-editor/variable-plugin/types.ts | 8 ++++---- .../components/draggable-transformer.tsx | 4 ++-- .../variable-popover/components/transformer-item.tsx | 4 ++-- .../variable-popover/components/transformer-list.tsx | 4 ++-- .../field-editor/variable-popover/types.ts | 12 ++++++------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/types.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/types.ts index a43bafdd01b..d14d790af3d 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/types.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/types.ts @@ -1,16 +1,16 @@ import { EditorView } from '@uiw/react-codemirror'; import { MutableRefObject } from 'react'; -export interface PluginState { +export type PluginState = { viewRef: MutableRefObject; lastCompletionRef: MutableRefObject<{ from: number; to: number } | null>; onSelect?: (value: string, from: number, to: number) => void; -} +}; -export interface VariableMatch { +export type VariableMatch = { fullVariableName: string; variableName: string; start: number; end: number; hasModifiers: boolean; -} +}; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx index 582c9e7e8a1..67d7af8bd34 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx @@ -6,7 +6,7 @@ import { Input } from '@/components/primitives/input'; import { TRANSFORMERS } from '../constants'; import { TransformerWithParam } from '../types'; -interface DraggableTransformerProps { +type DraggableTransformerProps = { transformer: TransformerWithParam; index: number; isLast: boolean; @@ -17,7 +17,7 @@ interface DraggableTransformerProps { onDrag: (e: any, info: any) => void; onRemove: (value: string) => void; onParamChange: (index: number, params: string[]) => void; -} +}; export function DraggableTransformer({ transformer, diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx index 12c4ddd233a..51a7822e4cc 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx @@ -1,9 +1,9 @@ import { Transformer } from '../types'; import TruncatedText from '@/components/truncated-text'; -interface TransformerItemProps { +type TransformerItemProps = { transformer: Transformer; -} +}; export function TransformerItem({ transformer }: TransformerItemProps) { return ( diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-list.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-list.tsx index 46f0240a87c..b6e17f892ea 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-list.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-list.tsx @@ -2,7 +2,7 @@ import { AnimatePresence } from 'motion/react'; import { DraggableTransformer } from './draggable-transformer'; import { TransformerWithParam } from '../types'; -interface TransformerListProps { +type TransformerListProps = { transformers: TransformerWithParam[]; dragOverIndex: number | null; draggingItem: number | null; @@ -11,7 +11,7 @@ interface TransformerListProps { onDrag: (e: any, info: any) => void; onRemove: (value: string) => void; onParamChange: (index: number, params: string[]) => void; -} +}; export function TransformerList({ transformers, diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts index 87c70c9ac75..87e7d9358ac 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts @@ -1,4 +1,4 @@ -export interface Transformer { +export type Transformer = { label: string; value: string; hasParam?: boolean; @@ -9,15 +9,15 @@ export interface Transformer { description?: string; type?: 'string' | 'number'; }[]; -} +}; -export interface TransformerWithParam { +export type TransformerWithParam = { value: string; params?: string[]; -} +}; -export interface VariablePopoverProps { +export type VariablePopoverProps = { variable: string; onClose: () => void; onUpdate: (newValue: string) => void; -} +}; From f97c433865b8e31227129a082d1bd0a80dd1ecec Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 30 Dec 2024 21:03:14 +0200 Subject: [PATCH 32/49] fix: remove unused --- .../primitives/field-editor/variable-plugin/constants.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/constants.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/constants.ts index a7b7932a0c5..827d603d633 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/constants.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/constants.ts @@ -1,4 +1,3 @@ export const VARIABLE_REGEX = /{{([^{}]+)}}/g; -export const DARK_MODE_CLASS = 'dark'; export const VARIABLE_PILL_CLASS = 'cm-variable-pill'; export const MODIFIERS_CLASS = 'has-modifiers'; From 2bd08b0e7efa0bf72a22f0122c02d5a94bd5124d Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Wed, 1 Jan 2025 15:41:21 +0200 Subject: [PATCH 33/49] Update field-editor.tsx --- .../src/components/primitives/field-editor/field-editor.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index 4688dd43a90..dcc747bef24 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -31,6 +31,8 @@ type FieldEditorProps = { size?: 'default' | 'lg'; fontFamily?: 'inherit'; id?: string; + singleLine?: boolean; + indentWithTab?: boolean; }; export function FieldEditor({ @@ -42,6 +44,8 @@ export function FieldEditor({ size = 'default', fontFamily = 'inherit', id, + singleLine, + indentWithTab, }: FieldEditorProps) { const viewRef = useRef(null); const lastCompletionRef = useRef(null); @@ -76,6 +80,8 @@ export function FieldEditor({ return (
Date: Thu, 2 Jan 2025 23:34:50 +0200 Subject: [PATCH 34/49] fix: close on blur --- .../primitives/field-editor/field-editor.tsx | 11 +++++------ .../field-editor/variable-popover/types.ts | 2 +- .../variable-popover/variable-popover.tsx | 14 +++++++------- .../workflow-editor/steps/base/base-body.tsx | 4 ++-- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index dcc747bef24..95bcf990270 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -1,15 +1,14 @@ +import { autocompletion, Completion, CompletionContext } from '@codemirror/autocomplete'; import { EditorView } from '@uiw/react-codemirror'; -import { Completion, CompletionContext } from '@codemirror/autocomplete'; -import { autocompletion } from '@codemirror/autocomplete'; import { Editor } from '@/components/primitives/editor'; +import { Popover, PopoverTrigger } from '@/components/primitives/popover'; import { completions } from '@/utils/liquid-autocomplete'; import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; -import { Popover, PopoverTrigger } from '@/components/primitives/popover'; +import { Dispatch, SetStateAction, useCallback, useMemo, useRef, useState } from 'react'; import { createVariablePlugin } from './variable-plugin'; -import { VariablePopover } from './variable-popover'; import { variablePillTheme } from './variable-plugin/variable-theme'; -import { useCallback, useMemo, useRef, useState, Dispatch, SetStateAction } from 'react'; +import { VariablePopover } from './variable-popover'; type SelectedVariable = { value: string; @@ -66,7 +65,7 @@ export function FieldEditor({ () => [ autocompletion({ override: [completionSource], - closeOnBlur: false, + closeOnBlur: true, defaultKeymap: true, activateOnTyping: true, }), diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts index 87e7d9358ac..cb6cfae26d8 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts @@ -17,7 +17,7 @@ export type TransformerWithParam = { }; export type VariablePopoverProps = { - variable: string; + variable?: string; onClose: () => void; onUpdate: (newValue: string) => void; }; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx index 61fe0354e9a..a74583fd10c 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx @@ -1,21 +1,21 @@ -import { useState, useRef, useEffect } from 'react'; -import { PopoverContent } from '@/components/primitives/popover'; -import { Input, InputField } from '@/components/primitives/input'; import { FormControl, FormItem } from '@/components/primitives/form/form'; +import { Input, InputField } from '@/components/primitives/input'; +import { PopoverContent } from '@/components/primitives/popover'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; import { Switch } from '@/components/primitives/switch'; +import { useEffect, useRef, useState } from 'react'; import { Code2 } from '../../../icons/code-2'; import { Separator } from '../../separator'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; import { TransformerItem } from './components/transformer-item'; import { TransformerList } from './components/transformer-list'; -import { useVariableParser } from './hooks/use-variable-parser'; import { useTransformerManager } from './hooks/use-transformer-manager'; -import { formatLiquidVariable } from './utils'; +import { useVariableParser } from './hooks/use-variable-parser'; import type { VariablePopoverProps } from './types'; +import { formatLiquidVariable } from './utils'; export function VariablePopover({ variable, onClose, onUpdate }: VariablePopoverProps) { const searchInputRef = useRef(null); - const { parsedName, parsedDefaultValue, parsedTransformers } = useVariableParser(variable); + const { parsedName, parsedDefaultValue, parsedTransformers } = useVariableParser(variable || ''); const [name, setName] = useState(parsedName); const [defaultVal, setDefaultVal] = useState(parsedDefaultValue); const [showRawLiquid, setShowRawLiquid] = useState(false); diff --git a/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx b/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx index f43fd1fccbc..1aa7856c1df 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/base/base-body.tsx @@ -1,12 +1,12 @@ import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; +import { FieldEditor } from '@/components/primitives/field-editor'; import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form'; import { InputField } from '@/components/primitives/input'; import { useWorkflow } from '@/components/workflow-editor/workflow-provider'; import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables'; import { capitalize } from '@/utils/string'; -import { FieldEditor } from '@/components/primitives/field-editor'; const bodyKey = 'body'; @@ -22,7 +22,7 @@ export const BaseBody = () => { render={({ field }) => ( - + Date: Fri, 3 Jan 2025 09:52:21 +0200 Subject: [PATCH 35/49] fix: refactor --- apps/api/src/app/workflows-v2/util/utils.ts | 6 +- .../primitives/field-editor/field-editor.tsx | 4 +- .../variable-plugin/plugin-view.ts | 4 +- .../variable-plugin/variable-pill-widget.ts | 2 +- .../workflow-editor/steps/base/base-body.tsx | 1 - .../steps/base/base-subject.tsx | 3 +- .../steps/controls/text-widget.tsx | 3 +- .../steps/digest/digest-key.tsx | 1 - .../steps/email/email-subject.tsx | 8 +- .../steps/in-app/in-app-body.tsx | 4 +- .../steps/in-app/in-app-subject.tsx | 3 +- .../components/workflow-editor/url-input.tsx | 3 +- .../src/utils/liquid-autocomplete.ts | 111 ++++++++++++++---- 13 files changed, 102 insertions(+), 51 deletions(-) diff --git a/apps/api/src/app/workflows-v2/util/utils.ts b/apps/api/src/app/workflows-v2/util/utils.ts index 8d83c3256ec..07245a3c389 100644 --- a/apps/api/src/app/workflows-v2/util/utils.ts +++ b/apps/api/src/app/workflows-v2/util/utils.ts @@ -1,12 +1,10 @@ import difference from 'lodash/difference'; import flatMap from 'lodash/flatMap'; +import isArray from 'lodash/isArray'; +import isObject from 'lodash/isObject'; import reduce from 'lodash/reduce'; import set from 'lodash/set'; import values from 'lodash/values'; -import isObject from 'lodash/isObject'; -import isArray from 'lodash/isArray'; - -import { BadRequestException } from '@nestjs/common'; import { JSONSchemaDto } from '@novu/shared'; diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index 95bcf990270..e8baf4106d9 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -28,7 +28,6 @@ type FieldEditorProps = { placeholder?: string; autoFocus?: boolean; size?: 'default' | 'lg'; - fontFamily?: 'inherit'; id?: string; singleLine?: boolean; indentWithTab?: boolean; @@ -41,7 +40,6 @@ export function FieldEditor({ placeholder, autoFocus, size = 'default', - fontFamily = 'inherit', id, singleLine, indentWithTab, @@ -79,12 +77,12 @@ export function FieldEditor({ return (
{ { { { singleLine indentWithTab={false} autoFocus={!field.value} - fontFamily="inherit" placeholder={capitalize(field.name)} id={field.name} value={field.value} diff --git a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx index 781f6083177..6b6eca1ff45 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-body.tsx @@ -1,11 +1,11 @@ import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; +import { FieldEditor } from '@/components/primitives/field-editor'; import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form'; import { useWorkflow } from '@/components/workflow-editor/workflow-provider'; import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables'; import { capitalize } from '@/utils/string'; -import { FieldEditor } from '@/components/primitives/field-editor'; import { InputField } from '../../../primitives/input'; const bodyKey = 'body'; @@ -24,14 +24,12 @@ export const InAppBody = () => { diff --git a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-subject.tsx index 1ed3c507618..e97736f5c52 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-subject.tsx @@ -1,12 +1,12 @@ import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; +import { FieldEditor } from '@/components/primitives/field-editor'; import { FormControl, FormField, FormItem, FormMessage } from '@/components/primitives/form/form'; import { InputField } from '@/components/primitives/input'; import { useWorkflow } from '@/components/workflow-editor/workflow-provider'; import { parseStepVariablesToLiquidVariables } from '@/utils/parseStepVariablesToLiquidVariables'; import { capitalize } from '@/utils/string'; -import { FieldEditor } from '@/components/primitives/field-editor'; const subjectKey = 'subject'; @@ -24,7 +24,6 @@ export const InAppSubject = () => { & { options: string[]; @@ -50,7 +50,6 @@ export const URLInput = ({ basicSetup={{ defaultKeymap: false, }} - fontFamily="inherit" placeholder={placeholder} value={field.value} onChange={field.onChange} diff --git a/apps/dashboard/src/utils/liquid-autocomplete.ts b/apps/dashboard/src/utils/liquid-autocomplete.ts index c6991e56d5c..7d1aed9ac93 100644 --- a/apps/dashboard/src/utils/liquid-autocomplete.ts +++ b/apps/dashboard/src/utils/liquid-autocomplete.ts @@ -1,6 +1,6 @@ +import { TRANSFORMERS } from '@/components/primitives/field-editor/variable-popover/constants'; import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; import { CompletionContext, CompletionResult } from '@codemirror/autocomplete'; -import { TRANSFORMERS } from '@/components/primitives/field-editor/variable-popover/constants'; interface CompletionOption { label: string; @@ -29,6 +29,85 @@ function getFilterCompletions(afterPipe: string): CompletionOption[] { })); } +interface PathConfig { + prefix: string; + getPath: (text: string) => string | null; + getPrefix: (text: string) => string; +} + +const VARIABLE_PATHS: PathConfig[] = [ + { + prefix: 'payload.', + getPath: (text) => text.slice(8), + getPrefix: () => 'payload.', + }, + { + prefix: 'subscriber.data.', + getPath: (text) => text.slice(15), + getPrefix: () => 'subscriber.data.', + }, + { + prefix: 'steps.', + getPath: (text) => { + const fullMatch = text.match(/^steps\.[^.]+\.events\[\d+\]\.payload\.(.*?)$/); + if (fullMatch) return fullMatch[1]; + + // Handle partial paths + if (text === 'steps.' || text.startsWith('steps.')) return ''; + return null; + }, + getPrefix: (text) => { + const match = text.match(/^(steps\.[^.]+\.events\[\d+\]\.payload)\./); + if (match) return `${match[1]}.`; + + // For partial paths, return what we have so far + return text; + }, + }, +]; + +function getVariableSuggestions( + word: { text: string; from: number; to?: number }, + variables: LiquidVariable[], + pathConfig: PathConfig +): CompletionOption[] { + const path = pathConfig.getPath(word.text); + if (path === null) return []; + + const prefix = pathConfig.getPrefix(word.text); + const existingVariables = variables.filter((v) => { + if (pathConfig.prefix === 'steps.') { + // For partial paths, show all step variables + if (word.text === 'steps.' || (word.text.startsWith('steps.') && !word.text.includes('events'))) { + return v.label.startsWith('steps.'); + } + + const stepMatch = word.text.match(/^steps\.([^.]+)\.events\[(\d+)\]/); + if (!stepMatch) return false; + const [, stepId, eventIndex] = stepMatch; + return v.label.startsWith(`steps.${stepId}.events[${eventIndex}].payload.`); + } + return v.label.startsWith(pathConfig.prefix); + }); + + const suggestions: CompletionOption[] = existingVariables + .filter((v) => path === '' || v.label.toLowerCase().includes(path.toLowerCase())) + .map((v) => ({ + label: v.label, + type: 'variable', + })); + + if (path && !existingVariables.some((v) => v.label === `${prefix}${path}`)) { + suggestions.unshift({ + label: `${prefix}${path}`, + type: 'variable', + boost: 99, + }); + } + + return suggestions; +} + function getVariableCompletions( word: { text: string; from: number; to?: number } | null, pos: number, @@ -36,30 +115,16 @@ function getVariableCompletions( ): CompletionResult | null { if (!word && !variables.length) return null; - if (word?.text.startsWith('payload.')) { - const payloadPath = word.text.slice(8); - const existingVariables = variables.filter((v) => v.label.startsWith('payload.')); + for (const pathConfig of VARIABLE_PATHS) { + if (word?.text.startsWith(pathConfig.prefix)) { + const suggestions = getVariableSuggestions(word, variables, pathConfig); - const suggestions: CompletionOption[] = existingVariables - .filter((v) => v.label.toLowerCase().includes(payloadPath.toLowerCase())) - .map((v) => ({ - label: v.label, - type: 'variable', - })); - - if (payloadPath && !existingVariables.some((v) => v.label === `payload.${payloadPath}`)) { - suggestions.unshift({ - label: `payload.${payloadPath}`, - type: 'variable', - boost: 99, - }); + return { + from: word.from, + to: word.to ?? pos, + options: suggestions, + }; } - - return { - from: word.from, - to: word.to ?? pos, - options: suggestions, - }; } if (word) { From d00d441b881c34b6b5eb7b287c6be7cacab48270 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Fri, 3 Jan 2025 10:47:32 +0200 Subject: [PATCH 36/49] fix: working state --- .../shared/map-step-type-to-result.mapper.ts | 12 +- .../src/utils/liquid-autocomplete.ts | 314 ++++++++++++------ 2 files changed, 216 insertions(+), 110 deletions(-) diff --git a/apps/api/src/app/workflows-v2/shared/map-step-type-to-result.mapper.ts b/apps/api/src/app/workflows-v2/shared/map-step-type-to-result.mapper.ts index 101b457d5bb..8de90cb6712 100644 --- a/apps/api/src/app/workflows-v2/shared/map-step-type-to-result.mapper.ts +++ b/apps/api/src/app/workflows-v2/shared/map-step-type-to-result.mapper.ts @@ -1,6 +1,6 @@ import { ActionStepEnum, actionStepSchemas, ChannelStepEnum, channelStepSchemas } from '@novu/framework/internal'; -import { JSONSchema } from 'json-schema-to-ts'; import { StepTypeEnum } from '@novu/shared'; +import { JSONSchema } from 'json-schema-to-ts'; export function computeResultSchema(stepType: StepTypeEnum, payloadSchema?: JSONSchema) { const mapStepTypeToResult: Record = { @@ -31,9 +31,13 @@ function buildDigestResult(payloadSchema?: JSONSchema) { time: { type: 'string', }, - payload: payloadSchema || { - type: 'object', - }, + payload: + payloadSchema && typeof payloadSchema === 'object' + ? { ...payloadSchema, additionalProperties: true } + : { + type: 'object', + additionalProperties: true, + }, }, required: ['id', 'time', 'payload'], additionalProperties: false, diff --git a/apps/dashboard/src/utils/liquid-autocomplete.ts b/apps/dashboard/src/utils/liquid-autocomplete.ts index 7d1aed9ac93..0bb82c1b762 100644 --- a/apps/dashboard/src/utils/liquid-autocomplete.ts +++ b/apps/dashboard/src/utils/liquid-autocomplete.ts @@ -2,178 +2,280 @@ import { TRANSFORMERS } from '@/components/primitives/field-editor/variable-popo import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; import { CompletionContext, CompletionResult } from '@codemirror/autocomplete'; +/** + * Liquid variable autocomplete for the following patterns: + * + * 1. Payload Variables: + * Valid: + * - payload. + * - payload.user + * - payload.anyNewField (allows any new field) + * - payload.deeply.nested.field + * Invalid: + * - pay (shows suggestions but won't validate) + * - payload (shows suggestions but won't validate) + * + * 2. Subscriber Variables: + * Valid: + * - subscriber.data. + * - subscriber.data.email + * - subscriber.data.firstName + * - subscriber.data.anyNewField (allows any new field) + * - subscriber.data.custom.nested.field + * - subscriber (shows suggestions but won't validate) + * - subscriber.email + * - subscriber.firstName + * Invalid: + * - subscriber.someOtherField (must use valid subscriber field) + * + * 3. Step Variables: + * Valid: + * - steps. + * - steps.digest-step (must be existing step ID) + * - steps.digest-step.events + * - steps.digest-step.events[0] + * - steps.digest-step.events[0].id + * - steps.digest-step.events[0].payload + * - steps.digest-step.events[0].payload.summary + * - steps.digest-step.events[0].payload.anyNewField (allows any new field after payload) + * - steps.digest-step.events[0].payload.deeply.nested.field + * Invalid: + * - steps.invalid-step (must use existing step ID) + * - steps.digest-step.payload (must use events[n].payload pattern) + * - steps.digest-step.events.payload (must use events[n] pattern) + * - steps.digest-step.invalidProp (only events[] is allowed) + * + * Autocomplete Behavior: + * 1. Shows suggestions when typing partial prefixes: + * - 'su' -> shows subscriber.data.* variables + * - 'pay' -> shows payload.* variables + * - 'ste' -> shows steps.* variables + * + * 2. Shows suggestions with closing braces: + * - '{{su}}' -> shows subscriber.data.* variables + * - '{{payload.}}' -> shows payload.* variables + * + * 3. Allows new variables after valid prefixes: + * - subscriber.data.* (any new field) + * - payload.* (any new field) + * - steps.{valid-step}.events[n].payload.* (any new field) + */ + interface CompletionOption { label: string; type: string; boost?: number; } -function isInsideLiquidBlock(beforeCursor: string): boolean { - const lastOpenBrace = beforeCursor.lastIndexOf('{{'); - const lastCloseBrace = beforeCursor.lastIndexOf('}}'); - - return lastOpenBrace !== -1 && lastOpenBrace > lastCloseBrace; -} - -function getContentAfterPipe(content: string): string | null { - const pipeIndex = content.lastIndexOf('|'); - if (pipeIndex === -1) return null; - - return content.slice(pipeIndex + 1).trimStart(); -} - -function getFilterCompletions(afterPipe: string): CompletionOption[] { - return TRANSFORMERS.filter((f) => f.label.toLowerCase().startsWith(afterPipe.toLowerCase())).map((f) => ({ - label: f.value, - type: 'function', - })); -} - interface PathConfig { prefix: string; getPath: (text: string) => string | null; getPrefix: (text: string) => string; } +/** + * Defines the valid path patterns and how to extract/validate them + * Each path config handles a specific variable pattern (payload/subscriber/steps) + */ const VARIABLE_PATHS: PathConfig[] = [ { prefix: 'payload.', - getPath: (text) => text.slice(8), + getPath: (text) => text.slice(8), // Example: "payload.name" -> "name" getPrefix: () => 'payload.', }, { prefix: 'subscriber.data.', - getPath: (text) => text.slice(15), + getPath: (text) => text.slice(16), // Example: "subscriber.data.email" -> "email" getPrefix: () => 'subscriber.data.', }, { prefix: 'steps.', getPath: (text) => { + // Extracts the path after payload in step variables + // Examples: + // "steps.digest.events[0].payload.name" -> "name" + // "steps.digest.events[0].payload.user.firstName" -> "user.firstName" + // "steps.digest.events[0].payload" -> null (no path after payload) + // "steps.digest" -> null (invalid format) const fullMatch = text.match(/^steps\.[^.]+\.events\[\d+\]\.payload\.(.*?)$/); if (fullMatch) return fullMatch[1]; - // Handle partial paths if (text === 'steps.' || text.startsWith('steps.')) return ''; return null; }, getPrefix: (text) => { + // Gets everything up to and including .payload. + // Examples: + // "steps.digest.events[0].payload.name" -> "steps.digest.events[0].payload." + // "steps.digest.events[0]" -> "steps.digest.events[0]" const match = text.match(/^(steps\.[^.]+\.events\[\d+\]\.payload)\./); if (match) return `${match[1]}.`; - - // For partial paths, return what we have so far return text; }, }, ]; -function getVariableSuggestions( - word: { text: string; from: number; to?: number }, - variables: LiquidVariable[], - pathConfig: PathConfig -): CompletionOption[] { - const path = pathConfig.getPath(word.text); - if (path === null) return []; - - const prefix = pathConfig.getPrefix(word.text); - const existingVariables = variables.filter((v) => { - if (pathConfig.prefix === 'steps.') { - // For partial paths, show all step variables - if (word.text === 'steps.' || (word.text.startsWith('steps.') && !word.text.includes('events'))) { - return v.label.startsWith('steps.'); - } +function isInsideLiquidBlock(beforeCursor: string): boolean { + const lastOpenBrace = beforeCursor.lastIndexOf('{{'); + return lastOpenBrace !== -1; +} - const stepMatch = word.text.match(/^steps\.([^.]+)\.events\[(\d+)\]/); - if (!stepMatch) return false; - const [, stepId, eventIndex] = stepMatch; - return v.label.startsWith(`steps.${stepId}.events[${eventIndex}].payload.`); - } - return v.label.startsWith(pathConfig.prefix); - }); - - const suggestions: CompletionOption[] = existingVariables - .filter((v) => path === '' || v.label.toLowerCase().includes(path.toLowerCase())) - .map((v) => ({ - label: v.label, - type: 'variable', - })); - - if (path && !existingVariables.some((v) => v.label === `${prefix}${path}`)) { - suggestions.unshift({ - label: `${prefix}${path}`, - type: 'variable', - boost: 99, - }); - } +function getContentAfterPipe(content: string): string | null { + const pipeIndex = content.lastIndexOf('|'); + if (pipeIndex === -1) return null; + return content.slice(pipeIndex + 1).trimStart(); +} - return suggestions; +function createCompletionOption(label: string, type: string, boost?: number): CompletionOption { + return { label, type, ...(boost && { boost }) }; } -function getVariableCompletions( - word: { text: string; from: number; to?: number } | null, - pos: number, - variables: LiquidVariable[] -): CompletionResult | null { - if (!word && !variables.length) return null; +function getFilterCompletions(afterPipe: string): CompletionOption[] { + return TRANSFORMERS.filter((f) => f.label.toLowerCase().startsWith(afterPipe.toLowerCase())).map((f) => + createCompletionOption(f.value, 'function') + ); +} - for (const pathConfig of VARIABLE_PATHS) { - if (word?.text.startsWith(pathConfig.prefix)) { - const suggestions = getVariableSuggestions(word, variables, pathConfig); +/** + * Gets matching variables based on what the user has typed. + * Different matching strategies based on the input: + * + * 1. Ends with dot: Show all variables starting with everything before the dot + * Example: "payload." -> shows all payload.* variables + * + * 2. Valid prefix: Show all variables for that prefix + * Example: "payload" -> shows all payload.* variables + * Example: "steps.digest" -> shows all variables for that step + * + * 3. Other text: Show any variables containing the text + * Example: "user" -> shows payload.user, subscriber.data.username, etc. + */ +function getMatchingVariables(searchText: string, variables: LiquidVariable[]): LiquidVariable[] { + // Handle empty search + if (!searchText) { + return variables; + } - return { - from: word.from, - to: word.to ?? pos, - options: suggestions, - }; + const searchLower = searchText.toLowerCase(); + + // Handle root prefixes and their partials + const rootPrefixes = { + subscriber: 'subscriber.', + payload: 'payload.', + steps: 'steps.', + }; + + // Check for partial matches of root prefixes + for (const [root, prefix] of Object.entries(rootPrefixes)) { + // If typing part of a root prefix (e.g., 'pay', 'sub', 'ste') + if (searchLower.startsWith(root) || root.startsWith(searchLower)) { + const matches = variables.filter((v) => v.label.startsWith(prefix)); + + // Special handling for subscriber fields + if (prefix === 'subscriber.') { + const parts = searchText.split('.'); + if (parts.length === 2 && parts[0] === 'subscriber') { + // Only allow subscriber fields that exist in the variables + if (!matches.some((v) => v.label === searchText)) { + return []; + } + } + } + + // For subscriber.data., payload., and step payload paths allow new paths + if ( + searchText.startsWith('subscriber.data.') || + searchText.startsWith('payload.') || + /^steps\.[^.]+\.events\[\d+\]\.payload\./.test(searchText) + ) { + if (!matches.some((v) => v.label === searchText)) { + matches.push({ label: searchText, type: 'variable' } as LiquidVariable); + } + } + + return matches; } } - if (word) { - const matchingVariables = variables.filter((v) => v.label.toLowerCase().includes(word.text.toLowerCase())); - - return { - from: word.from, - to: word.to ?? pos, - options: matchingVariables.map((v) => ({ - label: v.label, - type: 'variable', - })), - }; + // Handle dot endings + if (searchText.endsWith('.')) { + const prefix = searchText.slice(0, -1); + return variables.filter((v) => v.label.startsWith(prefix)); } - return { - from: pos, - options: variables.map((v) => ({ - label: v.label, - type: 'variable', - })), - }; + // Validate step ID exists in variables + if (searchText.startsWith('steps.')) { + const stepMatch = searchText.match(/^steps\.([^.]+)/); + if (stepMatch) { + const stepId = stepMatch[1]; + const stepExists = variables.some((v) => v.label.startsWith(`steps.${stepId}.`)); + if (!stepExists) { + return []; + } + } + } + + // Validate events array format + if (searchText.includes('.events')) { + const eventsFormatValid = /^steps\.[^.]+\.events\[\d+\]/.test(searchText); + if (!eventsFormatValid) { + return []; + } + } + + // Default case: show any variables containing the search text + const matches = variables.filter((v) => v.label.toLowerCase().includes(searchLower)); + + return matches; } +// Main completion function export const completions = (variables: LiquidVariable[]) => (context: CompletionContext): CompletionResult | null => { const { state, pos } = context; const beforeCursor = state.sliceDoc(0, pos); - if (!isInsideLiquidBlock(beforeCursor)) { - return null; - } - + // Only proceed if we're inside or just after {{ const lastOpenBrace = beforeCursor.lastIndexOf('{{'); + if (lastOpenBrace === -1) return null; + + // Get the content between {{ and cursor const insideBraces = state.sliceDoc(lastOpenBrace + 2, pos); - const afterPipe = getContentAfterPipe(insideBraces); - if (afterPipe !== null) { - const filterCompletions = getFilterCompletions(afterPipe); + // Get clean search text without braces and trim + const searchText = insideBraces.replace(/}+$/, '').trim(); + // Handle pipe filters + const afterPipe = getContentAfterPipe(searchText); + if (afterPipe !== null) { return { from: pos - afterPipe.length, to: pos, - options: filterCompletions, + options: getFilterCompletions(afterPipe), + }; + } + + // Get matching variables + const matchingVariables = getMatchingVariables(searchText, variables); + + // If we have matches, show them + if (matchingVariables.length > 0) { + return { + from: lastOpenBrace + 2, + to: pos, + options: matchingVariables.map((v) => createCompletionOption(v.label, 'variable')), }; } - const word = context.matchBefore(/[\w.]+/); + // If no matches but we're in a valid context, show all variables + if (isInsideLiquidBlock(beforeCursor)) { + return { + from: lastOpenBrace + 2, + to: pos, + options: variables.map((v) => createCompletionOption(v.label, 'variable')), + }; + } - return getVariableCompletions(word, pos, variables); + return null; }; From 36da57bfc80a2dca2d11bf1883fe5954b51fbdc4 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Fri, 3 Jan 2025 10:53:46 +0200 Subject: [PATCH 37/49] fix: auto complete --- .../src/utils/liquid-autocomplete.ts | 259 +++++++----------- 1 file changed, 92 insertions(+), 167 deletions(-) diff --git a/apps/dashboard/src/utils/liquid-autocomplete.ts b/apps/dashboard/src/utils/liquid-autocomplete.ts index 0bb82c1b762..2da881454e3 100644 --- a/apps/dashboard/src/utils/liquid-autocomplete.ts +++ b/apps/dashboard/src/utils/liquid-autocomplete.ts @@ -2,6 +2,20 @@ import { TRANSFORMERS } from '@/components/primitives/field-editor/variable-popo import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; import { CompletionContext, CompletionResult } from '@codemirror/autocomplete'; +interface CompletionOption { + label: string; + type: string; + boost?: number; +} + +const ROOT_PREFIXES = { + subscriber: 'subscriber.', + payload: 'payload.', + steps: 'steps.', +} as const; + +const VALID_DYNAMIC_PATHS = ['subscriber.data.', 'payload.', /^steps\.[^.]+\.events\[\d+\]\.payload\./] as const; + /** * Liquid variable autocomplete for the following patterns: * @@ -18,8 +32,6 @@ import { CompletionContext, CompletionResult } from '@codemirror/autocomplete'; * 2. Subscriber Variables: * Valid: * - subscriber.data. - * - subscriber.data.email - * - subscriber.data.firstName * - subscriber.data.anyNewField (allows any new field) * - subscriber.data.custom.nested.field * - subscriber (shows suggestions but won't validate) @@ -36,7 +48,6 @@ import { CompletionContext, CompletionResult } from '@codemirror/autocomplete'; * - steps.digest-step.events[0] * - steps.digest-step.events[0].id * - steps.digest-step.events[0].payload - * - steps.digest-step.events[0].payload.summary * - steps.digest-step.events[0].payload.anyNewField (allows any new field after payload) * - steps.digest-step.events[0].payload.deeply.nested.field * Invalid: @@ -60,69 +71,59 @@ import { CompletionContext, CompletionResult } from '@codemirror/autocomplete'; * - payload.* (any new field) * - steps.{valid-step}.events[n].payload.* (any new field) */ +export const completions = + (variables: LiquidVariable[]) => + (context: CompletionContext): CompletionResult | null => { + const { state, pos } = context; + const beforeCursor = state.sliceDoc(0, pos); -interface CompletionOption { - label: string; - type: string; - boost?: number; -} + // Only proceed if we're inside or just after {{ + const lastOpenBrace = beforeCursor.lastIndexOf('{{'); + if (lastOpenBrace === -1) return null; -interface PathConfig { - prefix: string; - getPath: (text: string) => string | null; - getPrefix: (text: string) => string; -} + // Get the content between {{ and cursor + const insideBraces = state.sliceDoc(lastOpenBrace + 2, pos); -/** - * Defines the valid path patterns and how to extract/validate them - * Each path config handles a specific variable pattern (payload/subscriber/steps) - */ -const VARIABLE_PATHS: PathConfig[] = [ - { - prefix: 'payload.', - getPath: (text) => text.slice(8), // Example: "payload.name" -> "name" - getPrefix: () => 'payload.', - }, - { - prefix: 'subscriber.data.', - getPath: (text) => text.slice(16), // Example: "subscriber.data.email" -> "email" - getPrefix: () => 'subscriber.data.', - }, - { - prefix: 'steps.', - getPath: (text) => { - // Extracts the path after payload in step variables - // Examples: - // "steps.digest.events[0].payload.name" -> "name" - // "steps.digest.events[0].payload.user.firstName" -> "user.firstName" - // "steps.digest.events[0].payload" -> null (no path after payload) - // "steps.digest" -> null (invalid format) - const fullMatch = text.match(/^steps\.[^.]+\.events\[\d+\]\.payload\.(.*?)$/); - if (fullMatch) return fullMatch[1]; - - if (text === 'steps.' || text.startsWith('steps.')) return ''; - return null; - }, - getPrefix: (text) => { - // Gets everything up to and including .payload. - // Examples: - // "steps.digest.events[0].payload.name" -> "steps.digest.events[0].payload." - // "steps.digest.events[0]" -> "steps.digest.events[0]" - const match = text.match(/^(steps\.[^.]+\.events\[\d+\]\.payload)\./); - if (match) return `${match[1]}.`; - return text; - }, - }, -]; + // Get clean search text without braces and trim + const searchText = insideBraces.replace(/}+$/, '').trim(); + + // Handle pipe filters + const afterPipe = getContentAfterPipe(searchText); + if (afterPipe !== null) { + return { + from: pos - afterPipe.length, + to: pos, + options: getFilterCompletions(afterPipe), + }; + } + + const matchingVariables = getMatchingVariables(searchText, variables); + + // If we have matches or we're in a valid context, show them + if (matchingVariables.length > 0 || isInsideLiquidBlock(beforeCursor)) { + return { + from: lastOpenBrace + 2, + to: pos, + options: + matchingVariables.length > 0 + ? matchingVariables.map((v) => createCompletionOption(v.label, 'variable')) + : variables.map((v) => createCompletionOption(v.label, 'variable')), + }; + } + + return null; + }; function isInsideLiquidBlock(beforeCursor: string): boolean { const lastOpenBrace = beforeCursor.lastIndexOf('{{'); + return lastOpenBrace !== -1; } function getContentAfterPipe(content: string): string | null { const pipeIndex = content.lastIndexOf('|'); if (pipeIndex === -1) return null; + return content.slice(pipeIndex + 1).trimStart(); } @@ -136,58 +137,50 @@ function getFilterCompletions(afterPipe: string): CompletionOption[] { ); } -/** - * Gets matching variables based on what the user has typed. - * Different matching strategies based on the input: - * - * 1. Ends with dot: Show all variables starting with everything before the dot - * Example: "payload." -> shows all payload.* variables - * - * 2. Valid prefix: Show all variables for that prefix - * Example: "payload" -> shows all payload.* variables - * Example: "steps.digest" -> shows all variables for that step - * - * 3. Other text: Show any variables containing the text - * Example: "user" -> shows payload.user, subscriber.data.username, etc. - */ -function getMatchingVariables(searchText: string, variables: LiquidVariable[]): LiquidVariable[] { - // Handle empty search - if (!searchText) { - return variables; +function isValidDynamicPath(searchText: string): boolean { + return VALID_DYNAMIC_PATHS.some((path) => + typeof path === 'string' ? searchText.startsWith(path) : path.test(searchText) + ); +} + +function validateSubscriberField(searchText: string, matches: LiquidVariable[]): LiquidVariable[] { + const parts = searchText.split('.'); + if (parts.length === 2 && parts[0] === 'subscriber') { + if (!matches.some((v) => v.label === searchText)) { + return []; + } } + return matches; +} + +function validateStepId(searchText: string, variables: LiquidVariable[]): boolean { + if (!searchText.startsWith('steps.')) return true; + + const stepMatch = searchText.match(/^steps\.([^.]+)/); + if (!stepMatch) return true; + + const stepId = stepMatch[1]; + return variables.some((v) => v.label.startsWith(`steps.${stepId}.`)); +} + +function getMatchingVariables(searchText: string, variables: LiquidVariable[]): LiquidVariable[] { + if (!searchText) return variables; + const searchLower = searchText.toLowerCase(); // Handle root prefixes and their partials - const rootPrefixes = { - subscriber: 'subscriber.', - payload: 'payload.', - steps: 'steps.', - }; - - // Check for partial matches of root prefixes - for (const [root, prefix] of Object.entries(rootPrefixes)) { - // If typing part of a root prefix (e.g., 'pay', 'sub', 'ste') + for (const [root, prefix] of Object.entries(ROOT_PREFIXES)) { if (searchLower.startsWith(root) || root.startsWith(searchLower)) { - const matches = variables.filter((v) => v.label.startsWith(prefix)); + let matches = variables.filter((v) => v.label.startsWith(prefix)); // Special handling for subscriber fields if (prefix === 'subscriber.') { - const parts = searchText.split('.'); - if (parts.length === 2 && parts[0] === 'subscriber') { - // Only allow subscriber fields that exist in the variables - if (!matches.some((v) => v.label === searchText)) { - return []; - } - } + matches = validateSubscriberField(searchText, matches); } - // For subscriber.data., payload., and step payload paths allow new paths - if ( - searchText.startsWith('subscriber.data.') || - searchText.startsWith('payload.') || - /^steps\.[^.]+\.events\[\d+\]\.payload\./.test(searchText) - ) { + // Allow new paths for dynamic paths + if (isValidDynamicPath(searchText)) { if (!matches.some((v) => v.label === searchText)) { matches.push({ label: searchText, type: 'variable' } as LiquidVariable); } @@ -203,79 +196,11 @@ function getMatchingVariables(searchText: string, variables: LiquidVariable[]): return variables.filter((v) => v.label.startsWith(prefix)); } - // Validate step ID exists in variables - if (searchText.startsWith('steps.')) { - const stepMatch = searchText.match(/^steps\.([^.]+)/); - if (stepMatch) { - const stepId = stepMatch[1]; - const stepExists = variables.some((v) => v.label.startsWith(`steps.${stepId}.`)); - if (!stepExists) { - return []; - } - } - } - - // Validate events array format - if (searchText.includes('.events')) { - const eventsFormatValid = /^steps\.[^.]+\.events\[\d+\]/.test(searchText); - if (!eventsFormatValid) { - return []; - } + // Validate step ID exists + if (!validateStepId(searchText, variables)) { + return []; } // Default case: show any variables containing the search text - const matches = variables.filter((v) => v.label.toLowerCase().includes(searchLower)); - - return matches; + return variables.filter((v) => v.label.toLowerCase().includes(searchLower)); } - -// Main completion function -export const completions = - (variables: LiquidVariable[]) => - (context: CompletionContext): CompletionResult | null => { - const { state, pos } = context; - const beforeCursor = state.sliceDoc(0, pos); - - // Only proceed if we're inside or just after {{ - const lastOpenBrace = beforeCursor.lastIndexOf('{{'); - if (lastOpenBrace === -1) return null; - - // Get the content between {{ and cursor - const insideBraces = state.sliceDoc(lastOpenBrace + 2, pos); - - // Get clean search text without braces and trim - const searchText = insideBraces.replace(/}+$/, '').trim(); - - // Handle pipe filters - const afterPipe = getContentAfterPipe(searchText); - if (afterPipe !== null) { - return { - from: pos - afterPipe.length, - to: pos, - options: getFilterCompletions(afterPipe), - }; - } - - // Get matching variables - const matchingVariables = getMatchingVariables(searchText, variables); - - // If we have matches, show them - if (matchingVariables.length > 0) { - return { - from: lastOpenBrace + 2, - to: pos, - options: matchingVariables.map((v) => createCompletionOption(v.label, 'variable')), - }; - } - - // If no matches but we're in a valid context, show all variables - if (isInsideLiquidBlock(beforeCursor)) { - return { - from: lastOpenBrace + 2, - to: pos, - options: variables.map((v) => createCompletionOption(v.label, 'variable')), - }; - } - - return null; - }; From c10c935ffbc7392bae1552a98d191be8812ca987 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Fri, 3 Jan 2025 11:59:42 +0200 Subject: [PATCH 38/49] fix: liquid --- apps/dashboard/package.json | 1 + .../primitives/field-editor/field-editor.tsx | 146 +- .../field-editor/hooks/use-variables.ts | 75 + .../components/draggable-transformer.tsx | 204 +-- .../hooks/use-variable-parser.ts | 109 +- .../variable-popover/variable-popover.tsx | 4 +- .../src/utils/liquid-autocomplete.ts | 15 + pnpm-lock.yaml | 1508 +++++++++-------- 8 files changed, 1043 insertions(+), 1019 deletions(-) create mode 100644 apps/dashboard/src/components/primitives/field-editor/hooks/use-variables.ts diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 383729ef4f5..6786be89a1b 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -75,6 +75,7 @@ "flat": "^6.0.1", "js-cookie": "^3.0.5", "launchdarkly-react-client-sdk": "^3.3.2", + "liquidjs": "^10.20.0", "lodash.debounce": "^4.0.8", "lodash.isequal": "^4.5.0", "lodash.merge": "^4.6.2", diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index e8baf4106d9..b89e34eeb14 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -1,21 +1,16 @@ -import { autocompletion, Completion, CompletionContext } from '@codemirror/autocomplete'; +import { autocompletion } from '@codemirror/autocomplete'; import { EditorView } from '@uiw/react-codemirror'; import { Editor } from '@/components/primitives/editor'; import { Popover, PopoverTrigger } from '@/components/primitives/popover'; -import { completions } from '@/utils/liquid-autocomplete'; +import { createAutocompleteSource } from '@/utils/liquid-autocomplete'; import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; -import { Dispatch, SetStateAction, useCallback, useMemo, useRef, useState } from 'react'; +import { useMemo, useRef } from 'react'; +import { useVariables } from './hooks/use-variables'; import { createVariablePlugin } from './variable-plugin'; import { variablePillTheme } from './variable-plugin/variable-theme'; import { VariablePopover } from './variable-popover'; -type SelectedVariable = { - value: string; - from: number; - to: number; -} | null; - type CompletionRange = { from: number; to: number; @@ -47,17 +42,12 @@ export function FieldEditor({ const viewRef = useRef(null); const lastCompletionRef = useRef(null); - const { selectedVariable, setSelectedVariable, handleVariableSelect, isUpdatingRef } = useVariableSelection(); - - const handleVariableUpdate = useVariableUpdate( - selectedVariable, + const { selectedVariable, setSelectedVariable, handleVariableSelect, handleVariableUpdate } = useVariables( viewRef, - isUpdatingRef, - onChange, - setSelectedVariable + onChange ); - const completionSource = useCompletionSource(variables, lastCompletionRef); + const completionSource = useMemo(() => createAutocompleteSource(variables), [variables]); const extensions = useMemo( () => [ @@ -111,125 +101,3 @@ export function FieldEditor({
); } - -/** - * Manages the state and selection of Liquid variables in the editor. - * - * This hook handles the complex state management needed when a user selects a variable in the editor: - * 1. Tracks which variable is currently selected - * 2. Prevents recursive updates when variables are being modified - */ -function useVariableSelection() { - const [selectedVariable, setSelectedVariable] = useState(null); - const isUpdatingRef = useRef(false); - - const handleVariableSelect = useCallback((value: string, from: number, to: number) => { - if (isUpdatingRef.current) return; - - requestAnimationFrame(() => { - setSelectedVariable({ value, from, to }); - }); - }, []); - - return { - selectedVariable, - setSelectedVariable, - handleVariableSelect, - isUpdatingRef, - }; -} - -/** - * Handles the logic of updating Liquid variables in the editor while maintaining proper syntax. - * - * This hook manages several critical aspects of variable editing: - * 1. Ensures proper Liquid syntax ({{...}}) is maintained when updating variables - * 2. Handles edge cases like existing closing brackets - * 3. Maintains cursor position after updates - * 4. Prevents recursive updates during the edit process - * - * The update process: - * 1. Checks if the new value already has Liquid syntax - * 2. Detects if there are existing closing brackets after the cursor - * 3. Calculates the correct range to replace - * 4. Updates the editor state while maintaining proper cursor position - */ -function useVariableUpdate( - selectedVariable: SelectedVariable, - viewRef: React.RefObject, - isUpdatingRef: React.MutableRefObject, - onChange: (value: string) => void, - setSelectedVariable: Dispatch> -) { - return useCallback( - (newValue: string) => { - if (!selectedVariable || !viewRef.current || isUpdatingRef.current) return; - - try { - isUpdatingRef.current = true; - const { from, to } = selectedVariable; - const view = viewRef.current; - - const hasLiquidSyntax = newValue.match(/^\{\{.*\}\}$/); - const newVariableText = hasLiquidSyntax ? newValue : `{{${newValue}}}`; - - const currentContent = view.state.doc.toString(); - const afterCursor = currentContent.slice(to).trim(); - const hasClosingBrackets = afterCursor.startsWith('}}'); - - const changes = { - from, - to: hasClosingBrackets ? to + 2 : to, - insert: newVariableText, - }; - - view.dispatch({ - changes, - selection: { anchor: from + newVariableText.length }, - }); - - onChange(view.state.doc.toString()); - - setSelectedVariable((prev: SelectedVariable) => - prev ? { ...prev, value: newValue, to: from + newVariableText.length } : null - ); - } finally { - isUpdatingRef.current = false; - } - }, - [selectedVariable, onChange, viewRef, isUpdatingRef, setSelectedVariable] - ); -} - -function useCompletionSource(variables: LiquidVariable[], lastCompletionRef: React.MutableRefObject) { - return useCallback( - (context: CompletionContext) => { - // Match text that starts with {{ and capture everything after it until the cursor position - const word = context.matchBefore(/\{\{([^}]*)/); - if (!word) return null; - - const options = completions(variables)(context); - if (!options) return null; - - return { - ...options, - apply: (view: EditorView, completion: Completion, from: number, to: number) => { - const text = completion.label; - lastCompletionRef.current = { from, to }; - - // Handle liquid variable syntax ({{variable}}) - // If we're not already inside liquid brackets, wrap the completion with {{}} - // If we're inside liquid brackets (detected by checking previous chars), just insert the variable name - const content = view.state.doc.toString(); - const before = content.slice(Math.max(0, from - 2), from); - const insert = before !== '{{' ? `{{${text}}} ` : `${text}}} `; - - view.dispatch({ - changes: { from, to, insert }, - }); - }, - }; - }, - [variables, lastCompletionRef] - ); -} diff --git a/apps/dashboard/src/components/primitives/field-editor/hooks/use-variables.ts b/apps/dashboard/src/components/primitives/field-editor/hooks/use-variables.ts new file mode 100644 index 00000000000..efc53ec0bd9 --- /dev/null +++ b/apps/dashboard/src/components/primitives/field-editor/hooks/use-variables.ts @@ -0,0 +1,75 @@ +import { EditorView } from '@uiw/react-codemirror'; +import { useCallback, useRef, useState } from 'react'; + +type SelectedVariable = { + value: string; + from: number; + to: number; +} | null; + +/** + * Manages variable selection and updates in the editor. + * + * This hook combines variable selection and update logic: + * 1. Tracks which variable is currently selected + * 2. Prevents recursive updates when variables are being modified + * 3. Handles proper Liquid syntax maintenance + * 4. Manages cursor position and editor state updates + */ +export function useVariables(viewRef: React.RefObject, onChange: (value: string) => void) { + const [selectedVariable, setSelectedVariable] = useState(null); + const isUpdatingRef = useRef(false); + + const handleVariableSelect = useCallback((value: string, from: number, to: number) => { + if (isUpdatingRef.current) return; + + setSelectedVariable({ value, from, to }); + }, []); + + const handleVariableUpdate = useCallback( + (newValue: string) => { + if (!selectedVariable || !viewRef.current || isUpdatingRef.current) return; + + try { + isUpdatingRef.current = true; + const { from, to } = selectedVariable; + const view = viewRef.current; + + const hasLiquidSyntax = newValue.match(/^\{\{.*\}\}$/); + const newVariableText = hasLiquidSyntax ? newValue : `{{${newValue}}}`; + + const currentContent = view.state.doc.toString(); + const afterCursor = currentContent.slice(to).trim(); + const hasClosingBrackets = afterCursor.startsWith('}}'); + + const changes = { + from, + to: hasClosingBrackets ? to + 2 : to, + insert: newVariableText, + }; + + view.dispatch({ + changes, + selection: { anchor: from + newVariableText.length }, + }); + + onChange(view.state.doc.toString()); + + setSelectedVariable((prev: SelectedVariable) => + prev ? { ...prev, value: newValue, to: from + newVariableText.length } : null + ); + } finally { + isUpdatingRef.current = false; + } + }, + [selectedVariable, onChange, viewRef] + ); + + return { + selectedVariable, + setSelectedVariable, + handleVariableSelect, + handleVariableUpdate, + isUpdatingRef, + }; +} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx index 67d7af8bd34..34b5ce98b24 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/draggable-transformer.tsx @@ -1,8 +1,9 @@ -import { motion } from 'motion/react'; -import { GripVertical } from 'lucide-react'; -import { RiCloseLine } from 'react-icons/ri'; import { Button } from '@/components/primitives/button'; import { Input } from '@/components/primitives/input'; +import { GripVertical } from 'lucide-react'; +import { motion } from 'motion/react'; +import { forwardRef } from 'react'; +import { RiCloseLine } from 'react-icons/ri'; import { TRANSFORMERS } from '../constants'; import { TransformerWithParam } from '../types'; @@ -19,105 +20,112 @@ type DraggableTransformerProps = { onParamChange: (index: number, params: string[]) => void; }; -export function DraggableTransformer({ - transformer, - index, - isLast, - draggingItem, - dragOverIndex, - onDragStart, - onDragEnd, - onDrag, - onRemove, - onParamChange, -}: DraggableTransformerProps) { - const transformerDef = TRANSFORMERS.find((t) => t.value === transformer.value); +export const DraggableTransformer = forwardRef( + ( + { + transformer, + index, + isLast, + draggingItem, + dragOverIndex, + onDragStart, + onDragEnd, + onDrag, + onRemove, + onParamChange, + }, + ref + ) => { + const transformerDef = TRANSFORMERS.find((t) => t.value === transformer.value); - return ( - - {dragOverIndex === index && ( - - )} + return ( onDragStart(index)} - onDragEnd={onDragEnd} - onDrag={onDrag} - data-index={index} - animate={draggingItem === index ? { scale: 1.02, zIndex: 50 } : { scale: 1, zIndex: 1 }} + ref={ref} + key={transformer.value + index} + className={`relative ${!isLast ? 'mb-1' : ''}`} + layout + layoutId={transformer.value + index} transition={{ - duration: 0.15, - ease: [0.32, 0.72, 0, 1], + layout: { duration: 0.2, ease: 'easeOut' }, + type: 'spring', + stiffness: 500, + damping: 30, }} > - -
-
-
{transformerDef?.label}
- {transformerDef?.hasParam && transformerDef.params && ( -
- {transformerDef.params.map((param, paramIndex) => ( - { - const newParams = [...(transformer.params || [])]; - newParams[paramIndex] = e.target.value; - onParamChange(index, newParams); - }} - className="border-stroke-soft ml-1 h-[20px] w-[calc(100%-8px)] border-l pl-1 text-xs" - placeholder={param.placeholder} - title={param.description} - /> - ))} -
- )} -
-
- + +
+
+
{transformerDef?.label}
+ {transformerDef?.hasParam && transformerDef.params && ( +
+ {transformerDef.params.map((param, paramIndex) => ( + { + const newParams = [...(transformer.params || [])]; + newParams[paramIndex] = e.target.value; + onParamChange(index, newParams); + }} + className="border-stroke-soft ml-1 h-[20px] w-[calc(100%-8px)] border-l pl-1 text-xs" + placeholder={param.placeholder} + title={param.description} + /> + ))} +
+ )} +
+
+ +
+ {dragOverIndex === index + 1 && ( + + )} - {dragOverIndex === index + 1 && ( - - )} - - ); -} + ); + } +); diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts index b78d17a2574..c4300a135cc 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts @@ -1,72 +1,63 @@ +import { Tokenizer, TokenKind, TopLevelToken } from 'liquidjs'; import { useMemo } from 'react'; -import { TransformerWithParam } from '../types'; import { TRANSFORMERS } from '../constants'; +import { TransformerWithParam } from '../types'; + +function isLiquidOutputToken(token: TopLevelToken): token is TopLevelToken & { + content: string; +} { + const t = token as any; + return token.kind === TokenKind.Output && typeof t.content === 'string'; +} export function useVariableParser(variable: string) { return useMemo(() => { if (!variable) { - return { parsedName: '', parsedDefaultValue: '', parsedTransformers: [] }; + return { parsedName: '', parsedDefaultValue: '', parsedTransformers: [], originalVariable: '' }; } - const parts = variable.split('|').map((part) => part.trim()); - const [nameWithDefault, ...restParts] = parts; - - // Handle default value - check both in nameWithDefault and rest parts - let name = nameWithDefault || ''; - let defaultVal = ''; - - // Function to extract default value from a part - const extractDefault = (part: string) => { - const defaultMatch = - part.match(/default:\s*'((?:[^'\\]|\\.)*)'/) || - part.match(/default:\s*"((?:[^"\\]|\\.)*)"/) || - part.match(/default:\s*([^}\s]+)/); - - if (defaultMatch) { - defaultVal = defaultMatch[1].replace(/\\\\'/g, "\\'").replace(/\\'/g, "'"); - return true; + try { + // The content before any filters is the variable name + const [variableName, ...filterParts] = variable.split('|'); + const parsedName = variableName.trim(); + + // Extract default value and transformers from the filters + let parsedDefaultValue = ''; + const parsedTransformers: TransformerWithParam[] = []; + + if (filterParts.length > 0) { + const filterTokenizer = new Tokenizer('|' + filterParts.join('|')); + const filters = filterTokenizer.readFilters(); + + filters.forEach((filter) => { + if (filter.kind === TokenKind.Filter && filter.name === 'default' && filter.args.length > 0) { + const arg = filter.args[0]; + + parsedDefaultValue = (arg as any).content; + } else if (TRANSFORMERS.some((t) => t.value === filter.name)) { + parsedTransformers.push({ + value: filter.name, + ...(filter.args.length > 0 + ? { + params: filter.args.map((arg) => { + return (arg as any).content; + }), + } + : {}), + }); + } + }); } - return false; - }; - // First check if default is in the nameWithDefault part - if (nameWithDefault?.includes('default:')) { - name = nameWithDefault.replace(/\s*\|\s*default:[^}]+/, '').trim(); - extractDefault(nameWithDefault); + return { + parsedName, + parsedDefaultValue, + parsedTransformers, + originalVariable: variable, + }; + } catch (error) { + console.error('Error parsing variable:', error); + return { parsedName: '', parsedDefaultValue: '', parsedTransformers: [], originalVariable: variable }; } - - // Also check rest parts for default value and filter out default parts - const transformerParts = restParts.filter((part) => { - if (part.startsWith('default:')) { - extractDefault(part); - return false; - } - return true; - }); - - // Get all transformers with their parameters - const transforms = - transformerParts.reduce((acc, part) => { - const [transformerValue, ...paramValues] = part.split(':').map((p) => p.trim()); - if (TRANSFORMERS.some((t) => t.value === transformerValue)) { - // Parse parameters, handling quoted values and commas - const params = - paramValues.length > 0 - ? paramValues - .join(':') - .split(',') - .map((param) => param.trim().replace(/^['"]|["']$/g, '')) - : undefined; - - acc.push({ value: transformerValue, ...(params ? { params } : {}) }); - } - return acc; - }, []) || []; - - return { - parsedName: name, - parsedDefaultValue: defaultVal, - parsedTransformers: transforms, - }; }, [variable]); } diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx index a74583fd10c..50e711cb02e 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx @@ -15,7 +15,7 @@ import { formatLiquidVariable } from './utils'; export function VariablePopover({ variable, onClose, onUpdate }: VariablePopoverProps) { const searchInputRef = useRef(null); - const { parsedName, parsedDefaultValue, parsedTransformers } = useVariableParser(variable || ''); + const { parsedName, parsedDefaultValue, parsedTransformers, originalVariable } = useVariableParser(variable || ''); const [name, setName] = useState(parsedName); const [defaultVal, setDefaultVal] = useState(parsedDefaultValue); const [showRawLiquid, setShowRawLiquid] = useState(false); @@ -134,7 +134,7 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover
handleRawLiquidChange(e.target.value)} className="h-7 text-sm" /> diff --git a/apps/dashboard/src/utils/liquid-autocomplete.ts b/apps/dashboard/src/utils/liquid-autocomplete.ts index 2da881454e3..6c82dac73f1 100644 --- a/apps/dashboard/src/utils/liquid-autocomplete.ts +++ b/apps/dashboard/src/utils/liquid-autocomplete.ts @@ -204,3 +204,18 @@ function getMatchingVariables(searchText: string, variables: LiquidVariable[]): // Default case: show any variables containing the search text return variables.filter((v) => v.label.toLowerCase().includes(searchLower)); } + +export function createAutocompleteSource(variables: LiquidVariable[]) { + return (context: CompletionContext) => { + // Match text that starts with {{ and capture everything after it until the cursor position + const word = context.matchBefore(/\{\{([^}]*)/); + if (!word) return null; + + const options = completions(variables)(context); + if (!options) return null; + + return { + ...options, + }; + }; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5943e1b2fd4..f12fff95d84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -137,10 +137,10 @@ importers: version: 4.13.0(typescript@5.6.2) eslint-config-airbnb-base: specifier: ^15.0.0 - version: 15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.1))(eslint@8.57.1) + version: 15.0.0(eslint-plugin-import@2.29.1)(eslint@8.57.1) eslint-config-airbnb-typescript: specifier: ^18.0.0 - version: 18.0.0(@typescript-eslint/eslint-plugin@8.18.2(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.1))(eslint@8.57.1) + version: 18.0.0(@typescript-eslint/eslint-plugin@8.18.2(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.29.1)(eslint@8.57.1) eslint-config-auto: specifier: ^0.9.0 version: 0.9.0(typescript@5.6.2) @@ -242,7 +242,7 @@ importers: version: 12.1.1(eslint@8.57.1) eslint-plugin-sonarjs: specifier: ^2.0.1 - version: 2.0.1(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@8.57.1) + version: 2.0.1(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.1) eslint-plugin-spellcheck: specifier: 0.0.20 version: 0.0.20(eslint@8.57.1) @@ -344,7 +344,7 @@ importers: version: 7.1.0 ts-jest: specifier: 27.1.5 - version: 27.1.5(@babel/core@7.24.3)(@types/jest@29.5.13)(babel-jest@27.5.1(@babel/core@7.24.3))(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) + version: 27.1.5(@babel/core@7.25.2)(@types/jest@29.5.13)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) ts-node: specifier: ~10.9.1 version: 10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2) @@ -392,13 +392,13 @@ importers: version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) '@nestjs/swagger': specifier: 7.4.0 - version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) + version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@nestjs/terminus': specifier: 10.2.3 - version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/throttler': specifier: 6.2.1 - version: 6.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2) + version: 6.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(reflect-metadata@0.2.2) '@novu/api': specifier: 0.0.1-alpha.149 version: 0.0.1-alpha.149(zod@3.23.8) @@ -434,7 +434,7 @@ importers: version: 7.114.0 '@sentry/nestjs': specifier: ^8.33.1 - version: 8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)) + version: 8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) '@sentry/node': specifier: ^8.33.1 version: 8.33.1 @@ -521,7 +521,7 @@ importers: version: 3.3.6 nest-raven: specifier: 10.1.0 - version: 10.1.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@sentry/node@8.33.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.1.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@sentry/node@8.33.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(rxjs@7.8.1) newrelic: specifier: ^12.8.1 version: 12.8.2 @@ -610,7 +610,7 @@ importers: version: 10.1.4(chokidar@3.6.0)(typescript@5.6.2) '@nestjs/testing': specifier: 10.4.1 - version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)) + version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@nestjs/platform-express@10.4.1) '@stoplight/spectral-cli': specifier: ^6.11.0 version: 6.11.0(encoding@0.1.13) @@ -842,6 +842,9 @@ importers: launchdarkly-react-client-sdk: specifier: ^3.3.2 version: 3.3.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + liquidjs: + specifier: ^10.20.0 + version: 10.20.0 lodash.debounce: specifier: ^4.0.8 version: 4.0.8 @@ -1022,7 +1025,7 @@ importers: version: 7.114.0 '@sentry/nestjs': specifier: ^8.33.1 - version: 8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)) + version: 8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) '@sentry/node': specifier: ^8.33.1 version: 8.33.1 @@ -1137,13 +1140,13 @@ importers: dependencies: '@babel/plugin-proposal-optional-chaining': specifier: ^7.20.7 - version: 7.21.0(@babel/core@7.22.11) + version: 7.21.0(@babel/core@7.21.4) '@babel/plugin-transform-react-display-name': specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.22.11) + version: 7.18.6(@babel/core@7.21.4) '@babel/plugin-transform-runtime': specifier: ^7.23.2 - version: 7.23.2(@babel/core@7.22.11) + version: 7.23.2(@babel/core@7.21.4) '@clerk/clerk-react': specifier: ^5.15.1 version: 5.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1329,7 +1332,7 @@ importers: version: 11.9.0 html-webpack-plugin: specifier: 5.5.3 - version: 5.5.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + version: 5.5.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) js-cookie: specifier: ^3.0.5 version: 3.0.5 @@ -1365,7 +1368,7 @@ importers: version: 4.3.2 mdx-bundler: specifier: 10.0.2 - version: 10.0.2(esbuild@0.18.20) + version: 10.0.2(esbuild@0.23.1) mixpanel-browser: specifier: ^2.52.0 version: 2.53.0 @@ -1468,13 +1471,13 @@ importers: version: 7.12.1 '@babel/preset-env': specifier: ^7.23.2 - version: 7.23.2(@babel/core@7.22.11) + version: 7.23.2(@babel/core@7.21.4) '@babel/preset-react': specifier: ^7.13.13 - version: 7.18.6(@babel/core@7.22.11) + version: 7.18.6(@babel/core@7.21.4) '@babel/preset-typescript': specifier: ^7.13.0 - version: 7.21.4(@babel/core@7.22.11) + version: 7.21.4(@babel/core@7.21.4) '@babel/runtime': specifier: ^7.20.13 version: 7.21.0 @@ -1519,13 +1522,13 @@ importers: version: 7.4.2 '@storybook/preset-create-react-app': specifier: ^7.4.2 - version: 7.4.2(ucmnrhmq4kewpo24xrp57f5r6y) + version: 7.4.2(f7avyblvzm233o6g7idqcb345u) '@storybook/react': specifier: ^7.4.2 version: 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) '@storybook/react-webpack5': specifier: ^7.4.2 - version: 7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1) + version: 7.4.2(@babel/core@7.21.4)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(encoding@0.1.13)(esbuild@0.23.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)))(webpack-hot-middleware@2.26.1) '@testing-library/jest-dom': specifier: ^4.2.4 version: 4.2.4 @@ -1552,16 +1555,16 @@ importers: version: 0.13.0 less-loader: specifier: 4.1.0 - version: 4.1.0(less@4.1.3)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + version: 4.1.0(less@4.1.3)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) react-app-rewired: specifier: ^2.2.1 - version: 2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)) + version: 2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.21.4))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(esbuild@0.23.1)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)) react-error-overlay: specifier: 6.0.11 version: 6.0.11 react-scripts: specifier: ^5.0.1 - version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) + version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.21.4))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(esbuild@0.23.1)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) sinon: specifier: 9.2.4 version: 9.2.4 @@ -1573,13 +1576,13 @@ importers: version: 5.6.2 webpack: specifier: 5.78.0 - version: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + version: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) webpack-bundle-analyzer: specifier: ^4.9.0 version: 4.9.0 webpack-dev-server: specifier: 4.11.1 - version: 4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + version: 4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) apps/webhook: dependencies: @@ -1597,7 +1600,7 @@ importers: version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) '@nestjs/terminus': specifier: 10.2.3 - version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) '@novu/application-generic': specifier: workspace:* version: link:../../libs/application-generic @@ -1621,7 +1624,7 @@ importers: version: 7.114.0 '@sentry/nestjs': specifier: ^8.33.1 - version: 8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)) + version: 8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) '@sentry/node': specifier: ^8.33.1 version: 8.33.1 @@ -1654,7 +1657,7 @@ importers: version: 4.17.21 nest-raven: specifier: 10.1.0 - version: 10.1.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@sentry/node@8.33.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.1.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@sentry/node@8.33.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(rxjs@7.8.1) newrelic: specifier: ^12.8.1 version: 12.8.2 @@ -1676,7 +1679,7 @@ importers: version: 10.1.4(chokidar@3.6.0)(typescript@5.6.2) '@nestjs/testing': specifier: 10.4.1 - version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)) + version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@nestjs/platform-express@10.4.1) '@types/chai': specifier: ^4.3.4 version: 4.3.4 @@ -1878,34 +1881,34 @@ importers: version: 1.14.2 html-webpack-plugin: specifier: 5.5.3 - version: 5.5.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + version: 5.5.3(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) http-server: specifier: ^0.13.0 version: 0.13.0 jest: specifier: 27.5.1 - version: 27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) + version: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) less: specifier: ^4.1.0 version: 4.1.3 less-loader: specifier: 4.1.0 - version: 4.1.0(less@4.1.3)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + version: 4.1.0(less@4.1.3)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) react-app-rewired: specifier: ^2.2.1 - version: 2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12)))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)) + version: 2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)) react-scripts: specifier: ^5.0.1 - version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12)))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) + version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) typescript: specifier: 5.6.2 version: 5.6.2 webpack: specifier: 5.78.0 - version: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + version: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) webpack-dev-server: specifier: 4.11.1 - version: 4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + version: 4.11.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) apps/worker: dependencies: @@ -1923,13 +1926,13 @@ importers: version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) '@nestjs/schedule': specifier: ^4.1.1 - version: 4.1.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)) + version: 4.1.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) '@nestjs/swagger': specifier: 7.4.0 - version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) + version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@nestjs/terminus': specifier: 10.2.3 - version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) '@novu/application-generic': specifier: workspace:* version: link:../../libs/application-generic @@ -1956,7 +1959,7 @@ importers: version: 7.114.0 '@sentry/nestjs': specifier: ^8.33.1 - version: 8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)) + version: 8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) '@sentry/node': specifier: ^8.33.1 version: 8.33.1 @@ -2013,7 +2016,7 @@ importers: version: 4.17.21 nest-raven: specifier: 10.1.0 - version: 10.1.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@sentry/node@8.33.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.1.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@sentry/node@8.33.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(rxjs@7.8.1) newrelic: specifier: ^12.8.1 version: 12.8.2 @@ -2057,7 +2060,7 @@ importers: version: 10.1.4(chokidar@3.6.0)(typescript@5.6.2) '@nestjs/testing': specifier: 10.4.1 - version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)) + version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@nestjs/platform-express@10.4.1) '@types/bcrypt': specifier: ^3.0.0 version: 3.0.1 @@ -2132,13 +2135,13 @@ importers: version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(rxjs@7.8.1) '@nestjs/serve-static': specifier: 4.0.2 - version: 4.0.2(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(express@4.21.0) + version: 4.0.2(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(express@4.21.0) '@nestjs/swagger': specifier: 7.4.0 - version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) + version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@nestjs/terminus': specifier: 10.2.3 - version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.7.7)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.7.7)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/websockets': specifier: 10.4.1 version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@nestjs/platform-socket.io@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -2162,7 +2165,7 @@ importers: version: 7.114.0 '@sentry/nestjs': specifier: ^8.33.1 - version: 8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)) + version: 8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) '@sentry/node': specifier: ^8.33.1 version: 8.33.1 @@ -2204,7 +2207,7 @@ importers: version: 4.17.21 nest-raven: specifier: 10.1.0 - version: 10.1.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@sentry/node@8.33.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.1.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@sentry/node@8.33.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(rxjs@7.8.1) newrelic: specifier: ^12.8.1 version: 12.8.2 @@ -2232,7 +2235,7 @@ importers: version: 10.1.4(chokidar@3.6.0)(typescript@5.6.2) '@nestjs/testing': specifier: 10.4.1 - version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)) + version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@nestjs/platform-express@10.4.1) '@types/chai': specifier: ^4.2.11 version: 4.3.4 @@ -2304,7 +2307,7 @@ importers: version: 10.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(passport@0.7.0) '@nestjs/swagger': specifier: 7.4.0 - version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) + version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@novu/application-generic': specifier: workspace:* version: link:../../../libs/application-generic @@ -2386,10 +2389,10 @@ importers: version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) '@nestjs/swagger': specifier: 7.4.0 - version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) + version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@nestjs/throttler': specifier: 6.2.1 - version: 6.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2) + version: 6.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(reflect-metadata@0.2.2) '@novu/application-generic': specifier: workspace:* version: link:../../../libs/application-generic @@ -2548,7 +2551,7 @@ importers: version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) '@nestjs/swagger': specifier: 7.4.0 - version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) + version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@novu/application-generic': specifier: workspace:* version: link:../../../libs/application-generic @@ -2648,13 +2651,13 @@ importers: version: 10.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(passport@0.7.0) '@nestjs/swagger': specifier: 7.4.0 - version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) + version: 7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@nestjs/terminus': specifier: 10.2.3 - version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.575.0))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.575.0))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/testing': specifier: 10.4.1 - version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)) + version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@nestjs/platform-express@10.4.1) '@novu/dal': specifier: workspace:* version: link:../dal @@ -2780,7 +2783,7 @@ importers: version: 3.3.7 nestjs-otel: specifier: 6.1.1 - version: 6.1.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)) + version: 6.1.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) nestjs-pino: specifier: 4.1.0 version: 4.1.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(pino-http@8.3.3) @@ -2915,7 +2918,7 @@ importers: version: 20.16.5 jest: specifier: ^29.4.1 - version: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + version: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) jest-environment-jsdom: specifier: ^29.4.1 version: 29.5.0 @@ -2927,7 +2930,7 @@ importers: version: 20.1.2(@swc-node/register@1.8.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(@swc/types@0.1.12)(typescript@5.6.2))(@swc/core@1.3.107(@swc/helpers@0.5.12)) ts-jest: specifier: ^29.1.0 - version: 29.1.2(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2) + version: 29.1.2(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) typescript: specifier: 5.6.2 version: 5.6.2 @@ -3082,13 +3085,13 @@ importers: version: 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) '@storybook/react-webpack5': specifier: ^7.4.2 - version: 7.4.2(@babel/core@7.23.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-hot-middleware@2.26.1) + version: 7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1) '@storybook/theming': specifier: ^7.4.2 version: 7.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@testing-library/jest-dom': specifier: ^6.4.1 - version: 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.13)(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(vitest@1.2.1(@edge-runtime/vm@4.0.2)(@types/node@20.16.5)(jsdom@24.0.0)(less@4.2.0)(lightningcss@1.26.0)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6)) + version: 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.13)(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(vitest@1.2.1(@edge-runtime/vm@4.0.2)(@types/node@20.16.5)(jsdom@24.0.0)(less@4.2.0)(lightningcss@1.26.0)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6)) '@testing-library/react': specifier: ^12.1.5 version: 12.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -3133,13 +3136,13 @@ importers: version: 7.4.2(encoding@0.1.13) ts-loader: specifier: ~9.4.0 - version: 9.4.4(typescript@5.6.2)(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + version: 9.4.4(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) typescript: specifier: 5.6.2 version: 5.6.2 url-loader: specifier: ^4.1.1 - version: 4.1.1(file-loader@6.2.0(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12))))(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + version: 4.1.1(file-loader@6.2.0(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) vite: specifier: ^4.5.2 version: 4.5.2(@types/node@20.16.5)(less@4.2.0)(lightningcss@1.26.0)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6) @@ -3370,7 +3373,7 @@ importers: version: 8.1.1 '@testing-library/jest-dom': specifier: ^6.4.1 - version: 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.13)(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(vitest@1.2.1(@edge-runtime/vm@4.0.2)(@types/node@20.16.5)(jsdom@25.0.0)(less@4.2.0)(lightningcss@1.26.0)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.38))(terser@5.31.6)) + version: 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.13)(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(vitest@1.2.1(@edge-runtime/vm@4.0.2)(@types/node@20.16.5)(jsdom@25.0.0)(less@4.2.0)(lightningcss@1.26.0)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.38))(terser@5.31.6)) '@testing-library/react': specifier: ^12.1.5 version: 12.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -3549,7 +3552,7 @@ importers: version: 3.8.3(encoding@0.1.13) jest: specifier: ^27.0.6 - version: 27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) + version: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -3558,7 +3561,7 @@ importers: version: 3.0.2 ts-jest: specifier: ^27.0.5 - version: 27.1.5(@babel/core@7.25.2)(@types/jest@29.5.2)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) + version: 27.1.5(@babel/core@7.25.2)(@types/jest@29.5.2)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2) typedoc: specifier: ^0.24.0 version: 0.24.6(typescript@5.6.2) @@ -3634,7 +3637,7 @@ importers: version: 8.0.0(typescript@5.6.2) next: specifier: ^13.5.4 - version: 13.5.6(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) + version: 13.5.6(@babel/core@7.25.2)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) prettier: specifier: ^3.2.5 version: 3.3.2 @@ -3695,7 +3698,7 @@ importers: version: 29.5.0 ts-jest: specifier: ^29.0.3 - version: 29.1.0(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.5.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2) + version: 29.1.0(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(esbuild@0.18.20)(jest@29.5.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2) typedoc: specifier: ^0.24.0 version: 0.24.6(typescript@5.6.2) @@ -3753,7 +3756,7 @@ importers: version: 5.3.0 compression-webpack-plugin: specifier: ^10.0.0 - version: 10.0.0(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4)) + version: 10.0.0(webpack@5.78.0) concurrently: specifier: ^5.3.0 version: 5.3.0 @@ -3801,7 +3804,7 @@ importers: version: 1.0.7(tailwindcss@3.4.4(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))) terser-webpack-plugin: specifier: ^5.3.9 - version: 5.3.9(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4)) + version: 5.3.9(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0) tiny-glob: specifier: ^0.2.9 version: 0.2.9 @@ -3810,7 +3813,7 @@ importers: version: 29.1.2(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(esbuild@0.23.1)(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2) ts-loader: specifier: ~9.4.0 - version: 9.4.4(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4)) + version: 9.4.4(typescript@5.6.2)(webpack@5.78.0) tsup: specifier: ^8.1.0 version: 8.1.0(@microsoft/api-extractor@7.47.7(@types/node@20.16.5))(@swc/core@1.7.26(@swc/helpers@0.5.12))(postcss@8.4.38)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))(typescript@5.6.2) @@ -3892,7 +3895,7 @@ importers: version: link:../react next: specifier: '>=13' - version: 13.5.6(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) + version: 13.5.6(@babel/core@7.25.2)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) react: specifier: '>=17' version: 18.3.1 @@ -3963,7 +3966,7 @@ importers: version: 3.8.3(encoding@0.1.13) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + version: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) nock: specifier: ^13.1.3 version: 13.3.0 @@ -3981,7 +3984,7 @@ importers: version: 0.0.0 ts-jest: specifier: ^29.1.2 - version: 29.1.2(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(esbuild@0.23.1)(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2) + version: 29.1.2(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) typedoc: specifier: ^0.24.0 version: 0.24.6(typescript@5.6.2) @@ -4072,7 +4075,7 @@ importers: version: 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) '@storybook/react-webpack5': specifier: ^7.4.2 - version: 7.4.2(@babel/core@7.25.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))(webpack-hot-middleware@2.26.1) + version: 7.4.2(@babel/core@7.25.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-cli@5.1.4)(webpack-dev-server@4.11.1)(webpack-hot-middleware@2.26.1) '@testing-library/dom': specifier: ^9.3.0 version: 9.3.0 @@ -4105,10 +4108,10 @@ importers: version: 8.8.2 babel-loader: specifier: ^8.2.4 - version: 8.3.0(@babel/core@7.25.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + version: 8.3.0(@babel/core@7.25.2)(webpack@5.78.0) compression-webpack-plugin: specifier: ^10.0.0 - version: 10.0.0(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + version: 10.0.0(webpack@5.78.0) jest: specifier: ^29.3.1 version: 29.5.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) @@ -4132,28 +4135,28 @@ importers: version: 7.4.2(encoding@0.1.13) terser-webpack-plugin: specifier: ^5.3.9 - version: 5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + version: 5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack@5.78.0) ts-jest: specifier: ^29.0.3 - version: 29.1.0(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.5.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2) + version: 29.1.0(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(esbuild@0.18.20)(jest@29.5.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2) ts-loader: specifier: ~9.4.0 - version: 9.4.4(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + version: 9.4.4(typescript@5.6.2)(webpack@5.78.0) typescript: specifier: 5.6.2 version: 5.6.2 url-loader: specifier: ^4.1.1 - version: 4.1.1(file-loader@6.2.0(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + version: 4.1.1(file-loader@6.2.0(webpack@5.78.0))(webpack@5.78.0) webpack: specifier: ^5.74.0 - version: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + version: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-bundle-analyzer: specifier: ^4.9.0 version: 4.9.0 webpack-cli: specifier: ^5.1.4 - version: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0) + version: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack-dev-server@4.11.1)(webpack@5.78.0) packages/novu: dependencies: @@ -4554,7 +4557,7 @@ importers: version: 3.8.3(encoding@0.1.13) jest: specifier: ^27.0.6 - version: 27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) + version: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -4569,7 +4572,7 @@ importers: version: 0.0.0 ts-jest: specifier: ^27.0.5 - version: 27.1.5(@babel/core@7.25.2)(@types/jest@29.5.2)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) + version: 27.1.5(@babel/core@7.25.2)(@types/jest@29.5.2)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2) typedoc: specifier: ^0.24.0 version: 0.24.6(typescript@5.6.2) @@ -4615,7 +4618,7 @@ importers: version: 10.1.4(chokidar@3.6.0)(typescript@5.6.2) '@nestjs/testing': specifier: 10.4.1 - version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)) + version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@nestjs/platform-express@10.4.1) '@swc/core': specifier: ^1.7.26 version: 1.7.26(@swc/helpers@0.5.12) @@ -4684,7 +4687,7 @@ importers: version: 0.439.0(react@18.3.1) next: specifier: 14.2.4 - version: 14.2.4(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.46.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) + version: 14.2.4(@babel/core@7.25.2)(@opentelemetry/api@1.9.0)(@playwright/test@1.46.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8) react: specifier: ^18 version: 18.3.1 @@ -26420,6 +26423,11 @@ packages: engines: {node: '>=14'} hasBin: true + liquidjs@10.20.0: + resolution: {integrity: sha512-y1tHDaHGtYXcZehRzy8UST0t0E9iPOdT4hAk5ZuSquaDRW4j1lGUePaVyX6AP07cZarm3tL8FEtHx19UXgi9pQ==} + engines: {node: '>=14'} + hasBin: true + list-stylesheets@2.0.1: resolution: {integrity: sha512-UUEFowqvgRKT1+OJ59Ga5gTfVOP3hkbFo7DwNIZcMuXzJRWndYMHyDYbuqKe6lrw8KCY7c/GN5mEoLx0c54HAw==} @@ -38803,17 +38811,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.22.11)': - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-compilation-targets': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - debug: 4.3.6(supports-color@8.1.1) - lodash.debounce: 4.0.8 - resolve: 1.22.8 - transitivePeerDependencies: - - supports-color - '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.3)': dependencies: '@babel/core': 7.24.3 @@ -38948,6 +38945,16 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.25.2(@babel/core@7.21.4)': + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.25.6 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.25.2(@babel/core@7.22.11)': dependencies: '@babel/core': 7.22.11 @@ -39406,13 +39413,6 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.22.11)': - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.11) - '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -39484,12 +39484,6 @@ snapshots: '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.3)': - dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.8 - optional: true - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 @@ -39605,14 +39599,14 @@ snapshots: '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11)': + '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.23.2)': + '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11)': dependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.22.11 '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.24.3)': @@ -39735,11 +39729,6 @@ snapshots: '@babel/core': 7.22.11 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.2)': - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 @@ -39750,9 +39739,9 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.22.11)': + '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.3)': @@ -39975,11 +39964,6 @@ snapshots: '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.22.11)': - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.24.4)': dependencies: '@babel/core': 7.24.4 @@ -40554,17 +40538,17 @@ snapshots: '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-flow': 7.22.5(@babel/core@7.21.4) - '@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.22.11)': + '@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.22.11) + '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.21.4) - '@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.23.2)': + '@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.22.11)': dependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.22.11 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.23.2) + '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.22.11) '@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.24.3)': dependencies: @@ -40798,10 +40782,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.22.15(@babel/core@7.22.11)': + '@babel/plugin-transform-modules-commonjs@7.22.15(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 - '@babel/helper-module-transforms': 7.25.2(@babel/core@7.22.11) + '@babel/core': 7.21.4 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-simple-access': 7.22.5 transitivePeerDependencies: @@ -41377,11 +41361,6 @@ snapshots: '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-transform-react-display-name@7.18.6(@babel/core@7.22.11)': - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.21.4)': dependencies: '@babel/core': 7.21.4 @@ -41392,11 +41371,6 @@ snapshots: '@babel/core': 7.22.11 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.23.2)': - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -41414,10 +41388,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-development@7.18.6(@babel/core@7.22.11)': + '@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 - '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.22.11) + '@babel/core': 7.21.4 + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.21.4) transitivePeerDependencies: - supports-color @@ -41428,13 +41402,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.23.2)': - dependencies: - '@babel/core': 7.23.2 - '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.2) - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -41490,14 +41457,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.21.0(@babel/core@7.22.11)': + '@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 - '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/core': 7.21.4 + '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-module-imports': 7.24.7 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.11) - '@babel/types': 7.22.19 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.21.4) + '@babel/types': 7.25.6 transitivePeerDependencies: - supports-color @@ -41512,17 +41479,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.23.2)': - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) - '@babel/types': 7.25.6 - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -41545,13 +41501,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11)': + '@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 + '@babel/core': 7.21.4 '@babel/helper-annotate-as-pure': 7.24.7 '@babel/helper-module-imports': 7.24.7 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.22.11) + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.21.4) '@babel/types': 7.25.6 transitivePeerDependencies: - supports-color @@ -41584,10 +41540,10 @@ snapshots: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-react-pure-annotations@7.18.6(@babel/core@7.22.11)': + '@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 - '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/core': 7.21.4 + '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.22.11)': @@ -41596,12 +41552,6 @@ snapshots: '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.23.2)': - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-react-pure-annotations@7.22.5(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -41681,18 +41631,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-runtime@7.23.2(@babel/core@7.22.11)': - dependencies: - '@babel/core': 7.22.11 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.8 - babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.22.11) - babel-plugin-polyfill-corejs3: 0.8.5(@babel/core@7.22.11) - babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.22.11) - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-runtime@7.23.2(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -41839,13 +41777,13 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-typescript@7.21.3(@babel/core@7.22.11)': + '@babel/plugin-transform-typescript@7.21.3(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 + '@babel/core': 7.21.4 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.22.11) + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.22.11) + '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.21.4) transitivePeerDependencies: - supports-color @@ -42442,19 +42380,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/preset-flow@7.22.15(@babel/core@7.22.11)': + '@babel/preset-flow@7.22.15(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-validator-option': 7.24.8 - '@babel/plugin-transform-flow-strip-types': 7.25.2(@babel/core@7.22.11) + '@babel/plugin-transform-flow-strip-types': 7.25.2(@babel/core@7.21.4) - '@babel/preset-flow@7.22.15(@babel/core@7.23.2)': + '@babel/preset-flow@7.22.15(@babel/core@7.22.11)': dependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.22.11 '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-validator-option': 7.24.8 - '@babel/plugin-transform-flow-strip-types': 7.25.2(@babel/core@7.23.2) + '@babel/plugin-transform-flow-strip-types': 7.25.2(@babel/core@7.22.11) '@babel/preset-flow@7.22.15(@babel/core@7.25.2)': dependencies: @@ -42517,15 +42455,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/preset-react@7.18.6(@babel/core@7.22.11)': + '@babel/preset-react@7.22.15(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.22.11) - '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.22.11) - '@babel/plugin-transform-react-jsx-development': 7.18.6(@babel/core@7.22.11) - '@babel/plugin-transform-react-pure-annotations': 7.18.6(@babel/core@7.22.11) + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-validator-option': 7.22.15 + '@babel/plugin-transform-react-display-name': 7.22.5(@babel/core@7.21.4) + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.21.4) + '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.21.4) + '@babel/plugin-transform-react-pure-annotations': 7.22.5(@babel/core@7.21.4) transitivePeerDependencies: - supports-color @@ -42541,18 +42479,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/preset-react@7.22.15(@babel/core@7.23.2)': - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-transform-react-display-name': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.2) - '@babel/plugin-transform-react-jsx-development': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-react-pure-annotations': 7.22.5(@babel/core@7.23.2) - transitivePeerDependencies: - - supports-color - '@babel/preset-react@7.22.15(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 @@ -42577,14 +42503,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.21.4(@babel/core@7.22.11)': + '@babel/preset-typescript@7.21.4(@babel/core@7.21.4)': dependencies: - '@babel/core': 7.22.11 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.11) - '@babel/plugin-transform-modules-commonjs': 7.22.15(@babel/core@7.22.11) - '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.22.11) + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.21.4) + '@babel/plugin-transform-modules-commonjs': 7.22.15(@babel/core@7.21.4) + '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.21.4) transitivePeerDependencies: - supports-color @@ -44105,11 +44031,11 @@ snapshots: to-pascal-case: 1.0.0 unescape-js: 1.1.4 - '@esbuild-plugins/node-resolve@0.2.2(esbuild@0.18.20)': + '@esbuild-plugins/node-resolve@0.2.2(esbuild@0.23.1)': dependencies: '@types/resolve': 1.20.2 debug: 4.3.6(supports-color@8.1.1) - esbuild: 0.18.20 + esbuild: 0.23.1 escape-string-regexp: 4.0.0 resolve: 1.22.8 transitivePeerDependencies: @@ -45124,7 +45050,7 @@ snapshots: - ts-node - utf-8-validate - '@jest/core@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2))': + '@jest/core@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))': dependencies: '@jest/console': 27.5.1 '@jest/reporters': 27.5.1 @@ -45138,7 +45064,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 27.5.1 - jest-config: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)) + jest-config: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) jest-haste-map: 27.5.1 jest-message-util: 27.5.1 jest-regex-util: 27.5.1 @@ -45161,42 +45087,44 @@ snapshots: - ts-node - utf-8-validate - '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))': + '@jest/core@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2))': dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 + '@jest/console': 27.5.1 + '@jest/reporters': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 '@types/node': 20.16.5 ansi-escapes: 4.3.2 chalk: 4.1.2 - ci-info: 3.9.0 + emittery: 0.8.1 exit: 0.1.2 graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 + jest-changed-files: 27.5.1 + jest-config: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)) + jest-haste-map: 27.5.1 + jest-message-util: 27.5.1 + jest-regex-util: 27.5.1 + jest-resolve: 27.5.1 + jest-resolve-dependencies: 27.5.1 + jest-runner: 27.5.1 + jest-runtime: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + jest-validate: 27.5.1 + jest-watcher: 27.5.1 micromatch: 4.0.8 - pretty-format: 29.7.0 + rimraf: 3.0.2 slash: 3.0.0 strip-ansi: 6.0.1 transitivePeerDependencies: - - babel-plugin-macros + - bufferutil + - canvas - supports-color - ts-node + - utf-8-validate - '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))': + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -45210,7 +45138,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -45231,7 +45159,7 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))': + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -45245,7 +45173,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -46517,11 +46445,11 @@ snapshots: - encoding - supports-color - '@mdx-js/esbuild@3.0.1(esbuild@0.18.20)': + '@mdx-js/esbuild@3.0.1(esbuild@0.23.1)': dependencies: '@mdx-js/mdx': 3.0.1 '@types/unist': 3.0.2 - esbuild: 0.18.20 + esbuild: 0.23.1 vfile: 6.0.1 vfile-message: 4.0.2 transitivePeerDependencies: @@ -46845,7 +46773,7 @@ snapshots: transitivePeerDependencies: - encoding - '@nestjs/graphql@12.0.9(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)': + '@nestjs/graphql@12.0.9(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)': dependencies: '@graphql-tools/merge': 9.0.0(graphql@16.9.0) '@graphql-tools/schema': 10.0.0(graphql@16.9.0) @@ -46925,7 +46853,7 @@ snapshots: - supports-color - utf-8-validate - '@nestjs/schedule@4.1.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))': + '@nestjs/schedule@4.1.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)': dependencies: '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -46954,7 +46882,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/serve-static@4.0.2(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(express@4.21.0)': + '@nestjs/serve-static@4.0.2(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(express@4.21.0)': dependencies: '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -46962,7 +46890,7 @@ snapshots: optionalDependencies: express: 4.21.0 - '@nestjs/swagger@7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)': + '@nestjs/swagger@7.4.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)': dependencies: '@microsoft/tsdoc': 0.15.0 '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -46977,7 +46905,7 @@ snapshots: class-transformer: 0.5.1 class-validator: 0.14.1 - '@nestjs/terminus@10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.575.0))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)': + '@nestjs/terminus@10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.575.0))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)': dependencies: '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -46991,7 +46919,7 @@ snapshots: '@nestjs/axios': 3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1) mongoose: 8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.575.0))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1) - '@nestjs/terminus@10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)': + '@nestjs/terminus@10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)': dependencies: '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -47005,7 +46933,7 @@ snapshots: '@nestjs/axios': 3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1) mongoose: 8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1) - '@nestjs/terminus@10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.7.7)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)': + '@nestjs/terminus@10.2.3(@grpc/grpc-js@1.12.4)(@grpc/proto-loader@0.7.13)(@nestjs/axios@3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.7.7)(rxjs@7.8.1))(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(mongoose@8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)': dependencies: '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -47019,7 +46947,7 @@ snapshots: '@nestjs/axios': 3.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.7.7)(rxjs@7.8.1) mongoose: 8.6.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.7.1) - '@nestjs/testing@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1))': + '@nestjs/testing@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@nestjs/platform-express@10.4.1)': dependencies: '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -47027,7 +46955,7 @@ snapshots: optionalDependencies: '@nestjs/platform-express': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1) - '@nestjs/throttler@6.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)': + '@nestjs/throttler@6.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(reflect-metadata@0.2.2)': dependencies: '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -49617,7 +49545,7 @@ snapshots: webpack-dev-server: 4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) webpack-hot-middleware: 2.26.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12)))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))': dependencies: ansi-html-community: 0.0.8 common-path-prefix: 3.0.0 @@ -49629,14 +49557,14 @@ snapshots: react-refresh: 0.11.0 schema-utils: 3.3.0 source-map: 0.7.4 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) optionalDependencies: - '@types/webpack': 5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12)) + '@types/webpack': 5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) type-fest: 2.19.0 - webpack-dev-server: 4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + webpack-dev-server: 4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) webpack-hot-middleware: 2.26.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.10(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0)))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.10(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1)(webpack-hot-middleware@2.26.1)(webpack@5.78.0)': dependencies: ansi-html-community: 0.0.8 common-path-prefix: 3.0.0 @@ -49648,13 +49576,14 @@ snapshots: react-refresh: 0.11.0 schema-utils: 3.3.0 source-map: 0.7.4 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) optionalDependencies: - '@types/webpack': 5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0)) + '@types/webpack': 5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) type-fest: 2.19.0 + webpack-dev-server: 4.11.1(webpack-cli@5.1.4)(webpack@5.78.0) webpack-hot-middleware: 2.26.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.10(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.10(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)))': dependencies: ansi-html-community: 0.0.8 common-path-prefix: 3.0.0 @@ -49670,6 +49599,7 @@ snapshots: optionalDependencies: '@types/webpack': 5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)) type-fest: 2.19.0 + webpack-dev-server: 4.11.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) webpack-hot-middleware: 2.26.1 '@pnpm/cli-meta@5.0.0': @@ -52115,7 +52045,7 @@ snapshots: '@sentry/types': 7.114.0 '@sentry/utils': 7.114.0 - '@sentry/nestjs@8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))': + '@sentry/nestjs@8.33.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)': dependencies: '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -54931,7 +54861,7 @@ snapshots: - uglify-js - webpack-cli - '@storybook/builder-webpack5@7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': + '@storybook/builder-webpack5@7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(webpack-cli@5.1.4)': dependencies: '@babel/core': 7.23.2 '@storybook/addons': 7.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -54953,30 +54883,30 @@ snapshots: '@swc/core': 1.3.107(@swc/helpers@0.5.12) '@types/node': 16.11.7 '@types/semver': 7.3.13 - babel-loader: 9.1.2(@babel/core@7.23.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + babel-loader: 9.1.2(@babel/core@7.23.2)(webpack@5.78.0) babel-plugin-named-exports-order: 0.0.2 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 constants-browserify: 1.0.0 - css-loader: 6.7.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + css-loader: 6.7.3(webpack@5.78.0) express: 4.21.0 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.6.2)(webpack@5.78.0) fs-extra: 11.2.0 - html-webpack-plugin: 5.5.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + html-webpack-plugin: 5.5.3(webpack@5.78.0) path-browserify: 1.0.1 process: 0.11.10 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) semver: 7.6.3 - style-loader: 3.3.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) - swc-loader: 0.2.3(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) - terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + style-loader: 3.3.2(webpack@5.78.0) + swc-loader: 0.2.3(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0) + terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack@5.78.0) ts-dedent: 2.2.0 url: 0.11.4 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) - webpack-dev-middleware: 6.1.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-dev-middleware: 6.1.1(webpack@5.78.0) webpack-hot-middleware: 2.25.3 webpack-virtual-modules: 0.5.0 optionalDependencies: @@ -54991,7 +54921,7 @@ snapshots: - uglify-js - webpack-cli - '@storybook/builder-webpack5@7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))': + '@storybook/builder-webpack5@7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(esbuild@0.23.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)': dependencies: '@babel/core': 7.23.2 '@storybook/addons': 7.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -55013,30 +54943,30 @@ snapshots: '@swc/core': 1.3.107(@swc/helpers@0.5.12) '@types/node': 16.11.7 '@types/semver': 7.3.13 - babel-loader: 9.1.2(@babel/core@7.23.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + babel-loader: 9.1.2(@babel/core@7.23.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) babel-plugin-named-exports-order: 0.0.2 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 constants-browserify: 1.0.0 - css-loader: 6.7.3(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + css-loader: 6.7.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) express: 4.21.0 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) fs-extra: 11.2.0 - html-webpack-plugin: 5.5.3(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + html-webpack-plugin: 5.5.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) path-browserify: 1.0.1 process: 0.11.10 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) semver: 7.6.3 - style-loader: 3.3.2(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) - swc-loader: 0.2.3(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) - terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + style-loader: 3.3.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) + swc-loader: 0.2.3(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) + terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) ts-dedent: 2.2.0 url: 0.11.4 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0)) - webpack-dev-middleware: 6.1.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) + webpack-dev-middleware: 6.1.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) webpack-hot-middleware: 2.25.3 webpack-virtual-modules: 0.5.0 optionalDependencies: @@ -55687,16 +55617,16 @@ snapshots: '@storybook/postinstall@7.4.2': {} - '@storybook/preset-create-react-app@7.4.2(ucmnrhmq4kewpo24xrp57f5r6y)': + '@storybook/preset-create-react-app@7.4.2(f7avyblvzm233o6g7idqcb345u)': dependencies: - '@babel/core': 7.22.11 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + '@babel/core': 7.21.4 + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) '@storybook/types': 7.4.2 '@types/babel__core': 7.20.0 babel-plugin-react-docgen: 4.2.1 pnp-webpack-plugin: 1.7.0(typescript@5.6.2) - react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) + react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.21.4))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(esbuild@0.23.1)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) semver: 7.5.4 transitivePeerDependencies: - '@types/webpack' @@ -55710,16 +55640,16 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/preset-react-webpack@7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)': + '@storybook/preset-react-webpack@7.4.2(@babel/core@7.21.4)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(encoding@0.1.13)(esbuild@0.23.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)))(webpack-hot-middleware@2.26.1)': dependencies: - '@babel/preset-flow': 7.22.15(@babel/core@7.22.11) - '@babel/preset-react': 7.22.15(@babel/core@7.22.11) - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + '@babel/preset-flow': 7.22.15(@babel/core@7.21.4) + '@babel/preset-react': 7.22.15(@babel/core@7.21.4) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) '@storybook/core-webpack': 7.4.2(encoding@0.1.13) '@storybook/docs-tools': 7.4.2(encoding@0.1.13) '@storybook/node-logger': 7.4.2 '@storybook/react': 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) '@types/node': 16.11.7 '@types/semver': 7.5.8 babel-plugin-add-react-displayname: 0.0.5 @@ -55729,9 +55659,9 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-refresh: 0.11.0 semver: 7.6.3 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) optionalDependencies: - '@babel/core': 7.22.11 + '@babel/core': 7.21.4 typescript: 5.6.2 transitivePeerDependencies: - '@swc/core' @@ -55747,16 +55677,16 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/preset-react-webpack@7.4.2(@babel/core@7.23.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-hot-middleware@2.26.1)': + '@storybook/preset-react-webpack@7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)': dependencies: - '@babel/preset-flow': 7.22.15(@babel/core@7.23.2) - '@babel/preset-react': 7.22.15(@babel/core@7.23.2) - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + '@babel/preset-flow': 7.22.15(@babel/core@7.22.11) + '@babel/preset-react': 7.22.15(@babel/core@7.22.11) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) '@storybook/core-webpack': 7.4.2(encoding@0.1.13) '@storybook/docs-tools': 7.4.2(encoding@0.1.13) '@storybook/node-logger': 7.4.2 '@storybook/react': 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) '@types/node': 16.11.7 '@types/semver': 7.5.8 babel-plugin-add-react-displayname: 0.0.5 @@ -55766,9 +55696,9 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-refresh: 0.11.0 semver: 7.6.3 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) optionalDependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.22.11 typescript: 5.6.2 transitivePeerDependencies: - '@swc/core' @@ -55784,16 +55714,16 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/preset-react-webpack@7.4.2(@babel/core@7.25.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))(webpack-hot-middleware@2.26.1)': + '@storybook/preset-react-webpack@7.4.2(@babel/core@7.25.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-cli@5.1.4)(webpack-dev-server@4.11.1)(webpack-hot-middleware@2.26.1)': dependencies: '@babel/preset-flow': 7.22.15(@babel/core@7.25.2) '@babel/preset-react': 7.22.15(@babel/core@7.25.2) - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0)))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1)(webpack-hot-middleware@2.26.1)(webpack@5.78.0) '@storybook/core-webpack': 7.4.2(encoding@0.1.13) '@storybook/docs-tools': 7.4.2(encoding@0.1.13) '@storybook/node-logger': 7.4.2 '@storybook/react': 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0) '@types/node': 16.11.7 '@types/semver': 7.5.8 babel-plugin-add-react-displayname: 0.0.5 @@ -55803,7 +55733,7 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-refresh: 0.11.0 semver: 7.6.3 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) optionalDependencies: '@babel/core': 7.25.2 typescript: 5.6.2 @@ -55907,7 +55837,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4))': + '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))': dependencies: debug: 4.3.6(supports-color@8.1.1) endent: 2.1.0 @@ -55917,11 +55847,11 @@ snapshots: react-docgen-typescript: 2.2.2(typescript@5.6.2) tslib: 2.7.0 typescript: 5.6.2 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) transitivePeerDependencies: - supports-color - '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)))': + '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.6.2)(webpack@5.78.0)': dependencies: debug: 4.3.6(supports-color@8.1.1) endent: 2.1.0 @@ -55931,7 +55861,7 @@ snapshots: react-docgen-typescript: 2.2.2(typescript@5.6.2) tslib: 2.7.0 typescript: 5.6.2 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - supports-color @@ -55971,16 +55901,16 @@ snapshots: - vite-plugin-glimmerx - webpack-sources - '@storybook/react-webpack5@7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)': + '@storybook/react-webpack5@7.4.2(@babel/core@7.21.4)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(encoding@0.1.13)(esbuild@0.23.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)))(webpack-hot-middleware@2.26.1)': dependencies: - '@storybook/builder-webpack5': 7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@storybook/preset-react-webpack': 7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1) + '@storybook/builder-webpack5': 7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(esbuild@0.23.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@storybook/preset-react-webpack': 7.4.2(@babel/core@7.21.4)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(encoding@0.1.13)(esbuild@0.23.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)))(webpack-hot-middleware@2.26.1) '@storybook/react': 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) '@types/node': 16.11.7 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@babel/core': 7.22.11 + '@babel/core': 7.21.4 typescript: 5.6.2 transitivePeerDependencies: - '@swc/core' @@ -55999,16 +55929,16 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/react-webpack5@7.4.2(@babel/core@7.23.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-hot-middleware@2.26.1)': + '@storybook/react-webpack5@7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)': dependencies: - '@storybook/builder-webpack5': 7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) - '@storybook/preset-react-webpack': 7.4.2(@babel/core@7.23.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-hot-middleware@2.26.1) + '@storybook/builder-webpack5': 7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) + '@storybook/preset-react-webpack': 7.4.2(@babel/core@7.22.11)(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1) '@storybook/react': 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) '@types/node': 16.11.7 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.22.11 typescript: 5.6.2 transitivePeerDependencies: - '@swc/core' @@ -56027,10 +55957,10 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/react-webpack5@7.4.2(@babel/core@7.25.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))(webpack-hot-middleware@2.26.1)': + '@storybook/react-webpack5@7.4.2(@babel/core@7.25.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-cli@5.1.4)(webpack-dev-server@4.11.1)(webpack-hot-middleware@2.26.1)': dependencies: - '@storybook/builder-webpack5': 7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0)) - '@storybook/preset-react-webpack': 7.4.2(@babel/core@7.25.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0)))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))(webpack-hot-middleware@2.26.1) + '@storybook/builder-webpack5': 7.4.2(@swc/helpers@0.5.12)(@types/react-dom@18.3.0)(@types/react@18.3.3)(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2)(webpack-cli@5.1.4) + '@storybook/preset-react-webpack': 7.4.2(@babel/core@7.25.2)(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4))(encoding@0.1.13)(esbuild@0.18.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(type-fest@2.19.0)(typescript@5.6.2)(webpack-cli@5.1.4)(webpack-dev-server@4.11.1)(webpack-hot-middleware@2.26.1) '@storybook/react': 7.4.2(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.2) '@types/node': 16.11.7 react: 18.3.1 @@ -56675,7 +56605,7 @@ snapshots: pretty-format: 24.9.0 redent: 3.0.0 - '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.13)(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(vitest@1.2.1(@edge-runtime/vm@4.0.2)(@types/node@20.16.5)(jsdom@24.0.0)(less@4.2.0)(lightningcss@1.26.0)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6))': + '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.13)(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(vitest@1.2.1(@edge-runtime/vm@4.0.2)(@types/node@20.16.5)(jsdom@24.0.0)(less@4.2.0)(lightningcss@1.26.0)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6))': dependencies: '@adobe/css-tools': 4.3.3 '@babel/runtime': 7.23.2 @@ -56688,10 +56618,10 @@ snapshots: optionalDependencies: '@jest/globals': 29.7.0 '@types/jest': 29.5.13 - jest: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) vitest: 1.2.1(@edge-runtime/vm@4.0.2)(@types/node@20.16.5)(jsdom@24.0.0)(less@4.2.0)(lightningcss@1.26.0)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6) - '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.13)(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(vitest@1.2.1(@edge-runtime/vm@4.0.2)(@types/node@20.16.5)(jsdom@25.0.0)(less@4.2.0)(lightningcss@1.26.0)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.38))(terser@5.31.6))': + '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.13)(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(vitest@1.2.1(@edge-runtime/vm@4.0.2)(@types/node@20.16.5)(jsdom@25.0.0)(less@4.2.0)(lightningcss@1.26.0)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.38))(terser@5.31.6))': dependencies: '@adobe/css-tools': 4.3.3 '@babel/runtime': 7.23.2 @@ -56704,7 +56634,7 @@ snapshots: optionalDependencies: '@jest/globals': 29.7.0 '@types/jest': 29.5.13 - jest: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) vitest: 1.2.1(@edge-runtime/vm@4.0.2)(@types/node@20.16.5)(jsdom@25.0.0)(less@4.2.0)(lightningcss@1.26.0)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.38))(terser@5.31.6) '@testing-library/react-hooks@8.0.1(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -58064,11 +57994,11 @@ snapshots: '@types/webidl-conversions@7.0.3': optional: true - '@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))': + '@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)': dependencies: '@types/node': 20.16.5 tapable: 2.2.1 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) transitivePeerDependencies: - '@swc/core' - esbuild @@ -58076,11 +58006,11 @@ snapshots: - webpack-cli optional: true - '@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)': + '@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)': dependencies: '@types/node': 20.16.5 tapable: 2.2.1 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) transitivePeerDependencies: - '@swc/core' - esbuild @@ -58100,11 +58030,11 @@ snapshots: - webpack-cli optional: true - '@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))': + '@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4)': dependencies: '@types/node': 20.16.5 tapable: 2.2.1 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) transitivePeerDependencies: - '@swc/core' - esbuild @@ -59739,36 +59669,28 @@ snapshots: '@webcontainer/api@1.2.0': {} - '@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4))': + '@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.78.0)': dependencies: webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0) - '@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4))': - dependencies: - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0) - - '@webpack-cli/info@2.0.2(webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4))': + '@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.78.0)': dependencies: webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0) - '@webpack-cli/info@2.0.2(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4))': + '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack-dev-server@4.11.1)(webpack@5.78.0)': dependencies: - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack-dev-server@4.11.1)(webpack@5.78.0) + optionalDependencies: + webpack-dev-server: 4.11.1(webpack-cli@5.1.4)(webpack@5.78.0) - '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4))': + '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack@5.78.0)': dependencies: webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0) - '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4))': - dependencies: - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0) - '@wry/context@0.4.4': dependencies: '@types/node': 20.16.5 @@ -60862,21 +60784,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-jest@27.5.1(@babel/core@7.24.3): - dependencies: - '@babel/core': 7.24.3 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - '@types/babel__core': 7.20.3 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 27.5.1(@babel/core@7.24.3) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - optional: true - babel-jest@27.5.1(@babel/core@7.24.4): dependencies: '@babel/core': 7.24.4 @@ -60919,32 +60826,32 @@ snapshots: transitivePeerDependencies: - supports-color - babel-loader@8.3.0(@babel/core@7.21.4)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + babel-loader@8.3.0(@babel/core@7.21.4)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: '@babel/core': 7.21.4 find-cache-dir: 3.3.2 loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - babel-loader@8.3.0(@babel/core@7.21.4)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + babel-loader@8.3.0(@babel/core@7.21.4)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: '@babel/core': 7.21.4 find-cache-dir: 3.3.2 loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) - babel-loader@8.3.0(@babel/core@7.25.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + babel-loader@8.3.0(@babel/core@7.25.2)(webpack@5.78.0): dependencies: '@babel/core': 7.25.2 find-cache-dir: 3.3.2 loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) babel-loader@9.1.2(@babel/core@7.23.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): dependencies: @@ -60953,19 +60860,19 @@ snapshots: schema-utils: 4.0.0 webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) - babel-loader@9.1.2(@babel/core@7.23.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + babel-loader@9.1.2(@babel/core@7.23.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: '@babel/core': 7.23.2 find-cache-dir: 3.3.2 schema-utils: 4.0.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - babel-loader@9.1.2(@babel/core@7.23.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + babel-loader@9.1.2(@babel/core@7.23.2)(webpack@5.78.0): dependencies: '@babel/core': 7.23.2 find-cache-dir: 3.3.2 schema-utils: 4.0.0 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) babel-plugin-add-react-displayname@0.0.5: {} @@ -61051,15 +60958,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.22.11): - dependencies: - '@babel/compat-data': 7.25.4 - '@babel/core': 7.22.11 - '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.22.11) - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.3): dependencies: '@babel/compat-data': 7.25.4 @@ -61213,23 +61111,6 @@ snapshots: '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.4) - babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.3): - dependencies: - '@babel/core': 7.24.3 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.3) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.3) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.3) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.3) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.3) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.3) - optional: true - babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.4): dependencies: '@babel/core': 7.24.4 @@ -61268,13 +61149,6 @@ snapshots: babel-plugin-jest-hoist: 27.5.1 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4) - babel-preset-jest@27.5.1(@babel/core@7.24.3): - dependencies: - '@babel/core': 7.24.3 - babel-plugin-jest-hoist: 27.5.1 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.3) - optional: true - babel-preset-jest@27.5.1(@babel/core@7.24.4): dependencies: '@babel/core': 7.24.4 @@ -62539,18 +62413,12 @@ snapshots: dependencies: mime-db: 1.52.0 - compression-webpack-plugin@10.0.0(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4)): + compression-webpack-plugin@10.0.0(webpack@5.78.0): dependencies: schema-utils: 4.0.0 serialize-javascript: 6.0.1 webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4) - compression-webpack-plugin@10.0.0(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): - dependencies: - schema-utils: 4.0.0 - serialize-javascript: 6.0.1 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) - compression@1.7.4: dependencies: accepts: 1.3.8 @@ -62867,13 +62735,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): + create-jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -62882,13 +62750,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): + create-jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -63243,7 +63111,19 @@ snapshots: semver: 7.6.3 webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) - css-loader@6.7.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + css-loader@6.7.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): + dependencies: + icss-utils: 5.1.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-modules-extract-imports: 3.0.0(postcss@8.4.47) + postcss-modules-local-by-default: 4.0.0(postcss@8.4.47) + postcss-modules-scope: 3.0.0(postcss@8.4.47) + postcss-modules-values: 4.0.0(postcss@8.4.47) + postcss-value-parser: 4.2.0 + semver: 7.6.3 + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) + + css-loader@6.7.3(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: icss-utils: 5.1.0(postcss@8.4.47) postcss: 8.4.47 @@ -63253,9 +63133,9 @@ snapshots: postcss-modules-values: 4.0.0(postcss@8.4.47) postcss-value-parser: 4.2.0 semver: 7.6.3 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) - css-loader@6.7.3(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + css-loader@6.7.3(webpack@5.78.0): dependencies: icss-utils: 5.1.0(postcss@8.4.47) postcss: 8.4.47 @@ -63265,9 +63145,9 @@ snapshots: postcss-modules-values: 4.0.0(postcss@8.4.47) postcss-value-parser: 4.2.0 semver: 7.6.3 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) - css-minimizer-webpack-plugin@3.4.1(esbuild@0.18.20)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + css-minimizer-webpack-plugin@3.4.1(esbuild@0.23.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: cssnano: 5.1.15(postcss@8.4.47) jest-worker: 27.5.1 @@ -63275,11 +63155,11 @@ snapshots: schema-utils: 4.0.0 serialize-javascript: 6.0.2 source-map: 0.6.1 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) optionalDependencies: - esbuild: 0.18.20 + esbuild: 0.23.1 - css-minimizer-webpack-plugin@3.4.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + css-minimizer-webpack-plugin@3.4.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: cssnano: 5.1.15(postcss@8.4.47) jest-worker: 27.5.1 @@ -63287,7 +63167,7 @@ snapshots: schema-utils: 4.0.0 serialize-javascript: 6.0.2 source-map: 0.6.1 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) css-prefers-color-scheme@6.0.3(postcss@8.4.47): dependencies: @@ -64845,7 +64725,7 @@ snapshots: - supports-color - typescript - eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.1))(eslint@8.57.1): + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.29.1)(eslint@8.57.1): dependencies: confusing-browser-globals: 1.0.11 eslint: 8.57.1 @@ -64854,12 +64734,12 @@ snapshots: object.entries: 1.1.8 semver: 6.3.1 - eslint-config-airbnb-typescript@18.0.0(@typescript-eslint/eslint-plugin@8.18.2(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.1))(eslint@8.57.1): + eslint-config-airbnb-typescript@18.0.0(@typescript-eslint/eslint-plugin@8.18.2(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.29.1)(eslint@8.57.1): dependencies: '@typescript-eslint/eslint-plugin': 8.18.2(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2) '@typescript-eslint/parser': 8.18.2(eslint@8.57.1)(typescript@5.6.2) eslint: 8.57.1 - eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.1))(eslint@8.57.1) + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.29.1)(eslint@8.57.1) transitivePeerDependencies: - eslint-plugin-import @@ -64882,7 +64762,7 @@ snapshots: dependencies: eslint: 8.57.1 - eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2): + eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.21.4))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2): dependencies: '@babel/core': 7.21.4 '@babel/eslint-parser': 7.25.1(@babel/core@7.21.4)(eslint@9.9.1(jiti@1.21.6)) @@ -64892,7 +64772,7 @@ snapshots: babel-preset-react-app: 10.0.1 confusing-browser-globals: 1.0.11 eslint: 9.9.1(jiti@1.21.6) - eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(eslint@9.9.1(jiti@1.21.6)) + eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.21.4))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4))(eslint@9.9.1(jiti@1.21.6)) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6)) eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) eslint-plugin-jsx-a11y: 6.9.0(eslint@9.9.1(jiti@1.21.6)) @@ -64909,7 +64789,7 @@ snapshots: - jest - supports-color - eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2): + eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): dependencies: '@babel/core': 7.21.4 '@babel/eslint-parser': 7.25.1(@babel/core@7.21.4)(eslint@9.9.1(jiti@1.21.6)) @@ -64921,7 +64801,7 @@ snapshots: eslint: 9.9.1(jiti@1.21.6) eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint@9.9.1(jiti@1.21.6)) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6)) - eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) + eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2) eslint-plugin-jsx-a11y: 6.9.0(eslint@9.9.1(jiti@1.21.6)) eslint-plugin-react: 7.35.0(eslint@9.9.1(jiti@1.21.6)) eslint-plugin-react-hooks: 4.6.2(eslint@9.9.1(jiti@1.21.6)) @@ -64973,7 +64853,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.2(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@8.57.1): + eslint-module-utils@2.8.2(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.1): dependencies: debug: 3.2.7(supports-color@8.1.1) optionalDependencies: @@ -65017,10 +64897,10 @@ snapshots: eslint: 8.57.1 ignore: 5.3.2 - eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(eslint@9.9.1(jiti@1.21.6)): + eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.21.4))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4))(eslint@9.9.1(jiti@1.21.6)): dependencies: - '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.22.11) - '@babel/plugin-transform-react-jsx': 7.25.2(@babel/core@7.22.11) + '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.21.4) + '@babel/plugin-transform-react-jsx': 7.25.2(@babel/core@7.21.4) eslint: 9.9.1(jiti@1.21.6) lodash: 4.17.21 string-natural-compare: 3.0.1 @@ -65088,7 +64968,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.2(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@8.57.1) + eslint-module-utils: 2.8.2(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -65128,6 +65008,17 @@ snapshots: - supports-color - typescript + eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): + dependencies: + '@typescript-eslint/experimental-utils': 5.58.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2) + eslint: 9.9.1(jiti@1.21.6) + optionalDependencies: + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2))(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2) + jest: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + transitivePeerDependencies: + - supports-color + - typescript + eslint-plugin-jest@28.8.0(@typescript-eslint/eslint-plugin@8.18.2(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2): dependencies: '@typescript-eslint/utils': 8.18.2(eslint@8.57.1)(typescript@5.6.2) @@ -65322,7 +65213,7 @@ snapshots: dependencies: eslint: 8.57.1 - eslint-plugin-sonarjs@2.0.1(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@8.57.1): + eslint-plugin-sonarjs@2.0.1(@typescript-eslint/parser@8.18.2(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-webpack@0.13.8)(eslint@8.57.1): dependencies: '@babel/core': 7.24.3 '@babel/eslint-parser': 7.24.1(@babel/core@7.24.3)(eslint@8.57.1) @@ -65448,7 +65339,7 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint-webpack-plugin@3.2.0(eslint@9.9.1(jiti@1.21.6))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + eslint-webpack-plugin@3.2.0(eslint@9.9.1(jiti@1.21.6))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: '@types/eslint': 8.56.12 eslint: 9.9.1(jiti@1.21.6) @@ -65456,9 +65347,9 @@ snapshots: micromatch: 4.0.8 normalize-path: 3.0.0 schema-utils: 4.0.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - eslint-webpack-plugin@3.2.0(eslint@9.9.1(jiti@1.21.6))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + eslint-webpack-plugin@3.2.0(eslint@9.9.1(jiti@1.21.6))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: '@types/eslint': 8.56.12 eslint: 9.9.1(jiti@1.21.6) @@ -65466,7 +65357,7 @@ snapshots: micromatch: 4.0.8 normalize-path: 3.0.0 schema-utils: 4.0.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) eslint@8.57.1: dependencies: @@ -66161,25 +66052,25 @@ snapshots: loader-utils: 2.0.4 schema-utils: 3.3.0 webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + optional: true - file-loader@6.2.0(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + file-loader@6.2.0(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - file-loader@6.2.0(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + file-loader@6.2.0(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) - optional: true + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) - file-loader@6.2.0(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): + file-loader@6.2.0(webpack@5.78.0): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) optional: true file-selector@0.6.0: @@ -66418,7 +66309,7 @@ snapshots: forever-agent@0.6.1: {} - fork-ts-checker-webpack-plugin@6.5.3(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + fork-ts-checker-webpack-plugin@6.5.3(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: '@babel/code-frame': 7.24.7 '@types/json-schema': 7.0.15 @@ -66434,12 +66325,12 @@ snapshots: semver: 7.6.3 tapable: 1.1.3 typescript: 5.6.2 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) optionalDependencies: eslint: 9.9.1(jiti@1.21.6) vue-template-compiler: 2.7.16 - fork-ts-checker-webpack-plugin@6.5.3(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + fork-ts-checker-webpack-plugin@6.5.3(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: '@babel/code-frame': 7.24.7 '@types/json-schema': 7.0.15 @@ -66455,7 +66346,7 @@ snapshots: semver: 7.6.3 tapable: 1.1.3 typescript: 5.6.2 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) optionalDependencies: eslint: 9.9.1(jiti@1.21.6) vue-template-compiler: 2.7.16 @@ -66477,7 +66368,7 @@ snapshots: typescript: 5.6.2 webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) - fork-ts-checker-webpack-plugin@8.0.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + fork-ts-checker-webpack-plugin@8.0.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: '@babel/code-frame': 7.24.7 chalk: 4.1.2 @@ -66492,9 +66383,9 @@ snapshots: semver: 7.6.3 tapable: 2.2.1 typescript: 5.6.2 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - fork-ts-checker-webpack-plugin@8.0.0(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + fork-ts-checker-webpack-plugin@8.0.0(typescript@5.6.2)(webpack@5.78.0): dependencies: '@babel/code-frame': 7.24.7 chalk: 4.1.2 @@ -66509,7 +66400,7 @@ snapshots: semver: 7.6.3 tapable: 2.2.1 typescript: 5.6.2 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) fork-ts-checker-webpack-plugin@9.0.2(typescript@5.3.3)(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: @@ -67733,23 +67624,32 @@ snapshots: tapable: 2.2.1 webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) - html-webpack-plugin@5.5.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + html-webpack-plugin@5.5.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 lodash: 4.17.21 pretty-error: 4.0.0 tapable: 2.2.1 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - html-webpack-plugin@5.5.3(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + html-webpack-plugin@5.5.3(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 lodash: 4.17.21 pretty-error: 4.0.0 tapable: 2.2.1 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) + + html-webpack-plugin@5.5.3(webpack@5.78.0): + dependencies: + '@types/html-minifier-terser': 6.1.0 + html-minifier-terser: 6.1.0 + lodash: 4.17.21 + pretty-error: 4.0.0 + tapable: 2.2.1 + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) htmlparser2@6.1.0: dependencies: @@ -68912,6 +68812,27 @@ snapshots: - ts-node - utf-8-validate + jest-cli@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): + dependencies: + '@jest/core': 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + import-local: 3.1.0 + jest-config: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest-util: 27.5.1 + jest-validate: 27.5.1 + prompts: 2.4.2 + yargs: 16.2.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + jest-cli@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)): dependencies: '@jest/core': 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)) @@ -68972,16 +68893,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): + jest-cli@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + create-jest: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -68991,16 +68912,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): + jest-cli@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + create-jest: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest-config: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -69131,7 +69052,7 @@ snapshots: - supports-color - utf-8-validate - jest-config@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)): + jest-config@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): dependencies: '@babel/core': 7.24.4 '@jest/test-sequencer': 27.5.1 @@ -69158,45 +69079,48 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - ts-node: 10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2) + ts-node: 10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2) transitivePeerDependencies: - bufferutil - canvas - supports-color - utf-8-validate - jest-config@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)): + jest-config@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)): dependencies: - '@babel/core': 7.25.2 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.25.2) + '@babel/core': 7.24.4 + '@jest/test-sequencer': 27.5.1 + '@jest/types': 27.5.1 + babel-jest: 27.5.1(@babel/core@7.24.4) chalk: 4.1.2 - ci-info: 3.9.0 + ci-info: 3.8.0 deepmerge: 4.3.1 glob: 7.2.3 graceful-fs: 4.2.11 - jest-circus: 29.7.0(babel-plugin-macros@3.1.0) - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 + jest-circus: 27.5.1 + jest-environment-jsdom: 27.5.1 + jest-environment-node: 27.5.1 + jest-get-type: 27.5.1 + jest-jasmine2: 27.5.1 + jest-regex-util: 27.5.1 + jest-resolve: 27.5.1 + jest-runner: 27.5.1 + jest-util: 27.5.1 + jest-validate: 27.5.1 + micromatch: 4.0.5 parse-json: 5.2.0 - pretty-format: 29.7.0 + pretty-format: 27.5.1 slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 18.16.9 - ts-node: 10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2) + ts-node: 10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2) transitivePeerDependencies: - - babel-plugin-macros + - bufferutil + - canvas - supports-color + - utf-8-validate - jest-config@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)): + jest-config@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)): dependencies: '@babel/core': 7.25.2 '@jest/test-sequencer': 29.7.0 @@ -69221,13 +69145,13 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.16.5 + '@types/node': 18.16.9 ts-node: 10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): + jest-config@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)): dependencies: '@babel/core': 7.25.2 '@jest/test-sequencer': 29.7.0 @@ -69253,12 +69177,12 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.16.5 - ts-node: 10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2) + ts-node: 10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): + jest-config@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): dependencies: '@babel/core': 7.25.2 '@jest/test-sequencer': 29.7.0 @@ -69284,7 +69208,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.16.5 - ts-node: 10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2) + ts-node: 10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -69871,6 +69795,17 @@ snapshots: string-length: 5.0.1 strip-ansi: 7.1.0 + jest-watch-typeahead@1.1.0(jest@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))): + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + jest: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest-regex-util: 28.0.2 + jest-watcher: 28.1.3 + slash: 4.0.0 + string-length: 5.0.1 + strip-ansi: 7.1.0 + jest-watcher@27.5.1: dependencies: '@jest/test-result': 27.5.1 @@ -69964,6 +69899,18 @@ snapshots: - ts-node - utf-8-validate + jest@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): + dependencies: + '@jest/core': 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + import-local: 3.1.0 + jest-cli: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + jest@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)): dependencies: '@jest/core': 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)) @@ -70000,24 +69947,24 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): + jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest-cli: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros - supports-color - ts-node - jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): + jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest-cli: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -70654,21 +70601,21 @@ snapshots: - encoding - supports-color - less-loader@4.1.0(less@4.1.3)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + less-loader@4.1.0(less@4.1.3)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: clone: 2.1.2 less: 4.1.3 loader-utils: 1.4.2 pify: 3.0.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - less-loader@4.1.0(less@4.1.3)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + less-loader@4.1.0(less@4.1.3)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: clone: 2.1.2 less: 4.1.3 loader-utils: 1.4.2 pify: 3.0.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) less@4.1.3: dependencies: @@ -70930,6 +70877,10 @@ snapshots: dependencies: commander: 10.0.1 + liquidjs@10.20.0: + dependencies: + commander: 10.0.1 + list-stylesheets@2.0.1: dependencies: cheerio: 1.0.0-rc.12 @@ -71826,13 +71777,13 @@ snapshots: mdurl@2.0.0: {} - mdx-bundler@10.0.2(esbuild@0.18.20): + mdx-bundler@10.0.2(esbuild@0.23.1): dependencies: '@babel/runtime': 7.24.7 - '@esbuild-plugins/node-resolve': 0.2.2(esbuild@0.18.20) + '@esbuild-plugins/node-resolve': 0.2.2(esbuild@0.23.1) '@fal-works/esbuild-plugin-global-externals': 2.1.2 - '@mdx-js/esbuild': 3.0.1(esbuild@0.18.20) - esbuild: 0.18.20 + '@mdx-js/esbuild': 3.0.1(esbuild@0.23.1) + esbuild: 0.23.1 gray-matter: 4.0.3 remark-frontmatter: 5.0.0 remark-mdx-frontmatter: 4.0.0 @@ -72496,15 +72447,15 @@ snapshots: min-indent@1.0.1: {} - mini-css-extract-plugin@2.7.5(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + mini-css-extract-plugin@2.7.5(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: schema-utils: 4.0.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - mini-css-extract-plugin@2.7.5(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + mini-css-extract-plugin@2.7.5(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: schema-utils: 4.0.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) minimalistic-assert@1.0.1: {} @@ -73057,13 +73008,13 @@ snapshots: neo-async@2.6.2: {} - nest-raven@10.1.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@sentry/node@8.33.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(rxjs@7.8.1): + nest-raven@10.1.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@sentry/node@8.33.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2)(rxjs@7.8.1): dependencies: '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@sentry/node': 8.33.1 rxjs: 7.8.1 optionalDependencies: - '@nestjs/graphql': 12.0.9(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2) + '@nestjs/graphql': 12.0.9(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.9.0)(reflect-metadata@0.2.2) transitivePeerDependencies: - '@apollo/subgraph' - '@nestjs/core' @@ -73077,7 +73028,7 @@ snapshots: nested-error-stacks@2.0.1: {} - nestjs-otel@6.1.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1)): + nestjs-otel@6.1.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1): dependencies: '@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.1)(@nestjs/websockets@10.4.1)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -73127,7 +73078,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - next@13.5.6(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8): + next@13.5.6(@babel/core@7.25.2)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8): dependencies: '@next/env': 13.5.6 '@swc/helpers': 0.5.2 @@ -73136,7 +73087,7 @@ snapshots: postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(@babel/core@7.24.3)(babel-plugin-macros@3.1.0)(react@18.3.1) + styled-jsx: 5.1.1(@babel/core@7.25.2)(babel-plugin-macros@3.1.0)(react@18.3.1) watchpack: 2.4.0 optionalDependencies: '@next/swc-darwin-arm64': 13.5.6 @@ -73182,7 +73133,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@14.2.4(@babel/core@7.24.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.46.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8): + next@14.2.4(@babel/core@7.25.2)(@opentelemetry/api@1.9.0)(@playwright/test@1.46.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8): dependencies: '@next/env': 14.2.4 '@swc/helpers': 0.5.5 @@ -73192,7 +73143,7 @@ snapshots: postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(@babel/core@7.24.3)(babel-plugin-macros@3.1.0)(react@18.3.1) + styled-jsx: 5.1.1(@babel/core@7.25.2)(babel-plugin-macros@3.1.0)(react@18.3.1) optionalDependencies: '@next/swc-darwin-arm64': 14.2.4 '@next/swc-darwin-x64': 14.2.4 @@ -75118,21 +75069,21 @@ snapshots: tsx: 4.19.0 yaml: 2.6.1 - postcss-loader@6.2.1(postcss@8.4.47)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + postcss-loader@6.2.1(postcss@8.4.47)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: cosmiconfig: 7.1.0 klona: 2.0.6 postcss: 8.4.47 semver: 7.6.3 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - postcss-loader@6.2.1(postcss@8.4.47)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + postcss-loader@6.2.1(postcss@8.4.47)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: cosmiconfig: 7.1.0 klona: 2.0.6 postcss: 8.4.47 semver: 7.6.3 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) postcss-logical@5.0.4(postcss@8.4.47): dependencies: @@ -76754,14 +76705,14 @@ snapshots: regenerator-runtime: 0.13.11 whatwg-fetch: 3.6.2 - react-app-rewired@2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)): + react-app-rewired@2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.21.4))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(esbuild@0.23.1)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)): dependencies: - react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) + react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.21.4))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(esbuild@0.23.1)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) semver: 5.7.2 - react-app-rewired@2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12)))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)): + react-app-rewired@2.2.1(react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1)): dependencies: - react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12)))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) + react-scripts: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1) semver: 5.7.2 react-chartjs-2@4.3.1(chart.js@3.9.1)(react@18.3.1): @@ -76802,7 +76753,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-dev-utils@12.0.1(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + react-dev-utils@12.0.1(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: '@babel/code-frame': 7.24.2 address: 1.2.2 @@ -76813,7 +76764,7 @@ snapshots: escape-string-regexp: 4.0.0 filesize: 8.0.7 find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.3(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + fork-ts-checker-webpack-plugin: 6.5.3(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) global-modules: 2.0.0 globby: 11.1.0 gzip-size: 6.0.0 @@ -76828,7 +76779,7 @@ snapshots: shell-quote: 1.8.1 strip-ansi: 6.0.1 text-table: 0.2.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) optionalDependencies: typescript: 5.6.2 transitivePeerDependencies: @@ -76836,7 +76787,7 @@ snapshots: - supports-color - vue-template-compiler - react-dev-utils@12.0.1(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + react-dev-utils@12.0.1(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: '@babel/code-frame': 7.24.2 address: 1.2.2 @@ -76847,7 +76798,7 @@ snapshots: escape-string-regexp: 4.0.0 filesize: 8.0.7 find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.3(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + fork-ts-checker-webpack-plugin: 6.5.3(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) global-modules: 2.0.0 globby: 11.1.0 gzip-size: 6.0.0 @@ -76862,7 +76813,7 @@ snapshots: shell-quote: 1.8.1 strip-ansi: 6.0.1 text-table: 0.2.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) optionalDependencies: typescript: 5.6.2 transitivePeerDependencies: @@ -77191,56 +77142,56 @@ snapshots: transitivePeerDependencies: - supports-color - react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(esbuild@0.18.20)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1): + react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.21.4))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(esbuild@0.23.1)(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1): dependencies: '@babel/core': 7.21.4 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) '@svgr/webpack': 5.5.0 babel-jest: 27.5.1(@babel/core@7.21.4) - babel-loader: 8.3.0(@babel/core@7.21.4)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + babel-loader: 8.3.0(@babel/core@7.21.4)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) babel-plugin-named-asset-import: 0.3.8(@babel/core@7.21.4) babel-preset-react-app: 10.0.1 bfj: 7.0.2 browserslist: 4.21.5 camelcase: 6.3.0 case-sensitive-paths-webpack-plugin: 2.4.0 - css-loader: 6.7.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) - css-minimizer-webpack-plugin: 3.4.1(esbuild@0.18.20)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + css-loader: 6.7.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) + css-minimizer-webpack-plugin: 3.4.1(esbuild@0.23.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) dotenv: 10.0.0 dotenv-expand: 5.1.0 eslint: 9.9.1(jiti@1.21.6) - eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.22.11))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.22.11))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) - eslint-webpack-plugin: 3.2.0(eslint@9.9.1(jiti@1.21.6))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) - file-loader: 6.2.0(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.21.4))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.21.4))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) + eslint-webpack-plugin: 3.2.0(eslint@9.9.1(jiti@1.21.6))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) + file-loader: 6.2.0(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) fs-extra: 10.1.0 - html-webpack-plugin: 5.5.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + html-webpack-plugin: 5.5.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) identity-obj-proxy: 3.0.0 jest: 27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) jest-resolve: 27.5.1 jest-watch-typeahead: 1.1.0(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))) - mini-css-extract-plugin: 2.7.5(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + mini-css-extract-plugin: 2.7.5(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) postcss: 8.4.47 postcss-flexbugs-fixes: 5.0.2(postcss@8.4.47) - postcss-loader: 6.2.1(postcss@8.4.47)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + postcss-loader: 6.2.1(postcss@8.4.47)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) postcss-normalize: 10.0.1(browserslist@4.21.5)(postcss@8.4.47) postcss-preset-env: 7.8.3(postcss@8.4.47) prompts: 2.4.2 react: 18.3.1 react-app-polyfill: 3.0.0 - react-dev-utils: 12.0.1(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + react-dev-utils: 12.0.1(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) react-refresh: 0.11.0 resolve: 1.22.2 resolve-url-loader: 4.0.0 - sass-loader: 12.6.0(sass@1.77.8)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + sass-loader: 12.6.0(sass@1.77.8)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) semver: 7.5.4 - source-map-loader: 3.0.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) - style-loader: 3.3.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + source-map-loader: 3.0.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) + style-loader: 3.3.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) tailwindcss: 3.4.13(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) - terser-webpack-plugin: 5.3.7(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) - webpack-dev-server: 4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) - webpack-manifest-plugin: 4.1.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) - workbox-webpack-plugin: 6.5.4(@types/babel__core@7.20.5)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + terser-webpack-plugin: 5.3.7(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) + webpack-dev-server: 4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) + webpack-manifest-plugin: 4.1.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) + workbox-webpack-plugin: 6.5.4(@types/babel__core@7.20.5)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) optionalDependencies: fsevents: 2.3.3 typescript: 5.6.2 @@ -77277,56 +77228,56 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12)))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1): + react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(react@18.3.1)(sass@1.77.8)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack-hot-middleware@2.26.1): dependencies: '@babel/core': 7.21.4 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.3.107(@swc/helpers@0.5.12)))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(@types/webpack@5.28.5(@swc/core@1.7.26(@swc/helpers@0.5.12)))(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))))(webpack-hot-middleware@2.26.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) '@svgr/webpack': 5.5.0 babel-jest: 27.5.1(@babel/core@7.21.4) - babel-loader: 8.3.0(@babel/core@7.21.4)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + babel-loader: 8.3.0(@babel/core@7.21.4)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) babel-plugin-named-asset-import: 0.3.8(@babel/core@7.21.4) babel-preset-react-app: 10.0.1 bfj: 7.0.2 browserslist: 4.21.5 camelcase: 6.3.0 case-sensitive-paths-webpack-plugin: 2.4.0 - css-loader: 6.7.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) - css-minimizer-webpack-plugin: 3.4.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + css-loader: 6.7.3(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + css-minimizer-webpack-plugin: 3.4.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) dotenv: 10.0.0 dotenv-expand: 5.1.0 eslint: 9.9.1(jiti@1.21.6) - eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2) - eslint-webpack-plugin: 3.2.0(eslint@9.9.1(jiti@1.21.6))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) - file-loader: 6.2.0(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.1)(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))))(eslint@9.9.1(jiti@1.21.6))(jest@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2) + eslint-webpack-plugin: 3.2.0(eslint@9.9.1(jiti@1.21.6))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + file-loader: 6.2.0(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) fs-extra: 10.1.0 - html-webpack-plugin: 5.5.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + html-webpack-plugin: 5.5.3(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) identity-obj-proxy: 3.0.0 - jest: 27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) + jest: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) jest-resolve: 27.5.1 - jest-watch-typeahead: 1.1.0(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2))) - mini-css-extract-plugin: 2.7.5(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + jest-watch-typeahead: 1.1.0(jest@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2))) + mini-css-extract-plugin: 2.7.5(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) postcss: 8.4.47 postcss-flexbugs-fixes: 5.0.2(postcss@8.4.47) - postcss-loader: 6.2.1(postcss@8.4.47)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + postcss-loader: 6.2.1(postcss@8.4.47)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) postcss-normalize: 10.0.1(browserslist@4.21.5)(postcss@8.4.47) postcss-preset-env: 7.8.3(postcss@8.4.47) prompts: 2.4.2 react: 18.3.1 react-app-polyfill: 3.0.0 - react-dev-utils: 12.0.1(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + react-dev-utils: 12.0.1(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)(vue-template-compiler@2.7.16)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) react-refresh: 0.11.0 resolve: 1.22.2 resolve-url-loader: 4.0.0 - sass-loader: 12.6.0(sass@1.77.8)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + sass-loader: 12.6.0(sass@1.77.8)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) semver: 7.5.4 - source-map-loader: 3.0.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) - style-loader: 3.3.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) - tailwindcss: 3.4.13(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) - terser-webpack-plugin: 5.3.7(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) - webpack-dev-server: 4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) - webpack-manifest-plugin: 4.1.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) - workbox-webpack-plugin: 6.5.4(@types/babel__core@7.20.5)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + source-map-loader: 3.0.2(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + style-loader: 3.3.2(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + tailwindcss: 3.4.13(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + terser-webpack-plugin: 5.3.7(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) + webpack-dev-server: 4.11.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + webpack-manifest-plugin: 4.1.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + workbox-webpack-plugin: 6.5.4(@types/babel__core@7.20.5)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) optionalDependencies: fsevents: 2.3.3 typescript: 5.6.2 @@ -78322,19 +78273,19 @@ snapshots: sanitize.css@13.0.0: {} - sass-loader@12.6.0(sass@1.77.8)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + sass-loader@12.6.0(sass@1.77.8)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: klona: 2.0.6 neo-async: 2.6.2 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) optionalDependencies: sass: 1.77.8 - sass-loader@12.6.0(sass@1.77.8)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + sass-loader@12.6.0(sass@1.77.8)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: klona: 2.0.6 neo-async: 2.6.2 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) optionalDependencies: sass: 1.77.8 @@ -78981,19 +78932,19 @@ snapshots: source-map-js@1.2.1: {} - source-map-loader@3.0.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + source-map-loader@3.0.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: abab: 2.0.6 iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - source-map-loader@3.0.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + source-map-loader@3.0.2(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: abab: 2.0.6 iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) source-map-resolve@0.5.3: dependencies: @@ -79518,13 +79469,17 @@ snapshots: dependencies: webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) - style-loader@3.3.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + style-loader@3.3.2(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - style-loader@3.3.2(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + style-loader@3.3.2(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) + + style-loader@3.3.2(webpack@5.78.0): + dependencies: + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) style-mod@4.1.2: {} @@ -79536,20 +79491,20 @@ snapshots: dependencies: inline-style-parser: 0.2.3 - styled-jsx@5.1.1(@babel/core@7.24.3)(babel-plugin-macros@3.1.0)(react@18.3.1): + styled-jsx@5.1.1(@babel/core@7.24.5)(babel-plugin-macros@3.1.0)(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 optionalDependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 babel-plugin-macros: 3.1.0 - styled-jsx@5.1.1(@babel/core@7.24.5)(babel-plugin-macros@3.1.0)(react@18.3.1): + styled-jsx@5.1.1(@babel/core@7.25.2)(babel-plugin-macros@3.1.0)(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 optionalDependencies: - '@babel/core': 7.24.5 + '@babel/core': 7.25.2 babel-plugin-macros: 3.1.0 stylehacks@5.1.1(postcss@8.4.47): @@ -79819,15 +79774,15 @@ snapshots: '@swc/core': 1.3.107(@swc/helpers@0.5.12) webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) - swc-loader@0.2.3(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + swc-loader@0.2.3(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: '@swc/core': 1.3.107(@swc/helpers@0.5.12) - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - swc-loader@0.2.3(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + swc-loader@0.2.3(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0): dependencies: '@swc/core': 1.3.107(@swc/helpers@0.5.12) - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) swr@2.2.5(react@18.3.1): dependencies: @@ -79922,6 +79877,33 @@ snapshots: transitivePeerDependencies: - ts-node + tailwindcss@3.4.13(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)): + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.1 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.6 + lilconfig: 2.1.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.4.47 + postcss-import: 15.1.0(postcss@8.4.47) + postcss-js: 4.0.1(postcss@8.4.47) + postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + postcss-nested: 6.0.1(postcss@8.4.47) + postcss-selector-parser: 6.1.2 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + tailwindcss@3.4.13(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)): dependencies: '@alloc/quick-lru': 5.2.0 @@ -80158,27 +80140,29 @@ snapshots: '@swc/core': 1.3.107(@swc/helpers@0.5.12) esbuild: 0.18.20 - terser-webpack-plugin@5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + terser-webpack-plugin@5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack@5.78.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.6 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) optionalDependencies: '@swc/core': 1.3.107(@swc/helpers@0.5.12) + esbuild: 0.18.20 - terser-webpack-plugin@5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + terser-webpack-plugin@5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.6 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) optionalDependencies: '@swc/core': 1.3.107(@swc/helpers@0.5.12) + esbuild: 0.23.1 terser-webpack-plugin@5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.94.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): dependencies: @@ -80191,28 +80175,29 @@ snapshots: optionalDependencies: '@swc/core': 1.3.107(@swc/helpers@0.5.12) - terser-webpack-plugin@5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4)): + terser-webpack-plugin@5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack@5.78.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.6 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) optionalDependencies: '@swc/core': 1.7.26(@swc/helpers@0.5.12) - esbuild: 0.23.1 + esbuild: 0.18.20 - terser-webpack-plugin@5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + terser-webpack-plugin@5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.6 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4) optionalDependencies: '@swc/core': 1.7.26(@swc/helpers@0.5.12) + esbuild: 0.23.1 terser-webpack-plugin@5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: @@ -80236,30 +80221,30 @@ snapshots: optionalDependencies: '@swc/core': 1.7.26(@swc/helpers@0.5.12) - terser-webpack-plugin@5.3.7(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + terser-webpack-plugin@5.3.7(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.6 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) optionalDependencies: '@swc/core': 1.3.107(@swc/helpers@0.5.12) - esbuild: 0.18.20 + esbuild: 0.23.1 - terser-webpack-plugin@5.3.7(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + terser-webpack-plugin@5.3.7(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.6 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) optionalDependencies: - '@swc/core': 1.3.107(@swc/helpers@0.5.12) + '@swc/core': 1.7.26(@swc/helpers@0.5.12) - terser-webpack-plugin@5.3.9(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4)): + terser-webpack-plugin@5.3.9(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 @@ -80616,11 +80601,11 @@ snapshots: dependencies: tslib: 1.14.1 - ts-jest@27.1.5(@babel/core@7.24.3)(@types/jest@29.5.13)(babel-jest@27.5.1(@babel/core@7.24.3))(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2): + ts-jest@27.1.5(@babel/core@7.24.4)(@types/jest@29.5.2)(babel-jest@27.5.1(@babel/core@7.24.4))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) + jest: 27.5.1(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) jest-util: 27.5.1 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -80629,11 +80614,11 @@ snapshots: typescript: 5.6.2 yargs-parser: 20.2.9 optionalDependencies: - '@babel/core': 7.24.3 - '@types/jest': 29.5.13 - babel-jest: 27.5.1(@babel/core@7.24.3) + '@babel/core': 7.24.4 + '@types/jest': 29.5.2 + babel-jest: 27.5.1(@babel/core@7.24.4) - ts-jest@27.1.5(@babel/core@7.24.4)(@types/jest@29.5.2)(babel-jest@27.5.1(@babel/core@7.24.4))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): + ts-jest@27.1.5(@babel/core@7.25.2)(@types/jest@29.5.1)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -80646,15 +80631,15 @@ snapshots: typescript: 5.6.2 yargs-parser: 20.2.9 optionalDependencies: - '@babel/core': 7.24.4 - '@types/jest': 29.5.2 - babel-jest: 27.5.1(@babel/core@7.24.4) + '@babel/core': 7.25.2 + '@types/jest': 29.5.1 + babel-jest: 27.5.1(@babel/core@7.25.2) - ts-jest@27.1.5(@babel/core@7.25.2)(@types/jest@29.5.1)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): + ts-jest@27.1.5(@babel/core@7.25.2)(@types/jest@29.5.13)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 27.5.1(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest: 29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) jest-util: 27.5.1 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -80664,7 +80649,7 @@ snapshots: yargs-parser: 20.2.9 optionalDependencies: '@babel/core': 7.25.2 - '@types/jest': 29.5.1 + '@types/jest': 29.5.13 babel-jest: 27.5.1(@babel/core@7.25.2) ts-jest@27.1.5(@babel/core@7.25.2)(@types/jest@29.5.13)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): @@ -80684,11 +80669,11 @@ snapshots: '@types/jest': 29.5.13 babel-jest: 27.5.1(@babel/core@7.25.2) - ts-jest@27.1.5(@babel/core@7.25.2)(@types/jest@29.5.2)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2): + ts-jest@27.1.5(@babel/core@7.25.2)(@types/jest@29.5.2)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 27.5.1(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) + jest: 27.5.1(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)) jest-util: 27.5.1 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -80701,11 +80686,11 @@ snapshots: '@types/jest': 29.5.2 babel-jest: 27.5.1(@babel/core@7.25.2) - ts-jest@27.1.5(@babel/core@7.25.2)(@types/jest@29.5.2)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)))(typescript@5.6.2): + ts-jest@27.1.5(@babel/core@7.25.2)(@types/jest@29.5.2)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 27.5.1(ts-node@10.9.1(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@22.7.4)(typescript@5.6.2)) + jest: 27.5.1(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) jest-util: 27.5.1 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -80735,7 +80720,7 @@ snapshots: '@types/jest': 29.5.2 babel-jest: 27.5.1(@babel/core@7.25.2) - ts-jest@29.1.0(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.5.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): + ts-jest@29.1.0(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(esbuild@0.18.20)(jest@29.5.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -80751,6 +80736,7 @@ snapshots: '@babel/core': 7.25.2 '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.25.2) + esbuild: 0.18.20 ts-jest@29.1.2(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(esbuild@0.23.1)(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): dependencies: @@ -80770,11 +80756,11 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.25.2) esbuild: 0.23.1 - ts-jest@29.1.2(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)))(typescript@5.6.2): + ts-jest@29.1.2(@babel/core@7.25.2)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(jest@29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2)) + jest: 29.7.0(@types/node@20.16.5)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@18.16.9)(typescript@5.6.2)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -80796,23 +80782,23 @@ snapshots: typescript: 5.6.2 webpack: 5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) - ts-loader@9.4.4(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4)): + ts-loader@9.4.4(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): dependencies: chalk: 4.1.2 enhanced-resolve: 5.17.1 micromatch: 4.0.8 semver: 7.6.3 typescript: 5.6.2 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) - ts-loader@9.4.4(typescript@5.6.2)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + ts-loader@9.4.4(typescript@5.6.2)(webpack@5.78.0): dependencies: chalk: 4.1.2 enhanced-resolve: 5.17.1 micromatch: 4.0.8 semver: 7.6.3 typescript: 5.6.2 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4) ts-loader@9.4.4(typescript@5.6.2)(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: @@ -80953,27 +80939,6 @@ snapshots: optionalDependencies: '@swc/core': 1.3.107(@swc/helpers@0.5.12) - ts-node@10.9.2(@swc/core@1.3.107(@swc/helpers@0.5.12))(@types/node@20.16.5)(typescript@5.6.2): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.3 - '@types/node': 20.16.5 - acorn: 8.12.1 - acorn-walk: 8.3.2 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.6.2 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optionalDependencies: - '@swc/core': 1.3.107(@swc/helpers@0.5.12) - optional: true - ts-node@10.9.2(@swc/core@1.7.26(@swc/helpers@0.5.12))(@types/node@20.14.10)(typescript@5.6.2): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -81750,23 +81715,23 @@ snapshots: url-join@5.0.0: {} - url-loader@4.1.1(file-loader@6.2.0(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + url-loader@4.1.1(file-loader@6.2.0(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) optionalDependencies: - file-loader: 6.2.0(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + file-loader: 6.2.0(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) - url-loader@4.1.1(file-loader@6.2.0(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12))))(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): + url-loader@4.1.1(file-loader@6.2.0(webpack@5.78.0))(webpack@5.78.0): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) optionalDependencies: - file-loader: 6.2.0(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) + file-loader: 6.2.0(webpack@5.78.0) url-parse@1.5.10: dependencies: @@ -82849,9 +82814,9 @@ snapshots: webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0): dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4)) - '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4)) - '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4)) + '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.78.0) + '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.78.0) + '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack@5.78.0) colorette: 2.0.20 commander: 10.0.1 cross-spawn: 7.0.3 @@ -82865,12 +82830,12 @@ snapshots: optionalDependencies: webpack-bundle-analyzer: 4.10.1 - webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0): + webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack-dev-server@4.11.1)(webpack@5.78.0): dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) - '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) - '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.78.0) + '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.78.0) + '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack-dev-server@4.11.1)(webpack@5.78.0) colorette: 2.0.20 commander: 10.0.1 cross-spawn: 7.0.3 @@ -82879,10 +82844,11 @@ snapshots: import-local: 3.1.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) webpack-merge: 5.9.0 optionalDependencies: webpack-bundle-analyzer: 4.9.0 + webpack-dev-server: 4.11.1(webpack-cli@5.1.4)(webpack@5.78.0) webpack-dev-middleware@5.3.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): dependencies: @@ -82892,15 +82858,35 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.0.0 webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + optional: true - webpack-dev-middleware@5.3.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + webpack-dev-middleware@5.3.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: colorette: 2.0.19 memfs: 3.5.0 mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.0.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) + + webpack-dev-middleware@5.3.3(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): + dependencies: + colorette: 2.0.19 + memfs: 3.5.0 + mime-types: 2.1.35 + range-parser: 1.2.1 + schema-utils: 4.0.0 + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) + + webpack-dev-middleware@5.3.3(webpack@5.78.0): + dependencies: + colorette: 2.0.19 + memfs: 3.5.0 + mime-types: 2.1.35 + range-parser: 1.2.1 + schema-utils: 4.0.0 + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) + optional: true webpack-dev-middleware@6.1.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): dependencies: @@ -82912,7 +82898,7 @@ snapshots: optionalDependencies: webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) - webpack-dev-middleware@6.1.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + webpack-dev-middleware@6.1.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: colorette: 2.0.19 memfs: 3.5.0 @@ -82920,9 +82906,9 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.0.0 optionalDependencies: - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) - webpack-dev-middleware@6.1.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)): + webpack-dev-middleware@6.1.1(webpack@5.78.0): dependencies: colorette: 2.0.19 memfs: 3.5.0 @@ -82930,7 +82916,48 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.0.0 optionalDependencies: - webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) + + webpack-dev-server@4.11.1(webpack-cli@5.1.4)(webpack@5.78.0): + dependencies: + '@types/bonjour': 3.5.10 + '@types/connect-history-api-fallback': 1.3.5 + '@types/express': 4.17.17 + '@types/serve-index': 1.9.1 + '@types/serve-static': 1.15.1 + '@types/sockjs': 0.3.33 + '@types/ws': 8.5.4 + ansi-html-community: 0.0.8 + bonjour-service: 1.1.1 + chokidar: 3.5.3 + colorette: 2.0.19 + compression: 1.7.4 + connect-history-api-fallback: 2.0.0 + default-gateway: 6.0.3 + express: 4.21.0 + graceful-fs: 4.2.11 + html-entities: 2.3.3 + http-proxy-middleware: 2.0.6(@types/express@4.17.17) + ipaddr.js: 2.0.1 + open: 8.4.2 + p-retry: 4.6.2 + rimraf: 3.0.2 + schema-utils: 4.0.0 + selfsigned: 2.1.1 + serve-index: 1.9.1 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4) + webpack-dev-middleware: 5.3.3(webpack@5.78.0) + ws: 8.13.0 + optionalDependencies: + webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack-dev-server@4.11.1)(webpack@5.78.0) + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + optional: true webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): dependencies: @@ -82969,8 +82996,47 @@ snapshots: - debug - supports-color - utf-8-validate + optional: true + + webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): + dependencies: + '@types/bonjour': 3.5.10 + '@types/connect-history-api-fallback': 1.3.5 + '@types/express': 4.17.17 + '@types/serve-index': 1.9.1 + '@types/serve-static': 1.15.1 + '@types/sockjs': 0.3.33 + '@types/ws': 8.5.4 + ansi-html-community: 0.0.8 + bonjour-service: 1.1.1 + chokidar: 3.5.3 + colorette: 2.0.19 + compression: 1.7.4 + connect-history-api-fallback: 2.0.0 + default-gateway: 6.0.3 + express: 4.21.0 + graceful-fs: 4.2.11 + html-entities: 2.3.3 + http-proxy-middleware: 2.0.6(@types/express@4.17.17) + ipaddr.js: 2.0.1 + open: 8.4.2 + p-retry: 4.6.2 + rimraf: 3.0.2 + schema-utils: 4.0.0 + selfsigned: 2.1.1 + serve-index: 1.9.1 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) + webpack-dev-middleware: 5.3.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate - webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + webpack-dev-server@4.11.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: '@types/bonjour': 3.5.10 '@types/connect-history-api-fallback': 1.3.5 @@ -82999,8 +83065,8 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) - webpack-dev-middleware: 5.3.3(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) + webpack-dev-middleware: 5.3.3(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))) ws: 8.13.0 transitivePeerDependencies: - bufferutil @@ -83021,16 +83087,16 @@ snapshots: strip-ansi: 6.0.1 optional: true - webpack-manifest-plugin@4.1.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + webpack-manifest-plugin@4.1.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: tapable: 2.2.1 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) webpack-sources: 2.3.1 - webpack-manifest-plugin@4.1.1(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + webpack-manifest-plugin@4.1.1(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: tapable: 2.2.1 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) webpack-sources: 2.3.1 webpack-merge@5.9.0: @@ -83056,7 +83122,7 @@ snapshots: webpack-virtual-modules@0.6.2: {} - webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)): + webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20): dependencies: '@types/eslint-scope': 3.7.4 '@types/estree': 0.0.51 @@ -83079,7 +83145,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))) + terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) watchpack: 2.4.2 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -83087,7 +83153,7 @@ snapshots: - esbuild - uglify-js - webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20): + webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4): dependencies: '@types/eslint-scope': 3.7.4 '@types/estree': 0.0.51 @@ -83110,15 +83176,17 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)) + terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack@5.78.0) watchpack: 2.4.2 webpack-sources: 3.2.3 + optionalDependencies: + webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack-dev-server@4.11.1)(webpack@5.78.0) transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack-cli@5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0)): + webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1): dependencies: '@types/eslint-scope': 3.7.4 '@types/estree': 0.0.51 @@ -83141,11 +83209,9 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + terser-webpack-plugin: 5.3.10(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)) watchpack: 2.4.2 webpack-sources: 3.2.3 - optionalDependencies: - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0) transitivePeerDependencies: - '@swc/core' - esbuild @@ -83182,7 +83248,7 @@ snapshots: - esbuild - uglify-js - webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4): + webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack-cli@5.1.4): dependencies: '@types/eslint-scope': 3.7.4 '@types/estree': 0.0.51 @@ -83205,17 +83271,17 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4)) + terser-webpack-plugin: 5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.18.20)(webpack@5.78.0) watchpack: 2.4.2 webpack-sources: 3.2.3 optionalDependencies: - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0) + webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack-dev-server@4.11.1)(webpack@5.78.0) transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4): + webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack-cli@5.1.4): dependencies: '@types/eslint-scope': 3.7.4 '@types/estree': 0.0.51 @@ -83238,11 +83304,11 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))(webpack-cli@5.1.4)) + terser-webpack-plugin: 5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.12))(esbuild@0.23.1)(webpack@5.78.0) watchpack: 2.4.2 webpack-sources: 3.2.3 optionalDependencies: - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.78.0) + webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.1)(webpack@5.78.0) transitivePeerDependencies: - '@swc/core' - esbuild @@ -83616,24 +83682,24 @@ snapshots: workbox-sw@6.5.4: {} - workbox-webpack-plugin@6.5.4(@types/babel__core@7.20.5)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20)): + workbox-webpack-plugin@6.5.4(@types/babel__core@7.20.5)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1)): dependencies: fast-json-stable-stringify: 2.1.0 pretty-bytes: 5.6.0 upath: 1.2.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.18.20) + webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))(esbuild@0.23.1) webpack-sources: 1.4.3 workbox-build: 6.5.4(@types/babel__core@7.20.5) transitivePeerDependencies: - '@types/babel__core' - supports-color - workbox-webpack-plugin@6.5.4(@types/babel__core@7.20.5)(webpack@5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12))): + workbox-webpack-plugin@6.5.4(@types/babel__core@7.20.5)(webpack@5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12))): dependencies: fast-json-stable-stringify: 2.1.0 pretty-bytes: 5.6.0 upath: 1.2.0 - webpack: 5.78.0(@swc/core@1.3.107(@swc/helpers@0.5.12)) + webpack: 5.78.0(@swc/core@1.7.26(@swc/helpers@0.5.12)) webpack-sources: 1.4.3 workbox-build: 6.5.4(@types/babel__core@7.20.5) transitivePeerDependencies: From d355467b16778e6c90429ad74c256065e5cc16f0 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Fri, 3 Jan 2025 12:18:26 +0200 Subject: [PATCH 39/49] fix: pr comments --- .../variable-plugin/variable-theme.ts | 1 + .../hooks/use-transformer-manager.ts | 11 ++- .../hooks/use-variable-parser.ts | 9 +-- .../variable-popover/variable-popover.tsx | 81 ++++++++++++++----- 4 files changed, 72 insertions(+), 30 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts index 7c6b8b56922..4f874ba1310 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts @@ -28,6 +28,7 @@ export const variablePillTheme = EditorView.baseTheme({ top: '50%', transform: 'translateY(-50%)', width: '12px', + minWidth: '12px', height: '12px', backgroundColor: 'hsl(var(--feature-base))', maskImage: `url("/images/code.svg")`, diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts index cb9acbbb68e..414f8920844 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts @@ -1,6 +1,6 @@ -import { useState, useCallback } from 'react'; -import { TransformerWithParam } from '../types'; +import { useCallback, useState } from 'react'; import { TRANSFORMERS } from '../constants'; +import { TransformerWithParam } from '../types'; interface UseTransformerManagerProps { initialTransformers: TransformerWithParam[]; @@ -42,20 +42,25 @@ export function useTransformerManager({ initialTransformers, onUpdate }: UseTran (index: number, params: string[]) => { setTransformers((current) => { const newTransformers = [...current]; + const transformerDef = TRANSFORMERS.find((def) => def.value === newTransformers[index].value); // Format params based on their types const formattedParams = params.map((param, paramIndex) => { const paramType = transformerDef?.params?.[paramIndex]?.type; + if (paramType === 'number') { - const numericValue = param.replace(/[^\d.-]/g, ''); + const numericValue = String(param).replace(/[^\d.-]/g, ''); + return isNaN(Number(numericValue)) ? '' : numericValue; } return param; }); newTransformers[index] = { ...newTransformers[index], params: formattedParams }; + onUpdate(newTransformers); + return newTransformers; }); }, diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts index c4300a135cc..5712bb99be4 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts @@ -1,15 +1,8 @@ -import { Tokenizer, TokenKind, TopLevelToken } from 'liquidjs'; +import { Tokenizer, TokenKind } from 'liquidjs'; import { useMemo } from 'react'; import { TRANSFORMERS } from '../constants'; import { TransformerWithParam } from '../types'; -function isLiquidOutputToken(token: TopLevelToken): token is TopLevelToken & { - content: string; -} { - const t = token as any; - return token.kind === TokenKind.Output && typeof t.content === 'string'; -} - export function useVariableParser(variable: string) { return useMemo(() => { if (!variable) { diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx index 50e711cb02e..d4835c692ed 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx @@ -3,7 +3,7 @@ import { Input, InputField } from '@/components/primitives/input'; import { PopoverContent } from '@/components/primitives/popover'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; import { Switch } from '@/components/primitives/switch'; -import { useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Code2 } from '../../../icons/code-2'; import { Separator } from '../../separator'; import { TransformerItem } from './components/transformer-item'; @@ -13,6 +13,23 @@ import { useVariableParser } from './hooks/use-variable-parser'; import type { VariablePopoverProps } from './types'; import { formatLiquidVariable } from './utils'; +function useDebounce void>(callback: T, delay: number) { + const timeoutRef = useRef(); + + return useCallback( + (...args: Parameters) => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + timeoutRef.current = setTimeout(() => { + callback(...args); + }, delay); + }, + [callback, delay] + ); +} + export function VariablePopover({ variable, onClose, onUpdate }: VariablePopoverProps) { const searchInputRef = useRef(null); const { parsedName, parsedDefaultValue, parsedTransformers, originalVariable } = useVariableParser(variable || ''); @@ -21,6 +38,15 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover const [showRawLiquid, setShowRawLiquid] = useState(false); const [searchQuery, setSearchQuery] = useState(''); + const updateVariable = useCallback( + (newName: string, newDefaultVal: string, newTransformers: any[]) => { + onUpdate(formatLiquidVariable(newName, newDefaultVal, newTransformers)); + }, + [onUpdate] + ); + + const debouncedUpdate = useDebounce(updateVariable, 300); + useEffect(() => { const handleEscape = (e: KeyboardEvent) => { if (e.key === 'Escape') { @@ -45,21 +71,27 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover } = useTransformerManager({ initialTransformers: parsedTransformers, onUpdate: (newTransformers) => { - onUpdate(formatLiquidVariable(name, defaultVal, newTransformers)); + debouncedUpdate(name, defaultVal, newTransformers); }, }); - const handleNameChange = (newName: string) => { - setName(newName); - onUpdate(formatLiquidVariable(newName, defaultVal, transformers)); - }; + const handleNameChange = useCallback( + (newName: string) => { + setName(newName); + debouncedUpdate(newName, defaultVal, transformers); + }, + [defaultVal, transformers, debouncedUpdate] + ); - const handleDefaultValueChange = (newDefaultVal: string) => { - setDefaultVal(newDefaultVal); - onUpdate(formatLiquidVariable(name, newDefaultVal, transformers)); - }; + const handleDefaultValueChange = useCallback( + (newDefaultVal: string) => { + setDefaultVal(newDefaultVal); + debouncedUpdate(name, newDefaultVal, transformers); + }, + [name, transformers, debouncedUpdate] + ); - const handleRawLiquidChange = (value: string) => { + const handleRawLiquidChange = useCallback((value: string) => { // Remove {{ and }} and trim const content = value.replace(/^\{\{\s*|\s*\}\}$/g, '').trim(); @@ -81,10 +113,20 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover setDefaultVal(newDefaultVal); } }); - }; + }, []); + + const filteredTransformers = useMemo( + () => getFilteredTransformers(searchQuery), + [getFilteredTransformers, searchQuery] + ); + + const currentLiquidValue = useMemo( + () => originalVariable || formatLiquidVariable(name, defaultVal, transformers), + [originalVariable, name, defaultVal, transformers] + ); return ( - +
@@ -100,12 +142,13 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover
- + handleNameChange(e.target.value)} className="h-7 text-sm" />
+
@@ -134,7 +177,7 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover
handleRawLiquidChange(e.target.value)} className="h-7 text-sm" /> @@ -161,7 +204,7 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover } }} > - +
@@ -194,7 +237,7 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover
- {getFilteredTransformers(searchQuery).length === 0 ? ( + {filteredTransformers.length === 0 ? (
{searchQuery ? 'No modifiers found' : 'All modifiers have been added'} @@ -202,7 +245,7 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover {searchQuery && Try searching for different terms}
) : ( - getFilteredTransformers(searchQuery).map((transformer) => ( + filteredTransformers.map((transformer) => ( Date: Fri, 3 Jan 2025 12:43:17 +0200 Subject: [PATCH 40/49] fix: initial values --- .../variable-popover/hooks/use-transformer-manager.ts | 7 ++++++- .../field-editor/variable-popover/variable-popover.tsx | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts index 414f8920844..8c69cda0020 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts @@ -1,4 +1,4 @@ -import { useCallback, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { TRANSFORMERS } from '../constants'; import { TransformerWithParam } from '../types'; @@ -12,6 +12,11 @@ export function useTransformerManager({ initialTransformers, onUpdate }: UseTran const [dragOverIndex, setDragOverIndex] = useState(null); const [draggingItem, setDraggingItem] = useState(null); + // Update transformers when initialTransformers changes + useEffect(() => { + setTransformers(initialTransformers); + }, [initialTransformers]); + const handleTransformerToggle = useCallback( (value: string) => { setTransformers((current) => { diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx index d4835c692ed..0af17ac25b8 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx @@ -38,6 +38,11 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover const [showRawLiquid, setShowRawLiquid] = useState(false); const [searchQuery, setSearchQuery] = useState(''); + useEffect(() => { + setName(parsedName); + setDefaultVal(parsedDefaultValue); + }, [parsedName, parsedDefaultValue]); + const updateVariable = useCallback( (newName: string, newDefaultVal: string, newTransformers: any[]) => { onUpdate(formatLiquidVariable(newName, newDefaultVal, newTransformers)); From 71d6d0858e35272768122e641a7c9d14cc184bba Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Fri, 3 Jan 2025 12:44:17 +0200 Subject: [PATCH 41/49] Update variable-pill-widget.ts --- .../variable-plugin/variable-pill-widget.ts | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts index 2f98b8a3749..fb4cc9a34d9 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-pill-widget.ts @@ -2,6 +2,8 @@ import { WidgetType } from '@uiw/react-codemirror'; import { MODIFIERS_CLASS, VARIABLE_PILL_CLASS } from './constants'; export class VariablePillWidget extends WidgetType { + private clickHandler: (e: MouseEvent) => void; + constructor( private variableName: string, private fullVariableName: string, @@ -11,6 +13,16 @@ export class VariablePillWidget extends WidgetType { private onSelect?: (value: string, from: number, to: number) => void ) { super(); + this.clickHandler = (e: MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + // setTimeout is used to defer the selection until after CodeMirror's own click handling + // This prevents race conditions where our selection might be immediately cleared by the editor + setTimeout(() => { + this.onSelect?.(this.fullVariableName, this.start, this.end); + }, 0); + }; } toDOM() { @@ -30,19 +42,7 @@ export class VariablePillWidget extends WidgetType { span.textContent = this.variableName; - const handleClick = (e: MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - - // setTimeout is used to defer the selection until after CodeMirror's own click handling - // This prevents race conditions where our selection might be immediately cleared by the editor - setTimeout(() => { - this.onSelect?.(this.fullVariableName, this.start, this.end); - }, 0); - }; - - span.addEventListener('mousedown', handleClick); - (span as any)._variableClickHandler = handleClick; + span.addEventListener('mousedown', this.clickHandler); return span; } @@ -66,10 +66,7 @@ export class VariablePillWidget extends WidgetType { * Removes event listeners to prevent memory leaks. */ destroy(dom: HTMLElement) { - if ((dom as any)._variableClickHandler) { - dom.removeEventListener('mousedown', (dom as any)._variableClickHandler); - delete (dom as any)._variableClickHandler; - } + dom.removeEventListener('mousedown', this.clickHandler); } /** From d1d512a841043f2191c4bd9511f484a72227544a Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Fri, 3 Jan 2025 12:53:02 +0200 Subject: [PATCH 42/49] fix: items --- .../variable-plugin/variable-theme.ts | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts index 4f874ba1310..e76bf367550 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts @@ -1,13 +1,13 @@ import { EditorView } from '@uiw/react-codemirror'; +import { VARIABLE_PILL_CLASS } from './constants'; export const variablePillTheme = EditorView.baseTheme({ - '.cm-variable-pill': { + [`.${VARIABLE_PILL_CLASS}`]: { backgroundColor: 'hsl(var(--bg-weak))', color: 'hsl(var(--text-sub))', border: '1px solid hsl(var(--stroke-soft))', - borderRadius: '9999px', - padding: '2px 6px 2px 18px', - margin: '0 4px', + borderRadius: '10px', + padding: '2px 6px 2px 6px', fontFamily: 'inherit', display: 'inline-flex', alignItems: 'center', @@ -17,19 +17,15 @@ export const variablePillTheme = EditorView.baseTheme({ cursor: 'pointer', position: 'relative', marginRight: '0px', - }, - '.cm-variable-pill.has-modifiers': { - paddingRight: '12px', + top: '2px', }, '.cm-variable-pill::before': { content: '""', - position: 'absolute', left: '4px', - top: '50%', - transform: 'translateY(-50%)', width: '12px', minWidth: '12px', height: '12px', + marginRight: '3px', backgroundColor: 'hsl(var(--feature-base))', maskImage: `url("/images/code.svg")`, maskRepeat: 'no-repeat', @@ -42,14 +38,11 @@ export const variablePillTheme = EditorView.baseTheme({ }, '.cm-variable-pill.has-modifiers::after': { content: '""', - position: 'absolute', - right: '5px', - top: '50%', - transform: 'translateY(-50%)', width: '4px', height: '4px', backgroundColor: 'hsl(var(--feature-base))', borderRadius: '50%', + marginLeft: '3px', }, '.cm-variable-pill .cm-bracket': { display: 'none', From 17164ed56dd42c83d972f1cc61ff48d3f4d29b99 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Fri, 3 Jan 2025 12:55:28 +0200 Subject: [PATCH 43/49] fix: popover --- .../variable-popover/variable-popover.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx index 0af17ac25b8..4d6ae7b9781 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx @@ -52,17 +52,6 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover const debouncedUpdate = useDebounce(updateVariable, 300); - useEffect(() => { - const handleEscape = (e: KeyboardEvent) => { - if (e.key === 'Escape') { - onClose(); - } - }; - - document.addEventListener('keydown', handleEscape); - return () => document.removeEventListener('keydown', handleEscape); - }, [onClose]); - const { transformers, dragOverIndex, From bda722af6a66b479a895dccc64eaf12492872110 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Fri, 3 Jan 2025 13:06:33 +0200 Subject: [PATCH 44/49] fix: command refactor --- .../src/components/primitives/command.tsx | 16 +-- .../components/transformer-item.tsx | 8 +- .../variable-popover/variable-popover.tsx | 110 ++++++++---------- 3 files changed, 60 insertions(+), 74 deletions(-) diff --git a/apps/dashboard/src/components/primitives/command.tsx b/apps/dashboard/src/components/primitives/command.tsx index cded89cfbc6..68db2fd5d4d 100644 --- a/apps/dashboard/src/components/primitives/command.tsx +++ b/apps/dashboard/src/components/primitives/command.tsx @@ -1,10 +1,10 @@ -import * as React from 'react'; import { type DialogProps } from '@radix-ui/react-dialog'; import { Command as CommandPrimitive } from 'cmdk'; +import * as React from 'react'; -import { cn } from '@/utils/ui'; import { Dialog, DialogContent } from '@/components/primitives/dialog'; import { InputField, inputVariants } from '@/components/primitives/input'; +import { cn } from '@/utils/ui'; const Command = React.forwardRef< React.ElementRef, @@ -37,9 +37,9 @@ const CommandDialog = ({ children, ...props }: CommandDialogProps) => { const CommandInput = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - + React.ComponentPropsWithoutRef & { inputFieldClassName?: string } +>(({ className, inputFieldClassName, ...props }, ref) => ( + )); @@ -115,11 +115,11 @@ CommandShortcut.displayName = 'CommandShortcut'; export { Command, CommandDialog, - CommandInput, - CommandList, CommandEmpty, CommandGroup, + CommandInput, CommandItem, - CommandShortcut, + CommandList, CommandSeparator, + CommandShortcut, }; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx index 51a7822e4cc..dfdab135b34 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/components/transformer-item.tsx @@ -1,5 +1,5 @@ -import { Transformer } from '../types'; import TruncatedText from '@/components/truncated-text'; +import { Transformer } from '../types'; type TransformerItemProps = { transformer: Transformer; @@ -10,13 +10,13 @@ export function TransformerItem({ transformer }: TransformerItemProps) {
- {transformer.label} + {transformer.label}
-

{transformer.description}

+

{transformer.description}

{transformer.example && ( - + {transformer.example} diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx index 4d6ae7b9781..4e65038d6d9 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx @@ -1,9 +1,17 @@ +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@/components/primitives/command'; import { FormControl, FormItem } from '@/components/primitives/form/form'; import { Input, InputField } from '@/components/primitives/input'; -import { PopoverContent } from '@/components/primitives/popover'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/primitives/select'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/primitives/popover'; import { Switch } from '@/components/primitives/switch'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { RiAddFill } from 'react-icons/ri'; import { Code2 } from '../../../icons/code-2'; import { Separator } from '../../separator'; import { TransformerItem } from './components/transformer-item'; @@ -37,6 +45,7 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover const [defaultVal, setDefaultVal] = useState(parsedDefaultValue); const [showRawLiquid, setShowRawLiquid] = useState(false); const [searchQuery, setSearchQuery] = useState(''); + const [isCommandOpen, setIsCommandOpen] = useState(false); useEffect(() => { setName(parsedName); @@ -189,69 +198,46 @@ export function VariablePopover({ variable, onClose, onUpdate }: VariablePopover
- + + + + + +
+ { - e.stopPropagation(); - setSearchQuery(e.target.value); - }} - className="h-7 text-sm" + onValueChange={setSearchQuery} placeholder="Search modifiers..." - autoFocus - onKeyDown={(e) => { - e.stopPropagation(); - if (e.key === 'Escape') { - setSearchQuery(''); - } - }} + inputFieldClassName="h-7" /> - -
-
- {filteredTransformers.length === 0 ? ( -
- - {searchQuery ? 'No modifiers found' : 'All modifiers have been added'} - - {searchQuery && Try searching for different terms} -
- ) : ( - filteredTransformers.map((transformer) => ( - - - - )) - )} -
- - +
+ + + No modifiers found + {filteredTransformers.length > 0 && ( + + {filteredTransformers.map((transformer) => ( + { + handleTransformerToggle(transformer.value); + setSearchQuery(''); + setIsCommandOpen(false); + }} + > + + + ))} + + )} + + + +
From cc3b33b895ae13c1557076ba08c64a133cb0b691 Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Fri, 3 Jan 2025 13:13:56 +0200 Subject: [PATCH 45/49] improve usememo --- .../primitives/field-editor/field-editor.tsx | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index b89e34eeb14..04cedd8a9fb 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -5,7 +5,7 @@ import { Editor } from '@/components/primitives/editor'; import { Popover, PopoverTrigger } from '@/components/primitives/popover'; import { createAutocompleteSource } from '@/utils/liquid-autocomplete'; import { LiquidVariable } from '@/utils/parseStepVariablesToLiquidVariables'; -import { useMemo, useRef } from 'react'; +import { useCallback, useMemo, useRef } from 'react'; import { useVariables } from './hooks/use-variables'; import { createVariablePlugin } from './variable-plugin'; import { variablePillTheme } from './variable-plugin/variable-theme'; @@ -28,6 +28,8 @@ type FieldEditorProps = { indentWithTab?: boolean; }; +const baseExtensions = [EditorView.lineWrapping, variablePillTheme]; + export function FieldEditor({ value, onChange, @@ -49,21 +51,43 @@ export function FieldEditor({ const completionSource = useMemo(() => createAutocompleteSource(variables), [variables]); - const extensions = useMemo( - () => [ + const autocompletionExtension = useMemo( + () => autocompletion({ override: [completionSource], closeOnBlur: true, defaultKeymap: true, activateOnTyping: true, }), - EditorView.lineWrapping, - variablePillTheme, - createVariablePlugin({ viewRef, lastCompletionRef, onSelect: handleVariableSelect }), - ], - [variables, completionSource, handleVariableSelect] + [completionSource] + ); + + const variablePlugin = useMemo( + () => + createVariablePlugin({ + viewRef, + lastCompletionRef, + onSelect: handleVariableSelect, + }), + [handleVariableSelect] ); + const extensions = useMemo( + () => [...baseExtensions, autocompletionExtension, variablePlugin], + [autocompletionExtension, variablePlugin] + ); + + const handleOpenChange = useCallback( + (open: boolean) => { + if (!open) { + setTimeout(() => setSelectedVariable(null), 0); + } + }, + [setSelectedVariable] + ); + + const handleClose = useCallback(() => setSelectedVariable(null), [setSelectedVariable]); + return (
- { - if (!open) { - setTimeout(() => setSelectedVariable(null), 0); - } - }} - > +
{selectedVariable && ( - setSelectedVariable(null)} - onUpdate={handleVariableUpdate} - /> + )}
From a8c181648ebb85046f4a4d4ff7c28b9c53aa291e Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Fri, 3 Jan 2025 14:25:17 +0200 Subject: [PATCH 46/49] fix: revie --- .../primitives/field-editor/field-editor.tsx | 6 +---- .../field-editor/variable-popover/types.ts | 1 - .../variable-popover/variable-popover.tsx | 23 +++---------------- .../steps/email/email-subject.tsx | 1 + .../components/workflow-editor/url-input.tsx | 3 --- 5 files changed, 5 insertions(+), 29 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index 04cedd8a9fb..ba066016d1a 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -86,8 +86,6 @@ export function FieldEditor({ [setSelectedVariable] ); - const handleClose = useCallback(() => setSelectedVariable(null), [setSelectedVariable]); - return (
- {selectedVariable && ( - - )} + {selectedVariable && }
); diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts index cb6cfae26d8..e03f74765cd 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/types.ts @@ -18,6 +18,5 @@ export type TransformerWithParam = { export type VariablePopoverProps = { variable?: string; - onClose: () => void; onUpdate: (newValue: string) => void; }; diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx index 4e65038d6d9..bba142c9d38 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/variable-popover.tsx @@ -10,8 +10,9 @@ import { FormControl, FormItem } from '@/components/primitives/form/form'; import { Input, InputField } from '@/components/primitives/input'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/primitives/popover'; import { Switch } from '@/components/primitives/switch'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { RiAddFill } from 'react-icons/ri'; +import { useDebounce } from '../../../../hooks/use-debounce'; import { Code2 } from '../../../icons/code-2'; import { Separator } from '../../separator'; import { TransformerItem } from './components/transformer-item'; @@ -21,25 +22,7 @@ import { useVariableParser } from './hooks/use-variable-parser'; import type { VariablePopoverProps } from './types'; import { formatLiquidVariable } from './utils'; -function useDebounce void>(callback: T, delay: number) { - const timeoutRef = useRef(); - - return useCallback( - (...args: Parameters) => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - } - - timeoutRef.current = setTimeout(() => { - callback(...args); - }, delay); - }, - [callback, delay] - ); -} - -export function VariablePopover({ variable, onClose, onUpdate }: VariablePopoverProps) { - const searchInputRef = useRef(null); +export function VariablePopover({ variable, onUpdate }: VariablePopoverProps) { const { parsedName, parsedDefaultValue, parsedTransformers, originalVariable } = useVariableParser(variable || ''); const [name, setName] = useState(parsedName); const [defaultVal, setDefaultVal] = useState(parsedDefaultValue); diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx index e6972ed6cff..ff4cb26f795 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-subject.tsx @@ -28,6 +28,7 @@ export const EmailSubject = () => { autoFocus={!field.value} placeholder={capitalize(field.name)} id={field.name} + variables={variables} value={field.value} onChange={(val) => field.onChange(val)} /> diff --git a/apps/dashboard/src/components/workflow-editor/url-input.tsx b/apps/dashboard/src/components/workflow-editor/url-input.tsx index a397add42c8..98757b80799 100644 --- a/apps/dashboard/src/components/workflow-editor/url-input.tsx +++ b/apps/dashboard/src/components/workflow-editor/url-input.tsx @@ -47,9 +47,6 @@ export const URLInput = ({ Date: Fri, 3 Jan 2025 14:45:15 +0200 Subject: [PATCH 47/49] fix: minor issues --- .../primitives/field-editor/field-editor.tsx | 3 ++ .../field-editor/hooks/use-variables.ts | 11 +++-- .../hooks/use-transformer-manager.ts | 49 ++++++++++++------- .../hooks/use-variable-parser.ts | 24 ++++++--- .../field-editor/variable-popover/utils.ts | 16 +++--- 5 files changed, 67 insertions(+), 36 deletions(-) diff --git a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx index ba066016d1a..325fcc1b86c 100644 --- a/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx +++ b/apps/dashboard/src/components/primitives/field-editor/field-editor.tsx @@ -93,6 +93,9 @@ export function FieldEditor({ singleLine={singleLine} indentWithTab={indentWithTab} size={size} + basicSetup={{ + defaultKeymap: true, + }} className="flex-1" autoFocus={autoFocus} placeholder={placeholder} diff --git a/apps/dashboard/src/components/primitives/field-editor/hooks/use-variables.ts b/apps/dashboard/src/components/primitives/field-editor/hooks/use-variables.ts index efc53ec0bd9..cf8d6904cf3 100644 --- a/apps/dashboard/src/components/primitives/field-editor/hooks/use-variables.ts +++ b/apps/dashboard/src/components/primitives/field-editor/hooks/use-variables.ts @@ -22,7 +22,6 @@ export function useVariables(viewRef: React.RefObject, onChange: (va const handleVariableSelect = useCallback((value: string, from: number, to: number) => { if (isUpdatingRef.current) return; - setSelectedVariable({ value, from, to }); }, []); @@ -35,16 +34,19 @@ export function useVariables(viewRef: React.RefObject, onChange: (va const { from, to } = selectedVariable; const view = viewRef.current; + // Ensure the new value has proper liquid syntax const hasLiquidSyntax = newValue.match(/^\{\{.*\}\}$/); const newVariableText = hasLiquidSyntax ? newValue : `{{${newValue}}}`; + // Calculate the actual end position including closing brackets const currentContent = view.state.doc.toString(); - const afterCursor = currentContent.slice(to).trim(); - const hasClosingBrackets = afterCursor.startsWith('}}'); + const afterCursor = currentContent.slice(to); + const closingBracketPos = afterCursor.indexOf('}}'); + const actualEnd = closingBracketPos >= 0 ? to + closingBracketPos + 2 : to; const changes = { from, - to: hasClosingBrackets ? to + 2 : to, + to: actualEnd, insert: newVariableText, }; @@ -55,6 +57,7 @@ export function useVariables(viewRef: React.RefObject, onChange: (va onChange(view.state.doc.toString()); + // Update the selected variable with new bounds setSelectedVariable((prev: SelectedVariable) => prev ? { ...prev, value: newValue, to: from + newVariableText.length } : null ); diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts index 8c69cda0020..1af497bf199 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-transformer-manager.ts @@ -1,29 +1,39 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useState } from 'react'; import { TRANSFORMERS } from '../constants'; -import { TransformerWithParam } from '../types'; +import type { TransformerWithParam } from '../types'; -interface UseTransformerManagerProps { +type UseTransformerManagerProps = { initialTransformers: TransformerWithParam[]; onUpdate: (transformers: TransformerWithParam[]) => void; -} +}; export function useTransformerManager({ initialTransformers, onUpdate }: UseTransformerManagerProps) { - const [transformers, setTransformers] = useState(initialTransformers); + const [transformers, setTransformers] = useState( + initialTransformers.filter((t) => t.value !== 'default') + ); const [dragOverIndex, setDragOverIndex] = useState(null); const [draggingItem, setDraggingItem] = useState(null); - // Update transformers when initialTransformers changes - useEffect(() => { - setTransformers(initialTransformers); - }, [initialTransformers]); - const handleTransformerToggle = useCallback( (value: string) => { setTransformers((current) => { - const newTransformers = current.some((t) => t.value === value) - ? current.filter((t) => t.value !== value) - : [...current, { value }]; + const index = current.findIndex((t) => t.value === value); + let newTransformers: TransformerWithParam[]; + + if (index === -1) { + const transformerDef = TRANSFORMERS.find((t) => t.value === value); + const newTransformer: TransformerWithParam = { + value, + ...(transformerDef?.hasParam ? { params: transformerDef.params?.map(() => '') } : {}), + }; + + newTransformers = [...current, newTransformer]; + } else { + newTransformers = current.filter((_, i) => i !== index); + } + onUpdate(newTransformers); + return newTransformers; }); }, @@ -31,12 +41,14 @@ export function useTransformerManager({ initialTransformers, onUpdate }: UseTran ); const moveTransformer = useCallback( - (from: number, to: number) => { + (fromIndex: number, toIndex: number) => { setTransformers((current) => { const newTransformers = [...current]; - const [removed] = newTransformers.splice(from, 1); - newTransformers.splice(to, 0, removed); + const [movedItem] = newTransformers.splice(fromIndex, 1); + newTransformers.splice(toIndex, 0, movedItem); + onUpdate(newTransformers); + return newTransformers; }); }, @@ -47,7 +59,6 @@ export function useTransformerManager({ initialTransformers, onUpdate }: UseTran (index: number, params: string[]) => { setTransformers((current) => { const newTransformers = [...current]; - const transformerDef = TRANSFORMERS.find((def) => def.value === newTransformers[index].value); // Format params based on their types @@ -56,14 +67,12 @@ export function useTransformerManager({ initialTransformers, onUpdate }: UseTran if (paramType === 'number') { const numericValue = String(param).replace(/[^\d.-]/g, ''); - return isNaN(Number(numericValue)) ? '' : numericValue; } return param; }); newTransformers[index] = { ...newTransformers[index], params: formattedParams }; - onUpdate(newTransformers); return newTransformers; @@ -76,6 +85,8 @@ export function useTransformerManager({ initialTransformers, onUpdate }: UseTran (query: string) => { const normalizedQuery = query.toLowerCase(); return TRANSFORMERS.filter((transformer) => { + // Never show default in the transformer list as it's handled separately + if (transformer.value === 'default') return false; if (transformers.some((t) => t.value === transformer.value)) return false; return ( diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts index 5712bb99be4..05c58b57b93 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/hooks/use-variable-parser.ts @@ -10,8 +10,11 @@ export function useVariableParser(variable: string) { } try { + // Remove {{ and }} and trim + const cleanVariable = variable.replace(/^\{\{|\}\}$/g, '').trim(); + // The content before any filters is the variable name - const [variableName, ...filterParts] = variable.split('|'); + const [variableName, ...filterParts] = cleanVariable.split('|'); const parsedName = variableName.trim(); // Extract default value and transformers from the filters @@ -22,12 +25,21 @@ export function useVariableParser(variable: string) { const filterTokenizer = new Tokenizer('|' + filterParts.join('|')); const filters = filterTokenizer.readFilters(); - filters.forEach((filter) => { + // First pass: find default value + for (const filter of filters) { if (filter.kind === TokenKind.Filter && filter.name === 'default' && filter.args.length > 0) { - const arg = filter.args[0]; + parsedDefaultValue = (filter.args[0] as any).content; + break; + } + } - parsedDefaultValue = (arg as any).content; - } else if (TRANSFORMERS.some((t) => t.value === filter.name)) { + // Second pass: collect other transformers + for (const filter of filters) { + if ( + filter.kind === TokenKind.Filter && + filter.name !== 'default' && + TRANSFORMERS.some((t) => t.value === filter.name) + ) { parsedTransformers.push({ value: filter.name, ...(filter.args.length > 0 @@ -39,7 +51,7 @@ export function useVariableParser(variable: string) { : {}), }); } - }); + } } return { diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts b/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts index 99a9f7adf97..59af92c28b0 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-popover/utils.ts @@ -22,18 +22,20 @@ export function formatLiquidVariable( parts.push(`default: '${escapeString(defaultValue.trim())}'`); } - parts.push( - ...transformers.map((t) => { - if (!t.params?.length) return t.value; + transformers.forEach((t) => { + if (t.value === 'default') return; + if (!t.params?.length) { + parts.push(t.value); + } else { const transformerDef = TRANSFORMERS.find((def) => def.value === t.value); const formattedParams = t.params.map((param, index) => formatParamValue(param, transformerDef?.params?.[index]?.type) ); - return `${t.value}: ${formattedParams.join(', ')}`; - }) - ); + parts.push(`${t.value}: ${formattedParams.join(', ')}`); + } + }); - return parts.join(' | '); + return `{{${parts.join(' | ')}}}`; } From 5957c023acc9024cb5b11f15e8dfc0373982108f Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 6 Jan 2025 18:35:17 +0200 Subject: [PATCH 48/49] fix: excess padding --- .../primitives/field-editor/variable-plugin/variable-theme.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts index e76bf367550..c1e3271da40 100644 --- a/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts +++ b/apps/dashboard/src/components/primitives/field-editor/variable-plugin/variable-theme.ts @@ -56,5 +56,6 @@ export const variablePillTheme = EditorView.baseTheme({ }, '.cm-line': { lineHeight: '19px !important', + paddingLeft: 0, }, }); From c064171306d8d0570963667ff2cddbde3c1c174e Mon Sep 17 00:00:00 2001 From: Dima Grossman Date: Mon, 6 Jan 2025 18:36:43 +0200 Subject: [PATCH 49/49] fix: gap --- .../src/components/workflow-editor/steps/digest/digest-key.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dashboard/src/components/workflow-editor/steps/digest/digest-key.tsx b/apps/dashboard/src/components/workflow-editor/steps/digest/digest-key.tsx index 2994c756231..c66bf736fb7 100644 --- a/apps/dashboard/src/components/workflow-editor/steps/digest/digest-key.tsx +++ b/apps/dashboard/src/components/workflow-editor/steps/digest/digest-key.tsx @@ -23,7 +23,7 @@ export const DigestKey = () => { Aggregated by - + subscriberId