();
@@ -100,7 +110,7 @@ function processLiquidRawOutput(rawOutputs: string[]): TemplateParseResult {
name: rawOutput,
message: e.message,
context: e.context,
- template: rawOutput,
+ output: rawOutput,
},
false
);
@@ -112,7 +122,7 @@ function processLiquidRawOutput(rawOutputs: string[]): TemplateParseResult {
return { validVariables, invalidVariables };
}
-function parseByLiquid(rawOutput: string): TemplateParseResult {
+function parseByLiquid(rawOutput: string): TemplateVariables {
const validVariables: Variable[] = [];
const invalidVariables: Variable[] = [];
const engine = new Liquid(LIQUID_CONFIG);
@@ -123,11 +133,11 @@ function parseByLiquid(rawOutput: string): TemplateParseResult {
const result = extractProps(template);
if (result.valid && result.props.length > 0) {
- validVariables.push({ name: result.props.join('.'), template: rawOutput });
+ validVariables.push({ name: result.props.join('.'), output: rawOutput });
}
if (!result.valid) {
- invalidVariables.push({ name: template?.token?.input, message: result.error, template: rawOutput });
+ invalidVariables.push({ name: template?.token?.input, message: result.error, output: rawOutput });
}
}
});
diff --git a/apps/api/src/app/workflows-v2/util/template-parser/parser-utils.ts b/apps/api/src/app/workflows-v2/util/template-parser/parser-utils.ts
index 36c48ba7e0f..68b1016e1f1 100644
--- a/apps/api/src/app/workflows-v2/util/template-parser/parser-utils.ts
+++ b/apps/api/src/app/workflows-v2/util/template-parser/parser-utils.ts
@@ -14,7 +14,7 @@ export function isValidTemplate(template: unknown): template is string {
export function extractLiquidExpressions(str: string): string[] {
if (!str) return [];
- const LIQUID_EXPRESSION_PATTERN = /{{\s*[^{}]+}}/g;
+ const LIQUID_EXPRESSION_PATTERN = /{{\s*[^{}]*}}/g;
return str.match(LIQUID_EXPRESSION_PATTERN) || [];
}
diff --git a/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts b/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts
index 2c55a9dc5d0..72473fa3baf 100644
--- a/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts
+++ b/apps/api/src/app/workflows-v2/workflow.controller.e2e.ts
@@ -15,7 +15,6 @@ import {
slugify,
StepContentIssueEnum,
StepCreateDto,
- StepDataDto,
StepTypeEnum,
StepUpdateDto,
UpdateStepBody,
@@ -220,8 +219,8 @@ describe('Workflow Controller E2E API Testing', () => {
}
const updatedWorkflow = novuRestResult.value;
const firstStep = updatedWorkflow.steps[0];
- expect(firstStep.issues?.body, JSON.stringify(firstStep)).to.be.empty;
- expect(firstStep.issues?.controls, JSON.stringify(firstStep.issues)).to.be.empty;
+ expect(firstStep.issues, JSON.stringify(firstStep)).to.be.empty;
+ expect(firstStep.issues, JSON.stringify(firstStep.issues)).to.be.empty;
});
});
@@ -253,15 +252,12 @@ describe('Workflow Controller E2E API Testing', () => {
});
it('should show digest control value issues when illegal value provided', async () => {
- const steps = [{ ...buildDigestStep() }];
+ const steps = [{ ...buildDigestStep({ controlValues: { amount: '555', unit: 'days' } }) }];
const workflowCreated = await createWorkflowAndReturn({ steps });
- const values = { controlValues: { amount: '555', unit: 'days' } };
- const updatedStep = await patchStepRest(workflowCreated._id, workflowCreated.steps[0]._id, values);
+ const step = workflowCreated.steps[0];
- expect(updatedStep.issues?.controls?.amount[0].issueType).to.deep.equal(
- StepContentIssueEnum.TIER_LIMIT_EXCEEDED
- );
- expect(updatedStep.issues?.controls?.unit[0].issueType).to.deep.equal(StepContentIssueEnum.TIER_LIMIT_EXCEEDED);
+ expect(step.issues?.controls?.amount[0].issueType).to.deep.equal(StepContentIssueEnum.TIER_LIMIT_EXCEEDED);
+ expect(step.issues?.controls?.unit[0].issueType).to.deep.equal(StepContentIssueEnum.TIER_LIMIT_EXCEEDED);
});
});
});
@@ -839,7 +835,7 @@ describe('Workflow Controller E2E API Testing', () => {
updatedWorkflow = await patchWorkflowAndReturnResponse(workflowDto._id, false);
expect(updatedWorkflow.status).to.equal(WorkflowStatusEnum.INACTIVE);
updatedWorkflow = await patchWorkflowAndReturnResponse(workflowDto._id, true);
- expect(updatedWorkflow.status).to.equal(WorkflowStatusEnum.ERROR);
+ expect(updatedWorkflow.status).to.equal(WorkflowStatusEnum.ACTIVE);
});
});
@@ -1114,6 +1110,7 @@ describe('Workflow Controller E2E API Testing', () => {
{
name: 'Email Test Step',
type: StepTypeEnum.EMAIL,
+ controlValues: { body: 'Welcome {{}}' },
},
],
});
@@ -1122,9 +1119,6 @@ describe('Workflow Controller E2E API Testing', () => {
expect(res.isSuccessResult()).to.be.true;
if (res.isSuccessResult()) {
const workflow = res.value;
- await workflowsClient.patchWorkflowStepData(workflow._id, workflow.steps[0]._id, {
- controlValues: { body: 'Welcome {{}}' },
- });
const stepData = await getStepData(workflow._id, workflow.steps[0]._id);
expect(stepData.issues, 'Step data should have issues').to.exist;
@@ -1134,12 +1128,18 @@ describe('Workflow Controller E2E API Testing', () => {
}
});
- it('should show issues for invalid URLs', async () => {
+ // todo add validation for invalid URLs
+ it.skip('should show issues for invalid URLs', async () => {
const createWorkflowDto: CreateWorkflowDto = buildCreateWorkflowDto('test-issues', {
steps: [
{
name: 'In-App Test Step',
type: StepTypeEnum.IN_APP,
+ controlValues: {
+ redirect: { url: 'not-good-url-please-replace' },
+ primaryAction: { redirect: { url: 'not-good-url-please-replace' } },
+ secondaryAction: { redirect: { url: 'not-good-url-please-replace' } },
+ },
},
],
});
@@ -1148,13 +1148,6 @@ describe('Workflow Controller E2E API Testing', () => {
expect(res.isSuccessResult()).to.be.true;
if (res.isSuccessResult()) {
const workflow = res.value;
- await workflowsClient.patchWorkflowStepData(workflow._id, workflow.steps[0]._id, {
- controlValues: {
- redirect: { url: 'not-good-url-please-replace' },
- primaryAction: { redirect: { url: 'not-good-url-please-replace' } },
- secondaryAction: { redirect: { url: 'not-good-url-please-replace' } },
- },
- });
const stepData = await getStepData(workflow._id, workflow.steps[0]._id);
expect(stepData.issues, 'Step data should have issues').to.exist;
diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json
index b996b2e27e5..ccbdb35797e 100644
--- a/apps/dashboard/package.json
+++ b/apps/dashboard/package.json
@@ -25,7 +25,7 @@
"@codemirror/autocomplete": "^6.18.3",
"@hookform/resolvers": "^3.9.0",
"@lezer/highlight": "^1.2.1",
- "@maily-to/core": "^0.0.18",
+ "@maily-to/core": "^0.0.19",
"@novu/framework": "workspace:*",
"@novu/js": "workspace:*",
"@novu/react": "workspace:*",
diff --git a/apps/dashboard/src/components/activity/activity-panel.tsx b/apps/dashboard/src/components/activity/activity-panel.tsx
index 02be4804ae0..07f5032b316 100644
--- a/apps/dashboard/src/components/activity/activity-panel.tsx
+++ b/apps/dashboard/src/components/activity/activity-panel.tsx
@@ -1,15 +1,20 @@
+import { useEffect, useState } from 'react';
+import { useQueryClient } from '@tanstack/react-query';
import { motion } from 'motion/react';
import { RiPlayCircleLine, RiRouteFill } from 'react-icons/ri';
+import { IActivityJob, JobStatusEnum } from '@novu/shared';
+
import { ActivityJobItem } from './activity-job-item';
import { InlineToast } from '../primitives/inline-toast';
import { useFetchActivity } from '@/hooks/use-fetch-activity';
import { ActivityOverview } from './components/activity-overview';
import { cn } from '../../utils/ui';
-import { IActivityJob } from '@novu/shared';
import { Skeleton } from '../primitives/skeleton';
+import { QueryKeys } from '@/utils/query-keys';
+import { useEnvironment } from '@/context/environment/hooks';
export interface ActivityPanelProps {
- activityId: string;
+ activityId?: string;
onActivitySelect: (activityId: string) => void;
headerClassName?: string;
overviewHeaderClassName?: string;
@@ -21,7 +26,34 @@ export function ActivityPanel({
headerClassName,
overviewHeaderClassName,
}: ActivityPanelProps) {
- const { activity, isPending, error } = useFetchActivity({ activityId });
+ const queryClient = useQueryClient();
+ const [shouldRefetch, setShouldRefetch] = useState(true);
+ const { currentEnvironment } = useEnvironment();
+ const { activity, isPending, error } = useFetchActivity(
+ { activityId },
+ {
+ refetchInterval: shouldRefetch ? 1000 : false,
+ }
+ );
+
+ useEffect(() => {
+ if (!activity) return;
+
+ const isPending = activity.jobs?.some(
+ (job) =>
+ job.status === JobStatusEnum.PENDING ||
+ job.status === JobStatusEnum.QUEUED ||
+ job.status === JobStatusEnum.RUNNING ||
+ job.status === JobStatusEnum.DELAYED
+ );
+
+ // Only stop refetching if we have an activity and it's not pending
+ setShouldRefetch(isPending || !activity?.jobs?.length);
+
+ queryClient.invalidateQueries({
+ queryKey: [QueryKeys.fetchActivity, currentEnvironment?._id, activityId],
+ });
+ }, [activity, queryClient, currentEnvironment, activityId]);
if (isPending) {
return (
@@ -77,7 +109,10 @@ export function ActivityPanel({
ctaClassName="text-foreground-950"
variant={'tip'}
ctaLabel="View Execution"
- onCtaClick={() => {
+ onCtaClick={(e) => {
+ e.stopPropagation();
+ e.preventDefault();
+
if (activity._digestedNotificationId) {
onActivitySelect(activity._digestedNotificationId);
}
diff --git a/apps/dashboard/src/components/activity/components/step-indicators.tsx b/apps/dashboard/src/components/activity/components/step-indicators.tsx
index a616b018137..05f70d9a1e3 100644
--- a/apps/dashboard/src/components/activity/components/step-indicators.tsx
+++ b/apps/dashboard/src/components/activity/components/step-indicators.tsx
@@ -19,7 +19,7 @@ export function StepIndicators({ jobs }: StepIndicatorsProps) {
diff --git a/apps/dashboard/src/components/activity/constants.ts b/apps/dashboard/src/components/activity/constants.ts
index c68cfa1efa5..90498053a6d 100644
--- a/apps/dashboard/src/components/activity/constants.ts
+++ b/apps/dashboard/src/components/activity/constants.ts
@@ -5,10 +5,10 @@ import { RiLoader3Line, RiLoader4Fill } from 'react-icons/ri';
import { IconType } from 'react-icons/lib';
export const STATUS_STYLES = {
- completed: 'border-[1px] border-success/40 bg-success/10 text-success/40',
- failed: 'border-[1px] border-destructive/40 bg-destructive/10 text-destructive/40',
- delayed: 'border-[1px] border-warning/40 bg-warning/10 text-warning/40',
- default: 'border-[1px] border-neutral-200 bg-neutral-50 text-neutral-200',
+ completed: 'border-[#99e3bb] bg-[#e9faf0] text-[#99e3bb]',
+ failed: 'border-[#ec98a0] bg-[#ffebed] text-[#ec98a0]',
+ delayed: 'border-[#F5A524] bg-[#FEF4E6] text-[#F8C16E]',
+ default: 'border-[#e0e4ea] bg-[#fbfbfb] text-[#e0e4ea]',
} as const;
export const JOB_STATUS_CONFIG: Record<
diff --git a/apps/dashboard/src/components/auth/region-picker.tsx b/apps/dashboard/src/components/auth/region-picker.tsx
index 04b17fa75fe..75632034220 100644
--- a/apps/dashboard/src/components/auth/region-picker.tsx
+++ b/apps/dashboard/src/components/auth/region-picker.tsx
@@ -26,10 +26,10 @@ export function RegionPicker() {
function handleRegionChange(value: RegionType) {
switch (value) {
case REGION_MAP.US:
- window.location.href = 'https://dashboard.novu.co';
+ window.location.href = 'https://dashboard-v2.novu.co';
break;
case REGION_MAP.EU:
- window.location.href = 'https://eu.dashboard.novu.co';
+ window.location.href = 'https://eu.dashboard-v2.novu.co';
break;
}
}
@@ -50,20 +50,22 @@ export function RegionPicker() {
-
-
-
-
-
- {Object.values(REGION_MAP).map((option) => (
-
-
- {option === REGION_MAP.US ? : } {option}
-
-
- ))}
-
-
+
+
+
+
+
+
+ {Object.values(REGION_MAP).map((option) => (
+
+
+ {option === REGION_MAP.US ? : } {option}
+
+
+ ))}
+
+
+
);
}
diff --git a/apps/dashboard/src/components/in-app-action-dropdown.tsx b/apps/dashboard/src/components/in-app-action-dropdown.tsx
index 787adf40edc..a61525783cb 100644
--- a/apps/dashboard/src/components/in-app-action-dropdown.tsx
+++ b/apps/dashboard/src/components/in-app-action-dropdown.tsx
@@ -51,7 +51,7 @@ export const InAppActionDropdown = ({ onMenuItemClick }: { onMenuItemClick?: ()
return (
<>
-
+
{!primaryAction && !secondaryAction && (
@@ -168,7 +168,7 @@ const ConfigureActionPopover = (props: ComponentProps
& {
);
return (
-
+
diff --git a/apps/dashboard/src/components/primitives/form/avatar-picker.tsx b/apps/dashboard/src/components/primitives/form/avatar-picker.tsx
index d0780010bad..d4f9a16ffaa 100644
--- a/apps/dashboard/src/components/primitives/form/avatar-picker.tsx
+++ b/apps/dashboard/src/components/primitives/form/avatar-picker.tsx
@@ -53,7 +53,7 @@ export const AvatarPicker = forwardRef
(
return (
-
+
{value ? (
diff --git a/apps/dashboard/src/components/primitives/inline-toast.tsx b/apps/dashboard/src/components/primitives/inline-toast.tsx
index 3c791ac42ed..41ee8abc0f3 100644
--- a/apps/dashboard/src/components/primitives/inline-toast.tsx
+++ b/apps/dashboard/src/components/primitives/inline-toast.tsx
@@ -41,7 +41,7 @@ export interface InlineToastProps
title?: string;
description?: string | React.ReactNode;
ctaLabel?: string;
- onCtaClick?: () => void;
+ onCtaClick?: React.MouseEventHandler;
isCtaLoading?: boolean;
ctaClassName?: string;
}
diff --git a/apps/dashboard/src/components/workflow-editor/steps/chat/chat-editor-preview.tsx b/apps/dashboard/src/components/workflow-editor/steps/chat/chat-editor-preview.tsx
index e28137a1e56..bc31871b01d 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/chat/chat-editor-preview.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/chat/chat-editor-preview.tsx
@@ -1,61 +1,26 @@
-import { CSSProperties, useEffect, useRef, useState } from 'react';
import { RiChat1Fill } from 'react-icons/ri';
-import { type StepDataDto, type WorkflowResponseDto } from '@novu/shared';
+import { GeneratePreviewResponseDto } from '@novu/shared';
-import { Code2 } from '@/components/icons/code-2';
-import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/primitives/accordion';
-import { Button } from '@/components/primitives/button';
-import { Editor } from '@/components/primitives/editor';
-import { loadLanguage } from '@uiw/codemirror-extensions-langs';
-import { useEditorPreview } from '@/components/workflow-editor/steps/use-editor-preview';
import { ChatPreview } from '@/components/workflow-editor/steps/chat/chat-preview';
import { TabsSection } from '@/components/workflow-editor/steps/tabs-section';
import { InlineToast } from '@/components/primitives/inline-toast';
-
-const getInitialAccordionValue = (value: string) => {
- try {
- return Object.keys(JSON.parse(value)).length > 0 ? 'payload' : undefined;
- } catch (e) {
- return undefined;
- }
-};
+import { ConfigurePreviewAccordion } from '../shared/configure-preview-accordion';
type ChatEditorPreviewProps = {
- workflow: WorkflowResponseDto;
- step: StepDataDto;
- formValues: Record;
+ editorValue: string;
+ setEditorValue: (value: string) => void;
+ previewStep: () => void;
+ previewData?: GeneratePreviewResponseDto;
+ isPreviewPending: boolean;
};
-const extensions = [loadLanguage('json')?.extension ?? []];
-
-export const ChatEditorPreview = ({ workflow, step, formValues }: ChatEditorPreviewProps) => {
- const workflowSlug = workflow.workflowId;
- const stepSlug = step.stepId;
- const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({
- workflowSlug,
- stepSlug,
- controlValues: formValues,
- });
- const [accordionValue, setAccordionValue] = useState(getInitialAccordionValue(editorValue));
- const [payloadError, setPayloadError] = useState('');
- const [height, setHeight] = useState(0);
- const contentRef = useRef(null);
-
- useEffect(() => {
- setAccordionValue(getInitialAccordionValue(editorValue));
- }, [editorValue]);
-
- useEffect(() => {
- const timeout = setTimeout(() => {
- if (contentRef.current) {
- const rect = contentRef.current.getBoundingClientRect();
- setHeight(rect.height);
- }
- }, 0);
-
- return () => clearTimeout(timeout);
- }, [editorValue]);
-
+export const ChatEditorPreview = ({
+ editorValue,
+ setEditorValue,
+ previewStep,
+ previewData,
+ isPreviewPending = false,
+}: ChatEditorPreviewProps) => {
return (
@@ -70,46 +35,7 @@ export const ChatEditorPreview = ({ workflow, step, formValues }: ChatEditorPrev
className="w-full px-3"
/>
-
-
-
-
-
- Configure preview
-
-
-
-
- {payloadError && {payloadError}
}
- {
- try {
- previewStep();
- setPayloadError('');
- } catch (e) {
- setPayloadError(String(e));
- }
- }}
- >
- Apply
-
-
-
-
+
);
diff --git a/apps/dashboard/src/components/workflow-editor/steps/chat/chat-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/chat/chat-tabs.tsx
index 92f6b83024a..848be0f7a96 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/chat/chat-tabs.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/chat/chat-tabs.tsx
@@ -7,6 +7,7 @@ import { CustomStepControls } from '@/components/workflow-editor/steps/controls/
import { TemplateTabs } from '@/components/workflow-editor/steps/template-tabs';
import { ChatEditor } from '@/components/workflow-editor/steps/chat/chat-editor';
import { ChatEditorPreview } from '@/components/workflow-editor/steps/chat/chat-editor-preview';
+import { useEditorPreview } from '../use-editor-preview';
export const ChatTabs = (props: StepEditorProps) => {
const { workflow, step } = props;
@@ -16,6 +17,12 @@ export const ChatTabs = (props: StepEditorProps) => {
const isNovuCloud = !!(workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema);
const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL;
+ const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({
+ workflowSlug: workflow.workflowId,
+ stepSlug: step.stepId,
+ controlValues: form.getValues(),
+ });
+
const editorContent = (
<>
{isNovuCloud && }
@@ -23,7 +30,15 @@ export const ChatTabs = (props: StepEditorProps) => {
>
);
- const previewContent = ;
+ const previewContent = (
+
+ );
return (
{
previewContent={previewContent}
tabsValue={tabsValue}
onTabChange={setTabsValue}
+ previewStep={previewStep}
/>
);
};
diff --git a/apps/dashboard/src/components/workflow-editor/steps/controls/custom-step-controls.tsx b/apps/dashboard/src/components/workflow-editor/steps/controls/custom-step-controls.tsx
index b3430817112..1d7b83f70f5 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/controls/custom-step-controls.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/controls/custom-step-controls.tsx
@@ -112,7 +112,7 @@ export const CustomStepControls = (props: CustomStepControlsProps) => {
Override code defined defaults
- Code-defined defaults are read-only by default, you can override them using this toggle.
+ Code-defined defaults are read-only by default, you can allow overrides using this toggle.
{
<>
-
-
- Aggregated by
-
+ Aggregated by
diff --git a/apps/dashboard/src/components/workflow-editor/steps/digest/digest-window.tsx b/apps/dashboard/src/components/workflow-editor/steps/digest/digest-window.tsx
index a34042728d7..eb5e43cebfe 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/digest/digest-window.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/digest/digest-window.tsx
@@ -1,6 +1,5 @@
import { useState } from 'react';
import { Tabs } from '@radix-ui/react-tabs';
-import { RiCalendarScheduleFill } from 'react-icons/ri';
import { FieldValues, useFormContext } from 'react-hook-form';
import { TimeUnitEnum } from '@novu/shared';
@@ -67,12 +66,7 @@ export const DigestWindow = () => {
return (
-
-
-
- Digest window
-
-
+
Digest window
{
- try {
- return Object.keys(JSON.parse(value)).length > 0 ? 'payload' : undefined;
- } catch (e) {
- return undefined;
- }
-};
+import { Skeleton } from '@/components/primitives/skeleton';
+import { ConfigurePreviewAccordion } from '../shared/configure-preview-accordion';
type EmailEditorPreviewProps = {
- workflow: WorkflowResponseDto;
- step: StepDataDto;
- formValues: Record;
+ editorValue: string;
+ setEditorValue: (value: string) => void;
+ previewStep: () => void;
+ previewData?: GeneratePreviewResponseDto;
+ isPreviewPending: boolean;
};
-export const EmailEditorPreview = ({ workflow, step, formValues }: EmailEditorPreviewProps) => {
- const workflowSlug = workflow.workflowId;
- const stepSlug = step.stepId;
- const { editorValue, setEditorValue, isPreviewPending, previewData, previewStep } = useEditorPreview({
- workflowSlug,
- stepSlug,
- controlValues: formValues,
- });
- const [accordionValue, setAccordionValue] = useState(getInitialAccordionValue(editorValue));
- const [payloadError, setPayloadError] = useState('');
- const [height, setHeight] = useState(0);
+export const EmailEditorPreview = ({
+ editorValue,
+ setEditorValue,
+ previewStep,
+ previewData,
+ isPreviewPending = false,
+}: EmailEditorPreviewProps) => {
const [activeTab, setActiveTab] = useState('desktop');
- const contentRef = useRef(null);
-
- useEffect(() => {
- setAccordionValue(getInitialAccordionValue(editorValue));
- }, [editorValue]);
-
- useEffect(() => {
- const timeout = setTimeout(() => {
- if (contentRef.current) {
- const rect = contentRef.current.getBoundingClientRect();
- setHeight(rect.height);
- }
- }, 0);
-
- return () => clearTimeout(timeout);
- }, [editorValue]);
return (
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
{isPreviewPending ? (
@@ -113,46 +84,7 @@ export const EmailEditorPreview = ({ workflow, step, formValues }: EmailEditorPr
>
)}
-
-
-
-
-
- Configure preview
-
-
-
-
- {payloadError && {payloadError}
}
- {
- try {
- previewStep();
- setPayloadError('');
- } catch (e) {
- setPayloadError(String(e));
- }
- }}
- >
- Apply
-
-
-
-
+
diff --git a/apps/dashboard/src/components/workflow-editor/steps/email/email-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/email/email-tabs.tsx
index e549af18818..c8ce24b76ff 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/email/email-tabs.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/email/email-tabs.tsx
@@ -6,6 +6,7 @@ import { EmailEditorPreview } from '@/components/workflow-editor/steps/email/ema
import { CustomStepControls } from '../controls/custom-step-controls';
import { StepEditorProps } from '@/components/workflow-editor/steps/configure-step-template-form';
import { TemplateTabs } from '@/components/workflow-editor/steps/template-tabs';
+import { useEditorPreview } from '../use-editor-preview';
export const EmailTabs = (props: StepEditorProps) => {
const { workflow, step } = props;
@@ -16,6 +17,12 @@ export const EmailTabs = (props: StepEditorProps) => {
const isNovuCloud = workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema;
const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL;
+ const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({
+ workflowSlug: workflow.workflowId,
+ stepSlug: step.stepId,
+ controlValues: form.getValues(),
+ });
+
const editorContent = (
<>
{isNovuCloud && }
@@ -23,10 +30,19 @@ export const EmailTabs = (props: StepEditorProps) => {
>
);
- const previewContent = ;
+ const previewContent = (
+
+ );
return (
{
const [_, setEditor] = useState();
const { control } = useFormContext();
+ const isForBlockEnabled = useFeatureFlag(FeatureFlagsKeysEnum.IS_ND_EMAIL_FOR_BLOCK_ENABLED);
+ const isShowEnabled = useFeatureFlag(FeatureFlagsKeysEnum.IS_ND_EMAIL_SHOW_ENABLED);
+
return (
{
render={({ field }) => {
return (
<>
+ {!isShowEnabled && (
+
+ )}
{
image,
section,
columns,
- forLoop,
+ ...(isForBlockEnabled ? [forLoop] : []),
divider,
spacer,
button,
@@ -85,7 +98,7 @@ export const Maily = (props: MailyProps) => {
variables={({ query, editor, from }) => {
const queryWithoutSuffix = query.replace(/}+$/, '');
- const autoAddVariable = () => {
+ function addInlineVariable() {
if (!query.endsWith('}}')) {
return;
}
@@ -98,9 +111,15 @@ export const Maily = (props: MailyProps) => {
editor?.commands.deleteRange({ from, to });
editor?.commands.insertContent({
type: 'variable',
- attrs: { id: queryWithoutSuffix, label: null, fallback: null, showIfKey: null },
+ attrs: {
+ id: queryWithoutSuffix,
+ label: null,
+ fallback: null,
+ showIfKey: null,
+ required: false,
+ },
});
- };
+ }
const filteredVariables: { name: string; required: boolean }[] = [];
@@ -110,7 +129,7 @@ export const Maily = (props: MailyProps) => {
filteredVariables.push({ name: queryWithoutSuffix, required: false });
}
- autoAddVariable();
+ addInlineVariable();
return dedupAndSortVariables(filteredVariables, queryWithoutSuffix);
}
@@ -123,7 +142,7 @@ export const Maily = (props: MailyProps) => {
filteredVariables.push({ name: queryWithoutSuffix, required: false });
}
- autoAddVariable();
+ addInlineVariable();
return dedupAndSortVariables(filteredVariables, queryWithoutSuffix);
}}
contentJson={field.value ? JSON.parse(field.value) : undefined}
diff --git a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-editor-preview.tsx b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-editor-preview.tsx
index dacfe550a16..3149e2c0f3a 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-editor-preview.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-editor-preview.tsx
@@ -1,60 +1,24 @@
-import { CSSProperties, useEffect, useRef, useState } from 'react';
-import { type StepDataDto, type WorkflowResponseDto } from '@novu/shared';
-
+import { GeneratePreviewResponseDto } from '@novu/shared';
import { Notification5Fill } from '@/components/icons';
-import { Code2 } from '@/components/icons/code-2';
-import { Button } from '@/components/primitives/button';
-import { Editor } from '@/components/primitives/editor';
-import { loadLanguage } from '@uiw/codemirror-extensions-langs';
-import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/primitives/accordion';
import { InAppTabsSection } from '@/components/workflow-editor/steps/in-app/in-app-tabs-section';
-import { useEditorPreview } from '../use-editor-preview';
import { InboxPreview } from './inbox-preview';
-
-const getInitialAccordionValue = (value: string) => {
- try {
- return Object.keys(JSON.parse(value)).length > 0 ? 'payload' : undefined;
- } catch (e) {
- return undefined;
- }
-};
+import { ConfigurePreviewAccordion } from '../shared/configure-preview-accordion';
type InAppEditorPreviewProps = {
- workflow: WorkflowResponseDto;
- step: StepDataDto;
- formValues: Record;
+ editorValue: string;
+ setEditorValue: (value: string) => void;
+ previewStep: () => void;
+ previewData?: GeneratePreviewResponseDto;
+ isPreviewPending: boolean;
};
-const extensions = [loadLanguage('json')?.extension ?? []];
-
-export const InAppEditorPreview = ({ workflow, step, formValues }: InAppEditorPreviewProps) => {
- const workflowSlug = workflow.workflowId;
- const stepSlug = step.stepId;
- const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({
- workflowSlug,
- stepSlug,
- controlValues: formValues,
- });
- const [accordionValue, setAccordionValue] = useState(getInitialAccordionValue(editorValue));
- const [payloadError, setPayloadError] = useState('');
- const [height, setHeight] = useState(0);
- const contentRef = useRef(null);
-
- useEffect(() => {
- setAccordionValue(getInitialAccordionValue(editorValue));
- }, [editorValue]);
-
- useEffect(() => {
- const timeout = setTimeout(() => {
- if (contentRef.current) {
- const rect = contentRef.current.getBoundingClientRect();
- setHeight(rect.height);
- }
- }, 0);
-
- return () => clearTimeout(timeout);
- }, [editorValue]);
-
+export const InAppEditorPreview = ({
+ editorValue,
+ setEditorValue,
+ previewStep,
+ previewData,
+ isPreviewPending = false,
+}: InAppEditorPreviewProps) => {
return (
@@ -63,46 +27,7 @@ export const InAppEditorPreview = ({ workflow, step, formValues }: InAppEditorPr
In-App template editor
-
-
-
-
-
- Configure preview
-
-
-
-
- {payloadError && {payloadError}
}
- {
- try {
- previewStep();
- setPayloadError('');
- } catch (e) {
- setPayloadError(String(e));
- }
- }}
- >
- Apply
-
-
-
-
+
);
diff --git a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-tabs.tsx
index 2ee9cf46e3f..50cc98a4179 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-tabs.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/in-app/in-app-tabs.tsx
@@ -6,6 +6,7 @@ import { CustomStepControls } from '../controls/custom-step-controls';
import { StepEditorProps } from '@/components/workflow-editor/steps/configure-step-template-form';
import { TemplateTabs } from '@/components/workflow-editor/steps/template-tabs';
import { WorkflowOriginEnum } from '@/utils/enums';
+import { useEditorPreview } from '../use-editor-preview';
export const InAppTabs = (props: StepEditorProps) => {
const { workflow, step } = props;
@@ -16,6 +17,12 @@ export const InAppTabs = (props: StepEditorProps) => {
const isNovuCloud = workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema;
const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL;
+ const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({
+ workflowSlug: workflow.workflowId,
+ stepSlug: step.stepId,
+ controlValues: form.getValues(),
+ });
+
const editorContent = (
<>
{isNovuCloud && }
@@ -23,10 +30,19 @@ export const InAppTabs = (props: StepEditorProps) => {
>
);
- const previewContent = ;
+ const previewContent = (
+
+ );
return (
{
- try {
- return Object.keys(JSON.parse(value)).length > 0 ? 'payload' : undefined;
- } catch (e) {
- return undefined;
- }
-};
+import { ConfigurePreviewAccordion } from '../shared/configure-preview-accordion';
+import { GeneratePreviewResponseDto } from '@novu/shared';
type PushEditorPreviewProps = {
- workflow: WorkflowResponseDto;
- step: StepDataDto;
- formValues: Record;
+ editorValue: string;
+ setEditorValue: (value: string) => void;
+ previewStep: () => void;
+ previewData?: GeneratePreviewResponseDto;
+ isPreviewPending: boolean;
};
-const extensions = [loadLanguage('json')?.extension ?? []];
-
-export const PushEditorPreview = ({ workflow, step, formValues }: PushEditorPreviewProps) => {
- const workflowSlug = workflow.workflowId;
- const stepSlug = step.stepId;
- const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({
- workflowSlug,
- stepSlug,
- controlValues: formValues,
- });
- const [accordionValue, setAccordionValue] = useState(getInitialAccordionValue(editorValue));
- const [payloadError, setPayloadError] = useState('');
- const [height, setHeight] = useState(0);
- const contentRef = useRef(null);
-
- useEffect(() => {
- setAccordionValue(getInitialAccordionValue(editorValue));
- }, [editorValue]);
-
- useEffect(() => {
- const timeout = setTimeout(() => {
- if (contentRef.current) {
- const rect = contentRef.current.getBoundingClientRect();
- setHeight(rect.height);
- }
- }, 0);
-
- return () => clearTimeout(timeout);
- }, [editorValue]);
-
+export const PushEditorPreview = ({
+ editorValue,
+ setEditorValue,
+ previewStep,
+ previewData,
+ isPreviewPending,
+}: PushEditorPreviewProps) => {
return (
@@ -70,46 +34,7 @@ export const PushEditorPreview = ({ workflow, step, formValues }: PushEditorPrev
className="w-full px-3"
/>
-
-
-
-
-
- Configure preview
-
-
-
-
- {payloadError && {payloadError}
}
- {
- try {
- previewStep();
- setPayloadError('');
- } catch (e) {
- setPayloadError(String(e));
- }
- }}
- >
- Apply
-
-
-
-
+
);
diff --git a/apps/dashboard/src/components/workflow-editor/steps/push/push-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/push/push-tabs.tsx
index ad9fb2a1e39..23a725daaaf 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/push/push-tabs.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/push/push-tabs.tsx
@@ -6,6 +6,7 @@ import { CustomStepControls } from '../controls/custom-step-controls';
import { TemplateTabs } from '../template-tabs';
import { PushEditorPreview } from './push-editor-preview';
import { useFormContext } from 'react-hook-form';
+import { useEditorPreview } from '../use-editor-preview';
export const PushTabs = (props: StepEditorProps) => {
const { workflow, step } = props;
@@ -15,6 +16,12 @@ export const PushTabs = (props: StepEditorProps) => {
const isNovuCloud = workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema;
const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL;
+ const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({
+ workflowSlug: workflow.workflowId,
+ stepSlug: step.stepId,
+ controlValues: form.getValues(),
+ });
+
const editorContent = (
<>
{isNovuCloud && }
@@ -22,10 +29,19 @@ export const PushTabs = (props: StepEditorProps) => {
>
);
- const previewContent = ;
+ const previewContent = (
+
+ );
return (
void;
+ onUpdate: () => void;
+};
+
+export const ConfigurePreviewAccordion = ({
+ editorValue,
+ setEditorValue,
+ onUpdate,
+}: ConfigurePreviewAccordionProps) => {
+ const [accordionValue, setAccordionValue] = useState('payload');
+ const [payloadError, setPayloadError] = useState('');
+ const [height, setHeight] = useState(0);
+ const contentRef = useRef(null);
+
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ if (contentRef.current) {
+ const rect = contentRef.current.getBoundingClientRect();
+ setHeight(rect.height);
+ }
+ }, 0);
+
+ return () => clearTimeout(timeout);
+ }, [editorValue]);
+
+ return (
+
+
+
+
+
+ Configure preview
+
+
+
+
+ {payloadError && {payloadError}
}
+ {
+ try {
+ onUpdate();
+ setPayloadError('');
+ } catch (e) {
+ setPayloadError(String(e));
+ }
+ }}
+ >
+ Apply
+
+
+
+
+ );
+};
diff --git a/apps/dashboard/src/components/workflow-editor/steps/sms/sms-editor-preview.tsx b/apps/dashboard/src/components/workflow-editor/steps/sms/sms-editor-preview.tsx
index 8f28420699b..8848158b799 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/sms/sms-editor-preview.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/sms/sms-editor-preview.tsx
@@ -1,61 +1,25 @@
-import { type StepDataDto, type WorkflowResponseDto } from '@novu/shared';
-import { CSSProperties, useEffect, useRef, useState } from 'react';
-
import { Sms } from '@/components/icons';
-import { Code2 } from '@/components/icons/code-2';
-import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/primitives/accordion';
-import { Button } from '@/components/primitives/button';
-import { Editor } from '@/components/primitives/editor';
import { SmsPreview } from '@/components/workflow-editor/steps/sms/sms-preview';
-import { loadLanguage } from '@uiw/codemirror-extensions-langs';
-import { useEditorPreview } from '../use-editor-preview';
import { InlineToast } from '@/components/primitives/inline-toast';
import { TabsSection } from '@/components/workflow-editor/steps/tabs-section';
-
-const getInitialAccordionValue = (value: string) => {
- try {
- return Object.keys(JSON.parse(value)).length > 0 ? 'payload' : undefined;
- } catch (e) {
- return undefined;
- }
-};
+import { ConfigurePreviewAccordion } from '../shared/configure-preview-accordion';
+import { GeneratePreviewResponseDto } from '@novu/shared';
type SmsEditorPreviewProps = {
- workflow: WorkflowResponseDto;
- step: StepDataDto;
- formValues: Record;
+ editorValue: string;
+ setEditorValue: (value: string) => void;
+ previewStep: () => void;
+ previewData?: GeneratePreviewResponseDto;
+ isPreviewPending: boolean;
};
-const extensions = [loadLanguage('json')?.extension ?? []];
-
-export const SmsEditorPreview = ({ workflow, step, formValues }: SmsEditorPreviewProps) => {
- const workflowSlug = workflow.workflowId;
- const stepSlug = step.stepId;
- const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({
- workflowSlug,
- stepSlug,
- controlValues: formValues,
- });
- const [accordionValue, setAccordionValue] = useState(getInitialAccordionValue(editorValue));
- const [payloadError, setPayloadError] = useState('');
- const [height, setHeight] = useState(0);
- const contentRef = useRef(null);
-
- useEffect(() => {
- setAccordionValue(getInitialAccordionValue(editorValue));
- }, [editorValue]);
-
- useEffect(() => {
- const timeout = setTimeout(() => {
- if (contentRef.current) {
- const rect = contentRef.current.getBoundingClientRect();
- setHeight(rect.height);
- }
- }, 0);
-
- return () => clearTimeout(timeout);
- }, [editorValue]);
-
+export const SmsEditorPreview = ({
+ editorValue,
+ setEditorValue,
+ previewStep,
+ previewData,
+ isPreviewPending = false,
+}: SmsEditorPreviewProps) => {
return (
@@ -70,46 +34,7 @@ export const SmsEditorPreview = ({ workflow, step, formValues }: SmsEditorPrevie
className="w-full px-3"
/>
-
-
-
-
-
- Configure preview
-
-
-
-
- {payloadError && {payloadError}
}
- {
- try {
- previewStep();
- setPayloadError('');
- } catch (e) {
- setPayloadError(String(e));
- }
- }}
- >
- Apply
-
-
-
-
+
);
diff --git a/apps/dashboard/src/components/workflow-editor/steps/sms/sms-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/sms/sms-tabs.tsx
index 5bda1b6e89f..dc32dd5730c 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/sms/sms-tabs.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/sms/sms-tabs.tsx
@@ -6,6 +6,7 @@ import { TemplateTabs } from '@/components/workflow-editor/steps/template-tabs';
import { WorkflowOriginEnum } from '@novu/shared';
import { useState } from 'react';
import { useFormContext } from 'react-hook-form';
+import { useEditorPreview } from '../use-editor-preview';
export const SmsTabs = (props: StepEditorProps) => {
const { workflow, step } = props;
@@ -13,6 +14,12 @@ export const SmsTabs = (props: StepEditorProps) => {
const form = useFormContext();
const [tabsValue, setTabsValue] = useState('editor');
+ const { editorValue, setEditorValue, previewStep, previewData, isPreviewPending } = useEditorPreview({
+ workflowSlug: workflow.workflowId,
+ stepSlug: step.stepId,
+ controlValues: form.getValues(),
+ });
+
const isNovuCloud = workflow.origin === WorkflowOriginEnum.NOVU_CLOUD && uiSchema;
const isExternal = workflow.origin === WorkflowOriginEnum.EXTERNAL;
@@ -23,7 +30,15 @@ export const SmsTabs = (props: StepEditorProps) => {
>
);
- const previewContent = ;
+ const previewContent = (
+
+ );
return (
{
previewContent={previewContent}
tabsValue={tabsValue}
onTabChange={setTabsValue}
+ previewStep={previewStep}
/>
);
};
diff --git a/apps/dashboard/src/components/workflow-editor/steps/template-tabs.tsx b/apps/dashboard/src/components/workflow-editor/steps/template-tabs.tsx
index 3f3b31fdea3..bbe587ec1ce 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/template-tabs.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/template-tabs.tsx
@@ -1,6 +1,7 @@
import { Cross2Icon } from '@radix-ui/react-icons';
import { RiEdit2Line, RiPencilRuler2Line } from 'react-icons/ri';
import { useNavigate } from 'react-router-dom';
+import React, { useEffect } from 'react';
import { Notification5Fill } from '@/components/icons';
import { Button } from '@/components/primitives/button';
@@ -12,11 +13,26 @@ interface TemplateTabsProps {
previewContent?: React.ReactNode;
tabsValue: string;
onTabChange: (tab: string) => void;
+ previewStep?: () => void;
}
-export const TemplateTabs = ({ editorContent, previewContent, tabsValue, onTabChange }: TemplateTabsProps) => {
+export const TemplateTabs = ({
+ editorContent,
+ previewContent,
+ tabsValue,
+ onTabChange,
+ previewStep,
+}: TemplateTabsProps) => {
const navigate = useNavigate();
+ useEffect(() => {
+ // We reload the preview when the tab changes to get the latest values
+ if (tabsValue === 'preview') {
+ previewStep?.();
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [tabsValue]);
+
return (
diff --git a/apps/dashboard/src/components/workflow-editor/steps/use-editor-preview.tsx b/apps/dashboard/src/components/workflow-editor/steps/use-editor-preview.tsx
index 73e7c78037c..181b33fe62f 100644
--- a/apps/dashboard/src/components/workflow-editor/steps/use-editor-preview.tsx
+++ b/apps/dashboard/src/components/workflow-editor/steps/use-editor-preview.tsx
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import * as Sentry from '@sentry/react';
+import isEqual from 'lodash.isequal';
import { usePreviewStep } from '@/hooks/use-preview-step';
import { useDataRef } from '@/hooks/use-data-ref';
@@ -20,7 +21,10 @@ export const useEditorPreview = ({
isPending: isPreviewPending,
} = usePreviewStep({
onSuccess: (res) => {
- setEditorValue(JSON.stringify(res.previewPayloadExample, null, 2));
+ const newValue = JSON.stringify(res.previewPayloadExample, null, 2);
+ if (!isEqual(editorValue, newValue)) {
+ setEditorValue(newValue);
+ }
},
onError: (error) => {
Sentry.captureException(error);
diff --git a/apps/dashboard/src/components/workflow-editor/test-workflow/test-workflow-logs-sidebar.tsx b/apps/dashboard/src/components/workflow-editor/test-workflow/test-workflow-logs-sidebar.tsx
index a8df615967e..d34b817fa6e 100644
--- a/apps/dashboard/src/components/workflow-editor/test-workflow/test-workflow-logs-sidebar.tsx
+++ b/apps/dashboard/src/components/workflow-editor/test-workflow/test-workflow-logs-sidebar.tsx
@@ -1,52 +1,44 @@
-import { ActivityPanel } from '@/components/activity/activity-panel';
import { useEffect, useState } from 'react';
-import { JobStatusEnum } from '@novu/shared';
import { Loader2 } from 'lucide-react';
+
+import { ActivityPanel } from '@/components/activity/activity-panel';
import { WorkflowTriggerInboxIllustration } from '../../icons/workflow-trigger-inbox';
import { useFetchActivities } from '../../../hooks/use-fetch-activities';
-import { QueryKeys } from '../../../utils/query-keys';
-import { useEnvironment } from '../../../context/environment/hooks';
-import { useQueryClient } from '@tanstack/react-query';
type TestWorkflowLogsSidebarProps = {
transactionId?: string;
};
export const TestWorkflowLogsSidebar = ({ transactionId }: TestWorkflowLogsSidebarProps) => {
- const queryClient = useQueryClient();
- const { currentEnvironment } = useEnvironment();
+ const [parentActivityId, setParentActivityId] = useState(undefined);
const [shouldRefetch, setShouldRefetch] = useState(true);
const { activities } = useFetchActivities(
{
filters: transactionId ? { transactionId } : undefined,
},
{
- enabled: transactionId !== undefined,
+ enabled: !!transactionId,
refetchInterval: shouldRefetch ? 1000 : false,
}
);
+ const activityId: string | undefined = parentActivityId ?? activities?.[0]?._id;
useEffect(() => {
- if (!activities?.length) return;
-
- const activity = activities[0];
- const isPending = activity.jobs?.some((job) => job.status === JobStatusEnum.PENDING);
-
- // Only stop refetching if we have an activity and it's not pending
- setShouldRefetch(isPending);
-
- queryClient.invalidateQueries({
- queryKey: [QueryKeys.fetchActivity, currentEnvironment?._id, activity._id],
- });
- }, [activities]);
+ if (activityId) {
+ setShouldRefetch(false);
+ }
+ }, [activityId]);
// Reset refetch when transaction ID changes
useEffect(() => {
+ if (!transactionId) {
+ return;
+ }
+
setShouldRefetch(true);
+ setParentActivityId(undefined);
}, [transactionId]);
- const activityId = activities?.[0]?._id;
-
return (
{transactionId && !activityId ? (
@@ -59,7 +51,7 @@ export const TestWorkflowLogsSidebar = ({ transactionId }: TestWorkflowLogsSideb
) : activityId ? (
{}}
+ onActivitySelect={setParentActivityId}
headerClassName="h-[49px]"
overviewHeaderClassName="border-t-0"
/>
diff --git a/apps/dashboard/src/hooks/use-fetch-activity.ts b/apps/dashboard/src/hooks/use-fetch-activity.ts
index fb5fa67fdd7..07ab1a67c72 100644
--- a/apps/dashboard/src/hooks/use-fetch-activity.ts
+++ b/apps/dashboard/src/hooks/use-fetch-activity.ts
@@ -4,13 +4,23 @@ import type { IActivity } from '@novu/shared';
import { useEnvironment } from '@/context/environment/hooks';
import { getNotification } from '@/api/activity';
-export function useFetchActivity({ activityId }: { activityId?: string }) {
+export function useFetchActivity(
+ { activityId }: { activityId?: string },
+ {
+ refetchInterval = false,
+ refetchOnWindowFocus = false,
+ staleTime = 0,
+ }: { refetchInterval?: number | false; refetchOnWindowFocus?: boolean; staleTime?: number } = {}
+) {
const { currentEnvironment } = useEnvironment();
const { data, isPending, error } = useQuery<{ data: IActivity }>({
queryKey: [QueryKeys.fetchActivity, currentEnvironment?._id, activityId],
queryFn: () => getNotification(activityId!, currentEnvironment!),
enabled: !!currentEnvironment?._id && !!activityId,
+ refetchInterval,
+ refetchOnWindowFocus,
+ staleTime,
});
return {
diff --git a/apps/dashboard/src/hooks/use-preview-step.ts b/apps/dashboard/src/hooks/use-preview-step.ts
index a31693f44ab..8fb2bb7590f 100644
--- a/apps/dashboard/src/hooks/use-preview-step.ts
+++ b/apps/dashboard/src/hooks/use-preview-step.ts
@@ -11,11 +11,13 @@ export const usePreviewStep = ({
onError,
}: { onSuccess?: (data: GeneratePreviewResponseDto) => void; onError?: (error: Error) => void } = {}) => {
const { currentEnvironment } = useEnvironment();
- const { mutateAsync, isPending, error, data } = useMutation({
- mutationFn: (args: PreviewStepParameters) => previewStep({ environment: currentEnvironment!, ...args }),
- onSuccess,
- onError,
- });
+ const { mutateAsync, isPending, error, data } = useMutation(
+ {
+ mutationFn: (args: PreviewStepParameters) => previewStep({ environment: currentEnvironment!, ...args }),
+ onSuccess,
+ onError,
+ }
+ );
return {
previewStep: mutateAsync,
diff --git a/apps/dashboard/src/hooks/use-sync-workflow.tsx b/apps/dashboard/src/hooks/use-sync-workflow.tsx
index cf58ffbf456..cce12d5142a 100644
--- a/apps/dashboard/src/hooks/use-sync-workflow.tsx
+++ b/apps/dashboard/src/hooks/use-sync-workflow.tsx
@@ -11,11 +11,14 @@ import { WorkflowOriginEnum, WorkflowStatusEnum } from '@novu/shared';
import { useMutation } from '@tanstack/react-query';
import { useMemo, useState } from 'react';
import { toast } from 'sonner';
+import { useNavigate } from 'react-router-dom';
+import { buildRoute, ROUTES } from '@/utils/routes';
export function useSyncWorkflow(workflow: WorkflowResponseDto | WorkflowListResponseDto) {
const { currentEnvironment, oppositeEnvironment, switchEnvironment } = useEnvironment();
const [isLoading, setIsLoading] = useState(false);
const [showConfirmModal, setShowConfirmModal] = useState(false);
+ const navigate = useNavigate();
let loadingToast: string | number | undefined = undefined;
@@ -79,7 +82,10 @@ export function useSyncWorkflow(workflow: WorkflowResponseDto | WorkflowListResp
actionLabel={`Switch to ${environment?.name}`}
onAction={() => {
close();
- switchEnvironment(environment?.slug || '');
+ const targetSlug = environment?.slug || '';
+ switchEnvironment(targetSlug);
+
+ navigate(buildRoute(ROUTES.WORKFLOWS, { environmentSlug: targetSlug }));
}}
onClose={close}
/>
diff --git a/apps/dashboard/src/index.css b/apps/dashboard/src/index.css
index a19dc9a225e..e4b76b388bd 100644
--- a/apps/dashboard/src/index.css
+++ b/apps/dashboard/src/index.css
@@ -353,4 +353,4 @@
height: 100%;
scroll-behavior: smooth;
}
-}
+}
\ No newline at end of file
diff --git a/apps/worker/src/app/workflow/usecases/add-job/add-job.usecase.ts b/apps/worker/src/app/workflow/usecases/add-job/add-job.usecase.ts
index 62b0e07fc9b..a29a6bdf30b 100644
--- a/apps/worker/src/app/workflow/usecases/add-job/add-job.usecase.ts
+++ b/apps/worker/src/app/workflow/usecases/add-job/add-job.usecase.ts
@@ -173,9 +173,9 @@ export class AddJob {
organizationId: command.organizationId,
})
);
-
- if (errors) {
- Logger.warn({ errors, jobId: job._id }, 'Defer duration limit exceeded for job', LOG_CONTEXT);
+ if (errors.length > 0) {
+ const errorMessages = errors?.map((error) => error.message).join(', ');
+ Logger.warn({ errors, jobId: job._id }, errorMessages, LOG_CONTEXT);
await this.executionLogRoute.execute(
ExecutionLogRouteCommand.create({
@@ -185,6 +185,7 @@ export class AddJob {
status: ExecutionDetailsStatusEnum.FAILED,
isTest: false,
isRetry: false,
+ raw: JSON.stringify({ errors: errorMessages }),
})
);
diff --git a/eslint.config.mjs b/eslint.config.mjs
index a3eef606f13..361bb97d41b 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -43,7 +43,6 @@ const noRestrictedImportsMultiLevelNovuPattern = {
group: [
'@novu/*/**/*',
'!@novu/api/**/*', // This allows all imports from @novu/api
- // '!@novu/api/funcs/triggerBulk',
// These packages have legitimate exports 1 path part below the root level
// This flatMap logic ignores the path 1 below the root level and prevents deeper imports.
...['framework', 'js', 'novui'].flatMap((pkg) => [`!@novu/${pkg}/**/*`, `@novu/${pkg}/*/**/*`]),
diff --git a/libs/application-generic/package.json b/libs/application-generic/package.json
index e25a2df3866..185854a1525 100644
--- a/libs/application-generic/package.json
+++ b/libs/application-generic/package.json
@@ -83,6 +83,7 @@
"launchdarkly-node-server-sdk": "^7.0.1",
"lodash": "^4.17.15",
"mixpanel": "^0.17.0",
+ "cron-parser": "^4.9.0",
"nanoid": "^3.1.20",
"nestjs-otel": "6.1.1",
"nestjs-pino": "4.1.0",
diff --git a/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.command.ts b/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.command.ts
index edc5ed496cd..b6344851820 100644
--- a/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.command.ts
+++ b/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.command.ts
@@ -1,13 +1,26 @@
-import { IsEnum, IsNumber, IsOptional } from 'class-validator';
-
+import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator';
import { StepTypeEnum } from '@novu/shared';
+import { Transform } from 'class-transformer';
import { OrganizationLevelCommand } from '../../commands';
export class TierRestrictionsValidateCommand extends OrganizationLevelCommand {
+ @IsOptional()
+ @Transform(({ value }) => (value ? String(value) : value))
+ @IsString()
+ amount?: string;
+
+ @IsString()
+ @IsOptional()
+ unit?: string;
+
@IsNumber()
@IsOptional()
deferDurationMs?: number;
+ @IsOptional()
+ @IsString()
+ cron?: string;
+
@IsEnum(StepTypeEnum)
@IsOptional()
stepType?: StepTypeEnum;
diff --git a/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.consts.ts b/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.consts.ts
deleted file mode 100644
index 33bee08b8b3..00000000000
--- a/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.consts.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export const MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
-export const FREE_TIER_MAX_DELAY_DAYS = 30;
-export const BUSINESS_TIER_MAX_DELAY_DAYS = 90;
-export const MAX_DELAY_FREE_TIER =
- FREE_TIER_MAX_DELAY_DAYS * MILLISECONDS_IN_DAY; // 30 days in milliseconds
-export const MAX_DELAY_BUSINESS_TIER =
- BUSINESS_TIER_MAX_DELAY_DAYS * MILLISECONDS_IN_DAY; // 90 days in milliseconds
diff --git a/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.response.ts b/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.response.ts
index 53d7b9220a6..c3675843885 100644
--- a/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.response.ts
+++ b/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.response.ts
@@ -1,10 +1,12 @@
export enum ErrorEnum {
TIER_LIMIT_EXCEEDED = 'TIER_LIMIT_EXCEEDED',
+ INVALID_DEFER_DURATION = 'INVALID_DEFER_DURATION',
}
-type TierValidationError = {
+export type TierValidationError = {
+ controlKey: string;
error: ErrorEnum;
message: string;
};
-export type TierRestrictionsValidateResponse = TierValidationError[] | null;
+export type TierRestrictionsValidateResponse = TierValidationError[];
diff --git a/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.usecase.ts b/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.usecase.ts
index 5d4ba0a931d..3aade636a7c 100644
--- a/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.usecase.ts
+++ b/libs/application-generic/src/usecases/tier-restrictions-validate/tier-restrictions-validate.usecase.ts
@@ -1,21 +1,31 @@
import { Injectable } from '@nestjs/common';
+import { parseExpression as parseCronExpression } from 'cron-parser';
-import { ApiServiceLevelEnum, StepTypeEnum } from '@novu/shared';
+import {
+ ApiServiceLevelEnum,
+ DigestUnitEnum,
+ StepTypeEnum,
+} from '@novu/shared';
import { CommunityOrganizationRepository } from '@novu/dal';
+import { differenceInMilliseconds } from 'date-fns';
+
import { TierRestrictionsValidateCommand } from './tier-restrictions-validate.command';
import {
ErrorEnum,
TierRestrictionsValidateResponse,
+ TierValidationError,
} from './tier-restrictions-validate.response';
-import {
- FREE_TIER_MAX_DELAY_DAYS,
- BUSINESS_TIER_MAX_DELAY_DAYS,
- MAX_DELAY_FREE_TIER,
- MAX_DELAY_BUSINESS_TIER,
-} from './tier-restrictions-validate.consts';
import { InstrumentUsecase } from '../../instrumentation';
+export const MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
+export const FREE_TIER_MAX_DELAY_DAYS = 30;
+export const BUSINESS_TIER_MAX_DELAY_DAYS = 90;
+export const MAX_DELAY_FREE_TIER =
+ FREE_TIER_MAX_DELAY_DAYS * MILLISECONDS_IN_DAY; // 30 days in milliseconds
+export const MAX_DELAY_BUSINESS_TIER =
+ BUSINESS_TIER_MAX_DELAY_DAYS * MILLISECONDS_IN_DAY; // 90 days in milliseconds
+
@Injectable()
export class TierRestrictionsValidateUsecase {
constructor(
@@ -26,57 +36,150 @@ export class TierRestrictionsValidateUsecase {
async execute(
command: TierRestrictionsValidateCommand,
): Promise {
- return this.validateControlValuesByTierLimits(
- command.organizationId,
- command.deferDurationMs,
- command.stepType,
- );
- }
+ if (![StepTypeEnum.DIGEST, StepTypeEnum.DELAY].includes(command.stepType)) {
+ return [];
+ }
- private async validateControlValuesByTierLimits(
- organizationId: string,
- deferDurationMs?: number,
- stepType?: StepTypeEnum,
- ): Promise {
- const controlValueNeedTierValidation =
- stepType === StepTypeEnum.DIGEST || stepType === StepTypeEnum.DELAY;
+ const apiServiceLevel = (
+ await this.organizationRepository.findById(command.organizationId)
+ )?.apiServiceLevel;
- if (!controlValueNeedTierValidation || !deferDurationMs) {
- return null;
- }
+ if (isCronExpression(command.cron)) {
+ // TODO: Implement cron expression validation
- const issues: TierRestrictionsValidateResponse = [];
- const organization =
- await this.organizationRepository.findById(organizationId);
- const tier = organization?.apiServiceLevel;
- const isPaidTier = [
- tier === undefined ||
- tier === ApiServiceLevelEnum.BUSINESS ||
- tier === ApiServiceLevelEnum.ENTERPRISE,
- ];
-
- if (isPaidTier) {
- if (deferDurationMs > MAX_DELAY_BUSINESS_TIER) {
- issues.push({
- error: ErrorEnum.TIER_LIMIT_EXCEEDED,
- message:
- `The maximum delay allowed is ${BUSINESS_TIER_MAX_DELAY_DAYS} days. ` +
- 'Please contact our support team to discuss extending this limit for your use case.',
- });
- }
+ /*
+ * const deferDurationMs = this.buildCronDeltaDeferDuration(command);
+ * const issue = buildIssue(
+ * deferDurationMs,
+ * getMaxDelay(apiServiceLevel),
+ * ErrorEnum.TIER_LIMIT_EXCEEDED,
+ * 'cron',
+ * );
+ */
+
+ return [];
}
- if (tier === ApiServiceLevelEnum.FREE) {
- if (deferDurationMs > MAX_DELAY_FREE_TIER) {
- issues.push({
- error: ErrorEnum.TIER_LIMIT_EXCEEDED,
- message:
- `The maximum delay allowed is ${FREE_TIER_MAX_DELAY_DAYS} days. ` +
- 'Please upgrade your plan.',
- });
- }
+ if (isRegularDeferAction(command)) {
+ const deferDurationMs = calculateDeferDuration(command);
+
+ const amountIssue = buildIssue(
+ deferDurationMs,
+ getMaxDelay(apiServiceLevel),
+ ErrorEnum.TIER_LIMIT_EXCEEDED,
+ 'amount',
+ );
+ const unitIssue = buildIssue(
+ deferDurationMs,
+ getMaxDelay(apiServiceLevel),
+ ErrorEnum.TIER_LIMIT_EXCEEDED,
+ 'unit',
+ );
+
+ return [amountIssue, unitIssue].filter(Boolean);
}
- return issues.length === 0 ? null : issues;
+ return [];
+ }
+
+ private buildCronDeltaDeferDuration(
+ command: TierRestrictionsValidateCommand,
+ ): number | null {
+ const cronExpression = parseCronExpression(command.cron);
+ const firstTime = cronExpression.next().toDate();
+ const secondTime = cronExpression.next().toDate();
+
+ return differenceInMilliseconds(firstTime, secondTime);
+ }
+}
+
+function calculateDeferDuration(
+ command: TierRestrictionsValidateCommand,
+): number | null {
+ if (command.deferDurationMs) {
+ return command.deferDurationMs;
+ }
+
+ if (isValidDigestUnit(command.unit) && isNumber(command.amount)) {
+ return calculateMilliseconds(command.amount, command.unit);
}
+
+ return null;
+}
+
+function isValidDigestUnit(unit: unknown): unit is DigestUnitEnum {
+ return Object.values(DigestUnitEnum).includes(unit as DigestUnitEnum);
+}
+
+function isNumber(value: unknown): value is number {
+ return !Number.isNaN(Number(value));
+}
+
+function calculateMilliseconds(amount: number, unit: DigestUnitEnum): number {
+ switch (unit) {
+ case DigestUnitEnum.SECONDS:
+ return amount * 1000;
+ case DigestUnitEnum.MINUTES:
+ return amount * 1000 * 60;
+ case DigestUnitEnum.HOURS:
+ return amount * 1000 * 60 * 60;
+ case DigestUnitEnum.DAYS:
+ return amount * 1000 * 60 * 60 * 24;
+ case DigestUnitEnum.WEEKS:
+ return amount * 1000 * 60 * 60 * 24 * 7;
+ case DigestUnitEnum.MONTHS:
+ return amount * 1000 * 60 * 60 * 24 * 30; // Using 30 days as an approximation for a month
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Cron expression is another term for a timed digest
+ */
+const isCronExpression = (cron: string) => {
+ return !!cron;
+};
+
+const isRegularDeferAction = (command: TierRestrictionsValidateCommand) => {
+ return (
+ !!command.amount &&
+ isNumber(command.amount) &&
+ !!command.unit &&
+ isValidDigestUnit(command.unit)
+ );
+};
+
+function getMaxDelay(tier: ApiServiceLevelEnum): number {
+ if (
+ tier === ApiServiceLevelEnum.BUSINESS ||
+ tier === ApiServiceLevelEnum.ENTERPRISE
+ ) {
+ return MAX_DELAY_BUSINESS_TIER;
+ }
+
+ return MAX_DELAY_FREE_TIER;
+}
+
+function buildIssue(
+ deferDurationMs: number,
+ maxDelayMs: number,
+ error: ErrorEnum,
+ controlKey: string,
+): TierValidationError | null {
+ if (deferDurationMs > maxDelayMs) {
+ return {
+ controlKey,
+ error,
+ message:
+ `The maximum delay allowed is ${msToDays(maxDelayMs)} days. ` +
+ 'Please contact our support team to discuss extending this limit for your use case.',
+ };
+ }
+
+ return null;
+}
+
+function msToDays(ms: number): number {
+ return Math.floor(ms / (1000 * 60 * 60 * 24));
}
diff --git a/libs/application-generic/src/utils/index.ts b/libs/application-generic/src/utils/index.ts
index a10c9bfa848..11f207996b1 100644
--- a/libs/application-generic/src/utils/index.ts
+++ b/libs/application-generic/src/utils/index.ts
@@ -12,4 +12,3 @@ export * from './subscriber';
export * from './variants';
export * from './deepmerge';
export * from './generate-id';
-export * from './sanitize-preview-control-values';
diff --git a/libs/application-generic/src/utils/sanitize-preview-control-values.ts b/libs/application-generic/src/utils/sanitize-preview-control-values.ts
deleted file mode 100644
index 03d19c4e6fc..00000000000
--- a/libs/application-generic/src/utils/sanitize-preview-control-values.ts
+++ /dev/null
@@ -1,222 +0,0 @@
-const EMPTY_TIP_TAP_OBJECT = JSON.stringify({
- type: 'doc',
- content: [
- {
- type: 'paragraph',
- attrs: { textAlign: 'left' },
- content: [{ type: 'text', text: ' ' }],
- },
- ],
-});
-const WHITESPACE = ' ';
-
-type Redirect = {
- url: string;
- target: '_self' | '_blank' | '_parent' | '_top' | '_unfencedTop';
-};
-
-type Action = {
- label?: string;
- redirect?: Redirect;
-};
-
-type LookBackWindow = {
- amount: number;
- unit: string;
-};
-
-function sanitizeRedirect(redirect: Redirect) {
- if (!redirect.url || !redirect.target) {
- return undefined;
- }
-
- return {
- url: redirect.url || 'https://example.com',
- target: redirect.target || '_self',
- };
-}
-
-function sanitizeAction(action: Action) {
- if (!action?.label) {
- return undefined;
- }
-
- return {
- label: action.label,
- redirect: sanitizeRedirect(action.redirect),
- };
-}
-
-function sanitizeInApp(controlValues: Record) {
- if (!controlValues) return controlValues;
-
- const normalized: Record = {
- subject: controlValues.subject || null,
- body:
- (controlValues.body as string)?.length === 0
- ? WHITESPACE
- : controlValues.body,
- avatar: controlValues.avatar || null,
- primaryAction: null,
- secondaryAction: null,
- redirect: null,
- data: controlValues.data || null,
- };
-
- if (controlValues.primaryAction) {
- normalized.primaryAction = sanitizeAction(
- controlValues.primaryAction as Action,
- );
- }
-
- if (controlValues.secondaryAction) {
- normalized.secondaryAction = sanitizeAction(
- controlValues.secondaryAction as Action,
- );
- }
-
- if (controlValues.redirect) {
- normalized.redirect = sanitizeRedirect(controlValues.redirect as Redirect);
- }
-
- if (typeof normalized === 'object' && normalized !== null) {
- return Object.fromEntries(
- Object.entries(normalized).filter(([_, value]) => value !== null),
- );
- }
-
- return normalized;
-}
-
-function sanitizeEmail(controlValues: Record) {
- if (!controlValues) return controlValues;
-
- const emailControls: Record = {};
-
- /*
- * if (controlValues.body != null) {
- * emailControls.body = controlValues.body || '';
- * }
- */
- emailControls.subject = controlValues.subject || '';
- emailControls.body = controlValues.body || EMPTY_TIP_TAP_OBJECT;
- emailControls.data = controlValues.data || null;
-
- return emailControls;
-}
-
-function sanitizeSms(controlValues: Record) {
- if (!controlValues) return controlValues;
-
- return {
- body: controlValues.body || '',
- data: controlValues.data || null,
- };
-}
-
-function sanitizePush(controlValues: Record) {
- if (!controlValues) return controlValues;
-
- const mappedValues = {
- subject: controlValues.subject || '',
- body: controlValues.body || '',
- data: controlValues.data || null,
- };
-
- if (typeof mappedValues === 'object' && mappedValues !== null) {
- return Object.fromEntries(
- Object.entries(mappedValues).filter(([_, value]) => value !== null),
- );
- }
-
- return mappedValues;
-}
-
-function sanitizeChat(controlValues: Record) {
- if (!controlValues) return controlValues;
-
- return {
- body: controlValues.body || '',
- data: controlValues.data || null,
- };
-}
-
-function sanitizeDigest(controlValues: Record) {
- if (!controlValues) return controlValues;
-
- const mappedValues = {
- cron: controlValues.cron || '',
- amount: controlValues.amount || 0,
- unit: controlValues.unit || '',
- digestKey: controlValues.digestKey || '',
- data: controlValues.data || null,
- lookBackWindow: controlValues.lookBackWindow
- ? {
- amount: (controlValues.lookBackWindow as LookBackWindow).amount || 0,
- unit: (controlValues.lookBackWindow as LookBackWindow).unit || '',
- }
- : null,
- };
-
- if (typeof mappedValues === 'object' && mappedValues !== null) {
- return Object.fromEntries(
- Object.entries(mappedValues).filter(([_, value]) => value !== null),
- );
- }
-
- return mappedValues;
-}
-
-/**
- * Sanitizes control values received from client-side forms into a clean minimal object.
- * This function processes potentially invalid form data that may contain default/placeholder values
- * and transforms it into a standardized format suitable for preview generation.
- *
- * @example
- * // Input from form with default values:
- * {
- * subject: "Hello",
- * body: null,
- * unusedField: "test"
- * }
- *
- * // Normalized output:
- * {
- * subject: "Hello",
- * body: " "
- * }
- *
- */
-export function sanitizePreviewControlValues(
- controlValues: Record,
- stepType: string,
-): Record | null {
- if (!controlValues) {
- return null;
- }
- let normalizedValues: Record;
- switch (stepType) {
- case 'in_app':
- normalizedValues = sanitizeInApp(controlValues);
- break;
- case 'email':
- normalizedValues = sanitizeEmail(controlValues);
- break;
- case 'sms':
- normalizedValues = sanitizeSms(controlValues);
- break;
- case 'push':
- normalizedValues = sanitizePush(controlValues);
- break;
- case 'chat':
- normalizedValues = sanitizeChat(controlValues);
- break;
- case 'digest':
- normalizedValues = sanitizeDigest(controlValues);
- break;
- default:
- normalizedValues = controlValues;
- }
-
- return normalizedValues;
-}
diff --git a/novu.code-workspace b/novu.code-workspace
index 71b928ad811..6014850fc68 100644
--- a/novu.code-workspace
+++ b/novu.code-workspace
@@ -5,7 +5,7 @@
"path": ".",
},
{
- "name": "🚀 @novu/api",
+ "name": "🚀 @novu/api-service",
"path": "apps/api",
},
{
diff --git a/nx.json b/nx.json
index ebadc5f9821..d7894d4c746 100644
--- a/nx.json
+++ b/nx.json
@@ -26,7 +26,7 @@
"groups": {
"apps": {
"projects": [
- "@novu/api",
+ "@novu/api-service",
"@novu/dashboard",
"@novu/inbound-mail",
"@novu/web",
diff --git a/package.json b/package.json
index 20983d0793c..992616b4411 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"scripts": {
"bootstrap": "npm run setup:dev",
"build-ee": "nx run-many --target=build-ee --all",
- "build:api": "nx build @novu/api",
+ "build:api": "nx build @novu/api-service",
"build:dashboard": "nx build @novu/dashboard",
"build:embed": "nx build @novu/embed",
"build:inbound-mail": "nx build @novu/inbound-mail",
@@ -44,9 +44,9 @@
"release": "node scripts/release.mjs",
"release:version:apps": "nx release version --projects=tag:type:app",
"setup:project": "npx --yes pnpm@9.11.0 i && node scripts/setup-env-files.js && pnpm build",
- "start:api:dev": "cross-env nx run @novu/api:start:dev",
- "start:api:test": "cross-env nx run-many --target=start:test --projects=@novu/api",
- "start:api": "cross-env nx run @novu/api:start",
+ "start:api:dev": "cross-env nx run @novu/api-service:start:dev",
+ "start:api:test": "cross-env nx run-many --target=start:test --projects=@novu/api-service",
+ "start:api": "cross-env nx run @novu/api-service:start",
"start:dal": "cross-env nx run @novu/dal:start",
"start:dashboard": "cross-env nx run @novu/dashboard:start",
"start:dev": "cross-env lerna run start:dev --stream --parallel --concurrency=20 --scope=@novu/{api,worker,web,widget,ws,notification-center}",
diff --git a/packages/framework/package.json b/packages/framework/package.json
index 75a65225a39..128226ea998 100644
--- a/packages/framework/package.json
+++ b/packages/framework/package.json
@@ -232,7 +232,6 @@
"zod-to-json-schema": "^3.23.3"
},
"dependencies": {
- "@novu/shared": "workspace:*",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"better-ajv-errors": "^1.2.0",
diff --git a/packages/framework/src/resources/workflow/map-preferences.ts b/packages/framework/src/resources/workflow/map-preferences.ts
index 8ae1ea6dd9f..c2c58586f6e 100644
--- a/packages/framework/src/resources/workflow/map-preferences.ts
+++ b/packages/framework/src/resources/workflow/map-preferences.ts
@@ -1,4 +1,4 @@
-import { ChannelTypeEnum, WorkflowPreferencesPartial } from '@novu/shared';
+import { ChannelTypeEnum, WorkflowPreferencesPartial } from '../../shared';
import { WorkflowChannelEnum } from '../../constants';
import { WorkflowPreferences } from '../../types';
diff --git a/packages/framework/src/schemas/providers/chat/index.ts b/packages/framework/src/schemas/providers/chat/index.ts
index ded0651ad23..a126953093e 100644
--- a/packages/framework/src/schemas/providers/chat/index.ts
+++ b/packages/framework/src/schemas/providers/chat/index.ts
@@ -1,4 +1,4 @@
-import { ChatProviderIdEnum } from '@novu/shared';
+import { ChatProviderIdEnum } from '../../../shared';
import type { JsonSchema } from '../../../types/schema.types';
import { genericProviderSchemas } from '../generic.schema';
import { slackProviderSchemas } from './slack.schema';
diff --git a/packages/framework/src/schemas/providers/email/index.ts b/packages/framework/src/schemas/providers/email/index.ts
index c831020b4ac..2415e496ec5 100644
--- a/packages/framework/src/schemas/providers/email/index.ts
+++ b/packages/framework/src/schemas/providers/email/index.ts
@@ -1,4 +1,4 @@
-import { EmailProviderIdEnum } from '@novu/shared';
+import { EmailProviderIdEnum } from '../../../shared';
import type { JsonSchema } from '../../../types/schema.types';
import { genericProviderSchemas } from '../generic.schema';
import { mailgunProviderSchemas } from './mailgun.schema';
diff --git a/packages/framework/src/schemas/providers/inApp/index.ts b/packages/framework/src/schemas/providers/inApp/index.ts
index d88a4df1331..9450f73b311 100644
--- a/packages/framework/src/schemas/providers/inApp/index.ts
+++ b/packages/framework/src/schemas/providers/inApp/index.ts
@@ -1,4 +1,4 @@
-import { InAppProviderIdEnum } from '@novu/shared';
+import { InAppProviderIdEnum } from '../../../shared';
import type { JsonSchema } from '../../../types/schema.types';
import { novuInAppProviderSchemas } from './novu-inapp.schema';
diff --git a/packages/framework/src/schemas/providers/push/index.ts b/packages/framework/src/schemas/providers/push/index.ts
index 7a7a66cc6f6..5b63ff0b7be 100644
--- a/packages/framework/src/schemas/providers/push/index.ts
+++ b/packages/framework/src/schemas/providers/push/index.ts
@@ -1,4 +1,4 @@
-import { PushProviderIdEnum } from '@novu/shared';
+import { PushProviderIdEnum } from '../../../shared';
import type { JsonSchema } from '../../../types/schema.types';
import { genericProviderSchemas } from '../generic.schema';
import { apnsProviderSchemas } from './apns.schema';
diff --git a/packages/framework/src/schemas/providers/sms/index.ts b/packages/framework/src/schemas/providers/sms/index.ts
index c782d592f21..c473a9858d7 100644
--- a/packages/framework/src/schemas/providers/sms/index.ts
+++ b/packages/framework/src/schemas/providers/sms/index.ts
@@ -1,4 +1,4 @@
-import { SmsProviderIdEnum } from '@novu/shared';
+import { SmsProviderIdEnum } from '../../../shared';
import type { JsonSchema } from '../../../types/schema.types';
import { genericProviderSchemas } from '../generic.schema';
import { novuSmsProviderSchemas } from './novu-sms.schema';
diff --git a/packages/framework/src/shared.ts b/packages/framework/src/shared.ts
new file mode 100644
index 00000000000..e8c9b0f5bf3
--- /dev/null
+++ b/packages/framework/src/shared.ts
@@ -0,0 +1,252 @@
+/**
+ * ==========
+ * | NOTICE |
+ * ==========
+ *
+ * This file contains copied code from @novu/shared in order to temporarily eliminate the dependency of
+ * framework on the shared package.
+ *
+ * The shared package, doesn't support ESM/CJS with strict TS yet.
+ * So we sacrificed a bit code duplication in order to address ESM/CJS issues reported on the @novu/framework
+ * caused by its @novu/shared dependency.
+ *
+ * Treat this as a temporary solution until the shared package is updated with the above.
+ *
+ */
+
+/* eslint-disable @typescript-eslint/naming-convention */
+
+export interface IResponseError {
+ error: string;
+ message: string;
+ statusCode: number;
+}
+
+/**
+ * Validate (type-guard) that an error response matches our IResponseError interface.
+ */
+export function checkIsResponseError(err: unknown): err is IResponseError {
+ return !!err && typeof err === 'object' && 'error' in err && 'message' in err && 'statusCode' in err;
+}
+
+export enum ChannelTypeEnum {
+ IN_APP = 'in_app',
+ EMAIL = 'email',
+ SMS = 'sms',
+ CHAT = 'chat',
+ PUSH = 'push',
+}
+
+export interface IAttachmentOptions {
+ mime: string;
+ file: Buffer;
+ name?: string;
+ channels?: ChannelTypeEnum[];
+ cid?: string;
+ disposition?: string;
+}
+
+export interface ITriggerPayload {
+ attachments?: IAttachmentOptions[];
+ [key: string]:
+ | string
+ | string[]
+ | boolean
+ | number
+ | undefined
+ | IAttachmentOptions
+ | IAttachmentOptions[]
+ | Record;
+}
+
+export interface ISubscriberPayload {
+ subscriberId: string;
+ firstName?: string;
+ lastName?: string;
+ email?: string;
+ phone?: string;
+ avatar?: string;
+ locale?: string;
+ data?: Record;
+ channels?: ISubscriberChannel[];
+}
+
+export interface ISubscriberChannel {
+ providerId: ChatProviderIdEnum | PushProviderIdEnum;
+ integrationIdentifier?: string;
+ credentials: IChannelCredentials;
+}
+
+export interface IChannelCredentials {
+ webhookUrl?: string;
+ deviceTokens?: string[];
+}
+
+export interface ITopic {
+ type: 'topic';
+ topicKey: string;
+}
+
+export type TriggerRecipientsPayload = string | ISubscriberPayload | ITopic | ISubscriberPayload[] | ITopic[];
+
+export enum TriggerEventStatusEnum {
+ ERROR = 'error',
+ NOT_ACTIVE = 'trigger_not_active',
+ NO_WORKFLOW_ACTIVE_STEPS = 'no_workflow_active_steps_defined',
+ NO_WORKFLOW_STEPS = 'no_workflow_steps_defined',
+ PROCESSED = 'processed',
+ // TODO: Seems not used. Remove.
+ SUBSCRIBER_MISSING = 'subscriber_id_missing',
+ TENANT_MISSING = 'no_tenant_found',
+}
+
+export enum EmailProviderIdEnum {
+ EmailJS = 'emailjs',
+ Mailgun = 'mailgun',
+ Mailjet = 'mailjet',
+ Mandrill = 'mandrill',
+ CustomSMTP = 'nodemailer',
+ Postmark = 'postmark',
+ SendGrid = 'sendgrid',
+ Sendinblue = 'sendinblue',
+ SES = 'ses',
+ NetCore = 'netcore',
+ Infobip = 'infobip-email',
+ Resend = 'resend',
+ Plunk = 'plunk',
+ MailerSend = 'mailersend',
+ Mailtrap = 'mailtrap',
+ Clickatell = 'clickatell',
+ Outlook365 = 'outlook365',
+ Novu = 'novu-email',
+ SparkPost = 'sparkpost',
+ EmailWebhook = 'email-webhook',
+ Braze = 'braze',
+}
+
+export enum SmsProviderIdEnum {
+ Nexmo = 'nexmo',
+ Plivo = 'plivo',
+ Sms77 = 'sms77',
+ SmsCentral = 'sms-central',
+ SNS = 'sns',
+ Telnyx = 'telnyx',
+ Twilio = 'twilio',
+ Gupshup = 'gupshup',
+ Firetext = 'firetext',
+ Infobip = 'infobip-sms',
+ BurstSms = 'burst-sms',
+ BulkSms = 'bulk-sms',
+ ISendSms = 'isend-sms',
+ Clickatell = 'clickatell',
+ FortySixElks = 'forty-six-elks',
+ Kannel = 'kannel',
+ Maqsam = 'maqsam',
+ Termii = 'termii',
+ AfricasTalking = 'africas-talking',
+ Novu = 'novu-sms',
+ Sendchamp = 'sendchamp',
+ GenericSms = 'generic-sms',
+ Clicksend = 'clicksend',
+ Bandwidth = 'bandwidth',
+ MessageBird = 'messagebird',
+ Simpletexting = 'simpletexting',
+ AzureSms = 'azure-sms',
+ RingCentral = 'ring-central',
+ BrevoSms = 'brevo-sms',
+ EazySms = 'eazy-sms',
+ Mobishastra = 'mobishastra',
+}
+
+export enum ChatProviderIdEnum {
+ Slack = 'slack',
+ Discord = 'discord',
+ MsTeams = 'msteams',
+ Mattermost = 'mattermost',
+ Ryver = 'ryver',
+ Zulip = 'zulip',
+ GrafanaOnCall = 'grafana-on-call',
+ GetStream = 'getstream',
+ RocketChat = 'rocket-chat',
+ WhatsAppBusiness = 'whatsapp-business',
+}
+
+export enum PushProviderIdEnum {
+ FCM = 'fcm',
+ APNS = 'apns',
+ EXPO = 'expo',
+ OneSignal = 'one-signal',
+ Pushpad = 'pushpad',
+ PushWebhook = 'push-webhook',
+ PusherBeams = 'pusher-beams',
+}
+
+export enum InAppProviderIdEnum {
+ Novu = 'novu',
+}
+
+/**
+ * A preference for a notification delivery workflow.
+ *
+ * This provides a shortcut to setting all channels to the same preference.
+ */
+export type WorkflowPreference = {
+ /**
+ * A flag specifying if notification delivery is enabled for the workflow.
+ *
+ * If `true`, notification delivery is enabled by default for all channels.
+ *
+ * This setting can be overridden by the channel preferences.
+ *
+ * @default true
+ */
+ enabled: boolean;
+ /**
+ * A flag specifying if the preference is read-only.
+ *
+ * If `true`, the preference cannot be changed by the Subscriber.
+ *
+ * @default false
+ */
+ readOnly: boolean;
+};
+
+/** A preference for a notification delivery channel. */
+export type ChannelPreference = {
+ /**
+ * A flag specifying if notification delivery is enabled for the channel.
+ *
+ * If `true`, notification delivery is enabled.
+ *
+ * @default true
+ */
+ enabled: boolean;
+};
+
+export type WorkflowPreferences = {
+ /**
+ * A preference for the workflow.
+ *
+ * The values specified here will be used if no preference is specified for a channel.
+ */
+ all: WorkflowPreference;
+ /**
+ * A preference for each notification delivery channel.
+ *
+ * If no preference is specified for a channel, the `all` preference will be used.
+ */
+ channels: Record;
+};
+
+/**
+ * Recursively make all properties of type `T` optional.
+ */
+// TODO: This utility also exists in src/types/util.types.ts. They should be consolidated.
+export type DeepPartial = T extends object
+ ? {
+ [P in keyof T]?: DeepPartial;
+ }
+ : T;
+
+/** A partial set of workflow preferences. */
+export type WorkflowPreferencesPartial = DeepPartial;
diff --git a/packages/framework/src/types/discover.types.ts b/packages/framework/src/types/discover.types.ts
index 91b67dcacbd..886e6f4162a 100644
--- a/packages/framework/src/types/discover.types.ts
+++ b/packages/framework/src/types/discover.types.ts
@@ -1,4 +1,4 @@
-import type { WorkflowPreferencesPartial } from '@novu/shared';
+import type { WorkflowPreferencesPartial } from '../shared';
import { ActionStepEnum, ChannelStepEnum } from '../constants';
import type { JsonSchema, Schema } from './schema.types';
import type { StepOptions } from './step.types';
diff --git a/packages/framework/src/types/event.types.ts b/packages/framework/src/types/event.types.ts
index 23772a9f8cf..491b3d77fa1 100644
--- a/packages/framework/src/types/event.types.ts
+++ b/packages/framework/src/types/event.types.ts
@@ -1,16 +1,11 @@
-import type {
- ITriggerPayload,
- TriggerEventStatusEnum,
- TriggerRecipientsPayload,
- TriggerRecipientSubscriber,
-} from '@novu/shared';
+import type { ISubscriberPayload, ITriggerPayload, TriggerEventStatusEnum, TriggerRecipientsPayload } from '../shared';
import { ConditionalPartial, PickRequiredKeys } from './util.types';
-type EventPayload = ITriggerPayload & {};
+type EventPayload = ITriggerPayload;
-type Actor = TriggerRecipientSubscriber & {};
+type Actor = string | ISubscriberPayload;
-type Recipients = TriggerRecipientsPayload & {};
+type Recipients = TriggerRecipientsPayload;
export type EventTriggerResult = {
/**
diff --git a/packages/framework/src/utils/http.utils.ts b/packages/framework/src/utils/http.utils.ts
index 361e57c5cfa..d8e9e5a914c 100644
--- a/packages/framework/src/utils/http.utils.ts
+++ b/packages/framework/src/utils/http.utils.ts
@@ -1,4 +1,4 @@
-import { checkIsResponseError } from '@novu/shared';
+import { checkIsResponseError } from '../shared';
import { BridgeError, MissingSecretKeyError, PlatformError } from '../errors';
export const initApiClient = (secretKey: string, apiUrl: string) => {
diff --git a/packages/js/src/ui/components/InboxTabs/InboxTab.tsx b/packages/js/src/ui/components/InboxTabs/InboxTab.tsx
index 5a06862eee2..57be770db48 100644
--- a/packages/js/src/ui/components/InboxTabs/InboxTab.tsx
+++ b/packages/js/src/ui/components/InboxTabs/InboxTab.tsx
@@ -53,6 +53,7 @@ export const InboxDropdownTab = (props: InboxDropdownTabProps) => {
return (
{props.label}
{props.rightIcon}
diff --git a/packages/providers/src/lib/push/one-signal/one-signal.provider.spec.ts b/packages/providers/src/lib/push/one-signal/one-signal.provider.spec.ts
index 84c9e6ab7f4..5482673d23b 100644
--- a/packages/providers/src/lib/push/one-signal/one-signal.provider.spec.ts
+++ b/packages/providers/src/lib/push/one-signal/one-signal.provider.spec.ts
@@ -44,7 +44,7 @@ describe('test onesignal notification api', () => {
const spy = vi.spyOn(provider, 'sendMessage');
const res = await provider.sendMessage(mockNotificationOptions, {
- iosBadgeCount: 2,
+ iosBadgeCount: 1,
includeExternalUserIds: ['test'],
});
expect(mockedAxios.request).toHaveBeenCalled();
@@ -59,14 +59,13 @@ describe('test onesignal notification api', () => {
contents: { en: 'Test push' },
subtitle: {},
data: { sound: 'test_sound' },
- ios_badge_type: 'Increase',
- ios_badgeCount: 2,
- ios_badge_count: 1,
+ ios_badgeType: 'Increase',
+ ios_badgeCount: 1,
include_external_user_ids: ['test'],
});
expect(spy).toHaveBeenCalledWith(mockNotificationOptions, {
- iosBadgeCount: 2,
+ iosBadgeCount: 1,
includeExternalUserIds: ['test'],
});
expect(res.id).toEqual(response.data.id);
@@ -89,7 +88,7 @@ describe('test onesignal notification api', () => {
const spy = vi.spyOn(provider, 'sendMessage');
const res = await provider.sendMessage(mockNotificationOptions, {
- iosBadgeCount: 2,
+ iosBadgeCount: 1,
includeExternalUserIds: ['test'],
_passthrough: {
body: {
@@ -109,14 +108,13 @@ describe('test onesignal notification api', () => {
contents: { en: 'Test push' },
subtitle: {},
data: { sound: 'test_sound' },
- ios_badge_type: 'Increase',
- ios_badgeCount: 2,
- ios_badge_count: 1,
+ ios_badgeType: 'Increase',
+ ios_badgeCount: 1,
include_external_user_ids: ['test', 'test1'],
});
expect(spy).toHaveBeenCalledWith(mockNotificationOptions, {
- iosBadgeCount: 2,
+ iosBadgeCount: 1,
includeExternalUserIds: ['test'],
_passthrough: {
body: {
diff --git a/packages/providers/src/lib/push/one-signal/one-signal.provider.ts b/packages/providers/src/lib/push/one-signal/one-signal.provider.ts
index 46ba4f99690..e2825cafe03 100644
--- a/packages/providers/src/lib/push/one-signal/one-signal.provider.ts
+++ b/packages/providers/src/lib/push/one-signal/one-signal.provider.ts
@@ -63,8 +63,8 @@ export class OneSignalPushProvider
contents: { en: options.content },
subtitle: { en: overrides.subtitle },
data: options.payload,
- ios_badge_type: 'Increase',
- ios_badge_count: 1,
+ ios_badgeType: 'Increase',
+ ios_badgeCount: 1,
ios_sound: sound,
android_sound: sound,
mutable_content: overrides.mutableContent,
diff --git a/packages/providers/src/lib/push/one-signal/one-signal.providerV2.spec.ts b/packages/providers/src/lib/push/one-signal/one-signal.providerV2.spec.ts
index 0713a9c3d4e..d8e74387743 100644
--- a/packages/providers/src/lib/push/one-signal/one-signal.providerV2.spec.ts
+++ b/packages/providers/src/lib/push/one-signal/one-signal.providerV2.spec.ts
@@ -45,7 +45,7 @@ describe('test onesignal notification user api', () => {
const spy = vi.spyOn(provider, 'sendMessage');
const res = await provider.sendMessage(mockNotificationOptions, {
- iosBadgeCount: 2,
+ iosBadgeCount: 1,
});
expect(mockedAxios.request).toHaveBeenCalled();
const data = JSON.parse(
@@ -62,9 +62,8 @@ describe('test onesignal notification user api', () => {
contents: { en: 'Test push' },
subtitle: {},
data: { sound: 'test_sound' },
- ios_badge_type: 'Increase',
- ios_badgeCount: 2,
- ios_badge_count: 1,
+ ios_badgeType: 'Increase',
+ ios_badgeCount: 1,
});
expect(res.id).toEqual(response.data.id);
diff --git a/packages/shared/src/dto/workflows/preview-step-response.dto.ts b/packages/shared/src/dto/workflows/preview-step-response.dto.ts
index 4b627a0474c..863dc455e26 100644
--- a/packages/shared/src/dto/workflows/preview-step-response.dto.ts
+++ b/packages/shared/src/dto/workflows/preview-step-response.dto.ts
@@ -43,7 +43,7 @@ class DigestRegularOutput {
amount: number;
unit: TimeUnitEnum;
digestKey?: string;
- lookBackWindow: {
+ lookBackWindow?: {
amount: number;
unit: TimeUnitEnum;
};
diff --git a/packages/shared/src/types/feature-flags.ts b/packages/shared/src/types/feature-flags.ts
index 0e3d4781cdb..f2fd069dae2 100644
--- a/packages/shared/src/types/feature-flags.ts
+++ b/packages/shared/src/types/feature-flags.ts
@@ -49,4 +49,6 @@ export enum FeatureFlagsKeysEnum {
IS_NEW_DASHBOARD_ACTIVITY_FEED_ENABLED = 'IS_NEW_DASHBOARD_ACTIVITY_FEED_ENABLED',
IS_ND_INTEGRATION_STORE_ENABLED = 'IS_ND_INTEGRATION_STORE_ENABLED',
IS_ND_SMS_CHAT_PUSH_ENABLED = 'IS_ND_SMS_CHAT_PUSH_ENABLED',
+ IS_ND_EMAIL_FOR_BLOCK_ENABLED = 'IS_ND_EMAIL_FOR_BLOCK_ENABLED',
+ IS_ND_EMAIL_SHOW_ENABLED = 'IS_ND_EMAIL_SHOW_ENABLED',
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5d15d57d79f..b9909c723e7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -364,8 +364,8 @@ importers:
specifier: ^6.2.3
version: 6.9.5(encoding@0.1.13)
'@maily-to/render':
- specifier: ^0.0.16
- version: 0.0.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ specifier: ^0.0.17
+ version: 0.0.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@nestjs/axios':
specifier: 3.0.3
version: 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)
@@ -394,8 +394,8 @@ importers:
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)
'@novu/api':
- specifier: 0.0.1-alpha.109
- version: 0.0.1-alpha.109(zod@3.23.8)
+ specifier: 0.0.1-alpha.149
+ version: 0.0.1-alpha.149(zod@3.23.8)
'@novu/application-generic':
specifier: workspace:*
version: link:../../libs/application-generic
@@ -444,6 +444,12 @@ importers:
'@upstash/ratelimit':
specifier: ^0.4.4
version: 0.4.4
+ ajv:
+ specifier: ^8.12.0
+ version: 8.13.0
+ ajv-formats:
+ specifier: ^2.1.1
+ version: 2.1.1(ajv@8.13.0)
axios:
specifier: ^1.6.8
version: 1.6.8
@@ -678,8 +684,8 @@ importers:
specifier: ^1.2.1
version: 1.2.1
'@maily-to/core':
- specifier: ^0.0.18
- version: 0.0.18(@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.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))
'@novu/framework':
specifier: workspace:*
version: link:../../packages/framework
@@ -1122,13 +1128,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.25.2)
'@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.25.2)
'@babel/plugin-transform-runtime':
specifier: ^7.23.2
- version: 7.23.2(@babel/core@7.22.11)
+ version: 7.23.2(@babel/core@7.25.2)
'@clerk/clerk-react':
specifier: ^5.15.1
version: 5.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -1453,13 +1459,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.25.2)
'@babel/preset-react':
specifier: ^7.13.13
- version: 7.18.6(@babel/core@7.22.11)
+ version: 7.18.6(@babel/core@7.25.2)
'@babel/preset-typescript':
specifier: ^7.13.0
- version: 7.21.4(@babel/core@7.22.11)
+ version: 7.21.4(@babel/core@7.25.2)
'@babel/runtime':
specifier: ^7.20.13
version: 7.21.0
@@ -1504,13 +1510,13 @@ importers:
version: 7.4.2
'@storybook/preset-create-react-app':
specifier: ^7.4.2
- version: 7.4.2(ucmnrhmq4kewpo24xrp57f5r6y)
+ version: 7.4.2(joi3nebcpxx5275442gow2nj4e)
'@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.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)
'@testing-library/jest-dom':
specifier: ^4.2.4
version: 4.2.4
@@ -1540,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.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.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-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.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)
sinon:
specifier: 9.2.4
version: 9.2.4
@@ -2727,6 +2733,9 @@ importers:
class-validator:
specifier: 0.14.1
version: 0.14.1
+ cron-parser:
+ specifier: ^4.9.0
+ version: 4.9.0
date-fns:
specifier: ^2.29.2
version: 2.29.3
@@ -3541,9 +3550,6 @@ importers:
packages/framework:
dependencies:
- '@novu/shared':
- specifier: workspace:*
- version: link:../shared
ajv:
specifier: ^8.12.0
version: 8.12.0
@@ -9898,14 +9904,14 @@ packages:
resolution: {integrity: sha512-SaNFseFPSDQlOYM9JTyYY6wauMu6qJ8eExo+jssFyb20ZaVvxKX1eTb3Gm5aW/4aWuxn6nofU+02sCk51//wdw==}
engines: {node: '>=10.0.0'}
- '@maily-to/core@0.0.18':
- resolution: {integrity: sha512-Ny+7kFDmLn0pZsE7/xcBJV2hhOXjtWccL5svZrtI0Md1WWsvcpO4Nib0GZVIbR+D28rHbYKYEu0LRn/PXxeKRg==}
+ '@maily-to/core@0.0.19':
+ resolution: {integrity: sha512-FppQl3Wb2iwxxKMBoHmaoCwYiIyVwZxygbEA7qCAaTiHFZjvZIvifA8toU5xvL5Z5SEUtjkLe9Q3UQUzSnS1aQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.3.1
- '@maily-to/render@0.0.16':
- resolution: {integrity: sha512-zskDYnGazUjp4diAdR7dQAAPCCW3fvyGCFFdn3jVbwKx0CNvi0r1uHSrytQ4gG6IGC7CCa84mkEe3JohS5WXhQ==}
+ '@maily-to/render@0.0.17':
+ resolution: {integrity: sha512-vFDlY9U9XtyDRzeVGVON9tgbzBRrq5wBOMG1aFGc/30soaamK7oYxRqSUbwNmrfT2ScBgL7YFtDLoeRZbpdmeA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^18.3.1
@@ -10613,8 +10619,8 @@ packages:
'@nothing-but/utils@0.12.1':
resolution: {integrity: sha512-1qZU1Q5El0IjE7JT/ucvJNzdr2hL3W8Rm27xNf1p6gb3Nw8pGnZmxp6/GEW9h+I1k1cICxXNq25hBwknTQ7yhg==}
- '@novu/api@0.0.1-alpha.109':
- resolution: {integrity: sha512-+fbdpvrhgzzRmdo6wh/gQG8whqSreTDGdkoW8N44sevTcMqg2LwQQs7E2hXkOcmBWUkyuZ7bbCPzMrf5uoKgfw==}
+ '@novu/api@0.0.1-alpha.149':
+ resolution: {integrity: sha512-HNLhe6+xsQGMnTD/EkaXumHuHcRyuu6c6yv987D7xjZa4CBXMyqi5rqQrCAUVs2go8+8WrqjoHEeTPHk5AmAoA==}
peerDependencies:
zod: '>= 3'
@@ -38614,17 +38620,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
@@ -39217,13 +39212,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
@@ -39416,11 +39404,6 @@ 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
@@ -39541,11 +39524,6 @@ 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
@@ -39561,11 +39539,6 @@ 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
@@ -39786,11 +39759,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
@@ -40365,12 +40333,6 @@ 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
@@ -40609,15 +40571,6 @@ 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
@@ -41188,9 +41141,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.22.11)':
+ '@babel/plugin-transform-react-display-name@7.18.6(@babel/core@7.25.2)':
dependencies:
- '@babel/core': 7.22.11
+ '@babel/core': 7.25.2
'@babel/helper-plugin-utils': 7.20.2
'@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.21.4)':
@@ -41198,11 +41151,6 @@ 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
@@ -41225,17 +41173,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.18.6(@babel/core@7.25.2)':
dependencies:
- '@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)
+ '@babel/core': 7.25.2
+ '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.25.2)
transitivePeerDependencies:
- supports-color
@@ -41301,28 +41242,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@babel/plugin-transform-react-jsx@7.21.0(@babel/core@7.22.11)':
+ '@babel/plugin-transform-react-jsx@7.21.0(@babel/core@7.25.2)':
dependencies:
- '@babel/core': 7.22.11
+ '@babel/core': 7.25.2
'@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.22.11)
+ '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.25.2)
'@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
@@ -41356,17 +41286,6 @@ 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
@@ -41395,18 +41314,12 @@ 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.18.6(@babel/core@7.25.2)':
dependencies:
- '@babel/core': 7.22.11
+ '@babel/core': 7.25.2
'@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
@@ -41492,18 +41405,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
@@ -41650,16 +41551,6 @@ 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
@@ -42253,13 +42144,6 @@ 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
@@ -42328,27 +42212,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@babel/preset-react@7.18.6(@babel/core@7.22.11)':
+ '@babel/preset-react@7.18.6(@babel/core@7.25.2)':
dependencies:
- '@babel/core': 7.22.11
+ '@babel/core': 7.25.2
'@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)
- 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)
+ '@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)
transitivePeerDependencies:
- supports-color
@@ -42388,17 +42260,6 @@ 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
@@ -46123,7 +45984,7 @@ snapshots:
transitivePeerDependencies:
- debug
- '@maily-to/core@0.0.18(@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.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))':
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)
@@ -46166,7 +46027,7 @@ snapshots:
- ts-node
- y-prosemirror
- '@maily-to/render@0.0.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@maily-to/render@0.0.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@react-email/components': 0.0.25(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@react-email/render': 1.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -47020,7 +46881,7 @@ snapshots:
'@nothing-but/utils@0.12.1': {}
- '@novu/api@0.0.1-alpha.109(zod@3.23.8)':
+ '@novu/api@0.0.1-alpha.149(zod@3.23.8)':
dependencies:
zod: 3.23.8
@@ -54942,7 +54803,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.22.11))
+ jscodeshift: 0.14.0(@babel/preset-env@7.23.2(@babel/core@7.25.2))
leven: 3.1.0
ora: 5.4.1
prettier: 2.8.8
@@ -55513,16 +55374,16 @@ snapshots:
'@storybook/postinstall@7.4.2': {}
- '@storybook/preset-create-react-app@7.4.2(ucmnrhmq4kewpo24xrp57f5r6y)':
+ '@storybook/preset-create-react-app@7.4.2(joi3nebcpxx5275442gow2nj4e)':
dependencies:
- '@babel/core': 7.22.11
+ '@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))
'@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.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.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)
semver: 7.5.4
transitivePeerDependencies:
- '@types/webpack'
@@ -55536,16 +55397,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.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.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.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
@@ -55555,9 +55416,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.22.11
+ '@babel/core': 7.23.2
typescript: 5.6.2
transitivePeerDependencies:
- '@swc/core'
@@ -55573,16 +55434,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.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)':
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.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))
'@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
@@ -55592,9 +55453,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.25.2
typescript: 5.6.2
transitivePeerDependencies:
- '@swc/core'
@@ -55797,16 +55658,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.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.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)(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.22.11
+ '@babel/core': 7.23.2
typescript: 5.6.2
transitivePeerDependencies:
- '@swc/core'
@@ -55825,16 +55686,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.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)':
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.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/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.25.2
typescript: 5.6.2
transitivePeerDependencies:
- '@swc/core'
@@ -60831,15 +60692,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
@@ -64662,33 +64514,6 @@ 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
@@ -64797,14 +64622,6 @@ 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)
@@ -69879,7 +69696,7 @@ snapshots:
jsbn@0.1.1: {}
- jscodeshift@0.14.0(@babel/preset-env@7.23.2(@babel/core@7.22.11)):
+ jscodeshift@0.14.0(@babel/preset-env@7.23.2(@babel/core@7.25.2)):
dependencies:
'@babel/core': 7.25.2
'@babel/parser': 7.25.6
@@ -69887,7 +69704,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.22.11)
+ '@babel/preset-env': 7.23.2(@babel/core@7.25.2)
'@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)
@@ -76526,9 +76343,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.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.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)):
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.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)
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)):
@@ -76963,7 +76780,7 @@ 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.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):
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))
@@ -76981,7 +76798,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.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.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))(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
diff --git a/scripts/jarvis.js b/scripts/jarvis.js
index 4d350901bf0..196a4dc9d29 100644
--- a/scripts/jarvis.js
+++ b/scripts/jarvis.js
@@ -96,7 +96,7 @@ async function setupRunner() {
if (answers.action === DEV_ENVIRONMENT_SETUP) {
shell.exec('npm run dev-environment-setup');
} else if (answers.runConfiguration === WEB_PROJECT_AND_WIDGET) {
- shell.exec('nx run-many --target=build --projects=@novu/api,@novu/worker');
+ shell.exec('nx run-many --target=build --projects=@novu/api-service,@novu/worker');
shell.exec('npm run start:dev', { async: true });
await waitPort({
@@ -127,7 +127,7 @@ async function setupRunner() {
`);
} else if (answers.runConfiguration === WEB_PROJECT) {
try {
- shell.exec('nx run-many --target=build --projects=@novu/api,@novu/worker,@novu/ws');
+ shell.exec('nx run-many --target=build --projects=@novu/api-service,@novu/worker,@novu/ws');
shell.exec('npm run start:api', { async: true });
shell.exec('npm run start:ws', { async: true });
@@ -162,7 +162,7 @@ async function setupRunner() {
console.error(`Failed to spin up the project ❌`, e);
}
} else if (answers.runConfiguration === API_AND_WORKER_ONLY) {
- shell.exec('nx run-many --target=build --projects=@novu/api,@novu/worker');
+ shell.exec('nx run-many --target=build --projects=@novu/api-service,@novu/worker');
shell.exec('npm run start:api', { async: true });
shell.exec('npm run start:worker', { async: true });
@@ -182,7 +182,7 @@ async function setupRunner() {
Worker: http://127.0.0.1:3004
`);
} else if (answers.runApiConfiguration === API_INTEGRATION_TESTS) {
- shell.exec('nx run-many --target=build --projects=@novu/api,@novu/worker');
+ shell.exec('nx run-many --target=build --projects=@novu/api-service,@novu/worker');
shell.exec('npm run start:worker:test', { async: true });
await waitPort({
@@ -192,7 +192,7 @@ async function setupRunner() {
shell.exec('npm run start:integration:api', { async: true });
} else if (answers.runApiConfiguration === API_E2E_TESTS) {
- shell.exec('nx run-many --target=build --projects=@novu/api,@novu/worker');
+ shell.exec('nx run-many --target=build --projects=@novu/api-service,@novu/worker');
shell.exec('npm run start:worker:test', { async: true });
await waitPort({
@@ -202,7 +202,7 @@ async function setupRunner() {
shell.exec('npm run start:e2e:api', { async: true });
} else if ([RUN_PLAYWRIGHT_CLI, RUN_PLAYWRIGHT_UI].includes(answers.runWebConfiguration)) {
- shell.exec('nx run-many --target=build --projects=@novu/api,@novu/worker,@novu/ws');
+ shell.exec('nx run-many --target=build --projects=@novu/api-service,@novu/worker,@novu/ws');
shell.exec('cd apps/web && npm run build:test');
shell.exec('npm run start:api:test', { async: true });
diff --git a/scripts/release.mjs b/scripts/release.mjs
index 2f0f8286758..ce30683ae2e 100644
--- a/scripts/release.mjs
+++ b/scripts/release.mjs
@@ -1,8 +1,8 @@
/**
* Release all packages in the monorepo.
- *
+ *
* Usage: pnpm release
- *
+ *
* Known issues:
* - nx release with independent versioning and updateDependents: "auto" increases patch by the amount of dependencies updated (https://github.com/nrwl/nx/issues/27823)
*/
@@ -13,8 +13,10 @@ import inquirer from 'inquirer';
import yargs from 'yargs/yargs';
import { execa } from 'execa';
+const projects = ['tag:type:package'];
+
(async () => {
- const { dryRun, verbose, ...rest } = yargs(hideBin(process.argv))
+ const { dryRun, verbose, from, ...rest } = yargs(hideBin(process.argv))
.version(false)
.option('dryRun', {
alias: 'd',
@@ -27,6 +29,11 @@ import { execa } from 'execa';
type: 'boolean',
default: false,
})
+ .option('from', {
+ description:
+ 'The git reference to use as the start of the changelog. If not set it will attempt to resolve the latest tag and use that.',
+ type: 'string',
+ })
.help()
.parse();
@@ -38,7 +45,7 @@ import { execa } from 'execa';
}
const { workspaceVersion, projectsVersionData } = await releaseVersion({
- projects: ['tag:type:package'],
+ projects,
specifier,
dryRun,
verbose,
@@ -46,12 +53,13 @@ import { execa } from 'execa';
});
await releaseChangelog({
- projects: ['tag:type:package'],
+ projects,
specifier,
versionData: projectsVersionData,
version: workspaceVersion,
dryRun,
verbose,
+ from,
});
await execa({
@@ -68,7 +76,7 @@ import { execa } from 'execa';
]);
await releasePublish({
- projects: ['tag:type:package'],
+ projects,
specifier: 'patch',
dryRun,
verbose,