runid-lys 0.6.0 → 0.7.1

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - `RouteProvider` exposing the active route, the route map and helpers via `useRouteInfo`, wiring page context and chatbot auto-open, and rendering project-supplied private/public templates
13
+ - `useRouteAccess` hook centralizing route permission checks (supports `string` and `string[]` any-of semantics for `mainWebserviceName`)
14
+ - Export `RouteProvider`, `useRouteInfo`, `useRouteAccess`, and related types (`RouteContextValue`, `RouteProviderProps`, `RouteTemplateProps`) from `runid-lys/providers`
15
+
16
+ ### Changed
17
+
18
+ - `RouteInterface.mainWebserviceName` and `PageDescriptionType.mainWebserviceName` accept `string | string[]` (any-of) in addition to `string`
19
+ - `useRestrictedLink` delegates permission checking to `useRouteAccess` so single/array semantics stay in one place
20
+
21
+ ### Fixed
22
+
23
+ - Empty-string URL query params are no longer coerced to `0` when forwarded to the chatbot page context
24
+ - `ChatbotProvider` resets its state (messages, conversation id, mode, streaming, refresh signal) when the connected user changes (login, logout, account switch) to prevent conversation leaks between accounts in the same browser session — implemented by keying the inner provider on `user?.id` so React unmounts the subtree atomically. Requires `ChatbotProvider` to be mounted inside `ConnectedUserProvider`.
25
+
10
26
  ## [0.6.0] - 2026-05-14
11
27
 
12
28
  ### Added
@@ -1,4 +1,6 @@
1
- import { createContext, useContext } from "react";
1
+ import { jsx, Fragment } from "react/jsx-runtime";
2
+ import { createContext, useContext, useEffect } from "react";
3
+ import { Navigate } from "react-router-dom";
2
4
  const ChatbotContext = createContext(null);
