@cedarjs/router 8.0.0-canary.3541 → 8.0.0-canary.3542

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,9 +1,11 @@
1
1
  import React from 'react';
2
- import type { GeneratedRoutesMap } from './analyzeRoutes.js';
3
2
  interface AuthenticatedRouteProps {
4
3
  children: React.ReactNode | Thenable<React.ReactNode>;
5
4
  roles?: string | string[];
6
- unauthenticated: keyof GeneratedRoutesMap;
5
+ userRoles?: string | string[];
6
+ orgRoles?: string | string[];
7
+ unauthenticated: string;
8
+ unauthorized?: string;
7
9
  whileLoadingAuth?: () => React.ReactElement | null;
8
10
  }
9
11
  export declare const AuthenticatedRoute: React.FC<AuthenticatedRouteProps>;
@@ -1 +1 @@
1
- {"version":3,"file":"AuthenticatedRoute.d.ts","sourceRoot":"","sources":["../src/AuthenticatedRoute.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAA;AAE1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAK5D,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACrD,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACzB,eAAe,EAAE,MAAM,kBAAkB,CAAA;IACzC,gBAAgB,CAAC,EAAE,MAAM,KAAK,CAAC,YAAY,GAAG,IAAI,CAAA;CACnD;AAED,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CA6DhE,CAAA"}
1
+ {"version":3,"file":"AuthenticatedRoute.d.ts","sourceRoot":"","sources":["../src/AuthenticatedRoute.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAQzB,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACrD,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC5B,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,KAAK,CAAC,YAAY,GAAG,IAAI,CAAA;CACnD;AAsED,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CA4FhE,CAAA"}
@@ -1,10 +1,51 @@
1
- import React, { useCallback } from "react";
2
- import { namedRoutes } from "./namedRoutes.js";
1
+ import React from "react";
2
+ import { useLocation } from "./location.js";
3
3
  import { Redirect } from "./redirect.js";
4
4
  import { useRouterState } from "./router-context.js";
5
+ import { paramsForRoute, pathParamsText } from "./util.js";
6
+ const REPLACE = { replace: true };
7
+ const useNoOrgRoles = () => null;
8
+ function currentPathParams(routerState, pathname) {
9
+ const route = Object.values(routerState.routes.pathRouteMap).find(
10
+ (candidate) => candidate.name === routerState.activeRouteName
11
+ );
12
+ if (!route?.path) {
13
+ return {};
14
+ }
15
+ return pathParamsText(route.path, pathname, routerState.paramTypes);
16
+ }
17
+ function redirectPath(routes, name, params) {
18
+ if (!Object.hasOwn(routes.namedRoutesMap, name)) {
19
+ throw new Error(`We could not find a route named ${name}`);
20
+ }
21
+ const route = Object.values(routes.pathRouteMap).find(
22
+ (candidate) => candidate.name === name
23
+ );
24
+ if (!route) {
25
+ return routes.namedRoutesMap[name]();
26
+ }
27
+ const routeParams = {};
28
+ for (const [paramName] of paramsForRoute(route.path)) {
29
+ const value = params[paramName];
30
+ if (value === void 0) {
31
+ throw new Error(
32
+ `Redirecting to route "${name}" requires the route parameter "${paramName}", which the current route doesn't have. Please choose a different route`
33
+ );
34
+ }
35
+ routeParams[paramName] = value;
36
+ }
37
+ try {
38
+ return routes.namedRoutesMap[name](routeParams);
39
+ } catch {
40
+ throw new Error(`Could not redirect to the route named ${name}`);
41
+ }
42
+ }
5
43
  const AuthenticatedRoute = ({
6
44
  unauthenticated,
45
+ unauthorized,
7
46
  roles,
47
+ userRoles,
48
+ orgRoles,
8
49
  whileLoadingAuth,
9
50
  children
10
51
  }) => {
@@ -14,35 +55,48 @@ const AuthenticatedRoute = ({
14
55
  isAuthenticated,
15
56
  hasRole
16
57
  } = routerState.useAuth();
17
- const unauthorized = useCallback(() => {
18
- return !(isAuthenticated && (!roles || hasRole(roles)));
19
- }, [isAuthenticated, roles, hasRole]);
20
- if (unauthorized()) {
21
- if (authLoading) {
22
- return whileLoadingAuth?.() || null;
23
- } else {
24
- const currentLocation = globalThis.location.pathname + encodeURIComponent(globalThis.location.search);
25
- const generatedRoutesMap = namedRoutes;
26
- if (!generatedRoutesMap[unauthenticated]) {
27
- throw new Error(`We could not find a route named ${unauthenticated}`);
28
- }
29
- let unauthenticatedPath;
30
- try {
31
- unauthenticatedPath = generatedRoutesMap[unauthenticated]();
32
- } catch (e) {
33
- if (e instanceof Error && /Missing parameter .* for route/.test(e.message)) {
34
- throw new Error(
35
- `Redirecting to route "${unauthenticated}" would require route parameters, which currently is not supported. Please choose a different route`
36
- );
37
- }
38
- throw new Error(
39
- `Could not redirect to the route named ${unauthenticated}`
40
- );
41
- }
42
- return /* @__PURE__ */ React.createElement(Redirect, { to: `${unauthenticatedPath}?redirectTo=${currentLocation}` });
58
+ const { pathname } = useLocation();
59
+ const pathParams = currentPathParams(routerState, pathname);
60
+ const useOrgRoles = routerState.useOrgRoles ?? useNoOrgRoles;
61
+ const hasOrgRole = useOrgRoles(routerState.useAuth, pathParams);
62
+ if (orgRoles !== void 0 && !hasOrgRole) {
63
+ if (!routerState.useOrgRoles) {
64
+ throw new Error(
65
+ "A PrivateSet with `orgRoles` needs `useOrgRoles` on the Router to check the role with. Please pass `useOrgRoles` from `@cedarjs/tenancy/web`: <Router useAuth={useAuth} useOrgRoles={useOrgRoles}>"
66
+ );
67
+ }
68
+ if (pathParams["orgSlug"] === void 0) {
69
+ throw new Error(
70
+ "A PrivateSet with `orgRoles` needs an organization to check the role in, and the route has no `orgSlug` param. Please only use `orgRoles` on routes with `{orgSlug}` in their path, or pass the Router a `useOrgRoles` that finds the organization another way"
71
+ );
43
72
  }
73
+ throw new Error(
74
+ "A PrivateSet with `orgRoles` got no role check from the Router's `useOrgRoles` for this route, so there is no organization to check the role in"
75
+ );
76
+ }
77
+ const hasRequiredRoles = (!roles || hasRole(roles)) && (!userRoles || hasRole(userRoles)) && (orgRoles === void 0 || !!hasOrgRole && hasOrgRole(orgRoles));
78
+ if (isAuthenticated && hasRequiredRoles) {
79
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
80
+ }
81
+ if (authLoading) {
82
+ return whileLoadingAuth?.() || null;
83
+ }
84
+ if (isAuthenticated && unauthorized) {
85
+ return /* @__PURE__ */ React.createElement(
86
+ Redirect,
87
+ {
88
+ to: redirectPath(routerState.routes, unauthorized, pathParams),
89
+ options: REPLACE
90
+ }
91
+ );
44
92
  }
45
- return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
93
+ const currentLocation = globalThis.location.pathname + encodeURIComponent(globalThis.location.search);
94
+ const unauthenticatedPath = redirectPath(
95
+ routerState.routes,
96
+ unauthenticated,
97
+ pathParams
98
+ );
99
+ return /* @__PURE__ */ React.createElement(Redirect, { to: `${unauthenticatedPath}?redirectTo=${currentLocation}` });
46
100
  };
47
101
  export {
48
102
  AuthenticatedRoute
package/dist/Set.d.ts CHANGED
@@ -26,12 +26,35 @@ export interface WrapperProps {
26
26
  */
27
27
  export type WrapperType = ComponentType<WrapperProps>;
28
28
  /**
29
- * The `roles` prop, which an app with tenancy doesn't have (see
30
- * `AuthTenancy`): there, every role check says which kind it is.
29
+ * The role props. An app with tenancy (see `AuthTenancy`) has `userRoles` and
30
+ * `orgRoles` instead of `roles`: there, every role check says which kind it
31
+ * is.
31
32
  */
32
33
  type SetRoles = AuthTenancy extends {
33
34
  enabled: true;
34
- } ? Record<never, never> : {
35
+ } ? {
36
+ /**
37
+ * Route is permitted when authenticated and user has any of the
38
+ * provided roles such as "admin" or ["admin", "editor"], in
39
+ * `currentUser.roles`, as opposed to a role in the organization they're
40
+ * in (see `orgRoles`). With `orgRoles` as well, they need both.
41
+ */
42
+ userRoles?: string | string[];
43
+ /**
44
+ * Route is permitted when authenticated and user has any of the
45
+ * provided roles such as "owner" or ["owner", "admin"] in the current
46
+ * organization. With `userRoles` as well, they need both.
47
+ *
48
+ * The organization is the one with the route's `orgSlug` param, and
49
+ * the router checks the role with the `useOrgRoles` passed to it:
50
+ *
51
+ * <Router useAuth={useAuth} useOrgRoles={useOrgRoles}>
52
+ *
53
+ * A `PrivateSet` with `orgRoles` throws without `useOrgRoles` on the
54
+ * router, or on a route without `{orgSlug}` in its path.
55
+ */
56
+ orgRoles?: string | string[];
57
+ } : {
35
58
  /**
36
59
  * Route is permitted when authenticated and user has any of the
37
60
  * provided roles such as "admin" or ["admin", "editor"], in
@@ -61,6 +84,13 @@ interface SetProps extends SetRoles {
61
84
  * @deprecated Please use `<PrivateSet>` instead and specify this prop there
62
85
  */
63
86
  unauthenticated?: string;
87
+ /**
88
+ * The page name where a user will be redirected when authenticated but
89
+ * without the required roles
90
+ *
91
+ * @deprecated Please use `<PrivateSet>` instead and specify this prop there
92
+ */
93
+ unauthorized?: string;
64
94
  /** Prerender all pages in the set */
65
95
  prerender?: boolean;
66
96
  children: ReactNode;
@@ -77,16 +107,28 @@ interface SetProps extends SetRoles {
77
107
  * </Set>
78
108
  */
79
109
  export declare function Set(props: SetProps): React.JSX.Element;
80
- type PrivateSetProps = Omit<SetProps, 'private' | 'unauthenticated'> & {
81
- /** The page name where a user will be redirected when not authenticated */
110
+ type PrivateSetProps = Omit<SetProps, 'private' | 'unauthenticated' | 'unauthorized'> & {
111
+ /**
112
+ * The page name where a user will be redirected when not authenticated.
113
+ * Also where one without the required roles is redirected, unless the set
114
+ * has `unauthorized`.
115
+ */
82
116
  unauthenticated: string;
117
+ /**
118
+ * The page name where a user will be redirected when authenticated but
119
+ * without the required roles. Without it, they're redirected to
120
+ * `unauthenticated`.
121
+ */
122
+ unauthorized?: string;
83
123
  };
84
124
  /** @deprecated Please use `<PrivateSet>` instead */
85
125
  export declare function Private(props: PrivateSetProps): React.JSX.Element;
86
126
  /**
87
127
  * Wraps a set of routes that require the user to be authenticated before
88
128
  * rendering. Unauthenticated users are redirected to the route named by
89
- * `unauthenticated`.
129
+ * `unauthenticated`, and users without the required roles to the one named by
130
+ * `unauthorized`. Either route can have path params the current route has,
131
+ * like `orgSlug`: they're filled in from the current route.
90
132
  *
91
133
  * Coming from React Router or a similar library and looking for something
92
134
  * like `RequireAuth` or `ProtectedRoute`? This is the Cedar equivalent.
package/dist/Set.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Set.d.ts","sourceRoot":"","sources":["../src/Set.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACnE,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;IAC/C,iBAAiB,EAAE,QAAQ;CAC5B,CAAA;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,WAAW,GAAG,aAAa,CAAC,YAAY,CAAC,CAAA;AAErD;;;GAGG;AACH,KAAK,QAAQ,GAAG,WAAW,SAAS;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjD,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GACpB;IACE;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAC1B,CAAA;AAEL,UAAU,QAAS,SAAQ,QAAQ;IACjC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,CAAA;IAC3C;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,qCAAqC;IACrC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,QAAQ,EAAE,SAAS,CAAA;IACnB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,YAAY,GAAG,IAAI,CAAA;IAC5C,gBAAgB,CAAC,EAAE,MAAM,YAAY,GAAG,IAAI,CAAA;CAC7C;AAED;;;;;;;GAOG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,QAAQ,qBAIlC;AAED,KAAK,eAAe,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,GAAG,iBAAiB,CAAC,GAAG;IACrE,2EAA2E;IAC3E,eAAe,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,oDAAoD;AACpD,wBAAgB,OAAO,CAAC,KAAK,EAAE,eAAe,qBAI7C;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,qBAIhD;AAED,eAAO,MAAM,SAAS,GAAI,MAAM,SAAS,KAAG,IAAI,IAAI,YAAY,CAAC,QAAQ,CAOxE,CAAA;AAED,eAAO,MAAM,gBAAgB,GAC3B,MAAM,SAAS,KACd,IAAI,IAAI,YAAY,CAAC,eAAe,CAEtC,CAAA;AAGD,eAAO,MAAM,aAAa,GACxB,MAAM,SAAS,KACd,IAAI,IAAI,YAAY,CAAC,eAAe,CAEtC,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,SAAS,aAKpB"}
1
+ {"version":3,"file":"Set.d.ts","sourceRoot":"","sources":["../src/Set.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACnE,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;IAC/C,iBAAiB,EAAE,QAAQ;CAC5B,CAAA;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,WAAW,GAAG,aAAa,CAAC,YAAY,CAAC,CAAA;AAErD;;;;GAIG;AACH,KAAK,QAAQ,GAAG,WAAW,SAAS;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjD;IACE;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC7B;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAC7B,GACD;IACE;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAC1B,CAAA;AAEL,UAAU,QAAS,SAAQ,QAAQ;IACjC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,WAAW,EAAE,CAAA;IAC3C;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qCAAqC;IACrC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,QAAQ,EAAE,SAAS,CAAA;IACnB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,YAAY,GAAG,IAAI,CAAA;IAC5C,gBAAgB,CAAC,EAAE,MAAM,YAAY,GAAG,IAAI,CAAA;CAC7C;AAED;;;;;;;GAOG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,QAAQ,qBAIlC;AAED,KAAK,eAAe,GAAG,IAAI,CACzB,QAAQ,EACR,SAAS,GAAG,iBAAiB,GAAG,cAAc,CAC/C,GAAG;IACF;;;;OAIG;IACH,eAAe,EAAE,MAAM,CAAA;IACvB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,oDAAoD;AACpD,wBAAgB,OAAO,CAAC,KAAK,EAAE,eAAe,qBAI7C;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,qBAIhD;AAED,eAAO,MAAM,SAAS,GAAI,MAAM,SAAS,KAAG,IAAI,IAAI,YAAY,CAAC,QAAQ,CAOxE,CAAA;AAED,eAAO,MAAM,gBAAgB,GAC3B,MAAM,SAAS,KACd,IAAI,IAAI,YAAY,CAAC,eAAe,CAEtC,CAAA;AAGD,eAAO,MAAM,aAAa,GACxB,MAAM,SAAS,KACd,IAAI,IAAI,YAAY,CAAC,eAAe,CAEtC,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,SAAS,aAKpB"}
@@ -14,7 +14,10 @@ export type Wrappers = readonly WrapperType[];
14
14
  interface SetRenderProps {
15
15
  private?: boolean;
16
16
  unauthenticated?: string;
17
+ unauthorized?: string;
17
18
  roles?: string | string[];
19
+ userRoles?: string | string[];
20
+ orgRoles?: string | string[];
18
21
  whileLoadingAuth?: () => ReactElement | null;
19
22
  }
20
23
  interface Set {
@@ -1 +1 @@
1
- {"version":3,"file":"analyzeRoutes.d.ts","sourceRoot":"","sources":["../src/analyzeRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAQzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,WAAW,CAAA;AAElB,KAAK,gBAAgB,GAAG,MAAM,YAAY,GAAG,IAAI,CAAA;AAIjD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,CACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,KACtD,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,SAAS,WAAW,EAAE,CAAA;AAE7C;;;GAGG;AACH,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,YAAY,GAAG,IAAI,CAAA;CAC7C;AAED,UAAU,GAAG;IACX,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,cAAc,CAAA;CACtB;AAuCD,KAAK,SAAS,GAAG,MAAM,CAAA;AAEvB;;;;GAIG;AACH,UAAU,aAAa;IACrB,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,IAAI,EAAE,GAAG,EAAE,CAAA;CACZ;AAED,UAAU,oBAAoB;IAC5B,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;CAC3C;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,SAAS,EACnB,EAAE,eAAe,EAAE,cAAc,EAAE,EAAE,oBAAoB;;;;;;EAqK1D"}
1
+ {"version":3,"file":"analyzeRoutes.d.ts","sourceRoot":"","sources":["../src/analyzeRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAQzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,WAAW,CAAA;AAElB,KAAK,gBAAgB,GAAG,MAAM,YAAY,GAAG,IAAI,CAAA;AAIjD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,CACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,KACtD,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,SAAS,WAAW,EAAE,CAAA;AAE7C;;;GAGG;AACH,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAEzB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,YAAY,GAAG,IAAI,CAAA;CAC7C;AAED,UAAU,GAAG;IACX,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,cAAc,CAAA;CACtB;AAmDD,KAAK,SAAS,GAAG,MAAM,CAAA;AAEvB;;;;GAIG;AACH,UAAU,aAAa;IACrB,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,IAAI,EAAE,GAAG,EAAE,CAAA;CACZ;AAED,UAAU,oBAAoB;IAC5B,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;CAC3C;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,SAAS,EACnB,EAAE,eAAe,EAAE,cAAc,EAAE,EAAE,oBAAoB;;;;;;EAqK1D"}
@@ -19,9 +19,18 @@ function pickSetRenderProps(props) {
19
19
  if (props.unauthenticated !== void 0) {
20
20
  picked.unauthenticated = props.unauthenticated;
21
21
  }
22
+ if (props.unauthorized !== void 0) {
23
+ picked.unauthorized = props.unauthorized;
24
+ }
22
25
  if (props.roles !== void 0) {
23
26
  picked.roles = props.roles;
24
27
  }
28
+ if (props.userRoles !== void 0) {
29
+ picked.userRoles = props.userRoles;
30
+ }
31
+ if (props.orgRoles !== void 0) {
32
+ picked.orgRoles = props.orgRoles;
33
+ }
25
34
  if (props.whileLoadingAuth !== void 0) {
26
35
  picked.whileLoadingAuth = props.whileLoadingAuth;
27
36
  }
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export { Redirect } from './redirect.js';
6
6
  export { usePageLoadingContext, PageLoadingContextProvider, } from './PageLoadingContext.js';
7
7
  export { useParams, ParamsProvider, ParamsContext } from './params.js';
8
8
  export { Router } from './router.js';
9
+ export type { UseOrgRoles } from './router-context.js';
9
10
  export { Route } from './Route.js';
10
11
  export { namedRoutes as routes } from './namedRoutes.js';
11
12
  export { Set, Private, PrivateSet, isSetNode, isPrivateSetNode, isPrivateNode, } from './Set.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACtE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,WAAW,IAAI,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAExD,OAAO,EACL,GAAG,EACH,OAAO,EACP,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,aAAa,GACd,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAEzD,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACtE,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,kBAAkB,CAAA;AACxD,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,eAAe,CAAA;AAC7B,cAAc,iBAAiB,CAAA;AAE/B,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAE1E,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE1D;;;;;;;;;GASG;AAGH,MAAM,WAAW,eAAe;CAAG;AAGnC,cAAc,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACtE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,WAAW,IAAI,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAExD,OAAO,EACL,GAAG,EACH,OAAO,EACP,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,aAAa,GACd,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAEzD,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACtE,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,kBAAkB,CAAA;AACxD,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,eAAe,CAAA;AAC7B,cAAc,iBAAiB,CAAA;AAE/B,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAE1E,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE1D;;;;;;;;;GASG;AAGH,MAAM,WAAW,eAAe;CAAG;AAGnC,cAAc,uBAAuB,CAAA"}
@@ -5,9 +5,28 @@ import type { AuthContextInterface } from '@cedarjs/auth' with {
5
5
  import type { analyzeRoutes } from './analyzeRoutes.js';
6
6
  import type { ParamType } from './util.js';
7
7
  type UseAuth = () => AuthContextInterface<unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown>;
8
+ /**
9
+ * Checks the user's role in the organization of the current route, for
10
+ * `<PrivateSet orgRoles>`, given the router's `useAuth` and the route's path
11
+ * params: `null` when the route isn't in an organization. In apps with
12
+ * tenancy, `useOrgRoles` from `@cedarjs/tenancy/web` is one. The router
13
+ * calls it like a hook, on every render of a private set.
14
+ *
15
+ * One of the app's own can answer in three ways:
16
+ * - a check, which the router uses for `orgRoles`. One that always returns
17
+ * `false` denies access and redirects to `unauthorized`.
18
+ * - `null`, for no organization: a set with `orgRoles` then throws the
19
+ * router's own error.
20
+ * - throw an error of its own, such as for an organization that's been
21
+ * suspended. It reaches the app's error boundary like any error thrown
22
+ * while rendering.
23
+ */
24
+ export type UseOrgRoles = (useAuth: UseAuth, params: Record<string, string>) => ((roles: string | string[]) => boolean) | null;
8
25
  export interface RouterState {
9
26
  paramTypes?: Record<string, ParamType>;
10
27
  useAuth: UseAuth;
28
+ /** Unset in apps that don't pass one to the router */
29
+ useOrgRoles?: UseOrgRoles;
11
30
  routes: ReturnType<typeof analyzeRoutes>;
12
31
  activeRouteName?: string | undefined | null;
13
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"router-context.d.ts","sourceRoot":"","sources":["../src/router-context.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6C,MAAM,OAAO,CAAA;AAEjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;IACxD,iBAAiB,EAAE,QAAQ;CAC5B,CAAA;AAGD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAE1C,KAAK,OAAO,GAAG,MAAM,oBAAoB,CACvC,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,CACR,CAAA;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IACtC,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,CAAA;IACxC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;CAC5C;AAID,MAAM,WAAW,0BAA2B,SAAQ,IAAI,CACtD,WAAW,EACX,SAAS,CACV;IACC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,CAAA;IACxC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;IAC3C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAsBtE,CAAA;AAED,eAAO,MAAM,cAAc,mBAU1B,CAAA"}
1
+ {"version":3,"file":"router-context.d.ts","sourceRoot":"","sources":["../src/router-context.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6C,MAAM,OAAO,CAAA;AAEjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;IACxD,iBAAiB,EAAE,QAAQ;CAC5B,CAAA;AAGD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAE1C,KAAK,OAAO,GAAG,MAAM,oBAAoB,CACvC,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,CACR,CAAA;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,WAAW,GAAG,CACxB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC3B,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,IAAI,CAAA;AAEnD,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IACtC,OAAO,EAAE,OAAO,CAAA;IAChB,sDAAsD;IACtD,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,CAAA;IACxC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;CAC5C;AAID,MAAM,WAAW,0BAA2B,SAAQ,IAAI,CACtD,WAAW,EACX,SAAS,CACV;IACC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,CAAA;IACxC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAA;IAC3C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAwBtE,CAAA;AAED,eAAO,MAAM,cAAc,mBAU1B,CAAA"}
@@ -3,6 +3,7 @@ import { useNoAuth } from "@cedarjs/auth";
3
3
  const RouterStateContext = createContext(void 0);
4
4
  const RouterContextProvider = ({
5
5
  useAuth,
6
+ useOrgRoles,
6
7
  paramTypes,
7
8
  routes,
8
9
  activeRouteName,
@@ -11,11 +12,12 @@ const RouterContextProvider = ({
11
12
  const state = useMemo(
12
13
  () => ({
13
14
  useAuth: useAuth || useNoAuth,
15
+ useOrgRoles,
14
16
  paramTypes,
15
17
  routes,
16
18
  activeRouteName
17
19
  }),
18
- [useAuth, paramTypes, routes, activeRouteName]
20
+ [useAuth, useOrgRoles, paramTypes, routes, activeRouteName]
19
21
  );
20
22
  return /* @__PURE__ */ React.createElement(RouterStateContext.Provider, { value: state }, children);
21
23
  };
package/dist/router.d.ts CHANGED
@@ -1,11 +1,31 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import React from 'react';
3
- import type { RouterContextProviderProps } from './router-context.js';
3
+ import type { AuthTenancy } from '@cedarjs/auth' with {
4
+ 'resolution-mode': 'import'
5
+ };
6
+ import type { RouterContextProviderProps, UseOrgRoles } from './router-context.js';
4
7
  import type { TrailingSlashesTypes } from './util.js';
5
- export interface RouterProps extends Omit<RouterContextProviderProps, 'routes' | 'activeRouteName'> {
8
+ /**
9
+ * `useOrgRoles`, only in an app with tenancy (see `AuthTenancy`), where sets
10
+ * have `orgRoles`
11
+ */
12
+ type RouterOrgRoles = AuthTenancy extends {
13
+ enabled: true;
14
+ } ? {
15
+ /**
16
+ * Checks the user's role in the organization of the current route, for
17
+ * `<PrivateSet orgRoles>`. Pass `useOrgRoles` from
18
+ * `@cedarjs/tenancy/web`:
19
+ *
20
+ * <Router useAuth={useAuth} useOrgRoles={useOrgRoles}>
21
+ */
22
+ useOrgRoles?: UseOrgRoles;
23
+ } : Record<never, never>;
24
+ export interface RouterProps extends Omit<RouterContextProviderProps, 'routes' | 'activeRouteName' | 'useOrgRoles'>, RouterOrgRoles {
6
25
  trailingSlashes?: TrailingSlashesTypes;
7
26
  pageLoadingDelay?: number;
8
27
  children: ReactNode;
9
28
  }
10
29
  export declare const Router: React.FC<RouterProps>;
30
+ export {};
11
31
  //# sourceMappingURL=router.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAA;AACpD,OAAO,KAAwB,MAAM,OAAO,CAAA;AAY5C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAA;AAKrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAErD,MAAM,WAAW,WAAY,SAAQ,IAAI,CACvC,0BAA0B,EAC1B,QAAQ,GAAG,iBAAiB,CAC7B;IACC,eAAe,CAAC,EAAE,oBAAoB,CAAA;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAoBxC,CAAA"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAA;AACpD,OAAO,KAAwB,MAAM,OAAO,CAAA;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;IAC/C,iBAAiB,EAAE,QAAQ;CAC5B,CAAA;AAYD,OAAO,KAAK,EACV,0BAA0B,EAC1B,WAAW,EACZ,MAAM,qBAAqB,CAAA;AAK5B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAErD;;;GAGG;AACH,KAAK,cAAc,GAAG,WAAW,SAAS;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACvD;IACE;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B,GACD,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AAExB,MAAM,WAAW,WACf,SACE,IAAI,CACF,0BAA0B,EAC1B,QAAQ,GAAG,iBAAiB,GAAG,aAAa,CAC7C,EACD,cAAc;IAChB,eAAe,CAAC,EAAE,oBAAoB,CAAA;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,SAAS,CAAA;CACpB;AAsCD,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAAwB,CAAA"}
package/dist/router.js CHANGED
@@ -12,8 +12,9 @@ import { RouterContextProvider } from "./router-context.js";
12
12
  import { wrapWithSetWrappers } from "./Set.js";
13
13
  import { SplashPage } from "./splash-page.js";
14
14
  import { matchPath, parseSearch, replaceParams, validatePath } from "./util.js";
15
- const Router = ({
15
+ const RouterImplementation = ({
16
16
  useAuth,
17
+ useOrgRoles,
17
18
  paramTypes,
18
19
  pageLoadingDelay,
19
20
  trailingSlashes = "never",
@@ -26,6 +27,7 @@ const Router = ({
26
27
  LocationAwareRouter,
27
28
  {
28
29
  useAuth,
30
+ useOrgRoles,
29
31
  paramTypes,
30
32
  pageLoadingDelay
31
33
  },
@@ -33,8 +35,11 @@ const Router = ({
33
35
  ))
34
36
  );
35
37
  };
38
+ RouterImplementation.displayName = "Router";
39
+ const Router = RouterImplementation;
36
40
  const LocationAwareRouter = ({
37
41
  useAuth,
42
+ useOrgRoles,
38
43
  paramTypes,
39
44
  pageLoadingDelay,
40
45
  children
@@ -78,6 +83,7 @@ const LocationAwareRouter = ({
78
83
  RouterContextProvider,
79
84
  {
80
85
  useAuth,
86
+ useOrgRoles,
81
87
  paramTypes,
82
88
  routes: analyzeRoutesResult
83
89
  },
@@ -122,6 +128,7 @@ const LocationAwareRouter = ({
122
128
  RouterContextProvider,
123
129
  {
124
130
  useAuth,
131
+ useOrgRoles,
125
132
  paramTypes,
126
133
  routes: analyzeRoutesResult,
127
134
  activeRouteName: name
@@ -154,7 +161,10 @@ const WrappedPage = memo(({ sets, children }) => {
154
161
  AuthenticatedRoute,
155
162
  {
156
163
  unauthenticated,
164
+ unauthorized: set.props.unauthorized,
157
165
  roles: set.props.roles,
166
+ userRoles: set.props.userRoles,
167
+ orgRoles: set.props.orgRoles,
158
168
  whileLoadingAuth: set.props.whileLoadingAuth
159
169
  },
160
170
  wrapped
@@ -96,10 +96,15 @@ function hasRole(requiredRoles) {
96
96
  }
97
97
  const AuthenticatedRoute = ({
98
98
  children,
99
- roles
99
+ roles,
100
+ userRoles,
101
+ orgRoles
100
102
  }) => {
101
103
  const { isAuthenticated } = getAuthState();
102
- const isAuthorized = isAuthenticated && (!roles || hasRole(roles));
104
+ if (orgRoles !== void 0) {
105
+ throw new Error("`orgRoles` is not supported with RSC yet");
106
+ }
107
+ const isAuthorized = isAuthenticated && (!roles || hasRole(roles)) && (!userRoles || hasRole(userRoles));
103
108
  if (isAuthorized) {
104
109
  return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
105
110
  }
@@ -126,6 +131,8 @@ const WrappedPage = ({ routeLoaderElement, sets }) => {
126
131
  {
127
132
  unauthenticated,
128
133
  roles: set.props.roles,
134
+ userRoles: set.props.userRoles,
135
+ orgRoles: set.props.orgRoles,
129
136
  whileLoadingAuth: set.props.whileLoadingAuth
130
137
  },
131
138
  wrapped
package/dist/util.d.ts CHANGED
@@ -50,6 +50,16 @@ export declare function matchPath(routeDefinition: string, pathname: string, { u
50
50
  match: boolean;
51
51
  params: Record<string, unknown>;
52
52
  };
53
+ /**
54
+ * The path params of `pathname` for the route `routeDefinition`, as the text
55
+ * in the path, without parsing them with their param types. For putting into
56
+ * another route's path, where a parsed value (whatever a custom type's `parse`
57
+ * returns, say) wouldn't be the same text.
58
+ *
59
+ * pathParamsText('/post/{id:Int}', '/post/7')
60
+ * => { id: '7' }
61
+ */
62
+ export declare function pathParamsText(routeDefinition: string, pathname: string, userParamTypes?: Record<string, ParamType>): Record<string, string>;
53
63
  interface GetRouteRegexOptions {
54
64
  matchSubPaths?: boolean;
55
65
  allParamTypes?: Record<string, ParamType>;
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,cAyB3C;AAED,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAA;AAElE,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAA;CAChC;AAwBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,SAAS,CACvB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,EACE,cAAc,EACd,aAAa,GACd,GAAE;IACD,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAA;CAIxB;;;;;;EA2CF;AAED,UAAU,oBAAoB;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;CAC1C;AACD;;;;;GAKG;AAEH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,EACE,aAAqB,EACrB,aAA8B,GAC/B,GAAE,oBAAoB,GAAG,SAAc;;;;EA+BzC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,MAAM,EACJ,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,eAAe,GAAG,SAAS,MAW7E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAyC3D;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,UAgCnC;AAED,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAExE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,6BAQtD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,YAMvB"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,cA0B3C;AAED,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAA;AAElE,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAA;CAChC;AAwBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,SAAS,CACvB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,EACE,cAAc,EACd,aAAa,GACd,GAAE;IACD,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAA;CAIxB;;;;;;EA2CF;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,cAAc,GAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAM,0BAgB/C;AAED,UAAU,oBAAoB;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;CAC1C;AACD;;;;;GAKG;AAEH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,EACE,aAAqB,EACrB,aAA8B,GAC/B,GAAE,oBAAoB,GAAG,SAAc;;;;EA+BzC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,MAAM,EACJ,MAAM,GAAG,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,eAAe,GAAG,SAAS,MAW7E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAyC3D;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,UAiCnC;AAED,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAExE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,6BAQtD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,YAMvB"}
package/dist/util.js CHANGED
@@ -1,5 +1,5 @@
1
1
  function paramsForRoute(route) {
2
- const params = [...route.matchAll(/\{([^}]+)\}/g)];
2
+ const params = [...route.matchAll(/\{([^{}]+)\}/g)];
3
3
  return params.map((match) => match[1]).map((match) => {
4
4
  const parts = match.split(":");
5
5
  let name = parts[0];
@@ -70,6 +70,19 @@ function matchPath(routeDefinition, pathname, {
70
70
  }
71
71
  return { match: true };
72
72
  }
73
+ function pathParamsText(routeDefinition, pathname, userParamTypes = {}) {
74
+ const { matchRegex, routeParams } = getRouteRegexAndParams(routeDefinition, {
75
+ allParamTypes: { ...coreParamTypes, ...userParamTypes }
76
+ });
77
+ const match = matchRegex.exec(pathname);
78
+ const params = {};
79
+ if (match) {
80
+ routeParams.forEach(([name], index) => {
81
+ params[name] = match[index + 1];
82
+ });
83
+ }
84
+ return params;
85
+ }
73
86
  function getRouteRegexAndParams(route, {
74
87
  matchSubPaths = false,
75
88
  allParamTypes = coreParamTypes
@@ -140,7 +153,7 @@ function replaceParams(route, args = {}) {
140
153
  const [name, _type, match] = param;
141
154
  const value = args[name];
142
155
  if (value !== void 0) {
143
- path = path.replace(match, value);
156
+ path = path.replace(match, () => String(value));
144
157
  } else {
145
158
  throw new Error(
146
159
  `Missing parameter '${name}' for route '${route}' when generating a navigation URL.`
@@ -178,6 +191,7 @@ export {
178
191
  matchPath,
179
192
  paramsForRoute,
180
193
  parseSearch,
194
+ pathParamsText,
181
195
  replaceParams,
182
196
  validatePath
183
197
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cedarjs/router",
3
- "version": "8.0.0-canary.3541",
3
+ "version": "8.0.0-canary.3542",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/cedarjs/cedar.git",
@@ -52,15 +52,15 @@
52
52
  "test:watch": "vitest watch"
53
53
  },
54
54
  "dependencies": {
55
- "@cedarjs/project-config": "8.0.0-canary.3541",
56
- "@cedarjs/server-store": "8.0.0-canary.3541",
55
+ "@cedarjs/project-config": "8.0.0-canary.3542",
56
+ "@cedarjs/server-store": "8.0.0-canary.3542",
57
57
  "react-server-dom-webpack": "19.3.1",
58
58
  "ts-toolbelt": "9.6.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@arethetypeswrong/cli": "0.18.5",
62
- "@cedarjs/auth": "8.0.0-canary.3541",
63
- "@cedarjs/framework-tools": "8.0.0-canary.3541",
62
+ "@cedarjs/auth": "8.0.0-canary.3542",
63
+ "@cedarjs/framework-tools": "8.0.0-canary.3542",
64
64
  "@testing-library/jest-dom": "6.9.1",
65
65
  "@types/react": "^19.2.0",
66
66
  "@types/react-dom": "^19.2.0",
@@ -74,7 +74,7 @@
74
74
  "vitest": "4.1.12"
75
75
  },
76
76
  "peerDependencies": {
77
- "@cedarjs/auth": "8.0.0-canary.3541",
77
+ "@cedarjs/auth": "8.0.0-canary.3542",
78
78
  "react": "19.3.1",
79
79
  "react-dom": "19.3.1"
80
80
  },