@webiny/app-websockets 6.4.5-beta.0 → 6.6.0-alpha.0

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.
@@ -1,13 +1,24 @@
1
1
  import react, { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { useFeature, useTenantContext } from "@webiny/app-admin";
3
3
  import { AuthenticationContextFeature } from "@webiny/app-admin/features/security/AuthenticationContext/feature.js";
4
+ import { EventPublisherFeature } from "@webiny/app/features/eventPublisher/feature.js";
5
+ import { EnvConfigFeature } from "@webiny/app/features/envConfig/feature.js";
6
+ import { WebsocketEvent } from "./events/WebsocketEvent.js";
4
7
  import { WebsocketsCloseCode } from "./types.js";
5
8
  import { createWebsocketsAction, createWebsocketsActions, createWebsocketsConnection, createWebsocketsManager, createWebsocketsSubscriptionManager } from "./domain/index.js";
6
- import { getUrl } from "./utils/getUrl.js";
9
+ const noopSubscription = ()=>({
10
+ cb: ()=>void 0,
11
+ id: "",
12
+ off: ()=>void 0
13
+ });
7
14
  const WebsocketsContext = /*#__PURE__*/ react.createContext(void 0);
8
15
  const WebsocketsContextProvider = (props)=>{
9
16
  const { tenant } = useTenantContext();
10
17
  const { authenticationContext } = useFeature(AuthenticationContextFeature);
18
+ const { eventPublisher } = useFeature(EventPublisherFeature);
19
+ const envConfig = useFeature(EnvConfigFeature);
20
+ const configuredWsUrl = envConfig.get("websocketUrl");
21
+ const wsUrl = configuredWsUrl ? configuredWsUrl : void 0;
11
22
  const socketsRef = useRef(null);
12
23
  const [current, setCurrent] = useState({});
13
24
  const getToken = useCallback(async ()=>await authenticationContext.getIdToken(), [
@@ -58,11 +69,8 @@ const WebsocketsContextProvider = (props)=>{
58
69
  if (!token || !tenant) return;
59
70
  if (current.tenant === tenant) return;
60
71
  if (socketsRef.current) await socketsRef.current.close(WebsocketsCloseCode.NORMAL, "Changing tenant.");
61
- const url = getUrl();
62
- if (!url) return void console.error("Not possible to connect to the websocket without a valid URL.", {
63
- tenant,
64
- token
65
- });
72
+ const url = wsUrl;
73
+ if (!url) return void console.warn("WebSocket URL not configured; real-time updates are disabled.");
66
74
  socketsRef.current = createWebsocketsManager(createWebsocketsConnection({
67
75
  subscriptionManager,
68
76
  url,
@@ -80,7 +88,20 @@ const WebsocketsContextProvider = (props)=>{
80
88
  }, [
81
89
  tenant,
82
90
  subscriptionManager,
83
- getToken
91
+ getToken,
92
+ wsUrl
93
+ ]);
94
+ useEffect(()=>{
95
+ if (!socketsRef.current) return;
96
+ const subscription = socketsRef.current.onMessage(async (event)=>{
97
+ await eventPublisher.publish(new WebsocketEvent(event.data));
98
+ });
99
+ return ()=>{
100
+ subscription.off();
101
+ };
102
+ }, [
103
+ current.tenant,
104
+ eventPublisher
84
105
  ]);
85
106
  const websocketActions = useMemo(()=>createWebsocketsActions({
86
107
  manager: socketsRef.current,
@@ -101,19 +122,28 @@ const WebsocketsContextProvider = (props)=>{
101
122
  const createAction = useCallback((name)=>createWebsocketsAction(websocketActions, name), [
102
123
  websocketActions
103
124
  ]);
104
- const onMessage = useCallback((action, cb)=>socketsRef.current.onMessage(async (event)=>{
125
+ const onMessage = useCallback((action, cb)=>{
126
+ if (!socketsRef.current) return noopSubscription();
127
+ return socketsRef.current.onMessage(async (event)=>{
105
128
  if (event.data.action !== action) return;
106
129
  cb(event.data);
107
- }), [
130
+ });
131
+ }, [
108
132
  socketsRef.current
109
133
  ]);
110
- const onError = useCallback((cb)=>socketsRef.current.onError((data)=>cb(data)), [
134
+ const onError = useCallback((cb)=>{
135
+ if (!socketsRef.current) return noopSubscription();
136
+ return socketsRef.current.onError((data)=>cb(data));
137
+ }, [
111
138
  socketsRef.current
112
139
  ]);
113
- const onClose = useCallback((cb)=>socketsRef.current.onClose((data)=>cb(data)), [
140
+ const onClose = useCallback((cb)=>{
141
+ if (!socketsRef.current) return noopSubscription();
142
+ return socketsRef.current.onClose((data)=>cb(data));
143
+ }, [
114
144
  socketsRef.current
115
145
  ]);
116
- if (!socketsRef.current) return props.loader || null;
146
+ if (wsUrl && !socketsRef.current) return props.loader || null;
117
147
  const value = {
118
148
  send,
119
149
  createAction,
@@ -1 +1 @@
1
- {"version":3,"file":"WebsocketsContextProvider.js","sources":["../src/WebsocketsContextProvider.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useFeature, useTenantContext } from \"@webiny/app-admin\";\nimport { AuthenticationContextFeature } from \"@webiny/app-admin/features/security/AuthenticationContext/feature.js\";\nimport type {\n IncomingGenericData,\n IWebsocketsContext,\n IWebsocketsContextSendCallable,\n IWebsocketsManagerCloseEvent,\n IWebsocketsManagerErrorEvent\n} from \"~/types.js\";\nimport { WebsocketsCloseCode } from \"~/types.js\";\nimport {\n createWebsocketsAction,\n createWebsocketsActions,\n createWebsocketsConnection,\n createWebsocketsManager,\n createWebsocketsSubscriptionManager\n} from \"./domain/index.js\";\nimport type { IGenericData, IWebsocketsManager } from \"./domain/types.js\";\nimport { getUrl } from \"./utils/getUrl.js\";\n\nexport interface IWebsocketsContextProviderProps {\n loader?: React.ReactElement;\n children: React.ReactNode;\n}\n\nexport const WebsocketsContext = React.createContext<IWebsocketsContext>(\n undefined as unknown as IWebsocketsContext\n);\n\ninterface ICurrentData {\n tenant?: string;\n}\n\nexport const WebsocketsContextProvider = (props: IWebsocketsContextProviderProps) => {\n const { tenant } = useTenantContext();\n const { authenticationContext } = useFeature(AuthenticationContextFeature);\n\n const socketsRef = useRef<IWebsocketsManager | null>(null);\n\n const [current, setCurrent] = useState<ICurrentData>({});\n\n const getToken = useCallback(async () => {\n return await authenticationContext.getIdToken();\n }, [authenticationContext]);\n\n const subscriptionManager = useMemo(() => {\n const manager = createWebsocketsSubscriptionManager();\n\n let currentIteration = 0;\n manager.onClose(event => {\n if (currentIteration > 5 || event.code !== WebsocketsCloseCode.GOING_AWAY) {\n return;\n }\n currentIteration++;\n setTimeout(() => {\n if (!socketsRef.current) {\n return;\n } else if (socketsRef.current.isClosed()) {\n console.log(\"Running auto-reconnect.\");\n\n socketsRef.current.connect();\n }\n }, 1000);\n });\n\n return manager;\n }, []);\n\n /**\n * We need this useEffect to close the websocket connection and remove window focus event in case component is unmounted.\n * This will, probably, happen only during the development phase.\n *\n * If we did not disconnect on component unmount, we would have a memory leak - multiple connections would be opened.\n */\n useEffect(() => {\n /**\n * We want to add a window event listener which will check if the connection is closed, and if its - it will connect again.\n */\n const abortController = new AbortController();\n\n window.addEventListener(\n \"focus\",\n () => {\n if (!socketsRef.current) {\n return;\n } else if (socketsRef.current.isClosed()) {\n console.log(\"Running auto-reconnect on focus.\");\n socketsRef.current.connect();\n }\n },\n { signal: abortController.signal }\n );\n window.addEventListener(\n \"close\",\n () => {\n subscriptionManager.triggerOnClose(\n new CloseEvent(\"windowClose\", {\n code: WebsocketsCloseCode.GOING_AWAY,\n reason: \"Closing Window or Tab.\"\n })\n );\n },\n { signal: abortController.signal }\n );\n\n return () => {\n abortController.abort();\n };\n }, []);\n\n useEffect(() => {\n (async () => {\n const token = await getToken();\n if (!token || !tenant) {\n return;\n } else if (current.tenant === tenant) {\n return;\n } else if (socketsRef.current) {\n await socketsRef.current.close(WebsocketsCloseCode.NORMAL, \"Changing tenant.\");\n }\n const url = getUrl();\n\n if (!url) {\n console.error(\"Not possible to connect to the websocket without a valid URL.\", {\n tenant,\n token\n });\n return;\n }\n\n socketsRef.current = createWebsocketsManager(\n createWebsocketsConnection({\n subscriptionManager,\n url,\n tenant,\n getToken,\n protocol: [\"webiny-ws-v1\"]\n })\n );\n await socketsRef.current.connect();\n\n setCurrent({ tenant });\n })();\n }, [tenant, subscriptionManager, getToken]);\n\n const websocketActions = useMemo(() => {\n return createWebsocketsActions({\n manager: socketsRef.current!,\n tenant,\n getToken\n });\n }, [socketsRef.current, tenant, getToken]);\n\n const send = useCallback<IWebsocketsContextSendCallable>(\n async (action, data, timeout) => {\n return websocketActions.run({\n action,\n data,\n timeout\n });\n },\n [websocketActions]\n );\n\n const createAction = useCallback(\n <T extends IGenericData = IGenericData, R extends IGenericData = IGenericData>(\n name: string\n ) => {\n return createWebsocketsAction<T, R>(websocketActions, name);\n },\n [websocketActions]\n );\n\n const onMessage = useCallback(\n <T extends IncomingGenericData = IncomingGenericData>(\n action: string,\n cb: (data: T) => void\n ) => {\n return socketsRef.current!.onMessage<T>(async event => {\n if (event.data.action !== action) {\n return;\n }\n cb(event.data);\n });\n },\n [socketsRef.current]\n );\n\n const onError = useCallback(\n (cb: (data: IWebsocketsManagerErrorEvent) => void) => {\n return socketsRef.current!.onError(data => {\n return cb(data);\n });\n },\n [socketsRef.current]\n );\n\n const onClose = useCallback(\n (cb: (data: IWebsocketsManagerCloseEvent) => void) => {\n return socketsRef.current!.onClose(data => {\n return cb(data);\n });\n },\n [socketsRef.current]\n );\n\n if (!socketsRef.current) {\n return props.loader || null;\n }\n\n const value: IWebsocketsContext = {\n send,\n createAction,\n onMessage,\n onError,\n onClose\n };\n return <WebsocketsContext.Provider value={value} {...props} />;\n};\n"],"names":["WebsocketsContext","React","undefined","WebsocketsContextProvider","props","tenant","useTenantContext","authenticationContext","useFeature","AuthenticationContextFeature","socketsRef","useRef","current","setCurrent","useState","getToken","useCallback","subscriptionManager","useMemo","manager","createWebsocketsSubscriptionManager","currentIteration","event","WebsocketsCloseCode","setTimeout","console","useEffect","abortController","AbortController","window","CloseEvent","token","url","getUrl","createWebsocketsManager","createWebsocketsConnection","websocketActions","createWebsocketsActions","send","action","data","timeout","createAction","name","createWebsocketsAction","onMessage","cb","onError","onClose","value"],"mappings":"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"}
1
+ {"version":3,"file":"WebsocketsContextProvider.js","sources":["../src/WebsocketsContextProvider.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useFeature, useTenantContext } from \"@webiny/app-admin\";\nimport { AuthenticationContextFeature } from \"@webiny/app-admin/features/security/AuthenticationContext/feature.js\";\nimport { EventPublisherFeature } from \"@webiny/app/features/eventPublisher/feature.js\";\nimport { EnvConfigFeature } from \"@webiny/app/features/envConfig/feature.js\";\nimport { WebsocketEvent } from \"./events/WebsocketEvent.js\";\nimport type {\n IncomingGenericData,\n IWebsocketsContext,\n IWebsocketsContextSendCallable,\n IWebsocketsManagerCloseEvent,\n IWebsocketsManagerErrorEvent\n} from \"~/types.js\";\nimport { WebsocketsCloseCode } from \"~/types.js\";\nimport {\n createWebsocketsAction,\n createWebsocketsActions,\n createWebsocketsConnection,\n createWebsocketsManager,\n createWebsocketsSubscriptionManager\n} from \"./domain/index.js\";\nimport type { IGenericData, IWebsocketsManager } from \"./domain/types.js\";\nimport type { IWebsocketsSubscription } from \"./domain/abstractions/IWebsocketsSubscriptionManager.js\";\n\n// No-op subscription returned when WS isn't connected (no URL configured).\nconst noopSubscription = (): IWebsocketsSubscription<any> => ({\n cb: () => undefined,\n id: \"\",\n off: () => undefined\n});\n\nexport interface IWebsocketsContextProviderProps {\n loader?: React.ReactElement;\n children: React.ReactNode;\n}\n\nexport const WebsocketsContext = React.createContext<IWebsocketsContext>(\n undefined as unknown as IWebsocketsContext\n);\n\ninterface ICurrentData {\n tenant?: string;\n}\n\nexport const WebsocketsContextProvider = (props: IWebsocketsContextProviderProps) => {\n const { tenant } = useTenantContext();\n const { authenticationContext } = useFeature(AuthenticationContextFeature);\n const { eventPublisher } = useFeature(EventPublisherFeature);\n\n // The WebSocket URL is resolved once, at the admin composition root, into EnvConfig (the only\n // place that reads process.env). Empty string = not configured → real-time updates are disabled.\n const envConfig = useFeature(EnvConfigFeature);\n const configuredWsUrl = envConfig.get(\"websocketUrl\");\n const wsUrl = configuredWsUrl ? configuredWsUrl : undefined;\n\n const socketsRef = useRef<IWebsocketsManager | null>(null);\n\n const [current, setCurrent] = useState<ICurrentData>({});\n\n const getToken = useCallback(async () => {\n return await authenticationContext.getIdToken();\n }, [authenticationContext]);\n\n const subscriptionManager = useMemo(() => {\n const manager = createWebsocketsSubscriptionManager();\n\n let currentIteration = 0;\n manager.onClose(event => {\n if (currentIteration > 5 || event.code !== WebsocketsCloseCode.GOING_AWAY) {\n return;\n }\n currentIteration++;\n setTimeout(() => {\n if (!socketsRef.current) {\n return;\n } else if (socketsRef.current.isClosed()) {\n console.log(\"Running auto-reconnect.\");\n\n socketsRef.current.connect();\n }\n }, 1000);\n });\n\n return manager;\n }, []);\n\n /**\n * We need this useEffect to close the websocket connection and remove window focus event in case component is unmounted.\n * This will, probably, happen only during the development phase.\n *\n * If we did not disconnect on component unmount, we would have a memory leak - multiple connections would be opened.\n */\n useEffect(() => {\n /**\n * We want to add a window event listener which will check if the connection is closed, and if its - it will connect again.\n */\n const abortController = new AbortController();\n\n window.addEventListener(\n \"focus\",\n () => {\n if (!socketsRef.current) {\n return;\n } else if (socketsRef.current.isClosed()) {\n console.log(\"Running auto-reconnect on focus.\");\n socketsRef.current.connect();\n }\n },\n { signal: abortController.signal }\n );\n window.addEventListener(\n \"close\",\n () => {\n subscriptionManager.triggerOnClose(\n new CloseEvent(\"windowClose\", {\n code: WebsocketsCloseCode.GOING_AWAY,\n reason: \"Closing Window or Tab.\"\n })\n );\n },\n { signal: abortController.signal }\n );\n\n return () => {\n abortController.abort();\n };\n }, []);\n\n useEffect(() => {\n (async () => {\n const token = await getToken();\n if (!token || !tenant) {\n return;\n } else if (current.tenant === tenant) {\n return;\n } else if (socketsRef.current) {\n await socketsRef.current.close(WebsocketsCloseCode.NORMAL, \"Changing tenant.\");\n }\n const url = wsUrl;\n\n if (!url) {\n // No WS URL configured (e.g. self-hosted server hosting type) — skip WS, run app anyway.\n console.warn(\"WebSocket URL not configured; real-time updates are disabled.\");\n return;\n }\n\n socketsRef.current = createWebsocketsManager(\n createWebsocketsConnection({\n subscriptionManager,\n url,\n tenant,\n getToken,\n protocol: [\"webiny-ws-v1\"]\n })\n );\n await socketsRef.current.connect();\n\n setCurrent({ tenant });\n })();\n }, [tenant, subscriptionManager, getToken, wsUrl]);\n\n /**\n * Bridge: subscribe once to ALL incoming websocket messages and re-publish each one through\n * the EventPublisher as a `WebsocketEvent`. Feature code registers `WebsocketEventHandler`\n * handlers instead of subscribing to the websocket service directly.\n */\n useEffect(() => {\n if (!socketsRef.current) {\n return;\n }\n const subscription = socketsRef.current.onMessage<IncomingGenericData>(async event => {\n await eventPublisher.publish(new WebsocketEvent(event.data));\n });\n return () => {\n subscription.off();\n };\n }, [current.tenant, eventPublisher]);\n\n const websocketActions = useMemo(() => {\n return createWebsocketsActions({\n manager: socketsRef.current!,\n tenant,\n getToken\n });\n }, [socketsRef.current, tenant, getToken]);\n\n const send = useCallback<IWebsocketsContextSendCallable>(\n async (action, data, timeout) => {\n return websocketActions.run({\n action,\n data,\n timeout\n });\n },\n [websocketActions]\n );\n\n const createAction = useCallback(\n <T extends IGenericData = IGenericData, R extends IGenericData = IGenericData>(\n name: string\n ) => {\n return createWebsocketsAction<T, R>(websocketActions, name);\n },\n [websocketActions]\n );\n\n const onMessage = useCallback(\n <T extends IncomingGenericData = IncomingGenericData>(\n action: string,\n cb: (data: T) => void\n ) => {\n // No-op subscription when WS isn't connected (e.g. no URL configured).\n if (!socketsRef.current) {\n return noopSubscription();\n }\n return socketsRef.current.onMessage<T>(async event => {\n if (event.data.action !== action) {\n return;\n }\n cb(event.data);\n });\n },\n [socketsRef.current]\n );\n\n const onError = useCallback(\n (cb: (data: IWebsocketsManagerErrorEvent) => void) => {\n if (!socketsRef.current) {\n return noopSubscription();\n }\n return socketsRef.current.onError(data => {\n return cb(data);\n });\n },\n [socketsRef.current]\n );\n\n const onClose = useCallback(\n (cb: (data: IWebsocketsManagerCloseEvent) => void) => {\n if (!socketsRef.current) {\n return noopSubscription();\n }\n return socketsRef.current.onClose(data => {\n return cb(data);\n });\n },\n [socketsRef.current]\n );\n\n // Only block the app on the WS connection when WS is actually configured. Without a URL\n // (e.g. the self-hosted server hosting type), skip WS and render the app anyway.\n if (wsUrl && !socketsRef.current) {\n return props.loader || null;\n }\n\n const value: IWebsocketsContext = {\n send,\n createAction,\n onMessage,\n onError,\n onClose\n };\n return <WebsocketsContext.Provider value={value} {...props} />;\n};\n"],"names":["noopSubscription","undefined","WebsocketsContext","React","WebsocketsContextProvider","props","tenant","useTenantContext","authenticationContext","useFeature","AuthenticationContextFeature","eventPublisher","EventPublisherFeature","envConfig","EnvConfigFeature","configuredWsUrl","wsUrl","socketsRef","useRef","current","setCurrent","useState","getToken","useCallback","subscriptionManager","useMemo","manager","createWebsocketsSubscriptionManager","currentIteration","event","WebsocketsCloseCode","setTimeout","console","useEffect","abortController","AbortController","window","CloseEvent","token","url","createWebsocketsManager","createWebsocketsConnection","subscription","WebsocketEvent","websocketActions","createWebsocketsActions","send","action","data","timeout","createAction","name","createWebsocketsAction","onMessage","cb","onError","onClose","value"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { BaseEvent } from "@webiny/app/features/eventPublisher/index.js";
2
+ import type { IncomingGenericData } from "../types.js";
3
+ /**
4
+ * Published for every incoming websocket message. Carries the raw message data
5
+ * (which always includes an `action`), and routes to `WebsocketEventHandler` handlers.
6
+ */
7
+ export declare class WebsocketEvent extends BaseEvent<IncomingGenericData> {
8
+ readonly eventType: "Websockets/MessageReceived";
9
+ getHandlerAbstraction(): import("@webiny/di").Abstraction<import("@webiny/app/features/eventPublisher/abstractions.js").IEventHandler<WebsocketEvent>>;
10
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseEvent } from "@webiny/app/features/eventPublisher/index.js";
2
+ import { WebsocketEventHandler } from "./abstractions.js";
3
+ class WebsocketEvent extends BaseEvent {
4
+ getHandlerAbstraction() {
5
+ return WebsocketEventHandler;
6
+ }
7
+ constructor(...args){
8
+ super(...args), this.eventType = "Websockets/MessageReceived";
9
+ }
10
+ }
11
+ export { WebsocketEvent };
12
+
13
+ //# sourceMappingURL=WebsocketEvent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events/WebsocketEvent.js","sources":["../../src/events/WebsocketEvent.ts"],"sourcesContent":["import { BaseEvent } from \"@webiny/app/features/eventPublisher/index.js\";\nimport { WebsocketEventHandler } from \"./abstractions.js\";\nimport type { IncomingGenericData } from \"~/types.js\";\n\n/**\n * Published for every incoming websocket message. Carries the raw message data\n * (which always includes an `action`), and routes to `WebsocketEventHandler` handlers.\n */\nexport class WebsocketEvent extends BaseEvent<IncomingGenericData> {\n readonly eventType = \"Websockets/MessageReceived\" as const;\n\n getHandlerAbstraction() {\n return WebsocketEventHandler;\n }\n}\n"],"names":["WebsocketEvent","BaseEvent","WebsocketEventHandler"],"mappings":";;AAQO,MAAMA,uBAAuBC;IAGhC,wBAAwB;QACpB,OAAOC;IACX;;QALG,qBACM,SAAS,GAAG;;AAKzB"}
@@ -0,0 +1,13 @@
1
+ import type { IEventHandler } from "@webiny/app/features/eventPublisher/index.js";
2
+ import type { WebsocketEvent } from "./WebsocketEvent.js";
3
+ /**
4
+ * Handlers registered against this abstraction receive EVERY incoming websocket message
5
+ * (published as a `WebsocketEvent` by the websockets→EventPublisher bridge). Each handler
6
+ * is responsible for filtering by `event.payload.action` and reacting to the ones it cares
7
+ * about, instead of subscribing to the websocket service directly.
8
+ */
9
+ export declare const WebsocketEventHandler: import("@webiny/di").Abstraction<IEventHandler<WebsocketEvent>>;
10
+ export declare namespace WebsocketEventHandler {
11
+ type Interface = IEventHandler<WebsocketEvent>;
12
+ type Event = WebsocketEvent;
13
+ }
@@ -0,0 +1,5 @@
1
+ import { createAbstraction } from "@webiny/feature/admin";
2
+ const WebsocketEventHandler = createAbstraction("App/WebsocketEventHandler");
3
+ export { WebsocketEventHandler };
4
+
5
+ //# sourceMappingURL=abstractions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events/abstractions.js","sources":["../../src/events/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { IEventHandler } from \"@webiny/app/features/eventPublisher/index.js\";\nimport type { WebsocketEvent } from \"./WebsocketEvent.js\";\n\n/**\n * Handlers registered against this abstraction receive EVERY incoming websocket message\n * (published as a `WebsocketEvent` by the websockets→EventPublisher bridge). Each handler\n * is responsible for filtering by `event.payload.action` and reacting to the ones it cares\n * about, instead of subscribing to the websocket service directly.\n */\nexport const WebsocketEventHandler = createAbstraction<IEventHandler<WebsocketEvent>>(\n \"App/WebsocketEventHandler\"\n);\n\nexport namespace WebsocketEventHandler {\n export type Interface = IEventHandler<WebsocketEvent>;\n export type Event = WebsocketEvent;\n}\n"],"names":["WebsocketEventHandler","createAbstraction"],"mappings":";AAUO,MAAMA,wBAAwBC,kBACjC"}
package/index.d.ts CHANGED
@@ -2,6 +2,9 @@ import React from "react";
2
2
  export interface WebsocketsProviderProps {
3
3
  children: React.ReactNode;
4
4
  }
5
- export declare const Websockets: React.MemoExoticComponent<() => React.JSX.Element>;
5
+ declare const WebsocketsExtension: () => React.JSX.Element;
6
+ export declare const Websockets: React.MemoExoticComponent<typeof WebsocketsExtension>;
6
7
  export * from "./types.js";
7
8
  export * from "./hooks/index.js";
9
+ export * from "./events/WebsocketEvent.js";
10
+ export * from "./events/abstractions.js";
package/index.js CHANGED
@@ -3,6 +3,8 @@ import { Provider } from "@webiny/app";
3
3
  import { WebsocketsContextProvider } from "./WebsocketsContextProvider.js";
4
4
  export * from "./types.js";
5
5
  export * from "./hooks/index.js";
6
+ export * from "./events/WebsocketEvent.js";
7
+ export * from "./events/abstractions.js";
6
8
  const WebsocketsHoc = (Component)=>function(props) {
7
9
  return /*#__PURE__*/ react.createElement(WebsocketsContextProvider, null, /*#__PURE__*/ react.createElement(Component, props));
8
10
  };
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.tsx"],"sourcesContent":["import React from \"react\";\nimport { Provider } from \"@webiny/app\";\nimport { WebsocketsContextProvider } from \"~/WebsocketsContextProvider.js\";\n\nexport interface WebsocketsProviderProps {\n children: React.ReactNode;\n}\n\nconst WebsocketsHoc = (Component: React.ComponentType<React.PropsWithChildren>) => {\n return function WebsocketsProvider(props: WebsocketsProviderProps) {\n return (\n <WebsocketsContextProvider>\n <Component {...props} />\n </WebsocketsContextProvider>\n );\n };\n};\n\nconst WebsocketsExtension = () => {\n return (\n <>\n <Provider hoc={WebsocketsHoc} />\n </>\n );\n};\n\nexport const Websockets = React.memo(WebsocketsExtension);\n\nexport * from \"./types.js\";\nexport * from \"./hooks/index.js\";\n"],"names":["WebsocketsHoc","Component","props","WebsocketsContextProvider","WebsocketsExtension","Provider","Websockets","React"],"mappings":";;;;;AAQA,MAAMA,gBAAgB,CAACC,YACZ,SAA4BC,KAA8B;QAC7D,OAAO,WAAP,GACI,oBAACC,2BAAyBA,MAAAA,WAAAA,GACtB,oBAACF,WAAcC;IAG3B;AAGJ,MAAME,sBAAsB,IACjB,WAAP,GACI,wDACI,oBAACC,UAAQA;QAAC,KAAKL;;AAKpB,MAAMM,aAAa,WAAHA,GAAGC,MAAAA,IAAU,CAACH"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.tsx"],"sourcesContent":["import React from \"react\";\nimport { Provider } from \"@webiny/app\";\nimport { WebsocketsContextProvider } from \"~/WebsocketsContextProvider.js\";\n\nexport interface WebsocketsProviderProps {\n children: React.ReactNode;\n}\n\nconst WebsocketsHoc = (Component: React.ComponentType<React.PropsWithChildren>) => {\n return function WebsocketsProvider(props: WebsocketsProviderProps) {\n return (\n <WebsocketsContextProvider>\n <Component {...props} />\n </WebsocketsContextProvider>\n );\n };\n};\n\nconst WebsocketsExtension = () => {\n return (\n <>\n <Provider hoc={WebsocketsHoc} />\n </>\n );\n};\n\nexport const Websockets = React.memo(WebsocketsExtension);\n\nexport * from \"./types.js\";\nexport * from \"./hooks/index.js\";\nexport * from \"./events/WebsocketEvent.js\";\nexport * from \"./events/abstractions.js\";\n"],"names":["WebsocketsHoc","Component","props","WebsocketsContextProvider","WebsocketsExtension","Provider","Websockets","React"],"mappings":";;;;;;;AAQA,MAAMA,gBAAgB,CAACC,YACZ,SAA4BC,KAA8B;QAC7D,OAAO,WAAP,GACI,oBAACC,2BAAyBA,MAAAA,WAAAA,GACtB,oBAACF,WAAcC;IAG3B;AAGJ,MAAME,sBAAsB,IACjB,WAAP,GACI,wDACI,oBAACC,UAAQA;QAAC,KAAKL;;AAKpB,MAAMM,aAAa,WAAHA,GAAGC,MAAAA,IAAU,CAACH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/app-websockets",
3
- "version": "6.4.5-beta.0",
3
+ "version": "6.6.0-alpha.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -17,16 +17,17 @@
17
17
  ],
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
- "@webiny/app": "6.4.5-beta.0",
21
- "@webiny/app-admin": "6.4.5-beta.0",
22
- "@webiny/utils": "6.4.5-beta.0",
20
+ "@webiny/app": "6.6.0-alpha.0",
21
+ "@webiny/app-admin": "6.6.0-alpha.0",
22
+ "@webiny/feature": "6.6.0-alpha.0",
23
+ "@webiny/utils": "6.6.0-alpha.0",
23
24
  "react": "18.3.1",
24
25
  "react-dom": "18.3.1"
25
26
  },
26
27
  "devDependencies": {
27
- "@webiny/build-tools": "6.4.5-beta.0",
28
+ "@webiny/build-tools": "6.6.0-alpha.0",
28
29
  "rimraf": "6.1.3",
29
- "typescript": "6.0.3"
30
+ "typescript": "7.0.2"
30
31
  },
31
32
  "publishConfig": {
32
33
  "access": "public"
package/utils/getUrl.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare const getUrl: () => string | undefined;
package/utils/getUrl.js DELETED
@@ -1,7 +0,0 @@
1
- const getUrl = ()=>{
2
- const websocketApiUrl = process.env.REACT_APP_WEBSOCKET_URL;
3
- return websocketApiUrl && "undefined" !== websocketApiUrl ? websocketApiUrl : void 0;
4
- };
5
- export { getUrl };
6
-
7
- //# sourceMappingURL=getUrl.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils/getUrl.js","sources":["../../src/utils/getUrl.ts"],"sourcesContent":["export const getUrl = (): string | undefined => {\n const websocketApiUrl = process.env.REACT_APP_WEBSOCKET_URL;\n\n return !websocketApiUrl || websocketApiUrl === \"undefined\" ? undefined : websocketApiUrl;\n};\n"],"names":["getUrl","websocketApiUrl","process","undefined"],"mappings":"AAAO,MAAMA,SAAS;IAClB,MAAMC,kBAAkBC,QAAQ,GAAG,CAAC,uBAAuB;IAE3D,OAAO,AAACD,mBAAmBA,AAAoB,gBAApBA,kBAA8CA,kBAAZE;AACjE"}