3
5
  const useChatbot = () => {
4
6
  const context = useContext(ChatbotContext);
@@ -28,10 +30,29 @@ const ConnectedUserContext = createContext({
28
30
  function useConnectedUserInfo() {
29
31
  return useContext(ConnectedUserContext);
30
32
  }
33
+ const PublicAppTemplate = ({
34
+ route,
35
+ defaultPrivateRoute,
36
+ children
37
+ }) => {
38
+ var _a;
39
+ const { user } = useConnectedUserInfo();
40
+ const { setIsChatbotEnabled } = useChatbot();
41
+ useEffect(() => {
42
+ setIsChatbotEnabled(false);
43
+ return () => setIsChatbotEnabled(true);
44
+ }, [setIsChatbotEnabled]);
45
+ const isOpened = ((_a = route.options) == null ? void 0 : _a.opened) === true;
46
+ if (user && !isOpened) {
47
+ return /* @__PURE__ */ jsx(Navigate, { to: defaultPrivateRoute.path, replace: true });
48
+ }
49
+ return /* @__PURE__ */ jsx(Fragment, { children });
50
+ };
31
51
  export {
32
52
  ChatbotContext as C,
53
+ PublicAppTemplate as P,
33
54
  useConnectedUserInfo as a,
34
55
  ConnectedUserContext as b,
35
56
  useChatbot as u
36
57
  };
37
- //# sourceMappingURL=hooks-CvhFUowR.js.map
58
+ //# sourceMappingURL=PublicAppTemplate-DLKcJZVR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PublicAppTemplate-DLKcJZVR.js","sources":["../src/providers/ChatbotProvider/hooks.ts","../src/providers/ConnectedUserProvider/hooks.ts","../src/templates/PublicAppTemplate.tsx"],"sourcesContent":["import {createContext, useContext} from \"react\";\nimport {ChatbotContextValue} from \"./types\";\n\nexport const ChatbotContext = createContext<ChatbotContextValue | null>(null);\n\nexport const useChatbot = (): ChatbotContextValue => {\n const context = useContext(ChatbotContext);\n if (!context) {\n throw new Error(\"useChatbot must be used within a ChatbotProvider\");\n }\n return context;\n};\n","import {createContext, useContext} from \"react\";\nimport {ConnectedUserInterface} from \"./types\";\n\n/**\n * Connected user context\n */\nconst ConnectedUserContext = createContext<{\n user: ConnectedUserInterface | undefined\n push(webservice: () => void): void\n login: [(login: string, password: string) => void, boolean],\n logout: [() => void, boolean],\n refresh: [() => void, boolean],\n handleSessionExpired: (onRefreshSuccess?: () => void) => void\n}>({\n user: undefined,\n push: () => {\n console.warn(\"ConnectedUserProvider not initialized: push\")\n },\n login: [() => {\n console.warn(\"ConnectedUserProvider not initialized: login\")\n }, false],\n logout: [() => {\n console.warn(\"ConnectedUserProvider not initialized: logout\")\n }, false],\n refresh: [() => {\n console.warn(\"ConnectedUserProvider not initialized: refresh\")\n }, false],\n handleSessionExpired: () => {\n console.warn(\"ConnectedUserProvider not initialized: handleSessionExpired\")\n }\n});\n\n/**\n * Hook to access connected user info\n */\nfunction useConnectedUserInfo() {\n return useContext(ConnectedUserContext)\n}\n\nexport {\n ConnectedUserContext,\n useConnectedUserInfo\n}\n","import * as React from \"react\";\nimport {useEffect} from \"react\";\nimport {Navigate} from \"react-router-dom\";\nimport {useConnectedUserInfo} from \"../providers/ConnectedUserProvider/hooks\";\nimport {useChatbot} from \"../providers/ChatbotProvider/hooks\";\nimport {RouteInterface} from \"../types/routeTypes\";\n\ninterface PublicAppTemplateProps {\n route: RouteInterface\n defaultPrivateRoute: RouteInterface\n defaultPublicRoute: RouteInterface\n children: React.ReactNode\n}\n\n/**\n * Public app template\n * Wraps public pages and redirects authenticated users to private area\n * Unless route.options?.opened is true (public pages accessible when connected)\n */\nconst PublicAppTemplate: React.ComponentType<PublicAppTemplateProps> = (\n {\n route,\n defaultPrivateRoute,\n children\n }) => {\n\n /*******************************************************************************************************************\n * HOOKS\n ******************************************************************************************************************/\n\n const {user} = useConnectedUserInfo();\n const {setIsChatbotEnabled} = useChatbot();\n\n /*******************************************************************************************************************\n * EFFECTS\n ******************************************************************************************************************/\n\n // Disable chatbot on public pages\n useEffect(() => {\n setIsChatbotEnabled(false);\n return () => setIsChatbotEnabled(true);\n }, [setIsChatbotEnabled]);\n\n /*******************************************************************************************************************\n * RENDER\n ******************************************************************************************************************/\n\n // Redirect authenticated users to private area (unless page is opened)\n const isOpened = route.options?.opened === true;\n if (user && !isOpened) {\n return <Navigate to={defaultPrivateRoute.path} replace />;\n }\n\n return (\n <>\n {children}\n </>\n );\n};\n\nexport default PublicAppTemplate;\n"],"names":[],"mappings":";;;AAGO,MAAM,iBAAiB,cAA0C,IAAI;AAErE,MAAM,aAAa,MAA2B;AACjD,QAAM,UAAU,WAAW,cAAc;AACzC,MAAI,CAAC,SAAS;AACV,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACtE;AACA,SAAO;AACX;ACLA,MAAM,uBAAuB,cAO1B;AAAA,EACC,MAAM;AAAA,EACN,MAAM,MAAM;AACR,YAAQ,KAAK,6CAA6C;AAAA,EAC9D;AAAA,EACA,OAAO,CAAC,MAAM;AACV,YAAQ,KAAK,8CAA8C;AAAA,EAC/D,GAAG,KAAK;AAAA,EACR,QAAQ,CAAC,MAAM;AACX,YAAQ,KAAK,+CAA+C;AAAA,EAChE,GAAG,KAAK;AAAA,EACR,SAAS,CAAC,MAAM;AACZ,YAAQ,KAAK,gDAAgD;AAAA,EACjE,GAAG,KAAK;AAAA,EACR,sBAAsB,MAAM;AACxB,YAAQ,KAAK,6DAA6D;AAAA,EAC9E;AACJ,CAAC;AAKD,SAAS,uBAAuB;AAC5B,SAAO,WAAW,oBAAoB;AAC1C;AClBA,MAAM,oBAAiE,CACnE;AAAA,EACI;AAAA,EACA;AAAA,EACA;AACJ,MAAM;;AAMN,QAAM,EAAC,KAAA,IAAQ,qBAAA;AACf,QAAM,EAAC,oBAAA,IAAuB,WAAA;AAO9B,YAAU,MAAM;AACZ,wBAAoB,KAAK;AACzB,WAAO,MAAM,oBAAoB,IAAI;AAAA,EACzC,GAAG,CAAC,mBAAmB,CAAC;AAOxB,QAAM,aAAW,WAAM,YAAN,mBAAe,YAAW;AAC3C,MAAI,QAAQ,CAAC,UAAU;AACnB,+BAAQ,UAAA,EAAS,IAAI,oBAAoB,MAAM,SAAO,MAAC;AAAA,EAC3D;AAEA,yCAES,UACL;AAER;"}
@@ -4,7 +4,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4
4
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
5
5
  import * as React from "react";
6
6
  import React__default, { createContext, useContext, useMemo, useState, useCallback, useRef, useEffect, useReducer, forwardRef, Suspense, useImperativeHandle, useTransition } from "react";
7
- import { C as ChatbotContext, b as ConnectedUserContext, a as useConnectedUserInfo } from "./hooks-CvhFUowR.js";
7
+ import { a as useConnectedUserInfo, C as ChatbotContext, b as ConnectedUserContext, u as useChatbot, P as PublicAppTemplate } from "./PublicAppTemplate-DLKcJZVR.js";
8
8
  import { useSearchParams, useLocation, matchPath, useNavigate } from "react-router-dom";
9
9
  import { usePreloadedQuery, useFragment, useMutation, useQueryLoader } from "react-relay";
10
10
  import { IntlProvider, useIntl } from "react-intl";
@@ -124,6 +124,10 @@ const useRefreshSignal = () => {
124
124
  return useContext(RefreshSignalContext);
125
125
  };
126
126
  const ChatbotProvider = ({ children }) => {
127
+ const { user } = useConnectedUserInfo();
128
+ return /* @__PURE__ */ jsx(ChatbotProviderInner, { children }, (user == null ? void 0 : user.id) ?? "anonymous");
129
+ };
130
+ const ChatbotProviderInner = ({ children }) => {
127
131
  const [messages, setMessages] = useState([]);
128
132
  const [conversationId, setConversationId] = useState(null);
129
133
  const [isChatbotMode, setIsChatbotMode] = useState(false);
@@ -2155,16 +2159,117 @@ const ClientProvider = ({ children, routes }) => {
2155
2159
  return /* @__PURE__ */ jsx(ClientContext.Provider, { value, children });
2156
2160
  };
2157
2161
  ClientProvider.displayName = "ClientProvider";
2162
+ const RouteContext = createContext({
2163
+ route: null,
2164
+ defaultPrivateRoute: null,
2165
+ defaultPublicRoute: null,
2166
+ allRoutes: {},
2167
+ getRouteByName: () => void 0
2168
+ });
2169
+ const useRouteInfo = () => useContext(RouteContext);
2170
+ const RouteProvider = ({
2171
+ route,
2172
+ routes,
2173
+ defaultPrivateRoute,
2174
+ defaultPublicRoute,
2175
+ privateTemplate: PrivateTemplate,
2176
+ publicTemplate: PublicTemplate = PublicAppTemplate,
2177
+ children
2178
+ }) => {
2179
+ const { setPageContext } = usePageContext();
2180
+ const { appliedParams } = useUrlQueries();
2181
+ const { setIsChatbotMode } = useChatbot();
2182
+ useEffect(() => {
2183
+ const params = {};
2184
+ let orderByField = null;
2185
+ let orderDir = null;
2186
+ appliedParams.forEach((value, key) => {
2187
+ if (key === "orderBy") {
2188
+ orderByField = value;
2189
+ return;
2190
+ }
2191
+ if (key === "orderDir") {
2192
+ orderDir = value;
2193
+ return;
2194
+ }
2195
+ if (value !== "" && !isNaN(Number(value))) {
2196
+ params[key] = Number(value);
2197
+ } else {
2198
+ params[key] = value;
2199
+ }
2200
+ });
2201
+ if (orderByField) {
2202
+ const isAscending = orderDir !== "DESC";
2203
+ params.orderBy = { [orderByField]: isAscending };
2204
+ }
2205
+ setPageContext(route.name, params);
2206
+ }, [route.name, appliedParams, setPageContext]);
2207
+ useEffect(() => {
2208
+ if (route.autoOpenChatbot) {
2209
+ setIsChatbotMode(true);
2210
+ }
2211
+ }, [route.name, route.autoOpenChatbot, setIsChatbotMode]);
2212
+ const allRoutes = useMemo(() => {
2213
+ const routesMap = {};
2214
+ routes.forEach((r) => {
2215
+ routesMap[r.name] = r;
2216
+ });
2217
+ return routesMap;
2218
+ }, [routes]);
2219
+ const getRouteByName = useCallback(
2220
+ (name) => allRoutes[name],
2221
+ [allRoutes]
2222
+ );
2223
+ return /* @__PURE__ */ jsxs(RouteContext.Provider, { value: {
2224
+ route,
2225
+ defaultPrivateRoute,
2226
+ defaultPublicRoute,
2227
+ allRoutes,
2228
+ getRouteByName
2229
+ }, children: [
2230
+ route.type === "public" && /* @__PURE__ */ jsx(
2231
+ PublicTemplate,
2232
+ {
2233
+ route,
2234
+ defaultPublicRoute,
2235
+ defaultPrivateRoute,
2236
+ children
2237
+ }
2238
+ ),
2239
+ route.type === "private" && /* @__PURE__ */ jsx(
2240
+ PrivateTemplate,
2241
+ {
2242
+ route,
2243
+ defaultPublicRoute,
2244
+ defaultPrivateRoute,
2245
+ children
2246
+ }
2247
+ )
2248
+ ] });
2249
+ };
2250
+ const useRouteAccess = () => {
2251
+ const { checkWebserviceAccess } = useWebserviceAccess();
2252
+ const { getRouteByName } = useRouteInfo();
2253
+ return useCallback(
2254
+ (routeOrName) => {
2255
+ if (!routeOrName) return false;
2256
+ const route = typeof routeOrName === "string" ? getRouteByName(routeOrName) : routeOrName;
2257
+ if (!route) return false;
2258
+ if (!route.mainWebserviceName) return true;
2259
+ if (Array.isArray(route.mainWebserviceName)) {
2260
+ return route.mainWebserviceName.some(checkWebserviceAccess);
2261
+ }
2262
+ return checkWebserviceAccess(route.mainWebserviceName);
2263
+ },
2264
+ [checkWebserviceAccess, getRouteByName]
2265
+ );
2266
+ };
2158
2267
  const EMPTY_PARAMS = {};
2159
2268
  function useRestrictedLink(route, parameters = EMPTY_PARAMS, queryParameters = EMPTY_PARAMS) {
2160
- const { checkWebserviceAccess } = useWebserviceAccess();
2269
+ const hasAccess = useRouteAccess();
2161
2270
  const routerNavigate = useNavigate();
2162
2271
  const [, startTransition] = useTransition();
2163
- const hasPermission = useMemo(() => {
2164
- if (!route) return false;
2165
- if (!route.mainWebserviceName) return true;
2166
- return checkWebserviceAccess(route.mainWebserviceName);
2167
- }, [route, checkWebserviceAccess]);
2272
+ const hasPermission = useMemo(() => hasAccess(route), [hasAccess, route]);
2168
2273
  const navigate = useCallback(() => {
2169
2274
  if (!route) return;
2170
2275
  startTransition(() => {
@@ -2179,10 +2284,13 @@ function useRestrictedLink(route, parameters = EMPTY_PARAMS, queryParameters = E
2179
2284
  const GRAPHQL_ERROR = "GraphQL Error";
2180
2285
  export {
2181
2286
  AlertMessageProvider as A,
2287
+ useUrlQueries as B,
2182
2288
  ChatbotProvider as C,
2289
+ useWebserviceAccess as D,
2183
2290
  ErrorBoundaryProvider as E,
2184
2291
  FilterLabelsProvider as F,
2185
2292
  GRAPHQL_ERROR as G,
2293
+ webserviceAccessProviderConfig as H,
2186
2294
  LocaleProvider as L,
2187
2295
  PageContextProvider as P,
2188
2296
  RefreshSignalContext as R,
@@ -2196,24 +2304,24 @@ export {
2196
2304
  LysLoadingContext as e,
2197
2305
  LysMutationProvider as f,
2198
2306
  LysQueryProvider as g,
2199
- useClientId as h,
2200
- useDialogWithUpdates as i,
2201
- useFilterLabels as j,
2202
- useLocale as k,
2203
- useLysDialog as l,
2204
- useLysLoadingFallback as m,
2205
- useLysMutation as n,
2206
- useLysQuery as o,
2207
- usePageContext as p,
2208
- usePermissionCheck as q,
2209
- useRefreshSignal as r,
2210
- useRestrictedLink as s,
2211
- useSignal as t,
2307
+ RouteProvider as h,
2308
+ useClientId as i,
2309
+ useDialogWithUpdates as j,
2310
+ useFilterLabels as k,
2311
+ useLocale as l,
2312
+ useLysDialog as m,
2313
+ useLysLoadingFallback as n,
2314
+ useLysMutation as o,
2315
+ useLysQuery as p,
2316
+ usePageContext as q,
2317
+ usePermissionCheck as r,
2318
+ useRefreshSignal as s,
2319
+ useRestrictedLink as t,
2212
2320
  useAlertMessages as u,
2213
- useSignalRefresh as v,
2214
- useSignalSubscription as w,
2215
- useUrlQueries as x,
2216
- useWebserviceAccess as y,
2217
- webserviceAccessProviderConfig as z
2321
+ useRouteAccess as v,
2322
+ useRouteInfo as w,
2323
+ useSignal as x,
2324
+ useSignalRefresh as y,
2325
+ useSignalSubscription as z
2218
2326
  };
2219
- //# sourceMappingURL=constants-BMsk7KvD.js.map
2327
+ //# sourceMappingURL=constants-mSRd0a6-.js.map