@freshheads/react-auth 0.0.1-alpha.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/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # react-vite-component-template
2
+
3
+ Template for building a **React component library**, with **Vite**, **TypeScript** and **Storybook**.
4
+
5
+ Check my post about this repository [here](https://victorlillo.dev/blog/react-typescript-vite-component-library).
6
+
7
+ ## Features
8
+
9
+ - โš›๏ธ **React** component library with **TypeScript**.
10
+
11
+ - ๐Ÿ—๏ธ **Vite** as development environment.
12
+
13
+ - ๐ŸŒณ **Tree shaking**, for not distributing dead-code.
14
+
15
+ - ๐Ÿงช Testing with **Vitest** and **React Testing Library**.
16
+
17
+ - โœ… Code quality tools with **ESLint**, **Prettier**
18
+
19
+ ## ๐Ÿค– Scripts
20
+
21
+ | Script | Function |
22
+ | :---------------: | ---------------------------------------------------------------------------------- |
23
+ | `build` | Build the `dist`, with types declarations, after checking types with TypeScript. |
24
+ | `lint` | Lint the project with **Eslint**. |
25
+ | `lint:fix` | Lint and fix the project with **Eslint**. |
26
+ | `format` | Check the project format with **Prettier**. |
27
+ | `format:fix` | Format the project code with **Prettier**. |
28
+ | `stylelint` | Lint the styles code with **Stylelint**. |
29
+ | `stylelint:fix` | Lint and fix the styles code with **Stylelint**. |
30
+ | `storybook` | Start a Storybook development server. |
31
+ | `build-storybook` | Build the Storybook `dist`. |
32
+ | `test` | Run the tests with **Vitest** using `jsdom` and starts a **Vitest UI** dev server. |
33
+ | `coverage` | Generate a coverage report, with **v8**. |
@@ -0,0 +1 @@
1
+ export declare function useSignIn(): void;
@@ -0,0 +1 @@
1
+ export declare function useSignOut(): void;
package/dist/main.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { useSignIn } from './hooks/useSignIn.ts';
2
+ export { useSignOut } from './hooks/useSignOut.ts';
3
+ export { default as withAuth } from './next/proxy.ts';
package/dist/main.js ADDED
@@ -0,0 +1,67 @@
1
+ import { NextResponse as i } from "next/server.js";
2
+ import { jwtDecode as f } from "jwt-decode";
3
+ import { match as g } from "path-to-regexp";
4
+ function y() {
5
+ throw new Error("useSignIn not yet implementend");
6
+ }
7
+ function S() {
8
+ throw new Error("useSignOut not yet implementend");
9
+ }
10
+ function p(e) {
11
+ if (!e.has("jwt_hp"))
12
+ return null;
13
+ const t = e.get("jwt_hp")?.value;
14
+ if (!t)
15
+ return null;
16
+ try {
17
+ return f(t).roles || null;
18
+ } catch (r) {
19
+ return console.error(r), null;
20
+ }
21
+ }
22
+ const m = (e, n) => {
23
+ const t = `{/:locale}${e}`;
24
+ return n ? t : e.replace(/\/\[\[\.\.\.([^\]]+)\]\]/g, "{/*$1}").replace(/\/\[\.\.\.([^\]]+)\]/g, "/*$1").replace(/\[([^\]]+)\]/g, ":$1");
25
+ }, a = (e, n) => {
26
+ const t = Object.values(e).map(
27
+ (r) => g(m(r, n))
28
+ );
29
+ return (r) => t.some((c) => c(r) !== !1);
30
+ }, w = (e, n, t) => {
31
+ const r = {
32
+ ...n.unauthenticated.routes,
33
+ ...n.open.routes
34
+ };
35
+ return !a(r, t)(e);
36
+ }, R = (e, n, t) => a(
37
+ n.unauthenticated.routes,
38
+ t
39
+ )(e), x = (e, n, t) => a(
40
+ n.open.routes,
41
+ t
42
+ )(e), A = (e, n) => n[e]?.landing || n.default.landing, L = (e, n) => e.filter(
43
+ (t) => n.includes(t)
44
+ ), O = (e, n) => L(e, n).length > 0;
45
+ function $(e) {
46
+ return function(t, r) {
47
+ const c = t.nextUrl.pathname, { rbac: u, authorizedRoles: d, withLocaleRoutePrefix: l } = e;
48
+ if (x(c, u, l))
49
+ return r ?? i.next();
50
+ const o = p(t.cookies), s = o !== null && o.length > 0, h = o === null || !s ? !1 : O(o, d);
51
+ return (!s || !h) && w(c, u, l) ? i.redirect(
52
+ new URL(u.default.login, t.url),
53
+ 307
54
+ ) : s && R(c, u, l) ? o === null || o.length === 0 || o[0] === void 0 ? i.redirect(
55
+ new URL(u.default.login, t.url),
56
+ 307
57
+ ) : i.redirect(
58
+ new URL(A(o[0], u), t.url),
59
+ 307
60
+ ) : r ?? i.next();
61
+ };
62
+ }
63
+ export {
64
+ y as useSignIn,
65
+ S as useSignOut,
66
+ $ as withAuth
67
+ };
@@ -0,0 +1,3 @@
1
+ import { NextRequest, NextResponse } from 'next/server.js';
2
+ import { AuthOptions } from '../types/options';
3
+ export default function withAuth<ValidRoute extends string, AuthorizedRole extends string>(options: AuthOptions<ValidRoute, AuthorizedRole[]>): (request: NextRequest, response?: NextResponse) => NextResponse<unknown>;
@@ -0,0 +1,9 @@
1
+ export type Auth = {
2
+ token: string;
3
+ expiresAt: number;
4
+ user: {
5
+ id: string;
6
+ email: string;
7
+ name: string;
8
+ };
9
+ };
@@ -0,0 +1,6 @@
1
+ import { RbacRoutes } from './rbac.ts';
2
+ export type AuthOptions<ValidRoute, AuthorizedRoles extends string[] = []> = {
3
+ rbac: RbacRoutes<ValidRoute, AuthorizedRoles>;
4
+ authorizedRoles: AuthorizedRoles;
5
+ withLocaleRoutePrefix: boolean;
6
+ };
@@ -0,0 +1,17 @@
1
+ export type RbacRoutes<ValidRoute, AuthorizedRoles extends string[] = []> = {
2
+ default: {
3
+ login: ValidRoute;
4
+ landing: ValidRoute;
5
+ };
6
+ open: {
7
+ routes: ValidRoute[];
8
+ };
9
+ unauthenticated: {
10
+ routes: ValidRoute[];
11
+ };
12
+ } & {
13
+ [key in AuthorizedRoles[number]]: {
14
+ routes: ValidRoute[];
15
+ landing?: ValidRoute;
16
+ };
17
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import { RequestCookies } from 'next/dist/server/web/spec-extension/cookies.js';
2
+ /**
3
+ * Retrieves the user roles from JWT stored in cookies.
4
+ *
5
+ * Note: by reading cookies you will opt out of static rendering for the entire route (group).
6
+ *
7
+ * @returns A promise that resolves to the user roles as an array, or null if not found or on error.
8
+ */
9
+ export declare function getRolesFromCookies<AuthorizedRole>(requestCookies: RequestCookies): AuthorizedRole[] | null;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Converts Next.js dynamic route segments to path-to-regexp format.
3
+ * - /[id] becomes /:id
4
+ * - /[...slug] becomes /*slug
5
+ * - /[[...slug]] becomes {/*slug}
6
+ */
7
+ export declare const convertNextJsPathToRegexp: (path: string, withLocaleRoutePrefix: boolean) => string;
8
+ /**
9
+ * Compiles a route path with parameters.
10
+ * Supports both Next.js dynamic segments and path-to-regexp syntax.
11
+ *
12
+ * Examples:
13
+ * - compileRoutePath('/member/:id', { id: '123' }) => '/member/123'
14
+ * - compileRoutePath('/member/[id]', { id: '123' }) => '/member/123'
15
+ * - compileRoutePath('/docs/[...slug]', { slug: ['a', 'b', 'c'] }) => '/docs/a/b/c'
16
+ * - compileRoutePath('/docs/[[...slug]]', { slug: ['a', 'b'] }) => '/docs/a/b'
17
+ */
18
+ export declare const compileRoutePath: <ValidRoute>(path: string, params: Record<string, string | string[]> | undefined, withLocaleRoutePrefix: boolean) => ValidRoute;
@@ -0,0 +1 @@
1
+ export declare const userRolesWithAccess: <Role extends string>(roles: Role[], authorizedRoles: Role[]) => Role[];
@@ -0,0 +1,10 @@
1
+ import { RbacRoutes } from '../types/rbac.ts';
2
+ /**
3
+ * Validates a given path against a set of routes.
4
+ * @returns true if the path matches any of the routes in the provided set.
5
+ */
6
+ export declare const createMatchers: (routes: string[], withLocaleRoutePrefix: boolean) => (path: string) => boolean;
7
+ export declare const isAuthenticatedRoute: <ValidRoute extends string>(pathname: string, RBACRoutes: RbacRoutes<ValidRoute>, withLocaleRoutePrefix: boolean) => boolean;
8
+ export declare const isUnauthenticatedRoute: <ValidRoute extends string>(pathname: string, RBACRoutes: RbacRoutes<ValidRoute>, withLocaleRoutePrefix: boolean) => boolean;
9
+ export declare const isOpenRoute: <ValidRoute extends string>(pathname: string, RBACRoutes: RbacRoutes<ValidRoute>, withLocaleRoutePrefix: boolean) => boolean;
10
+ export declare const getLandingRouteByRole: <Role extends string, ValidRoute>(role: Role, RBACRoutes: RbacRoutes<ValidRoute>) => ValidRoute;
@@ -0,0 +1 @@
1
+ export declare const userIsAuthorized: <Role extends string>(userRoles: Role[], authorizedRoles: Role[]) => boolean;
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@freshheads/react-auth",
3
+ "version": "0.0.1-alpha.1",
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "tsc -b && vite build",
7
+ "next:typegen": "next typegen",
8
+ "lint": "eslint . --ext ts,tsx",
9
+ "lint:fix": "eslint . --ext ts,tsx --fix"
10
+ },
11
+ "module": "./dist/main.js",
12
+ "exports": "./dist/main.js",
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "types": "./dist/main.d.ts",
17
+ "dependencies": {
18
+ "js-cookie": "^3.0.5",
19
+ "jwt-decode": "^4.0.0",
20
+ "path-to-regexp": "^8.3.0"
21
+ },
22
+ "devDependencies": {
23
+ "@eslint/js": "^9.39.1",
24
+ "@types/js-cookie": "^3.0.6",
25
+ "@types/node": "^24.10.12",
26
+ "@types/react": "^19.2.7",
27
+ "@types/react-dom": "^19.2.3",
28
+ "@vitejs/plugin-react-swc": "^4.3.0",
29
+ "eslint": "^9.39.1",
30
+ "eslint-config-next": "^16.1.6",
31
+ "eslint-config-prettier": "^10.1.8",
32
+ "eslint-plugin-prettier": "^5.5.5",
33
+ "eslint-plugin-react": "^7.37.5",
34
+ "eslint-plugin-react-hooks": "^7.0.1",
35
+ "eslint-plugin-react-refresh": "^0.4.24",
36
+ "glob": "^13.0.6",
37
+ "globals": "^16.5.0",
38
+ "prettier": "^3.8.1",
39
+ "prettier-plugin-organize-imports": "^4.3.0",
40
+ "react": "^19.2.4",
41
+ "typescript": "~5.9.3",
42
+ "typescript-eslint": "^8.48.0",
43
+ "vite": "^7.3.1",
44
+ "vite-plugin-dts": "^4.5.4",
45
+ "vite-plugin-externalize-deps": "^0.10.0"
46
+ },
47
+ "peerDependencies": {
48
+ "next": "^16.2.1",
49
+ "react": "^19.2.4",
50
+ "react-dom": "^19.2.4"
51
+ }
52
+ }