diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml new file mode 100644 index 00000000..88c95944 --- /dev/null +++ b/.github/workflows/prettier.yml @@ -0,0 +1,26 @@ +name: Prettier Format + +on: + pull_request: + +jobs: + format: + name: "Format" + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ github.head_ref }} + fetch-depth: 0 + - name: Install Packages + run: | + corepack enable + pnpm i --frozen-lockfile + - name: Prettier Action + uses: creyD/prettier_action@8c18391fdc98ed0d884c6345f03975edac71b8f0 + with: + prettier_options: --write . + git_identity: author + clean_node_folder: false + commit_message: "Automatic code format with Prettier" diff --git a/examples/base-app/.prettierrc b/examples/base-app/.prettierrc index 8b0bc4ef..b4bfed35 100644 --- a/examples/base-app/.prettierrc +++ b/examples/base-app/.prettierrc @@ -1,3 +1,3 @@ { "plugins": ["prettier-plugin-tailwindcss"] -} \ No newline at end of file +} diff --git a/examples/kitchen-sink/.prettierrc b/examples/kitchen-sink/.prettierrc index 8b0bc4ef..b4bfed35 100644 --- a/examples/kitchen-sink/.prettierrc +++ b/examples/kitchen-sink/.prettierrc @@ -1,3 +1,3 @@ { "plugins": ["prettier-plugin-tailwindcss"] -} \ No newline at end of file +} diff --git a/examples/kitchen-sink/app/auth.ts b/examples/kitchen-sink/app/auth.ts index 50810685..246b0ffa 100644 --- a/examples/kitchen-sink/app/auth.ts +++ b/examples/kitchen-sink/app/auth.ts @@ -1,43 +1,59 @@ import cookies from "@twofold/framework/cookies"; -import { allow, AuthPolicyProps, AuthPolicyResult, deny, response } from "@twofold/framework/auth"; +import { + allow, + AuthPolicyProps, + AuthPolicyResult, + deny, + response, +} from "@twofold/framework/auth"; import { redirect } from "@twofold/framework/redirect"; /** * This is an example of an authentication policy that layouts, pages, actions and routes can optionally use by exporting a 'const auth: AuthPolicyArray = [allowIfCookieSet]'. Non-default auth policies don't have to be specified in this file, but it's a convention so you know where all your authentication code is. - * + * * Authentication policies exported from layouts, pages, actions and routes by default inherit the authentication policies from their parent layouts and the root authentication policy. - * + * * If you want a specific route to discard all of it's authentication policies, you can use the special 'reset' value, like so: - * + * * import { reset } from "@twofold/framework/auth"; * export const auth: AuthPolicyArray = [reset, otherPolicy]; - * + * * @param props The authentication properties. * @returns The policy result. */ -export async function allowIfCookieSet({request}: AuthPolicyProps): Promise { +export async function allowIfCookieSet({ + request, +}: AuthPolicyProps): Promise { // This is an example of an auth policy that changes it's behaviour based on the request. - if (cookies.get('allow-access') === 'true') { + if (cookies.get("allow-access") === "true") { return allow(); } else { return deny("missing cookie"); } } -export async function behaveBasedOnQueryString({request}: AuthPolicyProps): Promise { +export async function behaveBasedOnQueryString({ + request, +}: AuthPolicyProps): Promise { // This policy changes it's behaviour based on the query string, and is used for the protected/ content under this app (in addition to the other policies here). const url = new URL(request.url); switch (url.searchParams.get("auth-behaviour")) { case "response": - return response(new Response("Access denied by behaveBasedOnQueryString (response)")); + return response( + new Response("Access denied by behaveBasedOnQueryString (response)"), + ); case "response-throw": - throw new Response("Access denied by behaveBasedOnQueryString (response-throw)."); + throw new Response( + "Access denied by behaveBasedOnQueryString (response-throw).", + ); case "deny": return deny("Access denied by behaveBasedOnQueryString (deny)."); case "deny-throw": throw "Access denied by behaveBasedOnQueryString (deny-throw)."; case "error": - throw new Error("This is an unhandled error for behaveBasedOnQueryString (error)."); + throw new Error( + "This is an unhandled error for behaveBasedOnQueryString (error).", + ); case "redirect": redirect("/"); case "allow": @@ -46,22 +62,30 @@ export async function behaveBasedOnQueryString({request}: AuthPolicyProps): Prom } } -export async function behaveBasedOnFormData({request}: AuthPolicyProps): Promise { +export async function behaveBasedOnFormData({ + request, +}: AuthPolicyProps): Promise { // This policy changes it's behaviour based on the 'behaviour' form data, which is how we demonstrate different behaviour for server actions. const formData = await request.formData(); // @note: This is '1_behaviour', due to the way React encodes form data fields. Since you're not expected to actually do auth based on form fields, it's fine as a workaround for the example app. switch (formData.get("1_behaviour")) { case "response": - return response(new Response("Access denied by behaveBasedOnQueryString (response)")); + return response( + new Response("Access denied by behaveBasedOnQueryString (response)"), + ); case "response-throw": - throw new Response("Access denied by behaveBasedOnQueryString (response-throw)."); + throw new Response( + "Access denied by behaveBasedOnQueryString (response-throw).", + ); case "deny": return deny("Access denied by behaveBasedOnQueryString (deny)."); case "deny-throw": throw "Access denied by behaveBasedOnQueryString (deny-throw)."; case "error": - throw new Error("This is an unhandled error for behaveBasedOnQueryString (error)."); + throw new Error( + "This is an unhandled error for behaveBasedOnQueryString (error).", + ); case "redirect": redirect("/"); case "allow": @@ -72,11 +96,13 @@ export async function behaveBasedOnFormData({request}: AuthPolicyProps): Promise /** * The default export from auth.ts is the root authentication policy that applies to all layouts, pages, actions and routes. - * + * * @param props The authentication properties. * @returns The policy result. */ -export default async function defaultPolicy(props: AuthPolicyProps) : Promise { +export default async function defaultPolicy( + props: AuthPolicyProps, +): Promise { // This is the default behaviour when auth.ts is not specified. return allow(); @@ -98,4 +124,4 @@ export default async function defaultPolicy(props: AuthPolicyProps) : PromiseTest + return ( + + Test + + ); } function Td(props: { children: React.ReactNode }) { - return {props.children} + return {props.children}; } -function TestServerAction(props: { behaviour: string, startTransition: any, setComponent: any, unprotected?: boolean }) { +function TestServerAction(props: { + behaviour: string; + startTransition: any; + setComponent: any; + unprotected?: boolean; +}) { const submitAction = async (formData: FormData) => { props.startTransition(async () => { try { - let c = await (props.unprotected ? unprotectedAction : protectedAction)(formData); + let c = await (props.unprotected ? unprotectedAction : protectedAction)( + formData, + ); props.setComponent(c); } catch (err) { if (isServerActionUnauthorizedError(err)) { @@ -28,229 +43,336 @@ function TestServerAction(props: { behaviour: string, startTransition: any, setC } } }); - } - return
- - -
+ }; + return ( +
+ + +
+ ); } export default function Control(props: { allowIfCookieSetWillPass: boolean }) { let [component, setComponent] = useState(); let [isPending, startTransition] = useTransition(); - return
-

Auth examples

-

- General access to routes protected by allowIfCookieSet: {!props.allowIfCookieSetWillPass ? Denied : Allowed} -

-
- -
-
- -
+ return ( +
+

Auth examples

+

+ General access to routes protected by allowIfCookieSet:{" "} + {!props.allowIfCookieSetWillPass ? ( + Denied + ) : ( + Allowed + )} +

+
+ +
+
+ +
-

Examples table

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Noneresponseresponse-throwdenydeny-throwerrorredirectallow
Page - - - - - - - - - - - - - - - -
Server action - - - - - - - - - - - - - - - -
API route - - - - - - - - - - - - - - - -
Unprotected page - - - - - - - - - - - - - - - -
Unprotected server action - - - - - - - - - - - - - - - -
Unprotected API route - - - - - - - - - - - - - - - -
+

+ Examples table +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
None + response + + response-throw + + deny + + deny-throw + + error + + redirect + + allow +
Page + + + + + + + + + + + + + + + +
Server action + + + + + + + + + + + + + + + +
API route + + + + + + + + + + + + + + + +
Unprotected page + + + + + + + + + + + + + + + +
Unprotected server action + + + + + + + + + + + + + + + +
Unprotected API route + + + + + + + + + + + + + + + +
- {isPending || component ? (<>

Server action returned component

- {isPending - ? "Component is loading..." - : component} -
) : null} + {isPending || component ? ( + <> +

+ Server action returned component +

+
+ {isPending ? "Component is loading..." : component} +
+ + ) : null} -

Page & API route test iframe

- -
+

+ Page & API route test iframe +

+ +
+ ); } - diff --git a/examples/kitchen-sink/app/pages/auth/cookie.ts b/examples/kitchen-sink/app/pages/auth/cookie.ts index b56b0068..1a69fa8e 100644 --- a/examples/kitchen-sink/app/pages/auth/cookie.ts +++ b/examples/kitchen-sink/app/pages/auth/cookie.ts @@ -13,4 +13,4 @@ export async function clearCookie() { "use server"; cookies.destroy("allow-access"); redirect("/auth"); -} \ No newline at end of file +} diff --git a/examples/kitchen-sink/app/pages/auth/index.page.tsx b/examples/kitchen-sink/app/pages/auth/index.page.tsx index a7f7e969..4e1df4a2 100644 --- a/examples/kitchen-sink/app/pages/auth/index.page.tsx +++ b/examples/kitchen-sink/app/pages/auth/index.page.tsx @@ -2,7 +2,7 @@ import cookies from "@twofold/framework/cookies"; import Control from "./Control"; export default function Page() { - const allowIfCookieSetWillPass = cookies.get("allow-access") === 'true'; + const allowIfCookieSetWillPass = cookies.get("allow-access") === "true"; return ; } diff --git a/examples/kitchen-sink/app/pages/auth/protected/action/protectedAction.tsx b/examples/kitchen-sink/app/pages/auth/protected/action/protectedAction.tsx index e10c27ba..15772ba0 100644 --- a/examples/kitchen-sink/app/pages/auth/protected/action/protectedAction.tsx +++ b/examples/kitchen-sink/app/pages/auth/protected/action/protectedAction.tsx @@ -6,5 +6,11 @@ import { AuthPolicyArray } from "@twofold/framework/auth"; export const auth: AuthPolicyArray = [behaveBasedOnFormData]; export default async function action(formData: FormData) { - return
This is a server component returned by a server action. It will only be returned if the authentication policies pass. The behaviour was '{formData.get("behaviour")?.toString()}'.
; + return ( +
+ This is a server component returned by a server action. It will only be + returned if the authentication policies pass. The behaviour was ' + {formData.get("behaviour")?.toString()}'. +
+ ); } diff --git a/examples/kitchen-sink/app/pages/auth/protected/api/index.api.ts b/examples/kitchen-sink/app/pages/auth/protected/api/index.api.ts index 948a7115..e58e6d7c 100644 --- a/examples/kitchen-sink/app/pages/auth/protected/api/index.api.ts +++ b/examples/kitchen-sink/app/pages/auth/protected/api/index.api.ts @@ -4,11 +4,15 @@ import { AuthPolicyArray } from "@twofold/framework/auth"; export const auth: AuthPolicyArray = [behaveBasedOnQueryString]; export function GET() { - return new Response(JSON.stringify({ - message: "This is an API route that only runs if the authentication policies pass." - }), { - headers: { - "content-type": "application/json", + return new Response( + JSON.stringify({ + message: + "This is an API route that only runs if the authentication policies pass.", + }), + { + headers: { + "content-type": "application/json", + }, }, - }); -} \ No newline at end of file + ); +} diff --git a/examples/kitchen-sink/app/pages/auth/protected/layout.tsx b/examples/kitchen-sink/app/pages/auth/protected/layout.tsx index 6358258e..79c9844c 100644 --- a/examples/kitchen-sink/app/pages/auth/protected/layout.tsx +++ b/examples/kitchen-sink/app/pages/auth/protected/layout.tsx @@ -1,4 +1,4 @@ import { allowIfCookieSet } from "@/app/auth"; import { AuthPolicyArray } from "@twofold/framework/auth"; -export const auth: AuthPolicyArray = [allowIfCookieSet]; \ No newline at end of file +export const auth: AuthPolicyArray = [allowIfCookieSet]; diff --git a/examples/kitchen-sink/app/pages/auth/protected/page/index.page.tsx b/examples/kitchen-sink/app/pages/auth/protected/page/index.page.tsx index 3f7c5525..6e5748f5 100644 --- a/examples/kitchen-sink/app/pages/auth/protected/page/index.page.tsx +++ b/examples/kitchen-sink/app/pages/auth/protected/page/index.page.tsx @@ -4,5 +4,9 @@ import { AuthPolicyArray } from "@twofold/framework/auth"; export const auth: AuthPolicyArray = [behaveBasedOnQueryString]; export default function Page() { - return
This message won't show unless the authentication policies allow it.
; + return ( +
+ This message won't show unless the authentication policies allow it. +
+ ); } diff --git a/examples/kitchen-sink/app/pages/auth/protected/unprotected/action/unprotectedAction.tsx b/examples/kitchen-sink/app/pages/auth/protected/unprotected/action/unprotectedAction.tsx index 83515ebe..e7010243 100644 --- a/examples/kitchen-sink/app/pages/auth/protected/unprotected/action/unprotectedAction.tsx +++ b/examples/kitchen-sink/app/pages/auth/protected/unprotected/action/unprotectedAction.tsx @@ -5,5 +5,11 @@ import { AuthPolicyArray, reset } from "@twofold/framework/auth"; export const auth: AuthPolicyArray = [reset]; export default async function action(formData: FormData) { - return
This is a server component returned by a server action. It is running on an action that uses 'reset' to allow access regardless of the parent routes. The behaviour was '{formData.get("behaviour")?.toString()}'.
; + return ( +
+ This is a server component returned by a server action. It is running on + an action that uses 'reset' to allow access regardless of the parent + routes. The behaviour was '{formData.get("behaviour")?.toString()}'. +
+ ); } diff --git a/examples/kitchen-sink/app/pages/auth/protected/unprotected/api/index.api.ts b/examples/kitchen-sink/app/pages/auth/protected/unprotected/api/index.api.ts index 3f220f89..63e91508 100644 --- a/examples/kitchen-sink/app/pages/auth/protected/unprotected/api/index.api.ts +++ b/examples/kitchen-sink/app/pages/auth/protected/unprotected/api/index.api.ts @@ -3,11 +3,15 @@ import { AuthPolicyArray, reset } from "@twofold/framework/auth"; export const auth: AuthPolicyArray = [reset]; export function GET() { - return new Response(JSON.stringify({ - message: "This is an API route that uses 'reset' to skip the authentication policies of the parent routes." - }), { - headers: { - "content-type": "application/json", + return new Response( + JSON.stringify({ + message: + "This is an API route that uses 'reset' to skip the authentication policies of the parent routes.", + }), + { + headers: { + "content-type": "application/json", + }, }, - }); -} \ No newline at end of file + ); +} diff --git a/examples/kitchen-sink/app/pages/auth/protected/unprotected/page/index.page.tsx b/examples/kitchen-sink/app/pages/auth/protected/unprotected/page/index.page.tsx index e5385eb4..c5c7c925 100644 --- a/examples/kitchen-sink/app/pages/auth/protected/unprotected/page/index.page.tsx +++ b/examples/kitchen-sink/app/pages/auth/protected/unprotected/page/index.page.tsx @@ -3,5 +3,10 @@ import { AuthPolicyArray, reset } from "@twofold/framework/auth"; export const auth: AuthPolicyArray = [reset]; export default function Page() { - return
This message should always show since this page resets the authentication policies of the parent routes.
; + return ( +
+ This message should always show since this page resets the authentication + policies of the parent routes. +
+ ); } diff --git a/examples/kitchen-sink/app/pages/client-components/counter.tsx b/examples/kitchen-sink/app/pages/client-components/counter.tsx index 04bf7d06..fb363991 100644 --- a/examples/kitchen-sink/app/pages/client-components/counter.tsx +++ b/examples/kitchen-sink/app/pages/client-components/counter.tsx @@ -15,7 +15,7 @@ export default function ClientComponent() { > - - + {count} - + {count} diff --git a/examples/kitchen-sink/app/pages/client-components/unformatted/counter.tsx b/examples/kitchen-sink/app/pages/client-components/unformatted/counter.tsx index 0b55606e..f5d74828 100644 --- a/examples/kitchen-sink/app/pages/client-components/unformatted/counter.tsx +++ b/examples/kitchen-sink/app/pages/client-components/unformatted/counter.tsx @@ -15,7 +15,7 @@ export default function ClientComponent() { > - - + {count}