docusaurus-roles-plugin 1.0.0-beta.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.
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/runtime/index.ts
31
+ var runtime_exports = {};
32
+ __export(runtime_exports, {
33
+ BaseForbiddenContent: () => BaseForbiddenContent,
34
+ RolesContext: () => RolesContext,
35
+ RolesProvider: () => RolesProvider,
36
+ isAllowed: () => isAllowed,
37
+ useRoles: () => useRoles
38
+ });
39
+ module.exports = __toCommonJS(runtime_exports);
40
+
41
+ // src/runtime/context.tsx
42
+ var import_react = __toESM(require("react"), 1);
43
+ var RolesContext = import_react.default.createContext({
44
+ roles: [],
45
+ loading: false,
46
+ hasProvider: false
47
+ });
48
+
49
+ // src/runtime/RolesProvider.tsx
50
+ var import_react2 = require("react");
51
+ var import_jsx_runtime = require("react/jsx-runtime");
52
+ var DefaultRolesLoading = () => {
53
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
54
+ "div",
55
+ {
56
+ style: {
57
+ display: "flex",
58
+ flexGrow: 1,
59
+ flexDirection: "column",
60
+ justifyContent: "center",
61
+ alignItems: "center"
62
+ },
63
+ children: "Checking Permissions..."
64
+ }
65
+ );
66
+ };
67
+ var DefaultRolesError = ({ error }) => {
68
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
69
+ "div",
70
+ {
71
+ style: {
72
+ display: "flex",
73
+ flexGrow: 1,
74
+ flexDirection: "column",
75
+ justifyContent: "center",
76
+ alignItems: "center",
77
+ color: "#c1121f"
78
+ },
79
+ children: error.message
80
+ }
81
+ );
82
+ };
83
+ function RolesProvider({
84
+ roles: getRoles,
85
+ slots = {},
86
+ children
87
+ }) {
88
+ const LoadingComponent = slots.loading ?? DefaultRolesLoading;
89
+ const ErrorComponent = slots.error ?? DefaultRolesError;
90
+ const [state, setState] = (0, import_react2.useState)({
91
+ roles: [],
92
+ loading: true,
93
+ hasProvider: true
94
+ });
95
+ (0, import_react2.useEffect)(() => {
96
+ const fetchRoles = async () => {
97
+ try {
98
+ setState((s) => ({ ...s, loading: true }));
99
+ const roles = await getRoles();
100
+ if (!roles) {
101
+ console.warn(
102
+ "No roles were returned an empty array should be returned if user has no roles."
103
+ );
104
+ }
105
+ setState((s) => ({
106
+ ...s,
107
+ loading: false,
108
+ roles
109
+ }));
110
+ } catch (e) {
111
+ setState((s) => ({
112
+ ...s,
113
+ loading: false,
114
+ roles: [],
115
+ error: e instanceof Error ? e : new Error(String(e))
116
+ }));
117
+ }
118
+ };
119
+ fetchRoles();
120
+ }, [getRoles]);
121
+ if (state.loading) {
122
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingComponent, {});
123
+ }
124
+ if (state.error) {
125
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorComponent, { error: state.error });
126
+ }
127
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RolesContext.Provider, { value: state, children });
128
+ }
129
+
130
+ // src/runtime/useRoles.ts
131
+ var import_react3 = require("react");
132
+ function useRoles() {
133
+ return (0, import_react3.useContext)(RolesContext);
134
+ }
135
+
136
+ // src/runtime/roleUtils.ts
137
+ function isAllowed(userRoles, required, mode) {
138
+ if (!required || required.length === 0) {
139
+ return { allowed: true, missing: [] };
140
+ }
141
+ const userRoleSet = new Set(userRoles);
142
+ if (mode === "all") {
143
+ const missing = required.filter((r) => !userRoleSet.has(r));
144
+ return {
145
+ allowed: missing.length === 0,
146
+ missing
147
+ };
148
+ }
149
+ const hasAny = required.some((r) => userRoleSet.has(r));
150
+ return {
151
+ allowed: hasAny,
152
+ missing: hasAny ? [] : required.slice()
153
+ // nothing matched → all are effectively missing
154
+ };
155
+ }
156
+
157
+ // src/runtime/BaseForbiddenContent.tsx
158
+ var import_Translate = __toESM(require("@docusaurus/Translate"), 1);
159
+ var import_Heading = __toESM(require("@theme/Heading"), 1);
160
+ var import_jsx_runtime2 = require("react/jsx-runtime");
161
+ function BaseForbiddenContent({
162
+ prefixTranslateId,
163
+ title = "Forbidden",
164
+ shortDescription = "You don't have permission to access this page.",
165
+ description = "If you believe this is a mistake, contact the site owner or sign in with an account that has access."
166
+ }) {
167
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "row", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "col", children: [
168
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Heading.default, { as: "h1", className: "hero__title", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
169
+ import_Translate.default,
170
+ {
171
+ id: `${prefixTranslateId}.title`,
172
+ description: "The title of the 403 page",
173
+ children: title
174
+ }
175
+ ) }),
176
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
177
+ import_Translate.default,
178
+ {
179
+ id: `${prefixTranslateId}.p1`,
180
+ description: "The first paragraph of the 403 page",
181
+ children: shortDescription
182
+ }
183
+ ) }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
185
+ import_Translate.default,
186
+ {
187
+ id: `${prefixTranslateId}.p2`,
188
+ description: "The 2nd paragraph of the 403 page",
189
+ children: description
190
+ }
191
+ ) })
192
+ ] }) });
193
+ }
194
+ // Annotate the CommonJS export names for ESM import in node:
195
+ 0 && (module.exports = {
196
+ BaseForbiddenContent,
197
+ RolesContext,
198
+ RolesProvider,
199
+ isAllowed,
200
+ useRoles
201
+ });
202
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/runtime/index.ts","../../src/runtime/context.tsx","../../src/runtime/RolesProvider.tsx","../../src/runtime/useRoles.ts","../../src/runtime/roleUtils.ts","../../src/runtime/BaseForbiddenContent.tsx"],"sourcesContent":["export { RolesContext } from \"./context\";\nexport type { RolesState } from \"./context\";\n\nexport { RolesProvider } from \"./RolesProvider\";\nexport type { RolesProviderProps } from \"./RolesProvider\";\n\nexport { useRoles } from \"./useRoles\";\nexport { isAllowed } from \"./roleUtils\";\nexport type { RoleMode } from \"./roleUtils\";\nexport * from \"./BaseForbiddenContent\";\n","import React from \"react\";\n\nexport type RolesState = {\n roles: string[];\n loading: boolean;\n error?: Error;\n /** true if RolesProvider is mounted */\n hasProvider: boolean;\n};\n\nexport const RolesContext = React.createContext<RolesState>({\n roles: [],\n loading: false,\n hasProvider: false,\n});\n","import React, { useEffect, useState } from \"react\";\nimport type { PropsWithChildren } from \"react\";\nimport { RolesContext, type RolesState } from \"./context\";\n\nexport type ErrorSlotProps = {\n error: Error;\n};\n\nexport type RolesProviderProps = PropsWithChildren<{\n roles: () => Promise<string[]> | string[];\n slots?: {\n loading?: React.ElementType;\n error?: React.ElementType<ErrorSlotProps>;\n };\n}>;\n\nexport const DefaultRolesLoading = () => {\n return (\n <div\n style={{\n display: \"flex\",\n flexGrow: 1,\n flexDirection: \"column\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n Checking Permissions...\n </div>\n );\n};\n\nexport const DefaultRolesError = ({ error }: ErrorSlotProps) => {\n return (\n <div\n style={{\n display: \"flex\",\n flexGrow: 1,\n flexDirection: \"column\",\n justifyContent: \"center\",\n alignItems: \"center\",\n color: \"#c1121f\",\n }}\n >\n {error.message}\n </div>\n );\n};\n\nexport function RolesProvider({\n roles: getRoles,\n slots = {},\n children,\n}: RolesProviderProps) {\n const LoadingComponent = slots.loading ?? DefaultRolesLoading;\n const ErrorComponent = slots.error ?? DefaultRolesError;\n\n const [state, setState] = useState<RolesState>({\n roles: [],\n loading: true,\n hasProvider: true,\n });\n\n useEffect(() => {\n const fetchRoles = async () => {\n try {\n setState((s) => ({ ...s, loading: true }));\n const roles = await getRoles();\n if (!roles) {\n console.warn(\n \"No roles were returned an empty array should be returned if user has no roles.\",\n );\n }\n setState((s) => ({\n ...s,\n loading: false,\n roles: roles,\n }));\n } catch (e) {\n setState((s) => ({\n ...s,\n loading: false,\n roles: [],\n error: e instanceof Error ? e : new Error(String(e)),\n }));\n }\n };\n\n fetchRoles();\n }, [getRoles]);\n\n if (state.loading) {\n return <LoadingComponent />;\n }\n\n if (state.error) {\n return <ErrorComponent error={state.error} />;\n }\n\n return (\n <RolesContext.Provider value={state}>{children}</RolesContext.Provider>\n );\n}\n","import { useContext } from \"react\";\nimport { RolesContext } from \"./context\";\n\nexport function useRoles() {\n return useContext(RolesContext);\n}\n","export type RoleMode = \"any\" | \"all\";\n\nexport type RoleCheckResult = {\n allowed: boolean;\n missing: string[];\n};\n\nexport function isAllowed(\n userRoles: string[],\n required: string[],\n mode: RoleMode,\n): RoleCheckResult {\n // No requirements → always allowed\n if (!required || required.length === 0) {\n return { allowed: true, missing: [] };\n }\n\n const userRoleSet = new Set(userRoles);\n\n if (mode === \"all\") {\n const missing = required.filter((r) => !userRoleSet.has(r));\n return {\n allowed: missing.length === 0,\n missing,\n };\n }\n\n // mode === \"any\"\n const hasAny = required.some((r) => userRoleSet.has(r));\n return {\n allowed: hasAny,\n missing: hasAny ? [] : required.slice(), // nothing matched → all are effectively missing\n };\n}\n","import { JSX } from \"react\";\nimport type { Props as NotFoundProps } from \"@theme/NotFound/Content\";\nimport Translate from \"@docusaurus/Translate\";\nimport Heading from \"@theme/Heading\";\n\nexport type BaseForbiddenContentProps = {\n prefixTranslateId: string;\n title?: string;\n shortDescription?: string;\n description?: string;\n} & NotFoundProps;\n\nexport function BaseForbiddenContent({\n prefixTranslateId,\n title = \"Forbidden\",\n shortDescription = \"You don't have permission to access this page.\",\n description = \"If you believe this is a mistake, contact the site owner or sign in with an account that has access.\",\n}: BaseForbiddenContentProps): JSX.Element {\n return (\n <div className=\"row\">\n <div className=\"col\">\n <Heading as=\"h1\" className=\"hero__title\">\n <Translate\n id={`${prefixTranslateId}.title`}\n description=\"The title of the 403 page\"\n >\n {title}\n </Translate>\n </Heading>\n <p>\n <Translate\n id={`${prefixTranslateId}.p1`}\n description=\"The first paragraph of the 403 page\"\n >\n {shortDescription}\n </Translate>\n </p>\n <p>\n <Translate\n id={`${prefixTranslateId}.p2`}\n description=\"The 2nd paragraph of the 403 page\"\n >\n {description}\n </Translate>\n </p>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["React","import_react","import_react","import_jsx_runtime","Heading","Translate"]}
@@ -0,0 +1,43 @@
1
+ import React, { PropsWithChildren, JSX } from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { Props } from '@theme/NotFound/Content';
4
+
5
+ type RolesState = {
6
+ roles: string[];
7
+ loading: boolean;
8
+ error?: Error;
9
+ /** true if RolesProvider is mounted */
10
+ hasProvider: boolean;
11
+ };
12
+ declare const RolesContext: React.Context<RolesState>;
13
+
14
+ type ErrorSlotProps = {
15
+ error: Error;
16
+ };
17
+ type RolesProviderProps = PropsWithChildren<{
18
+ roles: () => Promise<string[]> | string[];
19
+ slots?: {
20
+ loading?: React.ElementType;
21
+ error?: React.ElementType<ErrorSlotProps>;
22
+ };
23
+ }>;
24
+ declare function RolesProvider({ roles: getRoles, slots, children, }: RolesProviderProps): react_jsx_runtime.JSX.Element;
25
+
26
+ declare function useRoles(): RolesState;
27
+
28
+ type RoleMode = "any" | "all";
29
+ type RoleCheckResult = {
30
+ allowed: boolean;
31
+ missing: string[];
32
+ };
33
+ declare function isAllowed(userRoles: string[], required: string[], mode: RoleMode): RoleCheckResult;
34
+
35
+ type BaseForbiddenContentProps = {
36
+ prefixTranslateId: string;
37
+ title?: string;
38
+ shortDescription?: string;
39
+ description?: string;
40
+ } & Props;
41
+ declare function BaseForbiddenContent({ prefixTranslateId, title, shortDescription, description, }: BaseForbiddenContentProps): JSX.Element;
42
+
43
+ export { BaseForbiddenContent, type BaseForbiddenContentProps, type RoleMode, RolesContext, RolesProvider, type RolesProviderProps, type RolesState, isAllowed, useRoles };
@@ -0,0 +1,43 @@
1
+ import React, { PropsWithChildren, JSX } from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { Props } from '@theme/NotFound/Content';
4
+
5
+ type RolesState = {
6
+ roles: string[];
7
+ loading: boolean;
8
+ error?: Error;
9
+ /** true if RolesProvider is mounted */
10
+ hasProvider: boolean;
11
+ };
12
+ declare const RolesContext: React.Context<RolesState>;
13
+
14
+ type ErrorSlotProps = {
15
+ error: Error;
16
+ };
17
+ type RolesProviderProps = PropsWithChildren<{
18
+ roles: () => Promise<string[]> | string[];
19
+ slots?: {
20
+ loading?: React.ElementType;
21
+ error?: React.ElementType<ErrorSlotProps>;
22
+ };
23
+ }>;
24
+ declare function RolesProvider({ roles: getRoles, slots, children, }: RolesProviderProps): react_jsx_runtime.JSX.Element;
25
+
26
+ declare function useRoles(): RolesState;
27
+
28
+ type RoleMode = "any" | "all";
29
+ type RoleCheckResult = {
30
+ allowed: boolean;
31
+ missing: string[];
32
+ };
33
+ declare function isAllowed(userRoles: string[], required: string[], mode: RoleMode): RoleCheckResult;
34
+
35
+ type BaseForbiddenContentProps = {
36
+ prefixTranslateId: string;
37
+ title?: string;
38
+ shortDescription?: string;
39
+ description?: string;
40
+ } & Props;
41
+ declare function BaseForbiddenContent({ prefixTranslateId, title, shortDescription, description, }: BaseForbiddenContentProps): JSX.Element;
42
+
43
+ export { BaseForbiddenContent, type BaseForbiddenContentProps, type RoleMode, RolesContext, RolesProvider, type RolesProviderProps, type RolesState, isAllowed, useRoles };
@@ -0,0 +1,161 @@
1
+ // src/runtime/context.tsx
2
+ import React from "react";
3
+ var RolesContext = React.createContext({
4
+ roles: [],
5
+ loading: false,
6
+ hasProvider: false
7
+ });
8
+
9
+ // src/runtime/RolesProvider.tsx
10
+ import { useEffect, useState } from "react";
11
+ import { jsx } from "react/jsx-runtime";
12
+ var DefaultRolesLoading = () => {
13
+ return /* @__PURE__ */ jsx(
14
+ "div",
15
+ {
16
+ style: {
17
+ display: "flex",
18
+ flexGrow: 1,
19
+ flexDirection: "column",
20
+ justifyContent: "center",
21
+ alignItems: "center"
22
+ },
23
+ children: "Checking Permissions..."
24
+ }
25
+ );
26
+ };
27
+ var DefaultRolesError = ({ error }) => {
28
+ return /* @__PURE__ */ jsx(
29
+ "div",
30
+ {
31
+ style: {
32
+ display: "flex",
33
+ flexGrow: 1,
34
+ flexDirection: "column",
35
+ justifyContent: "center",
36
+ alignItems: "center",
37
+ color: "#c1121f"
38
+ },
39
+ children: error.message
40
+ }
41
+ );
42
+ };
43
+ function RolesProvider({
44
+ roles: getRoles,
45
+ slots = {},
46
+ children
47
+ }) {
48
+ const LoadingComponent = slots.loading ?? DefaultRolesLoading;
49
+ const ErrorComponent = slots.error ?? DefaultRolesError;
50
+ const [state, setState] = useState({
51
+ roles: [],
52
+ loading: true,
53
+ hasProvider: true
54
+ });
55
+ useEffect(() => {
56
+ const fetchRoles = async () => {
57
+ try {
58
+ setState((s) => ({ ...s, loading: true }));
59
+ const roles = await getRoles();
60
+ if (!roles) {
61
+ console.warn(
62
+ "No roles were returned an empty array should be returned if user has no roles."
63
+ );
64
+ }
65
+ setState((s) => ({
66
+ ...s,
67
+ loading: false,
68
+ roles
69
+ }));
70
+ } catch (e) {
71
+ setState((s) => ({
72
+ ...s,
73
+ loading: false,
74
+ roles: [],
75
+ error: e instanceof Error ? e : new Error(String(e))
76
+ }));
77
+ }
78
+ };
79
+ fetchRoles();
80
+ }, [getRoles]);
81
+ if (state.loading) {
82
+ return /* @__PURE__ */ jsx(LoadingComponent, {});
83
+ }
84
+ if (state.error) {
85
+ return /* @__PURE__ */ jsx(ErrorComponent, { error: state.error });
86
+ }
87
+ return /* @__PURE__ */ jsx(RolesContext.Provider, { value: state, children });
88
+ }
89
+
90
+ // src/runtime/useRoles.ts
91
+ import { useContext } from "react";
92
+ function useRoles() {
93
+ return useContext(RolesContext);
94
+ }
95
+
96
+ // src/runtime/roleUtils.ts
97
+ function isAllowed(userRoles, required, mode) {
98
+ if (!required || required.length === 0) {
99
+ return { allowed: true, missing: [] };
100
+ }
101
+ const userRoleSet = new Set(userRoles);
102
+ if (mode === "all") {
103
+ const missing = required.filter((r) => !userRoleSet.has(r));
104
+ return {
105
+ allowed: missing.length === 0,
106
+ missing
107
+ };
108
+ }
109
+ const hasAny = required.some((r) => userRoleSet.has(r));
110
+ return {
111
+ allowed: hasAny,
112
+ missing: hasAny ? [] : required.slice()
113
+ // nothing matched → all are effectively missing
114
+ };
115
+ }
116
+
117
+ // src/runtime/BaseForbiddenContent.tsx
118
+ import Translate from "@docusaurus/Translate";
119
+ import Heading from "@theme/Heading";
120
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
121
+ function BaseForbiddenContent({
122
+ prefixTranslateId,
123
+ title = "Forbidden",
124
+ shortDescription = "You don't have permission to access this page.",
125
+ description = "If you believe this is a mistake, contact the site owner or sign in with an account that has access."
126
+ }) {
127
+ return /* @__PURE__ */ jsx2("div", { className: "row", children: /* @__PURE__ */ jsxs("div", { className: "col", children: [
128
+ /* @__PURE__ */ jsx2(Heading, { as: "h1", className: "hero__title", children: /* @__PURE__ */ jsx2(
129
+ Translate,
130
+ {
131
+ id: `${prefixTranslateId}.title`,
132
+ description: "The title of the 403 page",
133
+ children: title
134
+ }
135
+ ) }),
136
+ /* @__PURE__ */ jsx2("p", { children: /* @__PURE__ */ jsx2(
137
+ Translate,
138
+ {
139
+ id: `${prefixTranslateId}.p1`,
140
+ description: "The first paragraph of the 403 page",
141
+ children: shortDescription
142
+ }
143
+ ) }),
144
+ /* @__PURE__ */ jsx2("p", { children: /* @__PURE__ */ jsx2(
145
+ Translate,
146
+ {
147
+ id: `${prefixTranslateId}.p2`,
148
+ description: "The 2nd paragraph of the 403 page",
149
+ children: description
150
+ }
151
+ ) })
152
+ ] }) });
153
+ }
154
+ export {
155
+ BaseForbiddenContent,
156
+ RolesContext,
157
+ RolesProvider,
158
+ isAllowed,
159
+ useRoles
160
+ };
161
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/runtime/context.tsx","../../src/runtime/RolesProvider.tsx","../../src/runtime/useRoles.ts","../../src/runtime/roleUtils.ts","../../src/runtime/BaseForbiddenContent.tsx"],"sourcesContent":["import React from \"react\";\n\nexport type RolesState = {\n roles: string[];\n loading: boolean;\n error?: Error;\n /** true if RolesProvider is mounted */\n hasProvider: boolean;\n};\n\nexport const RolesContext = React.createContext<RolesState>({\n roles: [],\n loading: false,\n hasProvider: false,\n});\n","import React, { useEffect, useState } from \"react\";\nimport type { PropsWithChildren } from \"react\";\nimport { RolesContext, type RolesState } from \"./context\";\n\nexport type ErrorSlotProps = {\n error: Error;\n};\n\nexport type RolesProviderProps = PropsWithChildren<{\n roles: () => Promise<string[]> | string[];\n slots?: {\n loading?: React.ElementType;\n error?: React.ElementType<ErrorSlotProps>;\n };\n}>;\n\nexport const DefaultRolesLoading = () => {\n return (\n <div\n style={{\n display: \"flex\",\n flexGrow: 1,\n flexDirection: \"column\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n Checking Permissions...\n </div>\n );\n};\n\nexport const DefaultRolesError = ({ error }: ErrorSlotProps) => {\n return (\n <div\n style={{\n display: \"flex\",\n flexGrow: 1,\n flexDirection: \"column\",\n justifyContent: \"center\",\n alignItems: \"center\",\n color: \"#c1121f\",\n }}\n >\n {error.message}\n </div>\n );\n};\n\nexport function RolesProvider({\n roles: getRoles,\n slots = {},\n children,\n}: RolesProviderProps) {\n const LoadingComponent = slots.loading ?? DefaultRolesLoading;\n const ErrorComponent = slots.error ?? DefaultRolesError;\n\n const [state, setState] = useState<RolesState>({\n roles: [],\n loading: true,\n hasProvider: true,\n });\n\n useEffect(() => {\n const fetchRoles = async () => {\n try {\n setState((s) => ({ ...s, loading: true }));\n const roles = await getRoles();\n if (!roles) {\n console.warn(\n \"No roles were returned an empty array should be returned if user has no roles.\",\n );\n }\n setState((s) => ({\n ...s,\n loading: false,\n roles: roles,\n }));\n } catch (e) {\n setState((s) => ({\n ...s,\n loading: false,\n roles: [],\n error: e instanceof Error ? e : new Error(String(e)),\n }));\n }\n };\n\n fetchRoles();\n }, [getRoles]);\n\n if (state.loading) {\n return <LoadingComponent />;\n }\n\n if (state.error) {\n return <ErrorComponent error={state.error} />;\n }\n\n return (\n <RolesContext.Provider value={state}>{children}</RolesContext.Provider>\n );\n}\n","import { useContext } from \"react\";\nimport { RolesContext } from \"./context\";\n\nexport function useRoles() {\n return useContext(RolesContext);\n}\n","export type RoleMode = \"any\" | \"all\";\n\nexport type RoleCheckResult = {\n allowed: boolean;\n missing: string[];\n};\n\nexport function isAllowed(\n userRoles: string[],\n required: string[],\n mode: RoleMode,\n): RoleCheckResult {\n // No requirements → always allowed\n if (!required || required.length === 0) {\n return { allowed: true, missing: [] };\n }\n\n const userRoleSet = new Set(userRoles);\n\n if (mode === \"all\") {\n const missing = required.filter((r) => !userRoleSet.has(r));\n return {\n allowed: missing.length === 0,\n missing,\n };\n }\n\n // mode === \"any\"\n const hasAny = required.some((r) => userRoleSet.has(r));\n return {\n allowed: hasAny,\n missing: hasAny ? [] : required.slice(), // nothing matched → all are effectively missing\n };\n}\n","import { JSX } from \"react\";\nimport type { Props as NotFoundProps } from \"@theme/NotFound/Content\";\nimport Translate from \"@docusaurus/Translate\";\nimport Heading from \"@theme/Heading\";\n\nexport type BaseForbiddenContentProps = {\n prefixTranslateId: string;\n title?: string;\n shortDescription?: string;\n description?: string;\n} & NotFoundProps;\n\nexport function BaseForbiddenContent({\n prefixTranslateId,\n title = \"Forbidden\",\n shortDescription = \"You don't have permission to access this page.\",\n description = \"If you believe this is a mistake, contact the site owner or sign in with an account that has access.\",\n}: BaseForbiddenContentProps): JSX.Element {\n return (\n <div className=\"row\">\n <div className=\"col\">\n <Heading as=\"h1\" className=\"hero__title\">\n <Translate\n id={`${prefixTranslateId}.title`}\n description=\"The title of the 403 page\"\n >\n {title}\n </Translate>\n </Heading>\n <p>\n <Translate\n id={`${prefixTranslateId}.p1`}\n description=\"The first paragraph of the 403 page\"\n >\n {shortDescription}\n </Translate>\n </p>\n <p>\n <Translate\n id={`${prefixTranslateId}.p2`}\n description=\"The 2nd paragraph of the 403 page\"\n >\n {description}\n </Translate>\n </p>\n </div>\n </div>\n );\n}\n"],"mappings":";AAAA,OAAO,WAAW;AAUX,IAAM,eAAe,MAAM,cAA0B;AAAA,EAC1D,OAAO,CAAC;AAAA,EACR,SAAS;AAAA,EACT,aAAa;AACf,CAAC;;;ACdD,SAAgB,WAAW,gBAAgB;AAkBvC;AAFG,IAAM,sBAAsB,MAAM;AACvC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,eAAe;AAAA,QACf,gBAAgB;AAAA,QAChB,YAAY;AAAA,MACd;AAAA,MACD;AAAA;AAAA,EAED;AAEJ;AAEO,IAAM,oBAAoB,CAAC,EAAE,MAAM,MAAsB;AAC9D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,eAAe;AAAA,QACf,gBAAgB;AAAA,QAChB,YAAY;AAAA,QACZ,OAAO;AAAA,MACT;AAAA,MAEC,gBAAM;AAAA;AAAA,EACT;AAEJ;AAEO,SAAS,cAAc;AAAA,EAC5B,OAAO;AAAA,EACP,QAAQ,CAAC;AAAA,EACT;AACF,GAAuB;AACrB,QAAM,mBAAmB,MAAM,WAAW;AAC1C,QAAM,iBAAiB,MAAM,SAAS;AAEtC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAqB;AAAA,IAC7C,OAAO,CAAC;AAAA,IACR,SAAS;AAAA,IACT,aAAa;AAAA,EACf,CAAC;AAED,YAAU,MAAM;AACd,UAAM,aAAa,YAAY;AAC7B,UAAI;AACF,iBAAS,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,KAAK,EAAE;AACzC,cAAM,QAAQ,MAAM,SAAS;AAC7B,YAAI,CAAC,OAAO;AACV,kBAAQ;AAAA,YACN;AAAA,UACF;AAAA,QACF;AACA,iBAAS,CAAC,OAAO;AAAA,UACf,GAAG;AAAA,UACH,SAAS;AAAA,UACT;AAAA,QACF,EAAE;AAAA,MACJ,SAAS,GAAG;AACV,iBAAS,CAAC,OAAO;AAAA,UACf,GAAG;AAAA,UACH,SAAS;AAAA,UACT,OAAO,CAAC;AAAA,UACR,OAAO,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC;AAAA,QACrD,EAAE;AAAA,MACJ;AAAA,IACF;AAEA,eAAW;AAAA,EACb,GAAG,CAAC,QAAQ,CAAC;AAEb,MAAI,MAAM,SAAS;AACjB,WAAO,oBAAC,oBAAiB;AAAA,EAC3B;AAEA,MAAI,MAAM,OAAO;AACf,WAAO,oBAAC,kBAAe,OAAO,MAAM,OAAO;AAAA,EAC7C;AAEA,SACE,oBAAC,aAAa,UAAb,EAAsB,OAAO,OAAQ,UAAS;AAEnD;;;ACtGA,SAAS,kBAAkB;AAGpB,SAAS,WAAW;AACzB,SAAO,WAAW,YAAY;AAChC;;;ACEO,SAAS,UACd,WACA,UACA,MACiB;AAEjB,MAAI,CAAC,YAAY,SAAS,WAAW,GAAG;AACtC,WAAO,EAAE,SAAS,MAAM,SAAS,CAAC,EAAE;AAAA,EACtC;AAEA,QAAM,cAAc,IAAI,IAAI,SAAS;AAErC,MAAI,SAAS,OAAO;AAClB,UAAM,UAAU,SAAS,OAAO,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,CAAC;AAC1D,WAAO;AAAA,MACL,SAAS,QAAQ,WAAW;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAGA,QAAM,SAAS,SAAS,KAAK,CAAC,MAAM,YAAY,IAAI,CAAC,CAAC;AACtD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS,SAAS,CAAC,IAAI,SAAS,MAAM;AAAA;AAAA,EACxC;AACF;;;AC/BA,OAAO,eAAe;AACtB,OAAO,aAAa;AAiBd,SAEI,OAAAA,MAFJ;AARC,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB,cAAc;AAChB,GAA2C;AACzC,SACE,gBAAAA,KAAC,SAAI,WAAU,OACb,+BAAC,SAAI,WAAU,OACb;AAAA,oBAAAA,KAAC,WAAQ,IAAG,MAAK,WAAU,eACzB,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,IAAI,GAAG,iBAAiB;AAAA,QACxB,aAAY;AAAA,QAEX;AAAA;AAAA,IACH,GACF;AAAA,IACA,gBAAAA,KAAC,OACC,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,IAAI,GAAG,iBAAiB;AAAA,QACxB,aAAY;AAAA,QAEX;AAAA;AAAA,IACH,GACF;AAAA,IACA,gBAAAA,KAAC,OACC,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,IAAI,GAAG,iBAAiB;AAAA,QACxB,aAAY;AAAA,QAEX;AAAA;AAAA,IACH,GACF;AAAA,KACF,GACF;AAEJ;","names":["jsx"]}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "docusaurus-roles-plugin",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "Role-based gating for Docusaurus docs/blog via React context + front matter",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/index.cjs",
8
+ "module": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "require": "./dist/index.cjs",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./runtime": {
17
+ "types": "./dist/runtime/index.d.ts",
18
+ "require": "./dist/runtime/index.cjs",
19
+ "import": "./dist/runtime/index.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "README.md",
25
+ "src/theme"
26
+ ],
27
+ "dependencies": {
28
+ "@docusaurus/logger": "^3.9.0"
29
+ },
30
+ "peerDependencies": {
31
+ "@docusaurus/core": "^3.9.0",
32
+ "@docusaurus/theme-common": "^3.9.0",
33
+ "@docusaurus/theme-translations": "^3.9.0",
34
+ "@docusaurus/utils-validation": "^3.9.0",
35
+ "react": ">=18",
36
+ "react-dom": ">=18"
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "^5.9.0",
40
+ "@docusaurus/types": "^3.9.0",
41
+ "@docusaurus/theme-classic": "^3.9.0",
42
+ "@docusaurus/module-type-aliases": "^3.9.0",
43
+ "@docusaurus/plugin-content-blog": "^3.9.0",
44
+ "@docusaurus/plugin-content-docs": "^3.9.0",
45
+ "tsup": "^8.5.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "dev": "tsup --watch",
50
+ "typecheck": "tsc -p tsconfig.json --noEmit"
51
+ }
52
+ }
@@ -0,0 +1,20 @@
1
+ import BlogListPage from "@theme-init/BlogListPage";
2
+ import { useRoles, isAllowed } from "docusaurus-roles-plugin/runtime";
3
+ import type { Props } from "@theme/BlogListPage";
4
+ import { getRoleFrontMatter } from "../../options";
5
+
6
+ export default function BlogListPageWrapper({ items, ...rest }: Props) {
7
+ const { roles, loading } = useRoles();
8
+
9
+ const filteredItems = items.filter((i) => {
10
+ const frontMatter = getRoleFrontMatter(i.content.frontMatter);
11
+ const { allowed } = isAllowed(
12
+ roles,
13
+ frontMatter.required_roles ?? [],
14
+ frontMatter.required_roles_mode ?? "all",
15
+ );
16
+ return allowed;
17
+ });
18
+
19
+ return <BlogListPage {...rest} items={filteredItems} />;
20
+ }
@@ -0,0 +1,17 @@
1
+ import { type ReactNode } from "react";
2
+ import { BaseForbiddenContent } from "docusaurus-roles-plugin/runtime";
3
+
4
+ export default function ForbiddenBlogPostItem({
5
+ missingRoles,
6
+ }: {
7
+ missingRoles: string[];
8
+ }): ReactNode {
9
+ return (
10
+ <main className={"container margin-vert--xl"}>
11
+ <BaseForbiddenContent
12
+ prefixTranslateId="theme.BlogPostItem.Forbidden"
13
+ missingRoles={missingRoles}
14
+ />
15
+ </main>
16
+ );
17
+ }
@@ -0,0 +1,22 @@
1
+ import BlogPostItem, { type Props } from "@theme-init/BlogPostItem";
2
+ import RoleGate from "../RoleGate";
3
+ import { getRoleFrontMatter } from "../../options";
4
+ import { useBlogPost } from "@docusaurus/plugin-content-blog/client";
5
+ import ForbiddenBlogPostItem from "./Forbidden";
6
+
7
+ export default function BlogPostItemWrapper(props: Props) {
8
+ const { metadata } = useBlogPost();
9
+ const frontMatter = getRoleFrontMatter(metadata.frontMatter);
10
+ const requiredRoles = frontMatter.required_roles;
11
+ const mode = frontMatter.required_roles_mode;
12
+
13
+ return (
14
+ <RoleGate
15
+ requiredRoles={requiredRoles}
16
+ mode={mode}
17
+ forbidden={ForbiddenBlogPostItem}
18
+ >
19
+ <BlogPostItem {...props} />
20
+ </RoleGate>
21
+ );
22
+ }
@@ -0,0 +1,26 @@
1
+ import { usePluginData } from "@docusaurus/useGlobalData";
2
+ import BlogSidebar, { type Props } from "@theme-init/BlogSidebar";
3
+ import { isAllowed, useRoles } from "docusaurus-roles-plugin/runtime";
4
+ import { type RoleInfo } from "../..";
5
+
6
+ export default function BlogSidebarWrapper({ sidebar, ...props }: Props) {
7
+ const plugin = usePluginData("docusaurus-roles-plugin") as {
8
+ rolesByPermalink: RoleInfo[];
9
+ };
10
+ const { roles } = useRoles();
11
+ if (sidebar) {
12
+ sidebar.items = sidebar.items.filter((i) => {
13
+ const roleInfo = plugin.rolesByPermalink.find(
14
+ (r) => r.permalink === i.permalink,
15
+ );
16
+ if (!roleInfo) return true;
17
+ const { allowed } = isAllowed(
18
+ roles,
19
+ roleInfo.requiredRoles,
20
+ roleInfo.requiredRolesMode,
21
+ );
22
+ return allowed;
23
+ });
24
+ }
25
+ return <BlogSidebar {...props} sidebar={sidebar} />;
26
+ }
@@ -0,0 +1,15 @@
1
+ import { JSX } from "react";
2
+ import { BaseForbiddenContent } from "docusaurus-roles-plugin/runtime";
3
+
4
+ export default function ForbiddenDocItem({
5
+ missingRoles,
6
+ }: {
7
+ missingRoles: string[];
8
+ }): JSX.Element {
9
+ return (
10
+ <BaseForbiddenContent
11
+ prefixTranslateId="theme.DocItem.Forbidden"
12
+ missingRoles={missingRoles}
13
+ />
14
+ );
15
+ }
@@ -0,0 +1,20 @@
1
+ import DocItem, { type Props } from "@theme-init/DocItem";
2
+ import RoleGate from "../RoleGate";
3
+ import { getRoleFrontMatter } from "../../options";
4
+ import ForbiddenDocItem from "./Forbidden";
5
+
6
+ export default function DocItemWrapper(props: Props) {
7
+ const frontMatter = getRoleFrontMatter(props?.content.frontMatter);
8
+ const requiredRoles = frontMatter.required_roles;
9
+ const mode = frontMatter.required_roles_mode;
10
+
11
+ return (
12
+ <RoleGate
13
+ requiredRoles={requiredRoles}
14
+ mode={mode}
15
+ forbidden={ForbiddenDocItem}
16
+ >
17
+ <DocItem {...props} />
18
+ </RoleGate>
19
+ );
20
+ }