@novu/react 3.18.1 → 3.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,9 +39,12 @@ var useWebSocketEvent = ({
39
39
  onMessage,
40
40
  enabled
41
41
  });
42
+ const shouldBroadcast = (0, import_requestLock.isWebLocksSupported)();
42
43
  const updateReadCount = (data) => {
43
44
  onMessage(data);
44
- postMessage(data);
45
+ if (shouldBroadcast) {
46
+ postMessage(data);
47
+ }
45
48
  };
46
49
  (0, import_react.useEffect)(() => {
47
50
  if (!enabled) return;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/hooks/internal/useWebsocketEvent.ts"],"sourcesContent":["import { EventHandler, Events, SocketEventNames } from '@novu/js';\nimport { useEffect } from 'react';\nimport { requestLock } from '../../utils/requestLock';\nimport { useNovu } from '../NovuProvider';\nimport { useBrowserTabsChannel } from './useBrowserTabsChannel';\n\nexport const useWebSocketEvent = <E extends SocketEventNames>({\n event: webSocketEvent,\n eventHandler: onMessage,\n enabled = true,\n}: {\n event: E;\n eventHandler: (args: Events[E]) => void;\n enabled?: boolean;\n}) => {\n const novu = useNovu();\n const channelName = `nv_ws_connection:a=${novu.applicationIdentifier}:s=${novu.subscriberId}:c=${novu.contextKey}:e=${webSocketEvent}`;\n\n const { postMessage } = useBrowserTabsChannel({\n channelName,\n onMessage,\n enabled,\n });\n\n const updateReadCount: EventHandler<Events[E]> = (data) => {\n onMessage(data);\n postMessage(data);\n };\n\n useEffect(() => {\n if (!enabled) return;\n\n let cleanup: () => void;\n const resolveLock = requestLock(channelName, () => {\n cleanup = novu.on(webSocketEvent, updateReadCount);\n });\n\n return () => {\n if (cleanup) {\n cleanup();\n }\n\n resolveLock();\n };\n }, [enabled]);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAA0B;AAC1B,yBAA4B;AAC5B,0BAAwB;AACxB,mCAAsC;AAE/B,IAAM,oBAAoB,CAA6B;AAAA,EAC5D,OAAO;AAAA,EACP,cAAc;AAAA,EACd,UAAU;AACZ,MAIM;AACJ,QAAM,WAAO,6BAAQ;AACrB,QAAM,cAAc,sBAAsB,KAAK,qBAAqB,MAAM,KAAK,YAAY,MAAM,KAAK,UAAU,MAAM,cAAc;AAEpI,QAAM,EAAE,YAAY,QAAI,oDAAsB;AAAA,IAC5C;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,kBAA2C,CAAC,SAAS;AACzD,cAAU,IAAI;AACd,gBAAY,IAAI;AAAA,EAClB;AAEA,8BAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAEd,QAAI;AACJ,UAAM,kBAAc,gCAAY,aAAa,MAAM;AACjD,gBAAU,KAAK,GAAG,gBAAgB,eAAe;AAAA,IACnD,CAAC;AAED,WAAO,MAAM;AACX,UAAI,SAAS;AACX,gBAAQ;AAAA,MACV;AAEA,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AACd;","names":[]}
1
+ {"version":3,"sources":["../../../../src/hooks/internal/useWebsocketEvent.ts"],"sourcesContent":["import { EventHandler, Events, SocketEventNames } from '@novu/js';\nimport { useEffect } from 'react';\nimport { isWebLocksSupported, requestLock } from '../../utils/requestLock';\nimport { useNovu } from '../NovuProvider';\nimport { useBrowserTabsChannel } from './useBrowserTabsChannel';\n\nexport const useWebSocketEvent = <E extends SocketEventNames>({\n event: webSocketEvent,\n eventHandler: onMessage,\n enabled = true,\n}: {\n event: E;\n eventHandler: (args: Events[E]) => void;\n enabled?: boolean;\n}) => {\n const novu = useNovu();\n const channelName = `nv_ws_connection:a=${novu.applicationIdentifier}:s=${novu.subscriberId}:c=${novu.contextKey}:e=${webSocketEvent}`;\n\n const { postMessage } = useBrowserTabsChannel({\n channelName,\n onMessage,\n enabled,\n });\n\n // When Web Locks are unavailable, requestLock runs the callback in every tab,\n // so each tab already receives the event through its own socket. Re-broadcasting\n // in that case would duplicate the event across tabs (count multiplied by tab\n // count). Only the exclusive lock owner should fan out to the other tabs.\n const shouldBroadcast = isWebLocksSupported();\n const updateReadCount: EventHandler<Events[E]> = (data) => {\n onMessage(data);\n if (shouldBroadcast) {\n postMessage(data);\n }\n };\n\n useEffect(() => {\n if (!enabled) return;\n\n let cleanup: () => void;\n const resolveLock = requestLock(channelName, () => {\n cleanup = novu.on(webSocketEvent, updateReadCount);\n });\n\n return () => {\n if (cleanup) {\n cleanup();\n }\n\n resolveLock();\n };\n }, [enabled]);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAA0B;AAC1B,yBAAiD;AACjD,0BAAwB;AACxB,mCAAsC;AAE/B,IAAM,oBAAoB,CAA6B;AAAA,EAC5D,OAAO;AAAA,EACP,cAAc;AAAA,EACd,UAAU;AACZ,MAIM;AACJ,QAAM,WAAO,6BAAQ;AACrB,QAAM,cAAc,sBAAsB,KAAK,qBAAqB,MAAM,KAAK,YAAY,MAAM,KAAK,UAAU,MAAM,cAAc;AAEpI,QAAM,EAAE,YAAY,QAAI,oDAAsB;AAAA,IAC5C;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAMD,QAAM,sBAAkB,wCAAoB;AAC5C,QAAM,kBAA2C,CAAC,SAAS;AACzD,cAAU,IAAI;AACd,QAAI,iBAAiB;AACnB,kBAAY,IAAI;AAAA,IAClB;AAAA,EACF;AAEA,8BAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAEd,QAAI;AACJ,UAAM,kBAAc,gCAAY,aAAa,MAAM;AACjD,gBAAU,KAAK,GAAG,gBAAgB,eAAe;AAAA,IACnD,CAAC;AAED,WAAO,MAAM;AACX,UAAI,SAAS;AACX,gBAAQ;AAAA,MACV;AAEA,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AACd;","names":[]}
@@ -20,11 +20,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/utils/requestLock.ts
21
21
  var requestLock_exports = {};
22
22
  __export(requestLock_exports, {
23
+ isWebLocksSupported: () => isWebLocksSupported,
23
24
  requestLock: () => requestLock
24
25
  });
25
26
  module.exports = __toCommonJS(requestLock_exports);
27
+ function isWebLocksSupported() {
28
+ return typeof navigator !== "undefined" && "locks" in navigator && !!navigator.locks;
29
+ }
26
30
  function requestLock(id, cb) {
27
- if (!("locks" in navigator)) {
31
+ if (!isWebLocksSupported()) {
28
32
  cb(id);
29
33
  return () => {
30
34
  };
@@ -47,6 +51,7 @@ function requestLock(id, cb) {
47
51
  }
48
52
  // Annotate the CommonJS export names for ESM import in node:
49
53
  0 && (module.exports = {
54
+ isWebLocksSupported,
50
55
  requestLock
51
56
  });
52
57
  //# sourceMappingURL=requestLock.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/requestLock.ts"],"sourcesContent":["export function requestLock(id: string, cb: (id: string) => void) {\n // Check if the Lock API is available\n if (!('locks' in navigator)) {\n // If Lock API is not available, immediately invoke the callback and return a no-op function\n cb(id);\n\n return () => {};\n }\n\n let isFulfilled = false;\n let promiseResolve: () => void;\n\n const promise = new Promise<void>((resolve) => {\n promiseResolve = resolve;\n });\n\n navigator.locks.request(id, () => {\n if (!isFulfilled) {\n cb(id);\n }\n\n return promise;\n });\n\n return () => {\n isFulfilled = true;\n promiseResolve();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,SAAS,YAAY,IAAY,IAA0B;AAEhE,MAAI,EAAE,WAAW,YAAY;AAE3B,OAAG,EAAE;AAEL,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAEA,MAAI,cAAc;AAClB,MAAI;AAEJ,QAAM,UAAU,IAAI,QAAc,CAAC,YAAY;AAC7C,qBAAiB;AAAA,EACnB,CAAC;AAED,YAAU,MAAM,QAAQ,IAAI,MAAM;AAChC,QAAI,CAAC,aAAa;AAChB,SAAG,EAAE;AAAA,IACP;AAEA,WAAO;AAAA,EACT,CAAC;AAED,SAAO,MAAM;AACX,kBAAc;AACd,mBAAe;AAAA,EACjB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/utils/requestLock.ts"],"sourcesContent":["export function isWebLocksSupported(): boolean {\n return typeof navigator !== 'undefined' && 'locks' in navigator && !!navigator.locks;\n}\n\nexport function requestLock(id: string, cb: (id: string) => void) {\n // Check if the Lock API is available\n if (!isWebLocksSupported()) {\n // If Lock API is not available, immediately invoke the callback and return a no-op function\n cb(id);\n\n return () => {};\n }\n\n let isFulfilled = false;\n let promiseResolve: () => void;\n\n const promise = new Promise<void>((resolve) => {\n promiseResolve = resolve;\n });\n\n navigator.locks.request(id, () => {\n if (!isFulfilled) {\n cb(id);\n }\n\n return promise;\n });\n\n return () => {\n isFulfilled = true;\n promiseResolve();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,SAAS,sBAA+B;AAC7C,SAAO,OAAO,cAAc,eAAe,WAAW,aAAa,CAAC,CAAC,UAAU;AACjF;AAEO,SAAS,YAAY,IAAY,IAA0B;AAEhE,MAAI,CAAC,oBAAoB,GAAG;AAE1B,OAAG,EAAE;AAEL,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAEA,MAAI,cAAc;AAClB,MAAI;AAEJ,QAAM,UAAU,IAAI,QAAc,CAAC,YAAY;AAC7C,qBAAiB;AAAA,EACnB,CAAC;AAED,YAAU,MAAM,QAAQ,IAAI,MAAM;AAChC,QAAI,CAAC,aAAa;AAChB,SAAG,EAAE;AAAA,IACP;AAEA,WAAO;AAAA,EACT,CAAC;AAED,SAAO,MAAM;AACX,kBAAc;AACd,mBAAe;AAAA,EACjB;AACF;","names":[]}
@@ -1,3 +1,4 @@
1
+ declare function isWebLocksSupported(): boolean;
1
2
  declare function requestLock(id: string, cb: (id: string) => void): () => void;
2
3
 
3
- export { requestLock };
4
+ export { isWebLocksSupported, requestLock };
@@ -1,6 +1,6 @@
1
1
  // src/hooks/internal/useWebsocketEvent.ts
2
2
  import { useEffect } from "react";
3
- import { requestLock } from "../../utils/requestLock.js";
3
+ import { isWebLocksSupported, requestLock } from "../../utils/requestLock.js";
4
4
  import { useNovu } from "../NovuProvider.js";
5
5
  import { useBrowserTabsChannel } from "./useBrowserTabsChannel.js";
6
6
  var useWebSocketEvent = ({
@@ -15,9 +15,12 @@ var useWebSocketEvent = ({
15
15
  onMessage,
16
16
  enabled
17
17
  });
18
+ const shouldBroadcast = isWebLocksSupported();
18
19
  const updateReadCount = (data) => {
19
20
  onMessage(data);
20
- postMessage(data);
21
+ if (shouldBroadcast) {
22
+ postMessage(data);
23
+ }
21
24
  };
22
25
  useEffect(() => {
23
26
  if (!enabled) return;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/hooks/internal/useWebsocketEvent.ts"],"sourcesContent":["import { EventHandler, Events, SocketEventNames } from '@novu/js';\nimport { useEffect } from 'react';\nimport { requestLock } from '../../utils/requestLock';\nimport { useNovu } from '../NovuProvider';\nimport { useBrowserTabsChannel } from './useBrowserTabsChannel';\n\nexport const useWebSocketEvent = <E extends SocketEventNames>({\n event: webSocketEvent,\n eventHandler: onMessage,\n enabled = true,\n}: {\n event: E;\n eventHandler: (args: Events[E]) => void;\n enabled?: boolean;\n}) => {\n const novu = useNovu();\n const channelName = `nv_ws_connection:a=${novu.applicationIdentifier}:s=${novu.subscriberId}:c=${novu.contextKey}:e=${webSocketEvent}`;\n\n const { postMessage } = useBrowserTabsChannel({\n channelName,\n onMessage,\n enabled,\n });\n\n const updateReadCount: EventHandler<Events[E]> = (data) => {\n onMessage(data);\n postMessage(data);\n };\n\n useEffect(() => {\n if (!enabled) return;\n\n let cleanup: () => void;\n const resolveLock = requestLock(channelName, () => {\n cleanup = novu.on(webSocketEvent, updateReadCount);\n });\n\n return () => {\n if (cleanup) {\n cleanup();\n }\n\n resolveLock();\n };\n }, [enabled]);\n};\n"],"mappings":";AACA,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAC5B,SAAS,eAAe;AACxB,SAAS,6BAA6B;AAE/B,IAAM,oBAAoB,CAA6B;AAAA,EAC5D,OAAO;AAAA,EACP,cAAc;AAAA,EACd,UAAU;AACZ,MAIM;AACJ,QAAM,OAAO,QAAQ;AACrB,QAAM,cAAc,sBAAsB,KAAK,qBAAqB,MAAM,KAAK,YAAY,MAAM,KAAK,UAAU,MAAM,cAAc;AAEpI,QAAM,EAAE,YAAY,IAAI,sBAAsB;AAAA,IAC5C;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,kBAA2C,CAAC,SAAS;AACzD,cAAU,IAAI;AACd,gBAAY,IAAI;AAAA,EAClB;AAEA,YAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAEd,QAAI;AACJ,UAAM,cAAc,YAAY,aAAa,MAAM;AACjD,gBAAU,KAAK,GAAG,gBAAgB,eAAe;AAAA,IACnD,CAAC;AAED,WAAO,MAAM;AACX,UAAI,SAAS;AACX,gBAAQ;AAAA,MACV;AAEA,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AACd;","names":[]}
1
+ {"version":3,"sources":["../../../../src/hooks/internal/useWebsocketEvent.ts"],"sourcesContent":["import { EventHandler, Events, SocketEventNames } from '@novu/js';\nimport { useEffect } from 'react';\nimport { isWebLocksSupported, requestLock } from '../../utils/requestLock';\nimport { useNovu } from '../NovuProvider';\nimport { useBrowserTabsChannel } from './useBrowserTabsChannel';\n\nexport const useWebSocketEvent = <E extends SocketEventNames>({\n event: webSocketEvent,\n eventHandler: onMessage,\n enabled = true,\n}: {\n event: E;\n eventHandler: (args: Events[E]) => void;\n enabled?: boolean;\n}) => {\n const novu = useNovu();\n const channelName = `nv_ws_connection:a=${novu.applicationIdentifier}:s=${novu.subscriberId}:c=${novu.contextKey}:e=${webSocketEvent}`;\n\n const { postMessage } = useBrowserTabsChannel({\n channelName,\n onMessage,\n enabled,\n });\n\n // When Web Locks are unavailable, requestLock runs the callback in every tab,\n // so each tab already receives the event through its own socket. Re-broadcasting\n // in that case would duplicate the event across tabs (count multiplied by tab\n // count). Only the exclusive lock owner should fan out to the other tabs.\n const shouldBroadcast = isWebLocksSupported();\n const updateReadCount: EventHandler<Events[E]> = (data) => {\n onMessage(data);\n if (shouldBroadcast) {\n postMessage(data);\n }\n };\n\n useEffect(() => {\n if (!enabled) return;\n\n let cleanup: () => void;\n const resolveLock = requestLock(channelName, () => {\n cleanup = novu.on(webSocketEvent, updateReadCount);\n });\n\n return () => {\n if (cleanup) {\n cleanup();\n }\n\n resolveLock();\n };\n }, [enabled]);\n};\n"],"mappings":";AACA,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB,mBAAmB;AACjD,SAAS,eAAe;AACxB,SAAS,6BAA6B;AAE/B,IAAM,oBAAoB,CAA6B;AAAA,EAC5D,OAAO;AAAA,EACP,cAAc;AAAA,EACd,UAAU;AACZ,MAIM;AACJ,QAAM,OAAO,QAAQ;AACrB,QAAM,cAAc,sBAAsB,KAAK,qBAAqB,MAAM,KAAK,YAAY,MAAM,KAAK,UAAU,MAAM,cAAc;AAEpI,QAAM,EAAE,YAAY,IAAI,sBAAsB;AAAA,IAC5C;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAMD,QAAM,kBAAkB,oBAAoB;AAC5C,QAAM,kBAA2C,CAAC,SAAS;AACzD,cAAU,IAAI;AACd,QAAI,iBAAiB;AACnB,kBAAY,IAAI;AAAA,IAClB;AAAA,EACF;AAEA,YAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAEd,QAAI;AACJ,UAAM,cAAc,YAAY,aAAa,MAAM;AACjD,gBAAU,KAAK,GAAG,gBAAgB,eAAe;AAAA,IACnD,CAAC;AAED,WAAO,MAAM;AACX,UAAI,SAAS;AACX,gBAAQ;AAAA,MACV;AAEA,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AACd;","names":[]}
@@ -1,3 +1,4 @@
1
+ declare function isWebLocksSupported(): boolean;
1
2
  declare function requestLock(id: string, cb: (id: string) => void): () => void;
2
3
 
3
- export { requestLock };
4
+ export { isWebLocksSupported, requestLock };
@@ -1,6 +1,9 @@
1
1
  // src/utils/requestLock.ts
2
+ function isWebLocksSupported() {
3
+ return typeof navigator !== "undefined" && "locks" in navigator && !!navigator.locks;
4
+ }
2
5
  function requestLock(id, cb) {
3
- if (!("locks" in navigator)) {
6
+ if (!isWebLocksSupported()) {
4
7
  cb(id);
5
8
  return () => {
6
9
  };
@@ -22,6 +25,7 @@ function requestLock(id, cb) {
22
25
  };
23
26
  }
24
27
  export {
28
+ isWebLocksSupported,
25
29
  requestLock
26
30
  };
27
31
  //# sourceMappingURL=requestLock.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/requestLock.ts"],"sourcesContent":["export function requestLock(id: string, cb: (id: string) => void) {\n // Check if the Lock API is available\n if (!('locks' in navigator)) {\n // If Lock API is not available, immediately invoke the callback and return a no-op function\n cb(id);\n\n return () => {};\n }\n\n let isFulfilled = false;\n let promiseResolve: () => void;\n\n const promise = new Promise<void>((resolve) => {\n promiseResolve = resolve;\n });\n\n navigator.locks.request(id, () => {\n if (!isFulfilled) {\n cb(id);\n }\n\n return promise;\n });\n\n return () => {\n isFulfilled = true;\n promiseResolve();\n };\n}\n"],"mappings":";AAAO,SAAS,YAAY,IAAY,IAA0B;AAEhE,MAAI,EAAE,WAAW,YAAY;AAE3B,OAAG,EAAE;AAEL,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAEA,MAAI,cAAc;AAClB,MAAI;AAEJ,QAAM,UAAU,IAAI,QAAc,CAAC,YAAY;AAC7C,qBAAiB;AAAA,EACnB,CAAC;AAED,YAAU,MAAM,QAAQ,IAAI,MAAM;AAChC,QAAI,CAAC,aAAa;AAChB,SAAG,EAAE;AAAA,IACP;AAEA,WAAO;AAAA,EACT,CAAC;AAED,SAAO,MAAM;AACX,kBAAc;AACd,mBAAe;AAAA,EACjB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/utils/requestLock.ts"],"sourcesContent":["export function isWebLocksSupported(): boolean {\n return typeof navigator !== 'undefined' && 'locks' in navigator && !!navigator.locks;\n}\n\nexport function requestLock(id: string, cb: (id: string) => void) {\n // Check if the Lock API is available\n if (!isWebLocksSupported()) {\n // If Lock API is not available, immediately invoke the callback and return a no-op function\n cb(id);\n\n return () => {};\n }\n\n let isFulfilled = false;\n let promiseResolve: () => void;\n\n const promise = new Promise<void>((resolve) => {\n promiseResolve = resolve;\n });\n\n navigator.locks.request(id, () => {\n if (!isFulfilled) {\n cb(id);\n }\n\n return promise;\n });\n\n return () => {\n isFulfilled = true;\n promiseResolve();\n };\n}\n"],"mappings":";AAAO,SAAS,sBAA+B;AAC7C,SAAO,OAAO,cAAc,eAAe,WAAW,aAAa,CAAC,CAAC,UAAU;AACjF;AAEO,SAAS,YAAY,IAAY,IAA0B;AAEhE,MAAI,CAAC,oBAAoB,GAAG;AAE1B,OAAG,EAAE;AAEL,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAEA,MAAI,cAAc;AAClB,MAAI;AAEJ,QAAM,UAAU,IAAI,QAAc,CAAC,YAAY;AAC7C,qBAAiB;AAAA,EACnB,CAAC;AAED,YAAU,MAAM,QAAQ,IAAI,MAAM;AAChC,QAAI,CAAC,aAAa;AAChB,SAAG,EAAE;AAAA,IACP;AAEA,WAAO;AAAA,EACT,CAAC;AAED,SAAO,MAAM;AACX,kBAAc;AACd,mBAAe;AAAA,EACjB;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@novu/react",
3
- "version": "3.18.1",
3
+ "version": "3.18.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/novuhq/novu",
@@ -121,7 +121,7 @@
121
121
  }
122
122
  },
123
123
  "dependencies": {
124
- "@novu/js": "3.18.1"
124
+ "@novu/js": "3.18.2"
125
125
  },
126
126
  "nx": {
127
127
  "tags": [