celestya 0.2.0 → 0.3.0
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 +5 -1
- package/dist/client/index.d.mts +5 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +27 -7
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +28 -8
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.js +10 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -136,6 +136,10 @@ const Home = async () => {
|
|
|
136
136
|
export default Navbar;
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
+
## How to upload to npm
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
139
143
|
## Todo
|
|
140
144
|
|
|
141
145
|
- [x]: Change returns at error
|
|
@@ -145,4 +149,4 @@ export default Navbar;
|
|
|
145
149
|
- [x]: Fix issue with api endpoints if no layout has been loaded (if accessing api directly)
|
|
146
150
|
- [X]: Refresh logic
|
|
147
151
|
- [X]: Fix Response types
|
|
148
|
-
- [ ]: Upload request with worker as helper (?)
|
|
152
|
+
- [ ]: Upload request with worker as helper (?)
|
package/dist/client/index.d.mts
CHANGED
|
@@ -94,14 +94,19 @@ interface IAuthContext<U> {
|
|
|
94
94
|
refreshUser: (force?: boolean) => Promise<void>;
|
|
95
95
|
get: <T>(props: {
|
|
96
96
|
url: string;
|
|
97
|
+
headers?: Record<string, string>;
|
|
97
98
|
}) => Promise<Result<T, BaseError>>;
|
|
98
99
|
post: <T>(props: {
|
|
99
100
|
url: string;
|
|
100
101
|
body: object;
|
|
102
|
+
headers?: Record<string, string>;
|
|
101
103
|
}) => Promise<Result<T, BaseError>>;
|
|
102
104
|
del: <T>(props: {
|
|
103
105
|
url: string;
|
|
106
|
+
headers?: Record<string, string>;
|
|
104
107
|
}) => Promise<Result<T, BaseError>>;
|
|
108
|
+
setHeader: (key: string, value: string) => void;
|
|
109
|
+
removeHeader: (key: string) => void;
|
|
105
110
|
}
|
|
106
111
|
interface IAuthContextOptions {
|
|
107
112
|
children: React.ReactNode;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -94,14 +94,19 @@ interface IAuthContext<U> {
|
|
|
94
94
|
refreshUser: (force?: boolean) => Promise<void>;
|
|
95
95
|
get: <T>(props: {
|
|
96
96
|
url: string;
|
|
97
|
+
headers?: Record<string, string>;
|
|
97
98
|
}) => Promise<Result<T, BaseError>>;
|
|
98
99
|
post: <T>(props: {
|
|
99
100
|
url: string;
|
|
100
101
|
body: object;
|
|
102
|
+
headers?: Record<string, string>;
|
|
101
103
|
}) => Promise<Result<T, BaseError>>;
|
|
102
104
|
del: <T>(props: {
|
|
103
105
|
url: string;
|
|
106
|
+
headers?: Record<string, string>;
|
|
104
107
|
}) => Promise<Result<T, BaseError>>;
|
|
108
|
+
setHeader: (key: string, value: string) => void;
|
|
109
|
+
removeHeader: (key: string) => void;
|
|
105
110
|
}
|
|
106
111
|
interface IAuthContextOptions {
|
|
107
112
|
children: React.ReactNode;
|
package/dist/client/index.js
CHANGED
|
@@ -124,6 +124,13 @@ var AuthContextProvider = ({
|
|
|
124
124
|
const [ready, setReady] = (0, import_react.useState)(false);
|
|
125
125
|
const [user, setUser] = (0, import_react.useState)({});
|
|
126
126
|
const router = (0, import_navigation.useRouter)();
|
|
127
|
+
const customHeaders = (0, import_react.useRef)({});
|
|
128
|
+
const setHeader = (0, import_react.useCallback)((key, value) => {
|
|
129
|
+
customHeaders.current[key] = value;
|
|
130
|
+
}, []);
|
|
131
|
+
const removeHeader = (0, import_react.useCallback)((key) => {
|
|
132
|
+
delete customHeaders.current[key];
|
|
133
|
+
}, []);
|
|
127
134
|
const loginRoute = routePrefix + "/login";
|
|
128
135
|
const registerRoute = routePrefix + "/register";
|
|
129
136
|
const logoutRoute = routePrefix + "/logout";
|
|
@@ -194,24 +201,35 @@ var AuthContextProvider = ({
|
|
|
194
201
|
router.refresh();
|
|
195
202
|
};
|
|
196
203
|
const get = async ({
|
|
197
|
-
url
|
|
204
|
+
url,
|
|
205
|
+
headers
|
|
198
206
|
}) => {
|
|
199
|
-
return clientSideFetch({
|
|
207
|
+
return clientSideFetch({
|
|
208
|
+
url: `${proxyRoute}${url}`,
|
|
209
|
+
headers: { ...customHeaders.current, ...headers }
|
|
210
|
+
});
|
|
200
211
|
};
|
|
201
212
|
const post = async ({
|
|
202
213
|
url,
|
|
203
|
-
body
|
|
214
|
+
body,
|
|
215
|
+
headers
|
|
204
216
|
}) => {
|
|
205
217
|
return clientSideFetch({
|
|
206
218
|
method: "POST",
|
|
207
219
|
url: `${proxyRoute}${url}`,
|
|
208
|
-
body
|
|
220
|
+
body,
|
|
221
|
+
headers: { ...customHeaders.current, ...headers }
|
|
209
222
|
});
|
|
210
223
|
};
|
|
211
224
|
const del = async ({
|
|
212
|
-
url
|
|
225
|
+
url,
|
|
226
|
+
headers
|
|
213
227
|
}) => {
|
|
214
|
-
return clientSideFetch({
|
|
228
|
+
return clientSideFetch({
|
|
229
|
+
method: "DELETE",
|
|
230
|
+
url: `${proxyRoute}${url}`,
|
|
231
|
+
headers: { ...customHeaders.current, ...headers }
|
|
232
|
+
});
|
|
215
233
|
};
|
|
216
234
|
(0, import_react.useEffect)(() => {
|
|
217
235
|
if (!ready) refreshUser();
|
|
@@ -227,7 +245,9 @@ var AuthContextProvider = ({
|
|
|
227
245
|
oAuth,
|
|
228
246
|
get,
|
|
229
247
|
post,
|
|
230
|
-
del
|
|
248
|
+
del,
|
|
249
|
+
setHeader,
|
|
250
|
+
removeHeader
|
|
231
251
|
/* upload, */
|
|
232
252
|
};
|
|
233
253
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AuthContext.Provider, { value: provider, children });
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/index.ts","../../src/client/contextProvider.tsx","../../src/types/response.ts","../../src/client/request.ts","../../src/client/Logout.tsx","../../src/client/useAuth.ts","../../src/client/useAPIWrapper.tsx"],"sourcesContent":["import AuthContextProvider from \"./contextProvider\";\nimport LogoutComponent from \"./Logout\";\n\nexport const AuthProvider = AuthContextProvider;\nexport const Logout = LogoutComponent;\n\nexport { useAuth } from \"./useAuth\";\nexport { APIWrapper } from \"./useAPIWrapper\";\n","\"use client\";\n\nimport { createContext, useEffect, useState } from \"react\";\nimport {\n IAuthContext,\n IAuthContextOptions,\n ILoginData,\n IOAuthData,\n IRegisterData,\n} from \"../types/internal\";\n\nimport { type Result } from \"../types/response\";\n\nimport { clientSideFetch } from \"./request\";\n\nimport { useRouter } from \"next/navigation\";\nimport { BaseError } from \"../types/response\";\n/* +\n Frontend context for providing login, logout, register and refresh\n 'use client' needed for using -> client components only\n server components need to use useSession()\n*/\n\nexport const AuthContext = createContext<IAuthContext<any>>(null as any);\n\nconst AuthContextProvider = <IU,>({\n children,\n routePrefix = \"/api\",\n}: IAuthContextOptions) => {\n const [isLoggedIn, setIsLoggedIn] = useState(false);\n const [ready, setReady] = useState(false);\n const [user, setUser] = useState<IU | {}>({});\n\n const router = useRouter();\n\n // POST /session/login\n const loginRoute = routePrefix + \"/login\";\n\n // POST /session/refresh\n const registerRoute = routePrefix + \"/register\";\n\n // GET /session/logout\n const logoutRoute = routePrefix + \"/logout\";\n\n // GET /session\n const userRoute = routePrefix + \"/user\";\n\n // GET /session/oauth/API_OAUTH_URL\n const oAuthRoute = routePrefix + \"/oauth\";\n\n // GET/POST/DELETE /proxy/URL\n const proxyRoute = routePrefix + \"/proxy\";\n\n const login = async (loginData: ILoginData): Promise<string> => {\n const res = await clientSideFetch({\n url: loginRoute,\n body: loginData.data,\n });\n if (res.isErr()) {\n return `${loginData.onErrorUrl || \"/\"}?error=${res.error.error}`;\n }\n\n setIsLoggedIn(true);\n\n return loginData.redirect || \"/\";\n };\n\n const register = async (registerData: IRegisterData): Promise<string> => {\n const res = await clientSideFetch<{ redirect: string }>({\n url: registerRoute,\n body: registerData.data,\n });\n\n if (res.isErr()) {\n return `${registerData.onErrorUrl || \"/\"}?error=${res.error.error}`;\n }\n return `${registerData.redirect || \"/\"}?success=true`;\n };\n\n const oAuth = async ({\n state,\n oAuthUrl,\n onErrorUrl,\n }: IOAuthData): Promise<string> => {\n const url = new URL(oAuthRoute, \"http://localhost/\");\n url.searchParams.set(\"authUrl\", oAuthUrl);\n\n if (state && state !== \"/\") url.searchParams.set(\"state\", state);\n\n const response = await clientSideFetch<string>({\n url: url.pathname + url.search,\n });\n if (response.isErr()) {\n return `${onErrorUrl || \"/\"}?error=${response.error.message}`;\n }\n\n return response.value.data;\n };\n\n const logout = async (): Promise<string> => {\n const data = await clientSideFetch<string>({\n url: logoutRoute,\n cache: \"no-store\",\n });\n\n if (data.isErr()) {\n return \"/\";\n }\n\n setIsLoggedIn(false);\n setUser({});\n\n return data.value.data;\n };\n\n const refreshUser = async (force?: boolean): Promise<void> => {\n const data = await clientSideFetch<IU>({\n url: `${userRoute}${force ? \"?force=true\" : \"\"}`,\n cache: \"no-store\",\n });\n\n if (data.isErr()) {\n setUser({});\n setIsLoggedIn(false);\n } else {\n setUser(data.value.data);\n }\n\n setReady(true);\n\n router.refresh();\n };\n\n const get = async <T,>({\n url,\n }: {\n url: string;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({ url: `${proxyRoute}${url}` });\n };\n\n const post = async <T,>({\n url,\n body,\n }: {\n url: string;\n body: object;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({\n method: \"POST\",\n url: `${proxyRoute}${url}`,\n body,\n });\n };\n\n const del = async <T,>({\n url,\n }: {\n url: string;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({ method: \"DELETE\", url: `${proxyRoute}${url}` });\n };\n\n /**\n * Can only be used if user is logged in! and already initialised\n * @param url url for upload\n * @param formData form data\n * @param setProgress progress dispatch\n * @returns object with data or error\n */\n /* const upload = async <T, U = any>(\n url: string,\n formName: string,\n files: File[],\n setProgress: (p: number) => void,\n ): Promise<TRequest<T, U>> => {\n try {\n if (!user.token) throw new Error('user not logged in')\n \n const res: Error | XMLHttpRequest = await new Promise(\n (resolve, reject) => {\n try {\n const xhr = new XMLHttpRequest()\n const formData = new FormData()\n \n for (let i = 0; i < files.length; i++)\n formData.append(formName, files[i])\n \n xhr.open('POST', `${API_URL}${url}`, true)\n xhr.setRequestHeader('Authorization', `Bearer ${user.token}`)\n xhr.upload.onprogress = (ev: ProgressEvent<EventTarget>) => {\n if (ev.lengthComputable) {\n const percentComplete = (ev.loaded / ev.total) * 100\n setProgress(percentComplete)\n }\n }\n \n xhr.onload = function () {\n console.log('break3?', this)\n return this.status === 200\n ? resolve(this)\n : reject(new Error('Error while uploading: ' + this.status))\n }\n \n xhr.onerror = (ev: ProgressEvent<EventTarget>) => {\n console.log('break2', ev.target)\n reject(ev)\n }\n \n xhr.send(formData)\n } catch (e) {\n console.log('break1', e)\n reject(e)\n }\n },\n )\n \n if (res instanceof Error) throw res\n \n const data: any = JSON.parse(res.responseText)\n \n if (data.error) throw new Error(data.data)\n \n return { data: data.data }\n } catch (e) {\n console.log('break4', e)\n return { error: e.message || 'upload error' }\n }\n }\n \n const getContext = () => {\n let context = user.id\n \n if (!user.id) {\n // if not logged in\n const cookie = document.cookie\n .split(';')\n .find((c) => c.includes('cycle_cid'))\n \n if (cookie) {\n // use cookie value\n context = cookie.split('=')[1]\n } else {\n // set new cookie\n context =\n Math.random().toString(36).substring(2, 15) +\n Math.random().toString(36).substring(2, 15)\n \n document.cookie = `cycle_cid=${context};path=/;max-age=31536000`\n }\n }\n \n return context\n }\n \n const event = async (name: string, value?: any): Promise<boolean> => {\n try {\n const contextId = getContext()\n \n const r = await fetch(NOFY_API_URL + '/event', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: 'Basic ' + ANALYTICS_KEY,\n },\n body: JSON.stringify({\n source: IS_PROD ? 'cycle-frontend' : 'cycle-frontend-dev',\n contextId,\n name,\n value,\n }),\n })\n \n const res = await r.json()\n if (res.error) throw new Error(res.error)\n \n return true\n } catch (e) {\n console.log('#> event error: ', e)\n return false\n }\n }\n \n const pageView = async (url: string, referer?: string): Promise<boolean> => {\n try {\n const contextId = getContext()\n \n const r = await fetch(NOFY_API_URL + '/log/count', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: 'Basic ' + ANALYTICS_KEY,\n },\n body: JSON.stringify({\n meta: {\n contextId,\n type: 'pageview',\n },\n referer,\n identifier: url,\n }),\n })\n \n const res = await r.json()\n if (res.error) throw new Error(res.error)\n \n return true\n } catch (e) {\n console.log('#> event error: ', e)\n return false\n }\n } */\n\n useEffect(() => {\n if (!ready) refreshUser();\n }, []);\n\n const provider = {\n ready,\n login,\n register,\n logout,\n isLoggedIn,\n refreshUser,\n user,\n oAuth,\n get,\n post,\n del,\n /* upload, */\n };\n\n return (\n <AuthContext.Provider value={provider}>{children}</AuthContext.Provider>\n );\n};\n\nexport default AuthContextProvider;\n","export type BaseError = {\n error: string;\n message: string;\n};\n\nexport type Success<S> = {\n data: S;\n};\n\n/**\n * A Result type that represents either a successful value (Ok) or an error (Err).\n * This is a discriminated union type that helps handle errors in a type-safe way.\n *\n * @template T - The type of the successful value\n * @template E - The type of the error, must extend BaseError\n */\nexport type Result<T, E extends BaseError> = Ok<T, E> | Err<T, E>;\n\ntype IResult<T, E extends BaseError> = {\n /**\n * Checks if the `Result` is an `Ok` instance\n */\n isOk: () => this is Ok<T, E>;\n\n /**\n * Checks if the `Result` is an `Err` instance.\n */\n isErr: () => this is Err<T, E>;\n};\n\n/**\n * Represents a successful `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Ok<T, E extends BaseError> implements IResult<T, E> {\n value: Success<T>;\n\n constructor(value: Success<T>) {\n this.value = value;\n }\n\n isOk(): this is Ok<T, E> {\n return true;\n }\n\n isErr(): this is Err<T, E> {\n return false;\n }\n}\n\n/**\n * Represents an error `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Err<T, E extends BaseError> implements IResult<T, E> {\n error: E;\n\n constructor(error: E) {\n this.error = error;\n }\n\n isOk(): this is Ok<T, E> {\n return false;\n }\n\n isErr(): this is Err<T, E> {\n return true;\n }\n}\n\n/**\n * Creates a new successful `Result` (i.e., an instance of `Ok`).\n *\n * @template T - The type of the successful value\n * @param value - The successful value\n * @returns A new `Ok` instance.\n */\nexport function ok<const T>(value: Success<T>): Result<T, never> {\n return new Ok(value);\n}\n\n/**\n * Creates a new error `Result` (i.e., an instance of `Err`).\n *\n * @template E - The type of the error, must extend `BaseError`.\n * @param error - The error value.\n * @returns A new `Err` instance.\n */\nexport function err<const E extends BaseError>(error: E): Result<never, E> {\n return new Err(error);\n}\n","import { BaseError, err, ok, Result, Success } from \"../types/response\";\n\nexport const clientSideFetch = async <T>({\n url,\n method = \"GET\",\n body,\n ...options\n}: {\n url: string;\n method?: \"GET\" | \"POST\" | \"DELETE\";\n body?: object;\n} & Omit<RequestInit, \"body\" | \"method\">): Promise<Result<T, BaseError>> => {\n const opts: RequestInit = {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n ...options,\n };\n\n try {\n if (body) opts.body = JSON.stringify(body);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse request body as JSON\",\n });\n }\n\n try {\n const response: Response = await fetch(url, opts);\n if (!response.ok) {\n return err({\n error: \"RESPONSE_ERROR\",\n message: `HTTP error! status: ${response.status}`,\n });\n }\n\n try {\n const res = await response.json();\n if (res.error !== undefined) {\n return err(res);\n }\n\n return ok(res as Success<T>);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse response as JSON\",\n });\n }\n } catch (e) {\n return err({\n error: \"FETCH_ERROR\",\n message: \"Failed to fetch the resource: \" + String(e),\n });\n }\n};\n","\"use client\";\n\nimport { useAuth } from \".\";\nimport { IChildProps } from \"../types/internal\";\n\nexport type LogoutProps = React.FC<React.ComponentProps<\"div\"> & IChildProps>;\n\nconst LogoutComponent: LogoutProps = ({ children, ...props }) => {\n const { logout } = useAuth();\n\n const handleLogout = async (e: any) => {\n e.preventDefault();\n await logout();\n };\n\n return (\n <div onClick={handleLogout} {...props}>\n {children}\n </div>\n );\n};\n\nexport default LogoutComponent;\n","import { useContext } from \"react\";\nimport { AuthContext } from \"./contextProvider\";\n\nexport const useAuth = () => {\n return useContext(AuthContext);\n};\n","import { useContext } from \"react\";\nimport { AuthContext } from \"./contextProvider\";\nimport { CallbackOptions, WrapperFunction } from \"../types\";\n\nexport const APIWrapper = <T,>(wrapperFunction: (cb: WrapperFunction) => T) => {\n return () => {\n const { get, post, del } = useContext(AuthContext);\n return wrapperFunction(async (data: CallbackOptions) => {\n const { method, url, body } = data;\n\n switch (method) {\n case \"GET\":\n return get({ url });\n case \"POST\":\n return post({ url, body: body ?? {} });\n case \"DELETE\":\n return del({ url });\n default:\n throw new Error(\"Unsupported method type\");\n }\n });\n };\n};\n"],"mappings":"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","names":["import_jsx_runtime","import_react","import_react"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/index.ts","../../src/client/contextProvider.tsx","../../src/types/response.ts","../../src/client/request.ts","../../src/client/Logout.tsx","../../src/client/useAuth.ts","../../src/client/useAPIWrapper.tsx"],"sourcesContent":["import AuthContextProvider from \"./contextProvider\";\nimport LogoutComponent from \"./Logout\";\n\nexport const AuthProvider = AuthContextProvider;\nexport const Logout = LogoutComponent;\n\nexport { useAuth } from \"./useAuth\";\nexport { APIWrapper } from \"./useAPIWrapper\";\n","\"use client\";\n\nimport { createContext, useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n IAuthContext,\n IAuthContextOptions,\n ILoginData,\n IOAuthData,\n IRegisterData,\n} from \"../types/internal\";\n\nimport { type Result } from \"../types/response\";\n\nimport { clientSideFetch } from \"./request\";\n\nimport { useRouter } from \"next/navigation\";\nimport { BaseError } from \"../types/response\";\n/* +\n Frontend context for providing login, logout, register and refresh\n 'use client' needed for using -> client components only\n server components need to use useSession()\n*/\n\nexport const AuthContext = createContext<IAuthContext<any>>(null as any);\n\nconst AuthContextProvider = <IU,>({\n children,\n routePrefix = \"/api\",\n}: IAuthContextOptions) => {\n const [isLoggedIn, setIsLoggedIn] = useState(false);\n const [ready, setReady] = useState(false);\n const [user, setUser] = useState<IU | {}>({});\n\n const router = useRouter();\n const customHeaders = useRef<Record<string, string>>({});\n\n const setHeader = useCallback((key: string, value: string) => {\n customHeaders.current[key] = value;\n }, []);\n\n const removeHeader = useCallback((key: string) => {\n delete customHeaders.current[key];\n }, []);\n\n // POST /session/login\n const loginRoute = routePrefix + \"/login\";\n\n // POST /session/refresh\n const registerRoute = routePrefix + \"/register\";\n\n // GET /session/logout\n const logoutRoute = routePrefix + \"/logout\";\n\n // GET /session\n const userRoute = routePrefix + \"/user\";\n\n // GET /session/oauth/API_OAUTH_URL\n const oAuthRoute = routePrefix + \"/oauth\";\n\n // GET/POST/DELETE /proxy/URL\n const proxyRoute = routePrefix + \"/proxy\";\n\n const login = async (loginData: ILoginData): Promise<string> => {\n const res = await clientSideFetch({\n url: loginRoute,\n body: loginData.data,\n });\n if (res.isErr()) {\n return `${loginData.onErrorUrl || \"/\"}?error=${res.error.error}`;\n }\n\n setIsLoggedIn(true);\n\n return loginData.redirect || \"/\";\n };\n\n const register = async (registerData: IRegisterData): Promise<string> => {\n const res = await clientSideFetch<{ redirect: string }>({\n url: registerRoute,\n body: registerData.data,\n });\n\n if (res.isErr()) {\n return `${registerData.onErrorUrl || \"/\"}?error=${res.error.error}`;\n }\n return `${registerData.redirect || \"/\"}?success=true`;\n };\n\n const oAuth = async ({\n state,\n oAuthUrl,\n onErrorUrl,\n }: IOAuthData): Promise<string> => {\n const url = new URL(oAuthRoute, \"http://localhost/\");\n url.searchParams.set(\"authUrl\", oAuthUrl);\n\n if (state && state !== \"/\") url.searchParams.set(\"state\", state);\n\n const response = await clientSideFetch<string>({\n url: url.pathname + url.search,\n });\n if (response.isErr()) {\n return `${onErrorUrl || \"/\"}?error=${response.error.message}`;\n }\n\n return response.value.data;\n };\n\n const logout = async (): Promise<string> => {\n const data = await clientSideFetch<string>({\n url: logoutRoute,\n cache: \"no-store\",\n });\n\n if (data.isErr()) {\n return \"/\";\n }\n\n setIsLoggedIn(false);\n setUser({});\n\n return data.value.data;\n };\n\n const refreshUser = async (force?: boolean): Promise<void> => {\n const data = await clientSideFetch<IU>({\n url: `${userRoute}${force ? \"?force=true\" : \"\"}`,\n cache: \"no-store\",\n });\n\n if (data.isErr()) {\n setUser({});\n setIsLoggedIn(false);\n } else {\n setUser(data.value.data);\n }\n\n setReady(true);\n\n router.refresh();\n };\n\n const get = async <T,>({\n url,\n headers,\n }: {\n url: string;\n headers?: Record<string, string>;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({\n url: `${proxyRoute}${url}`,\n headers: { ...customHeaders.current, ...headers },\n });\n };\n\n const post = async <T,>({\n url,\n body,\n headers,\n }: {\n url: string;\n body: object;\n headers?: Record<string, string>;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({\n method: \"POST\",\n url: `${proxyRoute}${url}`,\n body,\n headers: { ...customHeaders.current, ...headers },\n });\n };\n\n const del = async <T,>({\n url,\n headers,\n }: {\n url: string;\n headers?: Record<string, string>;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({\n method: \"DELETE\",\n url: `${proxyRoute}${url}`,\n headers: { ...customHeaders.current, ...headers },\n });\n };\n\n /**\n * Can only be used if user is logged in! and already initialised\n * @param url url for upload\n * @param formData form data\n * @param setProgress progress dispatch\n * @returns object with data or error\n */\n /* const upload = async <T, U = any>(\n url: string,\n formName: string,\n files: File[],\n setProgress: (p: number) => void,\n ): Promise<TRequest<T, U>> => {\n try {\n if (!user.token) throw new Error('user not logged in')\n \n const res: Error | XMLHttpRequest = await new Promise(\n (resolve, reject) => {\n try {\n const xhr = new XMLHttpRequest()\n const formData = new FormData()\n \n for (let i = 0; i < files.length; i++)\n formData.append(formName, files[i])\n \n xhr.open('POST', `${API_URL}${url}`, true)\n xhr.setRequestHeader('Authorization', `Bearer ${user.token}`)\n xhr.upload.onprogress = (ev: ProgressEvent<EventTarget>) => {\n if (ev.lengthComputable) {\n const percentComplete = (ev.loaded / ev.total) * 100\n setProgress(percentComplete)\n }\n }\n \n xhr.onload = function () {\n console.log('break3?', this)\n return this.status === 200\n ? resolve(this)\n : reject(new Error('Error while uploading: ' + this.status))\n }\n \n xhr.onerror = (ev: ProgressEvent<EventTarget>) => {\n console.log('break2', ev.target)\n reject(ev)\n }\n \n xhr.send(formData)\n } catch (e) {\n console.log('break1', e)\n reject(e)\n }\n },\n )\n \n if (res instanceof Error) throw res\n \n const data: any = JSON.parse(res.responseText)\n \n if (data.error) throw new Error(data.data)\n \n return { data: data.data }\n } catch (e) {\n console.log('break4', e)\n return { error: e.message || 'upload error' }\n }\n }\n \n const getContext = () => {\n let context = user.id\n \n if (!user.id) {\n // if not logged in\n const cookie = document.cookie\n .split(';')\n .find((c) => c.includes('cycle_cid'))\n \n if (cookie) {\n // use cookie value\n context = cookie.split('=')[1]\n } else {\n // set new cookie\n context =\n Math.random().toString(36).substring(2, 15) +\n Math.random().toString(36).substring(2, 15)\n \n document.cookie = `cycle_cid=${context};path=/;max-age=31536000`\n }\n }\n \n return context\n }\n \n const event = async (name: string, value?: any): Promise<boolean> => {\n try {\n const contextId = getContext()\n \n const r = await fetch(NOFY_API_URL + '/event', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: 'Basic ' + ANALYTICS_KEY,\n },\n body: JSON.stringify({\n source: IS_PROD ? 'cycle-frontend' : 'cycle-frontend-dev',\n contextId,\n name,\n value,\n }),\n })\n \n const res = await r.json()\n if (res.error) throw new Error(res.error)\n \n return true\n } catch (e) {\n console.log('#> event error: ', e)\n return false\n }\n }\n \n const pageView = async (url: string, referer?: string): Promise<boolean> => {\n try {\n const contextId = getContext()\n \n const r = await fetch(NOFY_API_URL + '/log/count', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: 'Basic ' + ANALYTICS_KEY,\n },\n body: JSON.stringify({\n meta: {\n contextId,\n type: 'pageview',\n },\n referer,\n identifier: url,\n }),\n })\n \n const res = await r.json()\n if (res.error) throw new Error(res.error)\n \n return true\n } catch (e) {\n console.log('#> event error: ', e)\n return false\n }\n } */\n\n useEffect(() => {\n if (!ready) refreshUser();\n }, []);\n\n const provider = {\n ready,\n login,\n register,\n logout,\n isLoggedIn,\n refreshUser,\n user,\n oAuth,\n get,\n post,\n del,\n setHeader,\n removeHeader,\n /* upload, */\n };\n\n return (\n <AuthContext.Provider value={provider}>{children}</AuthContext.Provider>\n );\n};\n\nexport default AuthContextProvider;\n","export type BaseError = {\n error: string;\n message: string;\n};\n\nexport type Success<S> = {\n data: S;\n};\n\n/**\n * A Result type that represents either a successful value (Ok) or an error (Err).\n * This is a discriminated union type that helps handle errors in a type-safe way.\n *\n * @template T - The type of the successful value\n * @template E - The type of the error, must extend BaseError\n */\nexport type Result<T, E extends BaseError> = Ok<T, E> | Err<T, E>;\n\ntype IResult<T, E extends BaseError> = {\n /**\n * Checks if the `Result` is an `Ok` instance\n */\n isOk: () => this is Ok<T, E>;\n\n /**\n * Checks if the `Result` is an `Err` instance.\n */\n isErr: () => this is Err<T, E>;\n};\n\n/**\n * Represents a successful `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Ok<T, E extends BaseError> implements IResult<T, E> {\n value: Success<T>;\n\n constructor(value: Success<T>) {\n this.value = value;\n }\n\n isOk(): this is Ok<T, E> {\n return true;\n }\n\n isErr(): this is Err<T, E> {\n return false;\n }\n}\n\n/**\n * Represents an error `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Err<T, E extends BaseError> implements IResult<T, E> {\n error: E;\n\n constructor(error: E) {\n this.error = error;\n }\n\n isOk(): this is Ok<T, E> {\n return false;\n }\n\n isErr(): this is Err<T, E> {\n return true;\n }\n}\n\n/**\n * Creates a new successful `Result` (i.e., an instance of `Ok`).\n *\n * @template T - The type of the successful value\n * @param value - The successful value\n * @returns A new `Ok` instance.\n */\nexport function ok<const T>(value: Success<T>): Result<T, never> {\n return new Ok(value);\n}\n\n/**\n * Creates a new error `Result` (i.e., an instance of `Err`).\n *\n * @template E - The type of the error, must extend `BaseError`.\n * @param error - The error value.\n * @returns A new `Err` instance.\n */\nexport function err<const E extends BaseError>(error: E): Result<never, E> {\n return new Err(error);\n}\n","import { BaseError, err, ok, Result, Success } from \"../types/response\";\n\nexport const clientSideFetch = async <T>({\n url,\n method = \"GET\",\n body,\n ...options\n}: {\n url: string;\n method?: \"GET\" | \"POST\" | \"DELETE\";\n body?: object;\n} & Omit<RequestInit, \"body\" | \"method\">): Promise<Result<T, BaseError>> => {\n const opts: RequestInit = {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n ...options,\n };\n\n try {\n if (body) opts.body = JSON.stringify(body);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse request body as JSON\",\n });\n }\n\n try {\n const response: Response = await fetch(url, opts);\n if (!response.ok) {\n return err({\n error: \"RESPONSE_ERROR\",\n message: `HTTP error! status: ${response.status}`,\n });\n }\n\n try {\n const res = await response.json();\n if (res.error !== undefined) {\n return err(res);\n }\n\n return ok(res as Success<T>);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse response as JSON\",\n });\n }\n } catch (e) {\n return err({\n error: \"FETCH_ERROR\",\n message: \"Failed to fetch the resource: \" + String(e),\n });\n }\n};\n","\"use client\";\n\nimport { useAuth } from \".\";\nimport { IChildProps } from \"../types/internal\";\n\nexport type LogoutProps = React.FC<React.ComponentProps<\"div\"> & IChildProps>;\n\nconst LogoutComponent: LogoutProps = ({ children, ...props }) => {\n const { logout } = useAuth();\n\n const handleLogout = async (e: any) => {\n e.preventDefault();\n await logout();\n };\n\n return (\n <div onClick={handleLogout} {...props}>\n {children}\n </div>\n );\n};\n\nexport default LogoutComponent;\n","import { useContext } from \"react\";\nimport { AuthContext } from \"./contextProvider\";\n\nexport const useAuth = () => {\n return useContext(AuthContext);\n};\n","import { useContext } from \"react\";\nimport { AuthContext } from \"./contextProvider\";\nimport { CallbackOptions, WrapperFunction } from \"../types\";\n\nexport const APIWrapper = <T,>(wrapperFunction: (cb: WrapperFunction) => T) => {\n return () => {\n const { get, post, del } = useContext(AuthContext);\n return wrapperFunction(async (data: CallbackOptions) => {\n const { method, url, body } = data;\n\n switch (method) {\n case \"GET\":\n return get({ url });\n case \"POST\":\n return post({ url, body: body ?? {} });\n case \"DELETE\":\n return del({ url });\n default:\n throw new Error(\"Unsupported method type\");\n }\n });\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,mBAAwE;;;ACkCjE,IAAM,KAAN,MAA0D;AAAA,EAG/D,YAAY,OAAmB;AAC7B,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,OAAyB;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,QAA2B;AACzB,WAAO;AAAA,EACT;AACF;AAQO,IAAM,MAAN,MAA2D;AAAA,EAGhE,YAAY,OAAU;AACpB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,OAAyB;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,QAA2B;AACzB,WAAO;AAAA,EACT;AACF;AASO,SAAS,GAAY,OAAqC;AAC/D,SAAO,IAAI,GAAG,KAAK;AACrB;AASO,SAAS,IAA+B,OAA4B;AACzE,SAAO,IAAI,IAAI,KAAK;AACtB;;;AC5FO,IAAM,kBAAkB,OAAU;AAAA,EACvC;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA,GAAG;AACL,MAI4E;AAC1E,QAAM,OAAoB;AAAA,IACxB;AAAA,IACA,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,GAAG,QAAQ;AAAA,IACb;AAAA,IACA,GAAG;AAAA,EACL;AAEA,MAAI;AACF,QAAI,KAAM,MAAK,OAAO,KAAK,UAAU,IAAI;AAAA,EAC3C,SAAS,GAAG;AACV,WAAO,IAAI;AAAA,MACT,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,MAAI;AACF,UAAM,WAAqB,MAAM,MAAM,KAAK,IAAI;AAChD,QAAI,CAAC,SAAS,IAAI;AAChB,aAAO,IAAI;AAAA,QACT,OAAO;AAAA,QACP,SAAS,uBAAuB,SAAS,MAAM;AAAA,MACjD,CAAC;AAAA,IACH;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,SAAS,KAAK;AAChC,UAAI,IAAI,UAAU,QAAW;AAC3B,eAAO,IAAI,GAAG;AAAA,MAChB;AAEA,aAAO,GAAG,GAAiB;AAAA,IAC7B,SAAS,GAAG;AACV,aAAO,IAAI;AAAA,QACT,OAAO;AAAA,QACP,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF,SAAS,GAAG;AACV,WAAO,IAAI;AAAA,MACT,OAAO;AAAA,MACP,SAAS,mCAAmC,OAAO,CAAC;AAAA,IACtD,CAAC;AAAA,EACH;AACF;;;AF3CA,wBAA0B;AAuVtB;AA/UG,IAAM,kBAAc,4BAAiC,IAAW;AAEvE,IAAM,sBAAsB,CAAM;AAAA,EAChC;AAAA,EACA,cAAc;AAChB,MAA2B;AACzB,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAClD,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,KAAK;AACxC,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAkB,CAAC,CAAC;AAE5C,QAAM,aAAS,6BAAU;AACzB,QAAM,oBAAgB,qBAA+B,CAAC,CAAC;AAEvD,QAAM,gBAAY,0BAAY,CAAC,KAAa,UAAkB;AAC5D,kBAAc,QAAQ,GAAG,IAAI;AAAA,EAC/B,GAAG,CAAC,CAAC;AAEL,QAAM,mBAAe,0BAAY,CAAC,QAAgB;AAChD,WAAO,cAAc,QAAQ,GAAG;AAAA,EAClC,GAAG,CAAC,CAAC;AAGL,QAAM,aAAa,cAAc;AAGjC,QAAM,gBAAgB,cAAc;AAGpC,QAAM,cAAc,cAAc;AAGlC,QAAM,YAAY,cAAc;AAGhC,QAAM,aAAa,cAAc;AAGjC,QAAM,aAAa,cAAc;AAEjC,QAAM,QAAQ,OAAO,cAA2C;AAC9D,UAAM,MAAM,MAAM,gBAAgB;AAAA,MAChC,KAAK;AAAA,MACL,MAAM,UAAU;AAAA,IAClB,CAAC;AACD,QAAI,IAAI,MAAM,GAAG;AACf,aAAO,GAAG,UAAU,cAAc,GAAG,UAAU,IAAI,MAAM,KAAK;AAAA,IAChE;AAEA,kBAAc,IAAI;AAElB,WAAO,UAAU,YAAY;AAAA,EAC/B;AAEA,QAAM,WAAW,OAAO,iBAAiD;AACvE,UAAM,MAAM,MAAM,gBAAsC;AAAA,MACtD,KAAK;AAAA,MACL,MAAM,aAAa;AAAA,IACrB,CAAC;AAED,QAAI,IAAI,MAAM,GAAG;AACf,aAAO,GAAG,aAAa,cAAc,GAAG,UAAU,IAAI,MAAM,KAAK;AAAA,IACnE;AACA,WAAO,GAAG,aAAa,YAAY,GAAG;AAAA,EACxC;AAEA,QAAM,QAAQ,OAAO;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAAmC;AACjC,UAAM,MAAM,IAAI,IAAI,YAAY,mBAAmB;AACnD,QAAI,aAAa,IAAI,WAAW,QAAQ;AAExC,QAAI,SAAS,UAAU,IAAK,KAAI,aAAa,IAAI,SAAS,KAAK;AAE/D,UAAM,WAAW,MAAM,gBAAwB;AAAA,MAC7C,KAAK,IAAI,WAAW,IAAI;AAAA,IAC1B,CAAC;AACD,QAAI,SAAS,MAAM,GAAG;AACpB,aAAO,GAAG,cAAc,GAAG,UAAU,SAAS,MAAM,OAAO;AAAA,IAC7D;AAEA,WAAO,SAAS,MAAM;AAAA,EACxB;AAEA,QAAM,SAAS,YAA6B;AAC1C,UAAM,OAAO,MAAM,gBAAwB;AAAA,MACzC,KAAK;AAAA,MACL,OAAO;AAAA,IACT,CAAC;AAED,QAAI,KAAK,MAAM,GAAG;AAChB,aAAO;AAAA,IACT;AAEA,kBAAc,KAAK;AACnB,YAAQ,CAAC,CAAC;AAEV,WAAO,KAAK,MAAM;AAAA,EACpB;AAEA,QAAM,cAAc,OAAO,UAAmC;AAC5D,UAAM,OAAO,MAAM,gBAAoB;AAAA,MACrC,KAAK,GAAG,SAAS,GAAG,QAAQ,gBAAgB,EAAE;AAAA,MAC9C,OAAO;AAAA,IACT,CAAC;AAED,QAAI,KAAK,MAAM,GAAG;AAChB,cAAQ,CAAC,CAAC;AACV,oBAAc,KAAK;AAAA,IACrB,OAAO;AACL,cAAQ,KAAK,MAAM,IAAI;AAAA,IACzB;AAEA,aAAS,IAAI;AAEb,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,MAAM,OAAW;AAAA,IACrB;AAAA,IACA;AAAA,EACF,MAGqC;AACnC,WAAO,gBAAgB;AAAA,MACrB,KAAK,GAAG,UAAU,GAAG,GAAG;AAAA,MACxB,SAAS,EAAE,GAAG,cAAc,SAAS,GAAG,QAAQ;AAAA,IAClD,CAAC;AAAA,EACH;AAEA,QAAM,OAAO,OAAW;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAIqC;AACnC,WAAO,gBAAgB;AAAA,MACrB,QAAQ;AAAA,MACR,KAAK,GAAG,UAAU,GAAG,GAAG;AAAA,MACxB;AAAA,MACA,SAAS,EAAE,GAAG,cAAc,SAAS,GAAG,QAAQ;AAAA,IAClD,CAAC;AAAA,EACH;AAEA,QAAM,MAAM,OAAW;AAAA,IACrB;AAAA,IACA;AAAA,EACF,MAGqC;AACnC,WAAO,gBAAgB;AAAA,MACrB,QAAQ;AAAA,MACR,KAAK,GAAG,UAAU,GAAG,GAAG;AAAA,MACxB,SAAS,EAAE,GAAG,cAAc,SAAS,GAAG,QAAQ;AAAA,IAClD,CAAC;AAAA,EACH;AAwJA,8BAAU,MAAM;AACd,QAAI,CAAC,MAAO,aAAY;AAAA,EAC1B,GAAG,CAAC,CAAC;AAEL,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,EAEF;AAEA,SACE,4CAAC,YAAY,UAAZ,EAAqB,OAAO,UAAW,UAAS;AAErD;AAEA,IAAO,0BAAQ;;;AG1VX,IAAAA,sBAAA;AATJ,IAAM,kBAA+B,CAAC,EAAE,UAAU,GAAG,MAAM,MAAM;AAC/D,QAAM,EAAE,OAAO,IAAI,QAAQ;AAE3B,QAAM,eAAe,OAAO,MAAW;AACrC,MAAE,eAAe;AACjB,UAAM,OAAO;AAAA,EACf;AAEA,SACE,6CAAC,SAAI,SAAS,cAAe,GAAG,OAC7B,UACH;AAEJ;AAEA,IAAO,iBAAQ;;;ACtBf,IAAAC,gBAA2B;AAGpB,IAAM,UAAU,MAAM;AAC3B,aAAO,0BAAW,WAAW;AAC/B;;;ACLA,IAAAC,gBAA2B;AAIpB,IAAM,aAAa,CAAK,oBAAgD;AAC7E,SAAO,MAAM;AACX,UAAM,EAAE,KAAK,MAAM,IAAI,QAAI,0BAAW,WAAW;AACjD,WAAO,gBAAgB,OAAO,SAA0B;AACtD,YAAM,EAAE,QAAQ,KAAK,KAAK,IAAI;AAE9B,cAAQ,QAAQ;AAAA,QACd,KAAK;AACH,iBAAO,IAAI,EAAE,IAAI,CAAC;AAAA,QACpB,KAAK;AACH,iBAAO,KAAK,EAAE,KAAK,MAAM,sBAAQ,CAAC,EAAE,CAAC;AAAA,QACvC,KAAK;AACH,iBAAO,IAAI,EAAE,IAAI,CAAC;AAAA,QACpB;AACE,gBAAM,IAAI,MAAM,yBAAyB;AAAA,MAC7C;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;ANnBO,IAAM,eAAe;AACrB,IAAM,SAAS;","names":["import_jsx_runtime","import_react","import_react"]}
|
package/dist/client/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/client/contextProvider.tsx
|
|
4
|
-
import { createContext, useEffect, useState } from "react";
|
|
4
|
+
import { createContext, useCallback, useEffect, useRef, useState } from "react";
|
|
5
5
|
|
|
6
6
|
// src/types/response.ts
|
|
7
7
|
var Ok = class {
|
|
@@ -96,6 +96,13 @@ var AuthContextProvider = ({
|
|
|
96
96
|
const [ready, setReady] = useState(false);
|
|
97
97
|
const [user, setUser] = useState({});
|
|
98
98
|
const router = useRouter();
|
|
99
|
+
const customHeaders = useRef({});
|
|
100
|
+
const setHeader = useCallback((key, value) => {
|
|
101
|
+
customHeaders.current[key] = value;
|
|
102
|
+
}, []);
|
|
103
|
+
const removeHeader = useCallback((key) => {
|
|
104
|
+
delete customHeaders.current[key];
|
|
105
|
+
}, []);
|
|
99
106
|
const loginRoute = routePrefix + "/login";
|
|
100
107
|
const registerRoute = routePrefix + "/register";
|
|
101
108
|
const logoutRoute = routePrefix + "/logout";
|
|
@@ -166,24 +173,35 @@ var AuthContextProvider = ({
|
|
|
166
173
|
router.refresh();
|
|
167
174
|
};
|
|
168
175
|
const get = async ({
|
|
169
|
-
url
|
|
176
|
+
url,
|
|
177
|
+
headers
|
|
170
178
|
}) => {
|
|
171
|
-
return clientSideFetch({
|
|
179
|
+
return clientSideFetch({
|
|
180
|
+
url: `${proxyRoute}${url}`,
|
|
181
|
+
headers: { ...customHeaders.current, ...headers }
|
|
182
|
+
});
|
|
172
183
|
};
|
|
173
184
|
const post = async ({
|
|
174
185
|
url,
|
|
175
|
-
body
|
|
186
|
+
body,
|
|
187
|
+
headers
|
|
176
188
|
}) => {
|
|
177
189
|
return clientSideFetch({
|
|
178
190
|
method: "POST",
|
|
179
191
|
url: `${proxyRoute}${url}`,
|
|
180
|
-
body
|
|
192
|
+
body,
|
|
193
|
+
headers: { ...customHeaders.current, ...headers }
|
|
181
194
|
});
|
|
182
195
|
};
|
|
183
196
|
const del = async ({
|
|
184
|
-
url
|
|
197
|
+
url,
|
|
198
|
+
headers
|
|
185
199
|
}) => {
|
|
186
|
-
return clientSideFetch({
|
|
200
|
+
return clientSideFetch({
|
|
201
|
+
method: "DELETE",
|
|
202
|
+
url: `${proxyRoute}${url}`,
|
|
203
|
+
headers: { ...customHeaders.current, ...headers }
|
|
204
|
+
});
|
|
187
205
|
};
|
|
188
206
|
useEffect(() => {
|
|
189
207
|
if (!ready) refreshUser();
|
|
@@ -199,7 +217,9 @@ var AuthContextProvider = ({
|
|
|
199
217
|
oAuth,
|
|
200
218
|
get,
|
|
201
219
|
post,
|
|
202
|
-
del
|
|
220
|
+
del,
|
|
221
|
+
setHeader,
|
|
222
|
+
removeHeader
|
|
203
223
|
/* upload, */
|
|
204
224
|
};
|
|
205
225
|
return /* @__PURE__ */ jsx(AuthContext.Provider, { value: provider, children });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/contextProvider.tsx","../../src/types/response.ts","../../src/client/request.ts","../../src/client/Logout.tsx","../../src/client/useAuth.ts","../../src/client/useAPIWrapper.tsx","../../src/client/index.ts"],"sourcesContent":["\"use client\";\n\nimport { createContext, useEffect, useState } from \"react\";\nimport {\n IAuthContext,\n IAuthContextOptions,\n ILoginData,\n IOAuthData,\n IRegisterData,\n} from \"../types/internal\";\n\nimport { type Result } from \"../types/response\";\n\nimport { clientSideFetch } from \"./request\";\n\nimport { useRouter } from \"next/navigation\";\nimport { BaseError } from \"../types/response\";\n/* +\n Frontend context for providing login, logout, register and refresh\n 'use client' needed for using -> client components only\n server components need to use useSession()\n*/\n\nexport const AuthContext = createContext<IAuthContext<any>>(null as any);\n\nconst AuthContextProvider = <IU,>({\n children,\n routePrefix = \"/api\",\n}: IAuthContextOptions) => {\n const [isLoggedIn, setIsLoggedIn] = useState(false);\n const [ready, setReady] = useState(false);\n const [user, setUser] = useState<IU | {}>({});\n\n const router = useRouter();\n\n // POST /session/login\n const loginRoute = routePrefix + \"/login\";\n\n // POST /session/refresh\n const registerRoute = routePrefix + \"/register\";\n\n // GET /session/logout\n const logoutRoute = routePrefix + \"/logout\";\n\n // GET /session\n const userRoute = routePrefix + \"/user\";\n\n // GET /session/oauth/API_OAUTH_URL\n const oAuthRoute = routePrefix + \"/oauth\";\n\n // GET/POST/DELETE /proxy/URL\n const proxyRoute = routePrefix + \"/proxy\";\n\n const login = async (loginData: ILoginData): Promise<string> => {\n const res = await clientSideFetch({\n url: loginRoute,\n body: loginData.data,\n });\n if (res.isErr()) {\n return `${loginData.onErrorUrl || \"/\"}?error=${res.error.error}`;\n }\n\n setIsLoggedIn(true);\n\n return loginData.redirect || \"/\";\n };\n\n const register = async (registerData: IRegisterData): Promise<string> => {\n const res = await clientSideFetch<{ redirect: string }>({\n url: registerRoute,\n body: registerData.data,\n });\n\n if (res.isErr()) {\n return `${registerData.onErrorUrl || \"/\"}?error=${res.error.error}`;\n }\n return `${registerData.redirect || \"/\"}?success=true`;\n };\n\n const oAuth = async ({\n state,\n oAuthUrl,\n onErrorUrl,\n }: IOAuthData): Promise<string> => {\n const url = new URL(oAuthRoute, \"http://localhost/\");\n url.searchParams.set(\"authUrl\", oAuthUrl);\n\n if (state && state !== \"/\") url.searchParams.set(\"state\", state);\n\n const response = await clientSideFetch<string>({\n url: url.pathname + url.search,\n });\n if (response.isErr()) {\n return `${onErrorUrl || \"/\"}?error=${response.error.message}`;\n }\n\n return response.value.data;\n };\n\n const logout = async (): Promise<string> => {\n const data = await clientSideFetch<string>({\n url: logoutRoute,\n cache: \"no-store\",\n });\n\n if (data.isErr()) {\n return \"/\";\n }\n\n setIsLoggedIn(false);\n setUser({});\n\n return data.value.data;\n };\n\n const refreshUser = async (force?: boolean): Promise<void> => {\n const data = await clientSideFetch<IU>({\n url: `${userRoute}${force ? \"?force=true\" : \"\"}`,\n cache: \"no-store\",\n });\n\n if (data.isErr()) {\n setUser({});\n setIsLoggedIn(false);\n } else {\n setUser(data.value.data);\n }\n\n setReady(true);\n\n router.refresh();\n };\n\n const get = async <T,>({\n url,\n }: {\n url: string;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({ url: `${proxyRoute}${url}` });\n };\n\n const post = async <T,>({\n url,\n body,\n }: {\n url: string;\n body: object;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({\n method: \"POST\",\n url: `${proxyRoute}${url}`,\n body,\n });\n };\n\n const del = async <T,>({\n url,\n }: {\n url: string;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({ method: \"DELETE\", url: `${proxyRoute}${url}` });\n };\n\n /**\n * Can only be used if user is logged in! and already initialised\n * @param url url for upload\n * @param formData form data\n * @param setProgress progress dispatch\n * @returns object with data or error\n */\n /* const upload = async <T, U = any>(\n url: string,\n formName: string,\n files: File[],\n setProgress: (p: number) => void,\n ): Promise<TRequest<T, U>> => {\n try {\n if (!user.token) throw new Error('user not logged in')\n \n const res: Error | XMLHttpRequest = await new Promise(\n (resolve, reject) => {\n try {\n const xhr = new XMLHttpRequest()\n const formData = new FormData()\n \n for (let i = 0; i < files.length; i++)\n formData.append(formName, files[i])\n \n xhr.open('POST', `${API_URL}${url}`, true)\n xhr.setRequestHeader('Authorization', `Bearer ${user.token}`)\n xhr.upload.onprogress = (ev: ProgressEvent<EventTarget>) => {\n if (ev.lengthComputable) {\n const percentComplete = (ev.loaded / ev.total) * 100\n setProgress(percentComplete)\n }\n }\n \n xhr.onload = function () {\n console.log('break3?', this)\n return this.status === 200\n ? resolve(this)\n : reject(new Error('Error while uploading: ' + this.status))\n }\n \n xhr.onerror = (ev: ProgressEvent<EventTarget>) => {\n console.log('break2', ev.target)\n reject(ev)\n }\n \n xhr.send(formData)\n } catch (e) {\n console.log('break1', e)\n reject(e)\n }\n },\n )\n \n if (res instanceof Error) throw res\n \n const data: any = JSON.parse(res.responseText)\n \n if (data.error) throw new Error(data.data)\n \n return { data: data.data }\n } catch (e) {\n console.log('break4', e)\n return { error: e.message || 'upload error' }\n }\n }\n \n const getContext = () => {\n let context = user.id\n \n if (!user.id) {\n // if not logged in\n const cookie = document.cookie\n .split(';')\n .find((c) => c.includes('cycle_cid'))\n \n if (cookie) {\n // use cookie value\n context = cookie.split('=')[1]\n } else {\n // set new cookie\n context =\n Math.random().toString(36).substring(2, 15) +\n Math.random().toString(36).substring(2, 15)\n \n document.cookie = `cycle_cid=${context};path=/;max-age=31536000`\n }\n }\n \n return context\n }\n \n const event = async (name: string, value?: any): Promise<boolean> => {\n try {\n const contextId = getContext()\n \n const r = await fetch(NOFY_API_URL + '/event', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: 'Basic ' + ANALYTICS_KEY,\n },\n body: JSON.stringify({\n source: IS_PROD ? 'cycle-frontend' : 'cycle-frontend-dev',\n contextId,\n name,\n value,\n }),\n })\n \n const res = await r.json()\n if (res.error) throw new Error(res.error)\n \n return true\n } catch (e) {\n console.log('#> event error: ', e)\n return false\n }\n }\n \n const pageView = async (url: string, referer?: string): Promise<boolean> => {\n try {\n const contextId = getContext()\n \n const r = await fetch(NOFY_API_URL + '/log/count', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: 'Basic ' + ANALYTICS_KEY,\n },\n body: JSON.stringify({\n meta: {\n contextId,\n type: 'pageview',\n },\n referer,\n identifier: url,\n }),\n })\n \n const res = await r.json()\n if (res.error) throw new Error(res.error)\n \n return true\n } catch (e) {\n console.log('#> event error: ', e)\n return false\n }\n } */\n\n useEffect(() => {\n if (!ready) refreshUser();\n }, []);\n\n const provider = {\n ready,\n login,\n register,\n logout,\n isLoggedIn,\n refreshUser,\n user,\n oAuth,\n get,\n post,\n del,\n /* upload, */\n };\n\n return (\n <AuthContext.Provider value={provider}>{children}</AuthContext.Provider>\n );\n};\n\nexport default AuthContextProvider;\n","export type BaseError = {\n error: string;\n message: string;\n};\n\nexport type Success<S> = {\n data: S;\n};\n\n/**\n * A Result type that represents either a successful value (Ok) or an error (Err).\n * This is a discriminated union type that helps handle errors in a type-safe way.\n *\n * @template T - The type of the successful value\n * @template E - The type of the error, must extend BaseError\n */\nexport type Result<T, E extends BaseError> = Ok<T, E> | Err<T, E>;\n\ntype IResult<T, E extends BaseError> = {\n /**\n * Checks if the `Result` is an `Ok` instance\n */\n isOk: () => this is Ok<T, E>;\n\n /**\n * Checks if the `Result` is an `Err` instance.\n */\n isErr: () => this is Err<T, E>;\n};\n\n/**\n * Represents a successful `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Ok<T, E extends BaseError> implements IResult<T, E> {\n value: Success<T>;\n\n constructor(value: Success<T>) {\n this.value = value;\n }\n\n isOk(): this is Ok<T, E> {\n return true;\n }\n\n isErr(): this is Err<T, E> {\n return false;\n }\n}\n\n/**\n * Represents an error `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Err<T, E extends BaseError> implements IResult<T, E> {\n error: E;\n\n constructor(error: E) {\n this.error = error;\n }\n\n isOk(): this is Ok<T, E> {\n return false;\n }\n\n isErr(): this is Err<T, E> {\n return true;\n }\n}\n\n/**\n * Creates a new successful `Result` (i.e., an instance of `Ok`).\n *\n * @template T - The type of the successful value\n * @param value - The successful value\n * @returns A new `Ok` instance.\n */\nexport function ok<const T>(value: Success<T>): Result<T, never> {\n return new Ok(value);\n}\n\n/**\n * Creates a new error `Result` (i.e., an instance of `Err`).\n *\n * @template E - The type of the error, must extend `BaseError`.\n * @param error - The error value.\n * @returns A new `Err` instance.\n */\nexport function err<const E extends BaseError>(error: E): Result<never, E> {\n return new Err(error);\n}\n","import { BaseError, err, ok, Result, Success } from \"../types/response\";\n\nexport const clientSideFetch = async <T>({\n url,\n method = \"GET\",\n body,\n ...options\n}: {\n url: string;\n method?: \"GET\" | \"POST\" | \"DELETE\";\n body?: object;\n} & Omit<RequestInit, \"body\" | \"method\">): Promise<Result<T, BaseError>> => {\n const opts: RequestInit = {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n ...options,\n };\n\n try {\n if (body) opts.body = JSON.stringify(body);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse request body as JSON\",\n });\n }\n\n try {\n const response: Response = await fetch(url, opts);\n if (!response.ok) {\n return err({\n error: \"RESPONSE_ERROR\",\n message: `HTTP error! status: ${response.status}`,\n });\n }\n\n try {\n const res = await response.json();\n if (res.error !== undefined) {\n return err(res);\n }\n\n return ok(res as Success<T>);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse response as JSON\",\n });\n }\n } catch (e) {\n return err({\n error: \"FETCH_ERROR\",\n message: \"Failed to fetch the resource: \" + String(e),\n });\n }\n};\n","\"use client\";\n\nimport { useAuth } from \".\";\nimport { IChildProps } from \"../types/internal\";\n\nexport type LogoutProps = React.FC<React.ComponentProps<\"div\"> & IChildProps>;\n\nconst LogoutComponent: LogoutProps = ({ children, ...props }) => {\n const { logout } = useAuth();\n\n const handleLogout = async (e: any) => {\n e.preventDefault();\n await logout();\n };\n\n return (\n <div onClick={handleLogout} {...props}>\n {children}\n </div>\n );\n};\n\nexport default LogoutComponent;\n","import { useContext } from \"react\";\nimport { AuthContext } from \"./contextProvider\";\n\nexport const useAuth = () => {\n return useContext(AuthContext);\n};\n","import { useContext } from \"react\";\nimport { AuthContext } from \"./contextProvider\";\nimport { CallbackOptions, WrapperFunction } from \"../types\";\n\nexport const APIWrapper = <T,>(wrapperFunction: (cb: WrapperFunction) => T) => {\n return () => {\n const { get, post, del } = useContext(AuthContext);\n return wrapperFunction(async (data: CallbackOptions) => {\n const { method, url, body } = data;\n\n switch (method) {\n case \"GET\":\n return get({ url });\n case \"POST\":\n return post({ url, body: body ?? {} });\n case \"DELETE\":\n return del({ url });\n default:\n throw new Error(\"Unsupported method type\");\n }\n });\n };\n};\n","import AuthContextProvider from \"./contextProvider\";\nimport LogoutComponent from \"./Logout\";\n\nexport const AuthProvider = AuthContextProvider;\nexport const Logout = LogoutComponent;\n\nexport { useAuth } from \"./useAuth\";\nexport { APIWrapper } from \"./useAPIWrapper\";\n"],"mappings":"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","names":["jsx","useContext","useContext"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/contextProvider.tsx","../../src/types/response.ts","../../src/client/request.ts","../../src/client/Logout.tsx","../../src/client/useAuth.ts","../../src/client/useAPIWrapper.tsx","../../src/client/index.ts"],"sourcesContent":["\"use client\";\n\nimport { createContext, useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n IAuthContext,\n IAuthContextOptions,\n ILoginData,\n IOAuthData,\n IRegisterData,\n} from \"../types/internal\";\n\nimport { type Result } from \"../types/response\";\n\nimport { clientSideFetch } from \"./request\";\n\nimport { useRouter } from \"next/navigation\";\nimport { BaseError } from \"../types/response\";\n/* +\n Frontend context for providing login, logout, register and refresh\n 'use client' needed for using -> client components only\n server components need to use useSession()\n*/\n\nexport const AuthContext = createContext<IAuthContext<any>>(null as any);\n\nconst AuthContextProvider = <IU,>({\n children,\n routePrefix = \"/api\",\n}: IAuthContextOptions) => {\n const [isLoggedIn, setIsLoggedIn] = useState(false);\n const [ready, setReady] = useState(false);\n const [user, setUser] = useState<IU | {}>({});\n\n const router = useRouter();\n const customHeaders = useRef<Record<string, string>>({});\n\n const setHeader = useCallback((key: string, value: string) => {\n customHeaders.current[key] = value;\n }, []);\n\n const removeHeader = useCallback((key: string) => {\n delete customHeaders.current[key];\n }, []);\n\n // POST /session/login\n const loginRoute = routePrefix + \"/login\";\n\n // POST /session/refresh\n const registerRoute = routePrefix + \"/register\";\n\n // GET /session/logout\n const logoutRoute = routePrefix + \"/logout\";\n\n // GET /session\n const userRoute = routePrefix + \"/user\";\n\n // GET /session/oauth/API_OAUTH_URL\n const oAuthRoute = routePrefix + \"/oauth\";\n\n // GET/POST/DELETE /proxy/URL\n const proxyRoute = routePrefix + \"/proxy\";\n\n const login = async (loginData: ILoginData): Promise<string> => {\n const res = await clientSideFetch({\n url: loginRoute,\n body: loginData.data,\n });\n if (res.isErr()) {\n return `${loginData.onErrorUrl || \"/\"}?error=${res.error.error}`;\n }\n\n setIsLoggedIn(true);\n\n return loginData.redirect || \"/\";\n };\n\n const register = async (registerData: IRegisterData): Promise<string> => {\n const res = await clientSideFetch<{ redirect: string }>({\n url: registerRoute,\n body: registerData.data,\n });\n\n if (res.isErr()) {\n return `${registerData.onErrorUrl || \"/\"}?error=${res.error.error}`;\n }\n return `${registerData.redirect || \"/\"}?success=true`;\n };\n\n const oAuth = async ({\n state,\n oAuthUrl,\n onErrorUrl,\n }: IOAuthData): Promise<string> => {\n const url = new URL(oAuthRoute, \"http://localhost/\");\n url.searchParams.set(\"authUrl\", oAuthUrl);\n\n if (state && state !== \"/\") url.searchParams.set(\"state\", state);\n\n const response = await clientSideFetch<string>({\n url: url.pathname + url.search,\n });\n if (response.isErr()) {\n return `${onErrorUrl || \"/\"}?error=${response.error.message}`;\n }\n\n return response.value.data;\n };\n\n const logout = async (): Promise<string> => {\n const data = await clientSideFetch<string>({\n url: logoutRoute,\n cache: \"no-store\",\n });\n\n if (data.isErr()) {\n return \"/\";\n }\n\n setIsLoggedIn(false);\n setUser({});\n\n return data.value.data;\n };\n\n const refreshUser = async (force?: boolean): Promise<void> => {\n const data = await clientSideFetch<IU>({\n url: `${userRoute}${force ? \"?force=true\" : \"\"}`,\n cache: \"no-store\",\n });\n\n if (data.isErr()) {\n setUser({});\n setIsLoggedIn(false);\n } else {\n setUser(data.value.data);\n }\n\n setReady(true);\n\n router.refresh();\n };\n\n const get = async <T,>({\n url,\n headers,\n }: {\n url: string;\n headers?: Record<string, string>;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({\n url: `${proxyRoute}${url}`,\n headers: { ...customHeaders.current, ...headers },\n });\n };\n\n const post = async <T,>({\n url,\n body,\n headers,\n }: {\n url: string;\n body: object;\n headers?: Record<string, string>;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({\n method: \"POST\",\n url: `${proxyRoute}${url}`,\n body,\n headers: { ...customHeaders.current, ...headers },\n });\n };\n\n const del = async <T,>({\n url,\n headers,\n }: {\n url: string;\n headers?: Record<string, string>;\n }): Promise<Result<T, BaseError>> => {\n return clientSideFetch({\n method: \"DELETE\",\n url: `${proxyRoute}${url}`,\n headers: { ...customHeaders.current, ...headers },\n });\n };\n\n /**\n * Can only be used if user is logged in! and already initialised\n * @param url url for upload\n * @param formData form data\n * @param setProgress progress dispatch\n * @returns object with data or error\n */\n /* const upload = async <T, U = any>(\n url: string,\n formName: string,\n files: File[],\n setProgress: (p: number) => void,\n ): Promise<TRequest<T, U>> => {\n try {\n if (!user.token) throw new Error('user not logged in')\n \n const res: Error | XMLHttpRequest = await new Promise(\n (resolve, reject) => {\n try {\n const xhr = new XMLHttpRequest()\n const formData = new FormData()\n \n for (let i = 0; i < files.length; i++)\n formData.append(formName, files[i])\n \n xhr.open('POST', `${API_URL}${url}`, true)\n xhr.setRequestHeader('Authorization', `Bearer ${user.token}`)\n xhr.upload.onprogress = (ev: ProgressEvent<EventTarget>) => {\n if (ev.lengthComputable) {\n const percentComplete = (ev.loaded / ev.total) * 100\n setProgress(percentComplete)\n }\n }\n \n xhr.onload = function () {\n console.log('break3?', this)\n return this.status === 200\n ? resolve(this)\n : reject(new Error('Error while uploading: ' + this.status))\n }\n \n xhr.onerror = (ev: ProgressEvent<EventTarget>) => {\n console.log('break2', ev.target)\n reject(ev)\n }\n \n xhr.send(formData)\n } catch (e) {\n console.log('break1', e)\n reject(e)\n }\n },\n )\n \n if (res instanceof Error) throw res\n \n const data: any = JSON.parse(res.responseText)\n \n if (data.error) throw new Error(data.data)\n \n return { data: data.data }\n } catch (e) {\n console.log('break4', e)\n return { error: e.message || 'upload error' }\n }\n }\n \n const getContext = () => {\n let context = user.id\n \n if (!user.id) {\n // if not logged in\n const cookie = document.cookie\n .split(';')\n .find((c) => c.includes('cycle_cid'))\n \n if (cookie) {\n // use cookie value\n context = cookie.split('=')[1]\n } else {\n // set new cookie\n context =\n Math.random().toString(36).substring(2, 15) +\n Math.random().toString(36).substring(2, 15)\n \n document.cookie = `cycle_cid=${context};path=/;max-age=31536000`\n }\n }\n \n return context\n }\n \n const event = async (name: string, value?: any): Promise<boolean> => {\n try {\n const contextId = getContext()\n \n const r = await fetch(NOFY_API_URL + '/event', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: 'Basic ' + ANALYTICS_KEY,\n },\n body: JSON.stringify({\n source: IS_PROD ? 'cycle-frontend' : 'cycle-frontend-dev',\n contextId,\n name,\n value,\n }),\n })\n \n const res = await r.json()\n if (res.error) throw new Error(res.error)\n \n return true\n } catch (e) {\n console.log('#> event error: ', e)\n return false\n }\n }\n \n const pageView = async (url: string, referer?: string): Promise<boolean> => {\n try {\n const contextId = getContext()\n \n const r = await fetch(NOFY_API_URL + '/log/count', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: 'Basic ' + ANALYTICS_KEY,\n },\n body: JSON.stringify({\n meta: {\n contextId,\n type: 'pageview',\n },\n referer,\n identifier: url,\n }),\n })\n \n const res = await r.json()\n if (res.error) throw new Error(res.error)\n \n return true\n } catch (e) {\n console.log('#> event error: ', e)\n return false\n }\n } */\n\n useEffect(() => {\n if (!ready) refreshUser();\n }, []);\n\n const provider = {\n ready,\n login,\n register,\n logout,\n isLoggedIn,\n refreshUser,\n user,\n oAuth,\n get,\n post,\n del,\n setHeader,\n removeHeader,\n /* upload, */\n };\n\n return (\n <AuthContext.Provider value={provider}>{children}</AuthContext.Provider>\n );\n};\n\nexport default AuthContextProvider;\n","export type BaseError = {\n error: string;\n message: string;\n};\n\nexport type Success<S> = {\n data: S;\n};\n\n/**\n * A Result type that represents either a successful value (Ok) or an error (Err).\n * This is a discriminated union type that helps handle errors in a type-safe way.\n *\n * @template T - The type of the successful value\n * @template E - The type of the error, must extend BaseError\n */\nexport type Result<T, E extends BaseError> = Ok<T, E> | Err<T, E>;\n\ntype IResult<T, E extends BaseError> = {\n /**\n * Checks if the `Result` is an `Ok` instance\n */\n isOk: () => this is Ok<T, E>;\n\n /**\n * Checks if the `Result` is an `Err` instance.\n */\n isErr: () => this is Err<T, E>;\n};\n\n/**\n * Represents a successful `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Ok<T, E extends BaseError> implements IResult<T, E> {\n value: Success<T>;\n\n constructor(value: Success<T>) {\n this.value = value;\n }\n\n isOk(): this is Ok<T, E> {\n return true;\n }\n\n isErr(): this is Err<T, E> {\n return false;\n }\n}\n\n/**\n * Represents an error `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Err<T, E extends BaseError> implements IResult<T, E> {\n error: E;\n\n constructor(error: E) {\n this.error = error;\n }\n\n isOk(): this is Ok<T, E> {\n return false;\n }\n\n isErr(): this is Err<T, E> {\n return true;\n }\n}\n\n/**\n * Creates a new successful `Result` (i.e., an instance of `Ok`).\n *\n * @template T - The type of the successful value\n * @param value - The successful value\n * @returns A new `Ok` instance.\n */\nexport function ok<const T>(value: Success<T>): Result<T, never> {\n return new Ok(value);\n}\n\n/**\n * Creates a new error `Result` (i.e., an instance of `Err`).\n *\n * @template E - The type of the error, must extend `BaseError`.\n * @param error - The error value.\n * @returns A new `Err` instance.\n */\nexport function err<const E extends BaseError>(error: E): Result<never, E> {\n return new Err(error);\n}\n","import { BaseError, err, ok, Result, Success } from \"../types/response\";\n\nexport const clientSideFetch = async <T>({\n url,\n method = \"GET\",\n body,\n ...options\n}: {\n url: string;\n method?: \"GET\" | \"POST\" | \"DELETE\";\n body?: object;\n} & Omit<RequestInit, \"body\" | \"method\">): Promise<Result<T, BaseError>> => {\n const opts: RequestInit = {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n ...options,\n };\n\n try {\n if (body) opts.body = JSON.stringify(body);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse request body as JSON\",\n });\n }\n\n try {\n const response: Response = await fetch(url, opts);\n if (!response.ok) {\n return err({\n error: \"RESPONSE_ERROR\",\n message: `HTTP error! status: ${response.status}`,\n });\n }\n\n try {\n const res = await response.json();\n if (res.error !== undefined) {\n return err(res);\n }\n\n return ok(res as Success<T>);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse response as JSON\",\n });\n }\n } catch (e) {\n return err({\n error: \"FETCH_ERROR\",\n message: \"Failed to fetch the resource: \" + String(e),\n });\n }\n};\n","\"use client\";\n\nimport { useAuth } from \".\";\nimport { IChildProps } from \"../types/internal\";\n\nexport type LogoutProps = React.FC<React.ComponentProps<\"div\"> & IChildProps>;\n\nconst LogoutComponent: LogoutProps = ({ children, ...props }) => {\n const { logout } = useAuth();\n\n const handleLogout = async (e: any) => {\n e.preventDefault();\n await logout();\n };\n\n return (\n <div onClick={handleLogout} {...props}>\n {children}\n </div>\n );\n};\n\nexport default LogoutComponent;\n","import { useContext } from \"react\";\nimport { AuthContext } from \"./contextProvider\";\n\nexport const useAuth = () => {\n return useContext(AuthContext);\n};\n","import { useContext } from \"react\";\nimport { AuthContext } from \"./contextProvider\";\nimport { CallbackOptions, WrapperFunction } from \"../types\";\n\nexport const APIWrapper = <T,>(wrapperFunction: (cb: WrapperFunction) => T) => {\n return () => {\n const { get, post, del } = useContext(AuthContext);\n return wrapperFunction(async (data: CallbackOptions) => {\n const { method, url, body } = data;\n\n switch (method) {\n case \"GET\":\n return get({ url });\n case \"POST\":\n return post({ url, body: body ?? {} });\n case \"DELETE\":\n return del({ url });\n default:\n throw new Error(\"Unsupported method type\");\n }\n });\n };\n};\n","import AuthContextProvider from \"./contextProvider\";\nimport LogoutComponent from \"./Logout\";\n\nexport const AuthProvider = AuthContextProvider;\nexport const Logout = LogoutComponent;\n\nexport { useAuth } from \"./useAuth\";\nexport { APIWrapper } from \"./useAPIWrapper\";\n"],"mappings":"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","names":["jsx","useContext","useContext"]}
|
package/dist/index.js
CHANGED
|
@@ -121,8 +121,8 @@ var serverSideFetch = async ({
|
|
|
121
121
|
}
|
|
122
122
|
const opts = {
|
|
123
123
|
method,
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
...options,
|
|
125
|
+
headers
|
|
126
126
|
};
|
|
127
127
|
try {
|
|
128
128
|
if (body) opts.body = JSON.stringify(body);
|
|
@@ -380,11 +380,18 @@ async function refresh(request, config) {
|
|
|
380
380
|
}
|
|
381
381
|
async function proxyFunction(method, request, config, options) {
|
|
382
382
|
options.shift();
|
|
383
|
+
const forwardHeaders = {};
|
|
384
|
+
request.headers.forEach((value, key) => {
|
|
385
|
+
if (key.startsWith("x-") && key !== "x-forwarded-for" && key !== "x-forwarded-host" && key !== "x-forwarded-proto") {
|
|
386
|
+
forwardHeaders[key] = value;
|
|
387
|
+
}
|
|
388
|
+
});
|
|
383
389
|
const res = await serverSideFetch({
|
|
384
390
|
method,
|
|
385
391
|
url: `/${options.join("/")}${request.nextUrl.search}`,
|
|
386
392
|
body: method === "POST" ? await request.json() : void 0,
|
|
387
|
-
config
|
|
393
|
+
config,
|
|
394
|
+
headers: forwardHeaders
|
|
388
395
|
});
|
|
389
396
|
if (config.debug) console.log("#> proxyFunction", res);
|
|
390
397
|
if (res.isErr()) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types/response.ts","../src/server/api.ts","../src/server/session.ts","../src/server/fetch.ts","../src/server/index.ts","../src/server/wrapper.ts"],"sourcesContent":["export {\n type IConfig,\n type IRequestOptions,\n type Session,\n type WrapperFunction,\n} from \"./types\";\nexport {\n type Result,\n type BaseError,\n type Ok,\n type Err,\n ok,\n err,\n} from \"./types/response\";\n\nexport { CelestyaProxy } from \"./server\";\nexport { serverSideFetch, attemptTokenRefresh } from \"./server/fetch\";\nexport { serverAPIWrapper } from \"./server/wrapper\";\nexport { getSession } from \"./server/session\";\n","export type BaseError = {\n error: string;\n message: string;\n};\n\nexport type Success<S> = {\n data: S;\n};\n\n/**\n * A Result type that represents either a successful value (Ok) or an error (Err).\n * This is a discriminated union type that helps handle errors in a type-safe way.\n *\n * @template T - The type of the successful value\n * @template E - The type of the error, must extend BaseError\n */\nexport type Result<T, E extends BaseError> = Ok<T, E> | Err<T, E>;\n\ntype IResult<T, E extends BaseError> = {\n /**\n * Checks if the `Result` is an `Ok` instance\n */\n isOk: () => this is Ok<T, E>;\n\n /**\n * Checks if the `Result` is an `Err` instance.\n */\n isErr: () => this is Err<T, E>;\n};\n\n/**\n * Represents a successful `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Ok<T, E extends BaseError> implements IResult<T, E> {\n value: Success<T>;\n\n constructor(value: Success<T>) {\n this.value = value;\n }\n\n isOk(): this is Ok<T, E> {\n return true;\n }\n\n isErr(): this is Err<T, E> {\n return false;\n }\n}\n\n/**\n * Represents an error `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Err<T, E extends BaseError> implements IResult<T, E> {\n error: E;\n\n constructor(error: E) {\n this.error = error;\n }\n\n isOk(): this is Ok<T, E> {\n return false;\n }\n\n isErr(): this is Err<T, E> {\n return true;\n }\n}\n\n/**\n * Creates a new successful `Result` (i.e., an instance of `Ok`).\n *\n * @template T - The type of the successful value\n * @param value - The successful value\n * @returns A new `Ok` instance.\n */\nexport function ok<const T>(value: Success<T>): Result<T, never> {\n return new Ok(value);\n}\n\n/**\n * Creates a new error `Result` (i.e., an instance of `Err`).\n *\n * @template E - The type of the error, must extend `BaseError`.\n * @param error - The error value.\n * @returns A new `Err` instance.\n */\nexport function err<const E extends BaseError>(error: E): Result<never, E> {\n return new Err(error);\n}\n","import { NextRequest } from \"next/server\";\nimport { revalidatePath } from \"next/cache\";\nimport { jwtDecode } from \"jwt-decode\";\nimport { IConfig, IRequestOptions, RouteHandler } from \"../types\";\nimport { DefaultUser, getSession } from \"./session\";\nimport { serverSideFetch, attemptTokenRefresh } from \"./fetch\";\n\nconst rHandler: RouteHandler = {\n GET: {\n user: ({ request, config }) => getUser(request, config),\n refresh: ({ request, config }) => refresh(request, config),\n logout: ({ config }) => logout(config),\n debug: () => debug(),\n oauth: ({ request, config }) => oauth(request, config),\n oauth_callback: ({ request, config }) => oauth_callback(request, config),\n proxy: ({ request, config, options }) =>\n proxyFunction(\"GET\", request, config, options),\n },\n POST: {\n login: ({ request, config }) => login(request, config),\n proxy: ({ request, config, options }) =>\n proxyFunction(\"POST\", request, config, options),\n },\n DELETE: {\n proxy: ({ request, config, options }) =>\n proxyFunction(\"DELETE\", request, config, options),\n },\n};\n\nexport async function Proxy(\n method: string,\n request: NextRequest,\n options: IRequestOptions,\n config: IConfig\n) {\n try {\n const params = await options.params;\n const parameters = {\n request,\n path:\n params.endpoint[0] ||\n request.nextUrl.pathname.replace(config.route, \"\"),\n options: params.endpoint,\n config,\n };\n\n if (config.debug) console.log(\"#> proxy:\", parameters);\n\n if (rHandler[method][parameters.path])\n return await rHandler[method][parameters.path](parameters);\n\n return Response.json({\n error: \"INVALID_ENDPOINT\",\n message: \"the provided endpoint is not valid\",\n });\n } catch (e) {\n console.log(\"#> proxyError:\", e);\n Response.json({\n error: \"REQUEST_ERROR\",\n message: \"error while sending request through frontend-backend proxy\",\n });\n }\n}\n\nasync function login(request: NextRequest, config: IConfig) {\n const formData = await request.json();\n const res = await serverSideFetch<{ token: string; refresh: string }>({\n method: \"POST\",\n url: \"/login\",\n body: formData,\n sessionIsOptional: true,\n config,\n });\n\n if (config.debug) console.log(\"#> login\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n const dec = jwtDecode<any>(res.value.data.token);\n\n const session = await getSession();\n session.token = {\n jwt: res.value.data.token,\n refresh: res.value.data.refresh || \"refresh_token\",\n decoded: dec,\n };\n\n await session.save();\n\n return Response.json({\n redirect: \"/\",\n });\n}\n\nasync function getUser(request: NextRequest, config: IConfig) {\n const session = await getSession();\n\n if (session.token === undefined)\n return Response.json({\n error: \"SESSION_ERROR\",\n message: \"Session is required but not found.\",\n });\n\n const force = request.nextUrl.searchParams.get(\"force\") === \"true\";\n\n // * User already exists in session\n if (session.user !== undefined && !force)\n return Response.json({\n data: session.user,\n });\n\n // * User does not exist in session\n const res = await serverSideFetch<DefaultUser>({\n url: `${config.userEndpoint}`,\n config,\n });\n\n if (config.debug) console.log(\"#> getUser\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n session.user = res.value.data;\n await session.save();\n\n return Response.json({\n data: session.user,\n });\n}\n\nasync function oauth(request: NextRequest, config: IConfig) {\n const authUrl = request.nextUrl.searchParams.get(\"authUrl\");\n const state = request.nextUrl.searchParams.get(\"state\");\n\n if (!authUrl) throw new Error(\"No authUrl provided\");\n\n const url = new URL(config.route + \"/oauth_callback\", config.host);\n if (state) url.searchParams.set(\"state\", state);\n\n const oAuthUrl = new URL(authUrl, config.apiUrl);\n oAuthUrl.searchParams.set(\"returnUrl\", url.toString());\n\n const res = await serverSideFetch({\n url: oAuthUrl.pathname + oAuthUrl.search,\n config,\n sessionIsOptional: true,\n });\n\n if (res.isErr()) {\n if (config.debug) console.log(\"#> oauthError\", res.error);\n return Response.json(res.error);\n }\n\n return Response.json(res.value);\n}\n\nasync function oauth_callback(request: NextRequest, config: IConfig) {\n const refresh = request.nextUrl.searchParams.get(\"refresh\");\n\n const token = request.nextUrl.searchParams.get(\"token\");\n if (token === null) throw new Error(\"No token provided\");\n\n const dec = jwtDecode<any>(token);\n\n const session = await getSession();\n session.token = {\n jwt: token,\n refresh: refresh || \"refresh_token\",\n decoded: dec,\n };\n\n await session.save();\n\n // redirect to return url if provided\n const state = request.nextUrl.searchParams.get(\"state\");\n if (state !== null)\n return Response.redirect(\n state.includes(\"http\") ? state : config.host + state\n );\n\n return Response.redirect(config.host);\n}\n\nasync function logout(config: IConfig) {\n const session = await getSession();\n session.destroy();\n\n revalidatePath(config.host, \"layout\");\n\n return Response.json({\n redirect: \"/\",\n });\n}\n\nasync function debug() {\n return Response.json(await getSession());\n}\n\nasync function refresh(request: NextRequest, config: IConfig) {\n const result = await attemptTokenRefresh(config);\n\n if (config.debug) console.log(\"#> refresh result\", result);\n\n if (result.isErr()) {\n return Response.json(result.error, { status: 401 });\n }\n\n const url = request.nextUrl;\n url.pathname = decodeURIComponent(url.searchParams.get(\"r\") || \"\");\n url.searchParams.delete(\"r\");\n\n return Response.redirect(url);\n}\n\nasync function proxyFunction(\n method: \"GET\" | \"POST\" | \"DELETE\",\n request: NextRequest,\n config: IConfig,\n options: string[]\n) {\n options.shift(); // remove the first element which is the endpoint\n const res = await serverSideFetch({\n method,\n url: `/${options.join(\"/\")}${request.nextUrl.search}`,\n body: method === \"POST\" ? await request.json() : undefined,\n config,\n });\n\n if (config.debug) console.log(\"#> proxyFunction\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n return Response.json(res.value);\n}\n","import { SessionOptions } from \"iron-session\";\nimport { getIronSession } from \"iron-session\";\nimport { cookies } from \"next/headers\";\nimport { ServerSideSession } from \"../types/internal\";\nimport { Session } from \"../types\";\n\ndeclare module \"iron-session\" {\n interface IronSessionData<U, T> {\n user?: U;\n token?: {\n jwt: string;\n refresh: string;\n decoded: T;\n };\n }\n}\n\nexport interface DefaultUser {\n [key: string]: unknown;\n}\n\nexport interface Token {\n token: string;\n}\n\nexport const sessionOptions: SessionOptions = {\n password: process.env.CELESTYA_SECRET || \"PLEASE_SET_PASSWORD\",\n cookieName: process.env.CELESTYA_COOKIE_NAME || \"PLEASE_SET_COOKIE_NAME\",\n cookieOptions: { secure: process.env.SECURE === \"true\" },\n};\n\nexport const getSession = async <U = DefaultUser>(\n sessionOpts?: SessionOptions\n) => {\n if (\n sessionOpts === undefined &&\n (!process.env.CELESTYA_SECRET || !process.env.CELESTYA_COOKIE_NAME)\n ) {\n throw new Error(\n \"CELESTYA_SECRET and CELESTYA_COOKIE_NAME must be set in environment variables.\"\n );\n }\n\n const session: Session<U> = await getIronSession<ServerSideSession<U>>(\n await cookies(),\n sessionOpts || sessionOptions\n );\n\n return session;\n};\n","import { IConfig } from \"../types\";\nimport { BaseError, err, ok, Result, Success } from \"../types/response\";\nimport { getSession } from \"./session\";\n\n// Global refresh lock to prevent multiple simultaneous refresh attempts\nlet refreshPromise: Promise<Result<string, BaseError>> | null = null;\n\nexport const serverSideFetch = async <T>({\n url,\n method = \"GET\",\n config,\n body,\n sessionIsOptional = false,\n skipRefresh = false,\n ...options\n}: {\n url: string;\n method?: \"GET\" | \"POST\" | \"DELETE\";\n body?: object;\n config: IConfig;\n sessionIsOptional?: boolean;\n skipRefresh?: boolean;\n} & Omit<RequestInit, \"body\" | \"method\">): Promise<Result<T, BaseError>> => {\n const headers = new Headers({\n \"Content-Type\": \"application/json\",\n ...options.headers,\n });\n\n const session = await getSession();\n\n if (session.token !== undefined) {\n headers.set(\"Authorization\", `Bearer ${session.token.jwt}`);\n } else if (!sessionIsOptional) {\n return err({\n error: \"SESSION_ERROR\",\n message: \"Session is required but not found.\",\n });\n }\n\n const opts: RequestInit = {\n method,\n headers,\n ...options,\n };\n\n try {\n if (body) opts.body = JSON.stringify(body);\n } catch (e) {\n console.log(\"Error stringifying body: \", e);\n }\n\n try {\n const response: Response = await fetch(`${config.apiUrl}${url}`, opts);\n\n // Handle 401 Unauthorized - try to refresh token\n if (response.status === 401 && !skipRefresh && !sessionIsOptional) {\n if (config.debug)\n console.log(\"#> 401 detected, attempting token refresh\");\n\n // Local reference to the refresh promise to prevent race conditions\n let currentRefresh: Promise<Result<string, BaseError>>;\n\n // Check if refresh is already in progress\n if (refreshPromise === null) {\n // Start new refresh process\n refreshPromise = attemptTokenRefresh(config).finally(() => {\n // Clear the lock when done (success or failure)\n refreshPromise = null;\n });\n currentRefresh = refreshPromise;\n } else {\n if (config.debug)\n console.log(\"#> Refresh already in progress, waiting...\");\n currentRefresh = refreshPromise;\n }\n\n // Wait for the refresh to complete (whether we started it or not)\n const refreshResult = await currentRefresh;\n\n if (refreshResult.isOk()) {\n // Retry the original request with refreshed token\n return serverSideFetch<T>({\n url,\n method,\n config,\n body,\n sessionIsOptional,\n skipRefresh: true, // Prevent infinite loop\n ...options,\n });\n }\n\n // Refresh failed, return the error\n return err(refreshResult.error);\n }\n\n if (!response.ok) {\n return err({\n error: \"RESPONSE_ERROR\",\n message: `HTTP error! status: ${response.status}`,\n });\n }\n\n try {\n const res = await response.json();\n if (res.error !== undefined) {\n return err(res);\n }\n\n return ok(res as Success<T>);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse response as JSON\",\n });\n }\n } catch (e) {\n return err({\n error: \"FETCH_ERROR\",\n message: \"Failed to fetch the resource: \" + String(e),\n });\n }\n};\n\nexport async function attemptTokenRefresh(\n config: IConfig\n): Promise<Result<string, BaseError>> {\n if (config.debug) console.log(\"#> attemptTokenRefresh called\");\n\n const { jwtDecode } = await import(\"jwt-decode\");\n const session = await getSession();\n\n if (!session.token?.refresh) {\n return err({\n error: \"NO_REFRESH_TOKEN\",\n message: \"No refresh token available in session\",\n });\n }\n\n const res = await serverSideFetch<{ token: string }>({\n method: \"POST\",\n url: \"/refresh\",\n body: {\n refreshToken: session.token.refresh,\n },\n sessionIsOptional: true,\n skipRefresh: true, // Prevent infinite loop\n config,\n });\n\n if (config.debug) console.log(\"#> attemptTokenRefresh\", res);\n if (res.isErr()) {\n // Refresh token is invalid or expired, destroy session\n session.destroy();\n return err(res.error);\n }\n\n // Update session with new JWT\n const decoded = jwtDecode<any>(res.value.data.token);\n const newToken = {\n jwt: res.value.data.token,\n refresh: session.token.refresh, // Keep the same refresh token\n decoded,\n };\n\n session.token = newToken;\n\n await session.save();\n\n return ok({ data: \"success\" });\n}\n","import { NextRequest } from \"next/server\";\nimport { IConfig, IRequestOptions } from \"../types\";\nimport { Proxy } from \"./api\";\n\nexport const CelestyaProxy = (config: IConfig) => {\n return {\n POST: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"POST\", req, opt, config),\n GET: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"GET\", req, opt, config),\n DELETE: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"DELETE\", req, opt, config),\n };\n};\n","import { CallbackOptions, IConfig, WrapperFunction } from \"../types\";\nimport { serverSideFetch } from \"./fetch\";\n\n/**\n * Register new Wrapper for the API.\n *\n * Example Wrapper:\n * ```ts\n * const apiWrapper = (cb: WrapperFunction) => {\n * return {\n * commmands: {\n * get: () => cb<string>({ method: \"GET\", url: \"/command\" }),\n * update: (id: string, body: JSON) =>\n * cb({ method: \"POST\", url: `/command/${id}`, body }),\n * },\n * };\n * };\n * ````\n * @param apiWrapper\n * @param config\n * @returns\n */\nexport const serverAPIWrapper = <T>(\n apiWrapper: (cb: WrapperFunction) => T,\n config: IConfig\n) => {\n return apiWrapper(async (data: CallbackOptions) => {\n const { method, url, body } = data;\n\n return serverSideFetch({\n url,\n method,\n body,\n config,\n });\n });\n};\n"],"mappings":"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","names":["jwtDecode","Proxy","refresh","Proxy"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types/response.ts","../src/server/api.ts","../src/server/session.ts","../src/server/fetch.ts","../src/server/index.ts","../src/server/wrapper.ts"],"sourcesContent":["export {\n type IConfig,\n type IRequestOptions,\n type Session,\n type WrapperFunction,\n} from \"./types\";\nexport {\n type Result,\n type BaseError,\n type Ok,\n type Err,\n ok,\n err,\n} from \"./types/response\";\n\nexport { CelestyaProxy } from \"./server\";\nexport { serverSideFetch, attemptTokenRefresh } from \"./server/fetch\";\nexport { serverAPIWrapper } from \"./server/wrapper\";\nexport { getSession } from \"./server/session\";\n","export type BaseError = {\n error: string;\n message: string;\n};\n\nexport type Success<S> = {\n data: S;\n};\n\n/**\n * A Result type that represents either a successful value (Ok) or an error (Err).\n * This is a discriminated union type that helps handle errors in a type-safe way.\n *\n * @template T - The type of the successful value\n * @template E - The type of the error, must extend BaseError\n */\nexport type Result<T, E extends BaseError> = Ok<T, E> | Err<T, E>;\n\ntype IResult<T, E extends BaseError> = {\n /**\n * Checks if the `Result` is an `Ok` instance\n */\n isOk: () => this is Ok<T, E>;\n\n /**\n * Checks if the `Result` is an `Err` instance.\n */\n isErr: () => this is Err<T, E>;\n};\n\n/**\n * Represents a successful `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Ok<T, E extends BaseError> implements IResult<T, E> {\n value: Success<T>;\n\n constructor(value: Success<T>) {\n this.value = value;\n }\n\n isOk(): this is Ok<T, E> {\n return true;\n }\n\n isErr(): this is Err<T, E> {\n return false;\n }\n}\n\n/**\n * Represents an error `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Err<T, E extends BaseError> implements IResult<T, E> {\n error: E;\n\n constructor(error: E) {\n this.error = error;\n }\n\n isOk(): this is Ok<T, E> {\n return false;\n }\n\n isErr(): this is Err<T, E> {\n return true;\n }\n}\n\n/**\n * Creates a new successful `Result` (i.e., an instance of `Ok`).\n *\n * @template T - The type of the successful value\n * @param value - The successful value\n * @returns A new `Ok` instance.\n */\nexport function ok<const T>(value: Success<T>): Result<T, never> {\n return new Ok(value);\n}\n\n/**\n * Creates a new error `Result` (i.e., an instance of `Err`).\n *\n * @template E - The type of the error, must extend `BaseError`.\n * @param error - The error value.\n * @returns A new `Err` instance.\n */\nexport function err<const E extends BaseError>(error: E): Result<never, E> {\n return new Err(error);\n}\n","import { NextRequest } from \"next/server\";\nimport { revalidatePath } from \"next/cache\";\nimport { jwtDecode } from \"jwt-decode\";\nimport { IConfig, IRequestOptions, RouteHandler } from \"../types\";\nimport { DefaultUser, getSession } from \"./session\";\nimport { serverSideFetch, attemptTokenRefresh } from \"./fetch\";\n\nconst rHandler: RouteHandler = {\n GET: {\n user: ({ request, config }) => getUser(request, config),\n refresh: ({ request, config }) => refresh(request, config),\n logout: ({ config }) => logout(config),\n debug: () => debug(),\n oauth: ({ request, config }) => oauth(request, config),\n oauth_callback: ({ request, config }) => oauth_callback(request, config),\n proxy: ({ request, config, options }) =>\n proxyFunction(\"GET\", request, config, options),\n },\n POST: {\n login: ({ request, config }) => login(request, config),\n proxy: ({ request, config, options }) =>\n proxyFunction(\"POST\", request, config, options),\n },\n DELETE: {\n proxy: ({ request, config, options }) =>\n proxyFunction(\"DELETE\", request, config, options),\n },\n};\n\nexport async function Proxy(\n method: string,\n request: NextRequest,\n options: IRequestOptions,\n config: IConfig\n) {\n try {\n const params = await options.params;\n const parameters = {\n request,\n path:\n params.endpoint[0] ||\n request.nextUrl.pathname.replace(config.route, \"\"),\n options: params.endpoint,\n config,\n };\n\n if (config.debug) console.log(\"#> proxy:\", parameters);\n\n if (rHandler[method][parameters.path])\n return await rHandler[method][parameters.path](parameters);\n\n return Response.json({\n error: \"INVALID_ENDPOINT\",\n message: \"the provided endpoint is not valid\",\n });\n } catch (e) {\n console.log(\"#> proxyError:\", e);\n Response.json({\n error: \"REQUEST_ERROR\",\n message: \"error while sending request through frontend-backend proxy\",\n });\n }\n}\n\nasync function login(request: NextRequest, config: IConfig) {\n const formData = await request.json();\n const res = await serverSideFetch<{ token: string; refresh: string }>({\n method: \"POST\",\n url: \"/login\",\n body: formData,\n sessionIsOptional: true,\n config,\n });\n\n if (config.debug) console.log(\"#> login\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n const dec = jwtDecode<any>(res.value.data.token);\n\n const session = await getSession();\n session.token = {\n jwt: res.value.data.token,\n refresh: res.value.data.refresh || \"refresh_token\",\n decoded: dec,\n };\n\n await session.save();\n\n return Response.json({\n redirect: \"/\",\n });\n}\n\nasync function getUser(request: NextRequest, config: IConfig) {\n const session = await getSession();\n\n if (session.token === undefined)\n return Response.json({\n error: \"SESSION_ERROR\",\n message: \"Session is required but not found.\",\n });\n\n const force = request.nextUrl.searchParams.get(\"force\") === \"true\";\n\n // * User already exists in session\n if (session.user !== undefined && !force)\n return Response.json({\n data: session.user,\n });\n\n // * User does not exist in session\n const res = await serverSideFetch<DefaultUser>({\n url: `${config.userEndpoint}`,\n config,\n });\n\n if (config.debug) console.log(\"#> getUser\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n session.user = res.value.data;\n await session.save();\n\n return Response.json({\n data: session.user,\n });\n}\n\nasync function oauth(request: NextRequest, config: IConfig) {\n const authUrl = request.nextUrl.searchParams.get(\"authUrl\");\n const state = request.nextUrl.searchParams.get(\"state\");\n\n if (!authUrl) throw new Error(\"No authUrl provided\");\n\n const url = new URL(config.route + \"/oauth_callback\", config.host);\n if (state) url.searchParams.set(\"state\", state);\n\n const oAuthUrl = new URL(authUrl, config.apiUrl);\n oAuthUrl.searchParams.set(\"returnUrl\", url.toString());\n\n const res = await serverSideFetch({\n url: oAuthUrl.pathname + oAuthUrl.search,\n config,\n sessionIsOptional: true,\n });\n\n if (res.isErr()) {\n if (config.debug) console.log(\"#> oauthError\", res.error);\n return Response.json(res.error);\n }\n\n return Response.json(res.value);\n}\n\nasync function oauth_callback(request: NextRequest, config: IConfig) {\n const refresh = request.nextUrl.searchParams.get(\"refresh\");\n\n const token = request.nextUrl.searchParams.get(\"token\");\n if (token === null) throw new Error(\"No token provided\");\n\n const dec = jwtDecode<any>(token);\n\n const session = await getSession();\n session.token = {\n jwt: token,\n refresh: refresh || \"refresh_token\",\n decoded: dec,\n };\n\n await session.save();\n\n // redirect to return url if provided\n const state = request.nextUrl.searchParams.get(\"state\");\n if (state !== null)\n return Response.redirect(\n state.includes(\"http\") ? state : config.host + state\n );\n\n return Response.redirect(config.host);\n}\n\nasync function logout(config: IConfig) {\n const session = await getSession();\n session.destroy();\n\n revalidatePath(config.host, \"layout\");\n\n return Response.json({\n redirect: \"/\",\n });\n}\n\nasync function debug() {\n return Response.json(await getSession());\n}\n\nasync function refresh(request: NextRequest, config: IConfig) {\n const result = await attemptTokenRefresh(config);\n\n if (config.debug) console.log(\"#> refresh result\", result);\n\n if (result.isErr()) {\n return Response.json(result.error, { status: 401 });\n }\n\n const url = request.nextUrl;\n url.pathname = decodeURIComponent(url.searchParams.get(\"r\") || \"\");\n url.searchParams.delete(\"r\");\n\n return Response.redirect(url);\n}\n\nasync function proxyFunction(\n method: \"GET\" | \"POST\" | \"DELETE\",\n request: NextRequest,\n config: IConfig,\n options: string[]\n) {\n options.shift(); // remove the first element which is the endpoint\n\n // Forward custom headers (X-*) from the incoming request\n const forwardHeaders: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n if (key.startsWith(\"x-\") && key !== \"x-forwarded-for\" && key !== \"x-forwarded-host\" && key !== \"x-forwarded-proto\") {\n forwardHeaders[key] = value;\n }\n });\n\n const res = await serverSideFetch({\n method,\n url: `/${options.join(\"/\")}${request.nextUrl.search}`,\n body: method === \"POST\" ? await request.json() : undefined,\n config,\n headers: forwardHeaders,\n });\n\n if (config.debug) console.log(\"#> proxyFunction\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n return Response.json(res.value);\n}\n","import { SessionOptions } from \"iron-session\";\nimport { getIronSession } from \"iron-session\";\nimport { cookies } from \"next/headers\";\nimport { ServerSideSession } from \"../types/internal\";\nimport { Session } from \"../types\";\n\ndeclare module \"iron-session\" {\n interface IronSessionData<U, T> {\n user?: U;\n token?: {\n jwt: string;\n refresh: string;\n decoded: T;\n };\n }\n}\n\nexport interface DefaultUser {\n [key: string]: unknown;\n}\n\nexport interface Token {\n token: string;\n}\n\nexport const sessionOptions: SessionOptions = {\n password: process.env.CELESTYA_SECRET || \"PLEASE_SET_PASSWORD\",\n cookieName: process.env.CELESTYA_COOKIE_NAME || \"PLEASE_SET_COOKIE_NAME\",\n cookieOptions: { secure: process.env.SECURE === \"true\" },\n};\n\nexport const getSession = async <U = DefaultUser>(\n sessionOpts?: SessionOptions\n) => {\n if (\n sessionOpts === undefined &&\n (!process.env.CELESTYA_SECRET || !process.env.CELESTYA_COOKIE_NAME)\n ) {\n throw new Error(\n \"CELESTYA_SECRET and CELESTYA_COOKIE_NAME must be set in environment variables.\"\n );\n }\n\n const session: Session<U> = await getIronSession<ServerSideSession<U>>(\n await cookies(),\n sessionOpts || sessionOptions\n );\n\n return session;\n};\n","import { IConfig } from \"../types\";\nimport { BaseError, err, ok, Result, Success } from \"../types/response\";\nimport { getSession } from \"./session\";\n\n// Global refresh lock to prevent multiple simultaneous refresh attempts\nlet refreshPromise: Promise<Result<string, BaseError>> | null = null;\n\nexport const serverSideFetch = async <T>({\n url,\n method = \"GET\",\n config,\n body,\n sessionIsOptional = false,\n skipRefresh = false,\n ...options\n}: {\n url: string;\n method?: \"GET\" | \"POST\" | \"DELETE\";\n body?: object;\n config: IConfig;\n sessionIsOptional?: boolean;\n skipRefresh?: boolean;\n} & Omit<RequestInit, \"body\" | \"method\">): Promise<Result<T, BaseError>> => {\n const headers = new Headers({\n \"Content-Type\": \"application/json\",\n ...(options.headers as Record<string, string>),\n });\n\n const session = await getSession();\n\n if (session.token !== undefined) {\n headers.set(\"Authorization\", `Bearer ${session.token.jwt}`);\n } else if (!sessionIsOptional) {\n return err({\n error: \"SESSION_ERROR\",\n message: \"Session is required but not found.\",\n });\n }\n\n const opts: RequestInit = {\n method,\n ...options,\n headers,\n };\n\n try {\n if (body) opts.body = JSON.stringify(body);\n } catch (e) {\n console.log(\"Error stringifying body: \", e);\n }\n\n try {\n const response: Response = await fetch(`${config.apiUrl}${url}`, opts);\n\n // Handle 401 Unauthorized - try to refresh token\n if (response.status === 401 && !skipRefresh && !sessionIsOptional) {\n if (config.debug)\n console.log(\"#> 401 detected, attempting token refresh\");\n\n // Local reference to the refresh promise to prevent race conditions\n let currentRefresh: Promise<Result<string, BaseError>>;\n\n // Check if refresh is already in progress\n if (refreshPromise === null) {\n // Start new refresh process\n refreshPromise = attemptTokenRefresh(config).finally(() => {\n // Clear the lock when done (success or failure)\n refreshPromise = null;\n });\n currentRefresh = refreshPromise;\n } else {\n if (config.debug)\n console.log(\"#> Refresh already in progress, waiting...\");\n currentRefresh = refreshPromise;\n }\n\n // Wait for the refresh to complete (whether we started it or not)\n const refreshResult = await currentRefresh;\n\n if (refreshResult.isOk()) {\n // Retry the original request with refreshed token\n return serverSideFetch<T>({\n url,\n method,\n config,\n body,\n sessionIsOptional,\n skipRefresh: true, // Prevent infinite loop\n ...options,\n });\n }\n\n // Refresh failed, return the error\n return err(refreshResult.error);\n }\n\n if (!response.ok) {\n return err({\n error: \"RESPONSE_ERROR\",\n message: `HTTP error! status: ${response.status}`,\n });\n }\n\n try {\n const res = await response.json();\n if (res.error !== undefined) {\n return err(res);\n }\n\n return ok(res as Success<T>);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse response as JSON\",\n });\n }\n } catch (e) {\n return err({\n error: \"FETCH_ERROR\",\n message: \"Failed to fetch the resource: \" + String(e),\n });\n }\n};\n\nexport async function attemptTokenRefresh(\n config: IConfig\n): Promise<Result<string, BaseError>> {\n if (config.debug) console.log(\"#> attemptTokenRefresh called\");\n\n const { jwtDecode } = await import(\"jwt-decode\");\n const session = await getSession();\n\n if (!session.token?.refresh) {\n return err({\n error: \"NO_REFRESH_TOKEN\",\n message: \"No refresh token available in session\",\n });\n }\n\n const res = await serverSideFetch<{ token: string }>({\n method: \"POST\",\n url: \"/refresh\",\n body: {\n refreshToken: session.token.refresh,\n },\n sessionIsOptional: true,\n skipRefresh: true, // Prevent infinite loop\n config,\n });\n\n if (config.debug) console.log(\"#> attemptTokenRefresh\", res);\n if (res.isErr()) {\n // Refresh token is invalid or expired, destroy session\n session.destroy();\n return err(res.error);\n }\n\n // Update session with new JWT\n const decoded = jwtDecode<any>(res.value.data.token);\n const newToken = {\n jwt: res.value.data.token,\n refresh: session.token.refresh, // Keep the same refresh token\n decoded,\n };\n\n session.token = newToken;\n\n await session.save();\n\n return ok({ data: \"success\" });\n}\n","import { NextRequest } from \"next/server\";\nimport { IConfig, IRequestOptions } from \"../types\";\nimport { Proxy } from \"./api\";\n\nexport const CelestyaProxy = (config: IConfig) => {\n return {\n POST: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"POST\", req, opt, config),\n GET: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"GET\", req, opt, config),\n DELETE: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"DELETE\", req, opt, config),\n };\n};\n","import { CallbackOptions, IConfig, WrapperFunction } from \"../types\";\nimport { serverSideFetch } from \"./fetch\";\n\n/**\n * Register new Wrapper for the API.\n *\n * Example Wrapper:\n * ```ts\n * const apiWrapper = (cb: WrapperFunction) => {\n * return {\n * commmands: {\n * get: () => cb<string>({ method: \"GET\", url: \"/command\" }),\n * update: (id: string, body: JSON) =>\n * cb({ method: \"POST\", url: `/command/${id}`, body }),\n * },\n * };\n * };\n * ````\n * @param apiWrapper\n * @param config\n * @returns\n */\nexport const serverAPIWrapper = <T>(\n apiWrapper: (cb: WrapperFunction) => T,\n config: IConfig\n) => {\n return apiWrapper(async (data: CallbackOptions) => {\n const { method, url, body } = data;\n\n return serverSideFetch({\n url,\n method,\n body,\n config,\n });\n });\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACoCO,IAAM,KAAN,MAA0D;AAAA,EAG/D,YAAY,OAAmB;AAC7B,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,OAAyB;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,QAA2B;AACzB,WAAO;AAAA,EACT;AACF;AAQO,IAAM,MAAN,MAA2D;AAAA,EAGhE,YAAY,OAAU;AACpB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,OAAyB;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,QAA2B;AACzB,WAAO;AAAA,EACT;AACF;AASO,SAAS,GAAY,OAAqC;AAC/D,SAAO,IAAI,GAAG,KAAK;AACrB;AASO,SAAS,IAA+B,OAA4B;AACzE,SAAO,IAAI,IAAI,KAAK;AACtB;;;AC7FA,mBAA+B;AAC/B,wBAA0B;;;ACD1B,0BAA+B;AAC/B,qBAAwB;AAuBjB,IAAM,iBAAiC;AAAA,EAC5C,UAAU,QAAQ,IAAI,mBAAmB;AAAA,EACzC,YAAY,QAAQ,IAAI,wBAAwB;AAAA,EAChD,eAAe,EAAE,QAAQ,QAAQ,IAAI,WAAW,OAAO;AACzD;AAEO,IAAM,aAAa,OACxB,gBACG;AACH,MACE,gBAAgB,WACf,CAAC,QAAQ,IAAI,mBAAmB,CAAC,QAAQ,IAAI,uBAC9C;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAsB,UAAM;AAAA,IAChC,UAAM,wBAAQ;AAAA,IACd,eAAe;AAAA,EACjB;AAEA,SAAO;AACT;;;AC5CA,IAAI,iBAA4D;AAEzD,IAAM,kBAAkB,OAAU;AAAA,EACvC;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACd,GAAG;AACL,MAO4E;AAC1E,QAAM,UAAU,IAAI,QAAQ;AAAA,IAC1B,gBAAgB;AAAA,IAChB,GAAI,QAAQ;AAAA,EACd,CAAC;AAED,QAAM,UAAU,MAAM,WAAW;AAEjC,MAAI,QAAQ,UAAU,QAAW;AAC/B,YAAQ,IAAI,iBAAiB,UAAU,QAAQ,MAAM,GAAG,EAAE;AAAA,EAC5D,WAAW,CAAC,mBAAmB;AAC7B,WAAO,IAAI;AAAA,MACT,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,QAAM,OAAoB;AAAA,IACxB;AAAA,IACA,GAAG;AAAA,IACH;AAAA,EACF;AAEA,MAAI;AACF,QAAI,KAAM,MAAK,OAAO,KAAK,UAAU,IAAI;AAAA,EAC3C,SAAS,GAAG;AACV,YAAQ,IAAI,6BAA6B,CAAC;AAAA,EAC5C;AAEA,MAAI;AACF,UAAM,WAAqB,MAAM,MAAM,GAAG,OAAO,MAAM,GAAG,GAAG,IAAI,IAAI;AAGrE,QAAI,SAAS,WAAW,OAAO,CAAC,eAAe,CAAC,mBAAmB;AACjE,UAAI,OAAO;AACT,gBAAQ,IAAI,2CAA2C;AAGzD,UAAI;AAGJ,UAAI,mBAAmB,MAAM;AAE3B,yBAAiB,oBAAoB,MAAM,EAAE,QAAQ,MAAM;AAEzD,2BAAiB;AAAA,QACnB,CAAC;AACD,yBAAiB;AAAA,MACnB,OAAO;AACL,YAAI,OAAO;AACT,kBAAQ,IAAI,4CAA4C;AAC1D,yBAAiB;AAAA,MACnB;AAGA,YAAM,gBAAgB,MAAM;AAE5B,UAAI,cAAc,KAAK,GAAG;AAExB,eAAO,gBAAmB;AAAA,UACxB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,aAAa;AAAA;AAAA,UACb,GAAG;AAAA,QACL,CAAC;AAAA,MACH;AAGA,aAAO,IAAI,cAAc,KAAK;AAAA,IAChC;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,aAAO,IAAI;AAAA,QACT,OAAO;AAAA,QACP,SAAS,uBAAuB,SAAS,MAAM;AAAA,MACjD,CAAC;AAAA,IACH;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,SAAS,KAAK;AAChC,UAAI,IAAI,UAAU,QAAW;AAC3B,eAAO,IAAI,GAAG;AAAA,MAChB;AAEA,aAAO,GAAG,GAAiB;AAAA,IAC7B,SAAS,GAAG;AACV,aAAO,IAAI;AAAA,QACT,OAAO;AAAA,QACP,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF,SAAS,GAAG;AACV,WAAO,IAAI;AAAA,MACT,OAAO;AAAA,MACP,SAAS,mCAAmC,OAAO,CAAC;AAAA,IACtD,CAAC;AAAA,EACH;AACF;AAEA,eAAsB,oBACpB,QACoC;AA9HtC;AA+HE,MAAI,OAAO,MAAO,SAAQ,IAAI,+BAA+B;AAE7D,QAAM,EAAE,WAAAA,WAAU,IAAI,MAAM,OAAO,YAAY;AAC/C,QAAM,UAAU,MAAM,WAAW;AAEjC,MAAI,GAAC,aAAQ,UAAR,mBAAe,UAAS;AAC3B,WAAO,IAAI;AAAA,MACT,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,QAAM,MAAM,MAAM,gBAAmC;AAAA,IACnD,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,MAAM;AAAA,MACJ,cAAc,QAAQ,MAAM;AAAA,IAC9B;AAAA,IACA,mBAAmB;AAAA,IACnB,aAAa;AAAA;AAAA,IACb;AAAA,EACF,CAAC;AAED,MAAI,OAAO,MAAO,SAAQ,IAAI,0BAA0B,GAAG;AAC3D,MAAI,IAAI,MAAM,GAAG;AAEf,YAAQ,QAAQ;AAChB,WAAO,IAAI,IAAI,KAAK;AAAA,EACtB;AAGA,QAAM,UAAUA,WAAe,IAAI,MAAM,KAAK,KAAK;AACnD,QAAM,WAAW;AAAA,IACf,KAAK,IAAI,MAAM,KAAK;AAAA,IACpB,SAAS,QAAQ,MAAM;AAAA;AAAA,IACvB;AAAA,EACF;AAEA,UAAQ,QAAQ;AAEhB,QAAM,QAAQ,KAAK;AAEnB,SAAO,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B;;;AFnKA,IAAM,WAAyB;AAAA,EAC7B,KAAK;AAAA,IACH,MAAM,CAAC,EAAE,SAAS,OAAO,MAAM,QAAQ,SAAS,MAAM;AAAA,IACtD,SAAS,CAAC,EAAE,SAAS,OAAO,MAAM,QAAQ,SAAS,MAAM;AAAA,IACzD,QAAQ,CAAC,EAAE,OAAO,MAAM,OAAO,MAAM;AAAA,IACrC,OAAO,MAAM,MAAM;AAAA,IACnB,OAAO,CAAC,EAAE,SAAS,OAAO,MAAM,MAAM,SAAS,MAAM;AAAA,IACrD,gBAAgB,CAAC,EAAE,SAAS,OAAO,MAAM,eAAe,SAAS,MAAM;AAAA,IACvE,OAAO,CAAC,EAAE,SAAS,QAAQ,QAAQ,MACjC,cAAc,OAAO,SAAS,QAAQ,OAAO;AAAA,EACjD;AAAA,EACA,MAAM;AAAA,IACJ,OAAO,CAAC,EAAE,SAAS,OAAO,MAAM,MAAM,SAAS,MAAM;AAAA,IACrD,OAAO,CAAC,EAAE,SAAS,QAAQ,QAAQ,MACjC,cAAc,QAAQ,SAAS,QAAQ,OAAO;AAAA,EAClD;AAAA,EACA,QAAQ;AAAA,IACN,OAAO,CAAC,EAAE,SAAS,QAAQ,QAAQ,MACjC,cAAc,UAAU,SAAS,QAAQ,OAAO;AAAA,EACpD;AACF;AAEA,eAAsBC,OACpB,QACA,SACA,SACA,QACA;AACA,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ;AAC7B,UAAM,aAAa;AAAA,MACjB;AAAA,MACA,MACE,OAAO,SAAS,CAAC,KACjB,QAAQ,QAAQ,SAAS,QAAQ,OAAO,OAAO,EAAE;AAAA,MACnD,SAAS,OAAO;AAAA,MAChB;AAAA,IACF;AAEA,QAAI,OAAO,MAAO,SAAQ,IAAI,aAAa,UAAU;AAErD,QAAI,SAAS,MAAM,EAAE,WAAW,IAAI;AAClC,aAAO,MAAM,SAAS,MAAM,EAAE,WAAW,IAAI,EAAE,UAAU;AAE3D,WAAO,SAAS,KAAK;AAAA,MACnB,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH,SAAS,GAAG;AACV,YAAQ,IAAI,kBAAkB,CAAC;AAC/B,aAAS,KAAK;AAAA,MACZ,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,eAAe,MAAM,SAAsB,QAAiB;AAC1D,QAAM,WAAW,MAAM,QAAQ,KAAK;AACpC,QAAM,MAAM,MAAM,gBAAoD;AAAA,IACpE,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB;AAAA,EACF,CAAC;AAED,MAAI,OAAO,MAAO,SAAQ,IAAI,YAAY,GAAG;AAC7C,MAAI,IAAI,MAAM,GAAG;AACf,WAAO,SAAS,KAAK,IAAI,KAAK;AAAA,EAChC;AAEA,QAAM,UAAM,6BAAe,IAAI,MAAM,KAAK,KAAK;AAE/C,QAAM,UAAU,MAAM,WAAW;AACjC,UAAQ,QAAQ;AAAA,IACd,KAAK,IAAI,MAAM,KAAK;AAAA,IACpB,SAAS,IAAI,MAAM,KAAK,WAAW;AAAA,IACnC,SAAS;AAAA,EACX;AAEA,QAAM,QAAQ,KAAK;AAEnB,SAAO,SAAS,KAAK;AAAA,IACnB,UAAU;AAAA,EACZ,CAAC;AACH;AAEA,eAAe,QAAQ,SAAsB,QAAiB;AAC5D,QAAM,UAAU,MAAM,WAAW;AAEjC,MAAI,QAAQ,UAAU;AACpB,WAAO,SAAS,KAAK;AAAA,MACnB,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAEH,QAAM,QAAQ,QAAQ,QAAQ,aAAa,IAAI,OAAO,MAAM;AAG5D,MAAI,QAAQ,SAAS,UAAa,CAAC;AACjC,WAAO,SAAS,KAAK;AAAA,MACnB,MAAM,QAAQ;AAAA,IAChB,CAAC;AAGH,QAAM,MAAM,MAAM,gBAA6B;AAAA,IAC7C,KAAK,GAAG,OAAO,YAAY;AAAA,IAC3B;AAAA,EACF,CAAC;AAED,MAAI,OAAO,MAAO,SAAQ,IAAI,cAAc,GAAG;AAC/C,MAAI,IAAI,MAAM,GAAG;AACf,WAAO,SAAS,KAAK,IAAI,KAAK;AAAA,EAChC;AAEA,UAAQ,OAAO,IAAI,MAAM;AACzB,QAAM,QAAQ,KAAK;AAEnB,SAAO,SAAS,KAAK;AAAA,IACnB,MAAM,QAAQ;AAAA,EAChB,CAAC;AACH;AAEA,eAAe,MAAM,SAAsB,QAAiB;AAC1D,QAAM,UAAU,QAAQ,QAAQ,aAAa,IAAI,SAAS;AAC1D,QAAM,QAAQ,QAAQ,QAAQ,aAAa,IAAI,OAAO;AAEtD,MAAI,CAAC,QAAS,OAAM,IAAI,MAAM,qBAAqB;AAEnD,QAAM,MAAM,IAAI,IAAI,OAAO,QAAQ,mBAAmB,OAAO,IAAI;AACjE,MAAI,MAAO,KAAI,aAAa,IAAI,SAAS,KAAK;AAE9C,QAAM,WAAW,IAAI,IAAI,SAAS,OAAO,MAAM;AAC/C,WAAS,aAAa,IAAI,aAAa,IAAI,SAAS,CAAC;AAErD,QAAM,MAAM,MAAM,gBAAgB;AAAA,IAChC,KAAK,SAAS,WAAW,SAAS;AAAA,IAClC;AAAA,IACA,mBAAmB;AAAA,EACrB,CAAC;AAED,MAAI,IAAI,MAAM,GAAG;AACf,QAAI,OAAO,MAAO,SAAQ,IAAI,iBAAiB,IAAI,KAAK;AACxD,WAAO,SAAS,KAAK,IAAI,KAAK;AAAA,EAChC;AAEA,SAAO,SAAS,KAAK,IAAI,KAAK;AAChC;AAEA,eAAe,eAAe,SAAsB,QAAiB;AACnE,QAAMC,WAAU,QAAQ,QAAQ,aAAa,IAAI,SAAS;AAE1D,QAAM,QAAQ,QAAQ,QAAQ,aAAa,IAAI,OAAO;AACtD,MAAI,UAAU,KAAM,OAAM,IAAI,MAAM,mBAAmB;AAEvD,QAAM,UAAM,6BAAe,KAAK;AAEhC,QAAM,UAAU,MAAM,WAAW;AACjC,UAAQ,QAAQ;AAAA,IACd,KAAK;AAAA,IACL,SAASA,YAAW;AAAA,IACpB,SAAS;AAAA,EACX;AAEA,QAAM,QAAQ,KAAK;AAGnB,QAAM,QAAQ,QAAQ,QAAQ,aAAa,IAAI,OAAO;AACtD,MAAI,UAAU;AACZ,WAAO,SAAS;AAAA,MACd,MAAM,SAAS,MAAM,IAAI,QAAQ,OAAO,OAAO;AAAA,IACjD;AAEF,SAAO,SAAS,SAAS,OAAO,IAAI;AACtC;AAEA,eAAe,OAAO,QAAiB;AACrC,QAAM,UAAU,MAAM,WAAW;AACjC,UAAQ,QAAQ;AAEhB,mCAAe,OAAO,MAAM,QAAQ;AAEpC,SAAO,SAAS,KAAK;AAAA,IACnB,UAAU;AAAA,EACZ,CAAC;AACH;AAEA,eAAe,QAAQ;AACrB,SAAO,SAAS,KAAK,MAAM,WAAW,CAAC;AACzC;AAEA,eAAe,QAAQ,SAAsB,QAAiB;AAC5D,QAAM,SAAS,MAAM,oBAAoB,MAAM;AAE/C,MAAI,OAAO,MAAO,SAAQ,IAAI,qBAAqB,MAAM;AAEzD,MAAI,OAAO,MAAM,GAAG;AAClB,WAAO,SAAS,KAAK,OAAO,OAAO,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpD;AAEA,QAAM,MAAM,QAAQ;AACpB,MAAI,WAAW,mBAAmB,IAAI,aAAa,IAAI,GAAG,KAAK,EAAE;AACjE,MAAI,aAAa,OAAO,GAAG;AAE3B,SAAO,SAAS,SAAS,GAAG;AAC9B;AAEA,eAAe,cACb,QACA,SACA,QACA,SACA;AACA,UAAQ,MAAM;AAGd,QAAM,iBAAyC,CAAC;AAChD,UAAQ,QAAQ,QAAQ,CAAC,OAAO,QAAQ;AACtC,QAAI,IAAI,WAAW,IAAI,KAAK,QAAQ,qBAAqB,QAAQ,sBAAsB,QAAQ,qBAAqB;AAClH,qBAAe,GAAG,IAAI;AAAA,IACxB;AAAA,EACF,CAAC;AAED,QAAM,MAAM,MAAM,gBAAgB;AAAA,IAChC;AAAA,IACA,KAAK,IAAI,QAAQ,KAAK,GAAG,CAAC,GAAG,QAAQ,QAAQ,MAAM;AAAA,IACnD,MAAM,WAAW,SAAS,MAAM,QAAQ,KAAK,IAAI;AAAA,IACjD;AAAA,IACA,SAAS;AAAA,EACX,CAAC;AAED,MAAI,OAAO,MAAO,SAAQ,IAAI,oBAAoB,GAAG;AACrD,MAAI,IAAI,MAAM,GAAG;AACf,WAAO,SAAS,KAAK,IAAI,KAAK;AAAA,EAChC;AAEA,SAAO,SAAS,KAAK,IAAI,KAAK;AAChC;;;AGjPO,IAAM,gBAAgB,CAAC,WAAoB;AAChD,SAAO;AAAA,IACL,MAAM,CAAC,KAAkB,QACvBC,OAAM,QAAQ,KAAK,KAAK,MAAM;AAAA,IAChC,KAAK,CAAC,KAAkB,QACtBA,OAAM,OAAO,KAAK,KAAK,MAAM;AAAA,IAC/B,QAAQ,CAAC,KAAkB,QACzBA,OAAM,UAAU,KAAK,KAAK,MAAM;AAAA,EACpC;AACF;;;ACSO,IAAM,mBAAmB,CAC9B,YACA,WACG;AACH,SAAO,WAAW,OAAO,SAA0B;AACjD,UAAM,EAAE,QAAQ,KAAK,KAAK,IAAI;AAE9B,WAAO,gBAAgB;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;","names":["jwtDecode","Proxy","refresh","Proxy"]}
|
package/dist/index.mjs
CHANGED
|
@@ -79,8 +79,8 @@ var serverSideFetch = async ({
|
|
|
79
79
|
}
|
|
80
80
|
const opts = {
|
|
81
81
|
method,
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
...options,
|
|
83
|
+
headers
|
|
84
84
|
};
|
|
85
85
|
try {
|
|
86
86
|
if (body) opts.body = JSON.stringify(body);
|
|
@@ -338,11 +338,18 @@ async function refresh(request, config) {
|
|
|
338
338
|
}
|
|
339
339
|
async function proxyFunction(method, request, config, options) {
|
|
340
340
|
options.shift();
|
|
341
|
+
const forwardHeaders = {};
|
|
342
|
+
request.headers.forEach((value, key) => {
|
|
343
|
+
if (key.startsWith("x-") && key !== "x-forwarded-for" && key !== "x-forwarded-host" && key !== "x-forwarded-proto") {
|
|
344
|
+
forwardHeaders[key] = value;
|
|
345
|
+
}
|
|
346
|
+
});
|
|
341
347
|
const res = await serverSideFetch({
|
|
342
348
|
method,
|
|
343
349
|
url: `/${options.join("/")}${request.nextUrl.search}`,
|
|
344
350
|
body: method === "POST" ? await request.json() : void 0,
|
|
345
|
-
config
|
|
351
|
+
config,
|
|
352
|
+
headers: forwardHeaders
|
|
346
353
|
});
|
|
347
354
|
if (config.debug) console.log("#> proxyFunction", res);
|
|
348
355
|
if (res.isErr()) {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types/response.ts","../src/server/api.ts","../src/server/session.ts","../src/server/fetch.ts","../src/server/index.ts","../src/server/wrapper.ts"],"sourcesContent":["export type BaseError = {\n error: string;\n message: string;\n};\n\nexport type Success<S> = {\n data: S;\n};\n\n/**\n * A Result type that represents either a successful value (Ok) or an error (Err).\n * This is a discriminated union type that helps handle errors in a type-safe way.\n *\n * @template T - The type of the successful value\n * @template E - The type of the error, must extend BaseError\n */\nexport type Result<T, E extends BaseError> = Ok<T, E> | Err<T, E>;\n\ntype IResult<T, E extends BaseError> = {\n /**\n * Checks if the `Result` is an `Ok` instance\n */\n isOk: () => this is Ok<T, E>;\n\n /**\n * Checks if the `Result` is an `Err` instance.\n */\n isErr: () => this is Err<T, E>;\n};\n\n/**\n * Represents a successful `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Ok<T, E extends BaseError> implements IResult<T, E> {\n value: Success<T>;\n\n constructor(value: Success<T>) {\n this.value = value;\n }\n\n isOk(): this is Ok<T, E> {\n return true;\n }\n\n isErr(): this is Err<T, E> {\n return false;\n }\n}\n\n/**\n * Represents an error `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Err<T, E extends BaseError> implements IResult<T, E> {\n error: E;\n\n constructor(error: E) {\n this.error = error;\n }\n\n isOk(): this is Ok<T, E> {\n return false;\n }\n\n isErr(): this is Err<T, E> {\n return true;\n }\n}\n\n/**\n * Creates a new successful `Result` (i.e., an instance of `Ok`).\n *\n * @template T - The type of the successful value\n * @param value - The successful value\n * @returns A new `Ok` instance.\n */\nexport function ok<const T>(value: Success<T>): Result<T, never> {\n return new Ok(value);\n}\n\n/**\n * Creates a new error `Result` (i.e., an instance of `Err`).\n *\n * @template E - The type of the error, must extend `BaseError`.\n * @param error - The error value.\n * @returns A new `Err` instance.\n */\nexport function err<const E extends BaseError>(error: E): Result<never, E> {\n return new Err(error);\n}\n","import { NextRequest } from \"next/server\";\nimport { revalidatePath } from \"next/cache\";\nimport { jwtDecode } from \"jwt-decode\";\nimport { IConfig, IRequestOptions, RouteHandler } from \"../types\";\nimport { DefaultUser, getSession } from \"./session\";\nimport { serverSideFetch, attemptTokenRefresh } from \"./fetch\";\n\nconst rHandler: RouteHandler = {\n GET: {\n user: ({ request, config }) => getUser(request, config),\n refresh: ({ request, config }) => refresh(request, config),\n logout: ({ config }) => logout(config),\n debug: () => debug(),\n oauth: ({ request, config }) => oauth(request, config),\n oauth_callback: ({ request, config }) => oauth_callback(request, config),\n proxy: ({ request, config, options }) =>\n proxyFunction(\"GET\", request, config, options),\n },\n POST: {\n login: ({ request, config }) => login(request, config),\n proxy: ({ request, config, options }) =>\n proxyFunction(\"POST\", request, config, options),\n },\n DELETE: {\n proxy: ({ request, config, options }) =>\n proxyFunction(\"DELETE\", request, config, options),\n },\n};\n\nexport async function Proxy(\n method: string,\n request: NextRequest,\n options: IRequestOptions,\n config: IConfig\n) {\n try {\n const params = await options.params;\n const parameters = {\n request,\n path:\n params.endpoint[0] ||\n request.nextUrl.pathname.replace(config.route, \"\"),\n options: params.endpoint,\n config,\n };\n\n if (config.debug) console.log(\"#> proxy:\", parameters);\n\n if (rHandler[method][parameters.path])\n return await rHandler[method][parameters.path](parameters);\n\n return Response.json({\n error: \"INVALID_ENDPOINT\",\n message: \"the provided endpoint is not valid\",\n });\n } catch (e) {\n console.log(\"#> proxyError:\", e);\n Response.json({\n error: \"REQUEST_ERROR\",\n message: \"error while sending request through frontend-backend proxy\",\n });\n }\n}\n\nasync function login(request: NextRequest, config: IConfig) {\n const formData = await request.json();\n const res = await serverSideFetch<{ token: string; refresh: string }>({\n method: \"POST\",\n url: \"/login\",\n body: formData,\n sessionIsOptional: true,\n config,\n });\n\n if (config.debug) console.log(\"#> login\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n const dec = jwtDecode<any>(res.value.data.token);\n\n const session = await getSession();\n session.token = {\n jwt: res.value.data.token,\n refresh: res.value.data.refresh || \"refresh_token\",\n decoded: dec,\n };\n\n await session.save();\n\n return Response.json({\n redirect: \"/\",\n });\n}\n\nasync function getUser(request: NextRequest, config: IConfig) {\n const session = await getSession();\n\n if (session.token === undefined)\n return Response.json({\n error: \"SESSION_ERROR\",\n message: \"Session is required but not found.\",\n });\n\n const force = request.nextUrl.searchParams.get(\"force\") === \"true\";\n\n // * User already exists in session\n if (session.user !== undefined && !force)\n return Response.json({\n data: session.user,\n });\n\n // * User does not exist in session\n const res = await serverSideFetch<DefaultUser>({\n url: `${config.userEndpoint}`,\n config,\n });\n\n if (config.debug) console.log(\"#> getUser\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n session.user = res.value.data;\n await session.save();\n\n return Response.json({\n data: session.user,\n });\n}\n\nasync function oauth(request: NextRequest, config: IConfig) {\n const authUrl = request.nextUrl.searchParams.get(\"authUrl\");\n const state = request.nextUrl.searchParams.get(\"state\");\n\n if (!authUrl) throw new Error(\"No authUrl provided\");\n\n const url = new URL(config.route + \"/oauth_callback\", config.host);\n if (state) url.searchParams.set(\"state\", state);\n\n const oAuthUrl = new URL(authUrl, config.apiUrl);\n oAuthUrl.searchParams.set(\"returnUrl\", url.toString());\n\n const res = await serverSideFetch({\n url: oAuthUrl.pathname + oAuthUrl.search,\n config,\n sessionIsOptional: true,\n });\n\n if (res.isErr()) {\n if (config.debug) console.log(\"#> oauthError\", res.error);\n return Response.json(res.error);\n }\n\n return Response.json(res.value);\n}\n\nasync function oauth_callback(request: NextRequest, config: IConfig) {\n const refresh = request.nextUrl.searchParams.get(\"refresh\");\n\n const token = request.nextUrl.searchParams.get(\"token\");\n if (token === null) throw new Error(\"No token provided\");\n\n const dec = jwtDecode<any>(token);\n\n const session = await getSession();\n session.token = {\n jwt: token,\n refresh: refresh || \"refresh_token\",\n decoded: dec,\n };\n\n await session.save();\n\n // redirect to return url if provided\n const state = request.nextUrl.searchParams.get(\"state\");\n if (state !== null)\n return Response.redirect(\n state.includes(\"http\") ? state : config.host + state\n );\n\n return Response.redirect(config.host);\n}\n\nasync function logout(config: IConfig) {\n const session = await getSession();\n session.destroy();\n\n revalidatePath(config.host, \"layout\");\n\n return Response.json({\n redirect: \"/\",\n });\n}\n\nasync function debug() {\n return Response.json(await getSession());\n}\n\nasync function refresh(request: NextRequest, config: IConfig) {\n const result = await attemptTokenRefresh(config);\n\n if (config.debug) console.log(\"#> refresh result\", result);\n\n if (result.isErr()) {\n return Response.json(result.error, { status: 401 });\n }\n\n const url = request.nextUrl;\n url.pathname = decodeURIComponent(url.searchParams.get(\"r\") || \"\");\n url.searchParams.delete(\"r\");\n\n return Response.redirect(url);\n}\n\nasync function proxyFunction(\n method: \"GET\" | \"POST\" | \"DELETE\",\n request: NextRequest,\n config: IConfig,\n options: string[]\n) {\n options.shift(); // remove the first element which is the endpoint\n const res = await serverSideFetch({\n method,\n url: `/${options.join(\"/\")}${request.nextUrl.search}`,\n body: method === \"POST\" ? await request.json() : undefined,\n config,\n });\n\n if (config.debug) console.log(\"#> proxyFunction\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n return Response.json(res.value);\n}\n","import { SessionOptions } from \"iron-session\";\nimport { getIronSession } from \"iron-session\";\nimport { cookies } from \"next/headers\";\nimport { ServerSideSession } from \"../types/internal\";\nimport { Session } from \"../types\";\n\ndeclare module \"iron-session\" {\n interface IronSessionData<U, T> {\n user?: U;\n token?: {\n jwt: string;\n refresh: string;\n decoded: T;\n };\n }\n}\n\nexport interface DefaultUser {\n [key: string]: unknown;\n}\n\nexport interface Token {\n token: string;\n}\n\nexport const sessionOptions: SessionOptions = {\n password: process.env.CELESTYA_SECRET || \"PLEASE_SET_PASSWORD\",\n cookieName: process.env.CELESTYA_COOKIE_NAME || \"PLEASE_SET_COOKIE_NAME\",\n cookieOptions: { secure: process.env.SECURE === \"true\" },\n};\n\nexport const getSession = async <U = DefaultUser>(\n sessionOpts?: SessionOptions\n) => {\n if (\n sessionOpts === undefined &&\n (!process.env.CELESTYA_SECRET || !process.env.CELESTYA_COOKIE_NAME)\n ) {\n throw new Error(\n \"CELESTYA_SECRET and CELESTYA_COOKIE_NAME must be set in environment variables.\"\n );\n }\n\n const session: Session<U> = await getIronSession<ServerSideSession<U>>(\n await cookies(),\n sessionOpts || sessionOptions\n );\n\n return session;\n};\n","import { IConfig } from \"../types\";\nimport { BaseError, err, ok, Result, Success } from \"../types/response\";\nimport { getSession } from \"./session\";\n\n// Global refresh lock to prevent multiple simultaneous refresh attempts\nlet refreshPromise: Promise<Result<string, BaseError>> | null = null;\n\nexport const serverSideFetch = async <T>({\n url,\n method = \"GET\",\n config,\n body,\n sessionIsOptional = false,\n skipRefresh = false,\n ...options\n}: {\n url: string;\n method?: \"GET\" | \"POST\" | \"DELETE\";\n body?: object;\n config: IConfig;\n sessionIsOptional?: boolean;\n skipRefresh?: boolean;\n} & Omit<RequestInit, \"body\" | \"method\">): Promise<Result<T, BaseError>> => {\n const headers = new Headers({\n \"Content-Type\": \"application/json\",\n ...options.headers,\n });\n\n const session = await getSession();\n\n if (session.token !== undefined) {\n headers.set(\"Authorization\", `Bearer ${session.token.jwt}`);\n } else if (!sessionIsOptional) {\n return err({\n error: \"SESSION_ERROR\",\n message: \"Session is required but not found.\",\n });\n }\n\n const opts: RequestInit = {\n method,\n headers,\n ...options,\n };\n\n try {\n if (body) opts.body = JSON.stringify(body);\n } catch (e) {\n console.log(\"Error stringifying body: \", e);\n }\n\n try {\n const response: Response = await fetch(`${config.apiUrl}${url}`, opts);\n\n // Handle 401 Unauthorized - try to refresh token\n if (response.status === 401 && !skipRefresh && !sessionIsOptional) {\n if (config.debug)\n console.log(\"#> 401 detected, attempting token refresh\");\n\n // Local reference to the refresh promise to prevent race conditions\n let currentRefresh: Promise<Result<string, BaseError>>;\n\n // Check if refresh is already in progress\n if (refreshPromise === null) {\n // Start new refresh process\n refreshPromise = attemptTokenRefresh(config).finally(() => {\n // Clear the lock when done (success or failure)\n refreshPromise = null;\n });\n currentRefresh = refreshPromise;\n } else {\n if (config.debug)\n console.log(\"#> Refresh already in progress, waiting...\");\n currentRefresh = refreshPromise;\n }\n\n // Wait for the refresh to complete (whether we started it or not)\n const refreshResult = await currentRefresh;\n\n if (refreshResult.isOk()) {\n // Retry the original request with refreshed token\n return serverSideFetch<T>({\n url,\n method,\n config,\n body,\n sessionIsOptional,\n skipRefresh: true, // Prevent infinite loop\n ...options,\n });\n }\n\n // Refresh failed, return the error\n return err(refreshResult.error);\n }\n\n if (!response.ok) {\n return err({\n error: \"RESPONSE_ERROR\",\n message: `HTTP error! status: ${response.status}`,\n });\n }\n\n try {\n const res = await response.json();\n if (res.error !== undefined) {\n return err(res);\n }\n\n return ok(res as Success<T>);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse response as JSON\",\n });\n }\n } catch (e) {\n return err({\n error: \"FETCH_ERROR\",\n message: \"Failed to fetch the resource: \" + String(e),\n });\n }\n};\n\nexport async function attemptTokenRefresh(\n config: IConfig\n): Promise<Result<string, BaseError>> {\n if (config.debug) console.log(\"#> attemptTokenRefresh called\");\n\n const { jwtDecode } = await import(\"jwt-decode\");\n const session = await getSession();\n\n if (!session.token?.refresh) {\n return err({\n error: \"NO_REFRESH_TOKEN\",\n message: \"No refresh token available in session\",\n });\n }\n\n const res = await serverSideFetch<{ token: string }>({\n method: \"POST\",\n url: \"/refresh\",\n body: {\n refreshToken: session.token.refresh,\n },\n sessionIsOptional: true,\n skipRefresh: true, // Prevent infinite loop\n config,\n });\n\n if (config.debug) console.log(\"#> attemptTokenRefresh\", res);\n if (res.isErr()) {\n // Refresh token is invalid or expired, destroy session\n session.destroy();\n return err(res.error);\n }\n\n // Update session with new JWT\n const decoded = jwtDecode<any>(res.value.data.token);\n const newToken = {\n jwt: res.value.data.token,\n refresh: session.token.refresh, // Keep the same refresh token\n decoded,\n };\n\n session.token = newToken;\n\n await session.save();\n\n return ok({ data: \"success\" });\n}\n","import { NextRequest } from \"next/server\";\nimport { IConfig, IRequestOptions } from \"../types\";\nimport { Proxy } from \"./api\";\n\nexport const CelestyaProxy = (config: IConfig) => {\n return {\n POST: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"POST\", req, opt, config),\n GET: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"GET\", req, opt, config),\n DELETE: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"DELETE\", req, opt, config),\n };\n};\n","import { CallbackOptions, IConfig, WrapperFunction } from \"../types\";\nimport { serverSideFetch } from \"./fetch\";\n\n/**\n * Register new Wrapper for the API.\n *\n * Example Wrapper:\n * ```ts\n * const apiWrapper = (cb: WrapperFunction) => {\n * return {\n * commmands: {\n * get: () => cb<string>({ method: \"GET\", url: \"/command\" }),\n * update: (id: string, body: JSON) =>\n * cb({ method: \"POST\", url: `/command/${id}`, body }),\n * },\n * };\n * };\n * ````\n * @param apiWrapper\n * @param config\n * @returns\n */\nexport const serverAPIWrapper = <T>(\n apiWrapper: (cb: WrapperFunction) => T,\n config: IConfig\n) => {\n return apiWrapper(async (data: CallbackOptions) => {\n const { method, url, body } = data;\n\n return serverSideFetch({\n url,\n method,\n body,\n config,\n });\n });\n};\n"],"mappings":"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","names":["jwtDecode","refresh"]}
|
|
1
|
+
{"version":3,"sources":["../src/types/response.ts","../src/server/api.ts","../src/server/session.ts","../src/server/fetch.ts","../src/server/index.ts","../src/server/wrapper.ts"],"sourcesContent":["export type BaseError = {\n error: string;\n message: string;\n};\n\nexport type Success<S> = {\n data: S;\n};\n\n/**\n * A Result type that represents either a successful value (Ok) or an error (Err).\n * This is a discriminated union type that helps handle errors in a type-safe way.\n *\n * @template T - The type of the successful value\n * @template E - The type of the error, must extend BaseError\n */\nexport type Result<T, E extends BaseError> = Ok<T, E> | Err<T, E>;\n\ntype IResult<T, E extends BaseError> = {\n /**\n * Checks if the `Result` is an `Ok` instance\n */\n isOk: () => this is Ok<T, E>;\n\n /**\n * Checks if the `Result` is an `Err` instance.\n */\n isErr: () => this is Err<T, E>;\n};\n\n/**\n * Represents a successful `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Ok<T, E extends BaseError> implements IResult<T, E> {\n value: Success<T>;\n\n constructor(value: Success<T>) {\n this.value = value;\n }\n\n isOk(): this is Ok<T, E> {\n return true;\n }\n\n isErr(): this is Err<T, E> {\n return false;\n }\n}\n\n/**\n * Represents an error `Result` value.\n *\n * @template T - The type of the successful value.\n * @template E - The type of the error, must extend `BaseError`.\n */\nexport class Err<T, E extends BaseError> implements IResult<T, E> {\n error: E;\n\n constructor(error: E) {\n this.error = error;\n }\n\n isOk(): this is Ok<T, E> {\n return false;\n }\n\n isErr(): this is Err<T, E> {\n return true;\n }\n}\n\n/**\n * Creates a new successful `Result` (i.e., an instance of `Ok`).\n *\n * @template T - The type of the successful value\n * @param value - The successful value\n * @returns A new `Ok` instance.\n */\nexport function ok<const T>(value: Success<T>): Result<T, never> {\n return new Ok(value);\n}\n\n/**\n * Creates a new error `Result` (i.e., an instance of `Err`).\n *\n * @template E - The type of the error, must extend `BaseError`.\n * @param error - The error value.\n * @returns A new `Err` instance.\n */\nexport function err<const E extends BaseError>(error: E): Result<never, E> {\n return new Err(error);\n}\n","import { NextRequest } from \"next/server\";\nimport { revalidatePath } from \"next/cache\";\nimport { jwtDecode } from \"jwt-decode\";\nimport { IConfig, IRequestOptions, RouteHandler } from \"../types\";\nimport { DefaultUser, getSession } from \"./session\";\nimport { serverSideFetch, attemptTokenRefresh } from \"./fetch\";\n\nconst rHandler: RouteHandler = {\n GET: {\n user: ({ request, config }) => getUser(request, config),\n refresh: ({ request, config }) => refresh(request, config),\n logout: ({ config }) => logout(config),\n debug: () => debug(),\n oauth: ({ request, config }) => oauth(request, config),\n oauth_callback: ({ request, config }) => oauth_callback(request, config),\n proxy: ({ request, config, options }) =>\n proxyFunction(\"GET\", request, config, options),\n },\n POST: {\n login: ({ request, config }) => login(request, config),\n proxy: ({ request, config, options }) =>\n proxyFunction(\"POST\", request, config, options),\n },\n DELETE: {\n proxy: ({ request, config, options }) =>\n proxyFunction(\"DELETE\", request, config, options),\n },\n};\n\nexport async function Proxy(\n method: string,\n request: NextRequest,\n options: IRequestOptions,\n config: IConfig\n) {\n try {\n const params = await options.params;\n const parameters = {\n request,\n path:\n params.endpoint[0] ||\n request.nextUrl.pathname.replace(config.route, \"\"),\n options: params.endpoint,\n config,\n };\n\n if (config.debug) console.log(\"#> proxy:\", parameters);\n\n if (rHandler[method][parameters.path])\n return await rHandler[method][parameters.path](parameters);\n\n return Response.json({\n error: \"INVALID_ENDPOINT\",\n message: \"the provided endpoint is not valid\",\n });\n } catch (e) {\n console.log(\"#> proxyError:\", e);\n Response.json({\n error: \"REQUEST_ERROR\",\n message: \"error while sending request through frontend-backend proxy\",\n });\n }\n}\n\nasync function login(request: NextRequest, config: IConfig) {\n const formData = await request.json();\n const res = await serverSideFetch<{ token: string; refresh: string }>({\n method: \"POST\",\n url: \"/login\",\n body: formData,\n sessionIsOptional: true,\n config,\n });\n\n if (config.debug) console.log(\"#> login\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n const dec = jwtDecode<any>(res.value.data.token);\n\n const session = await getSession();\n session.token = {\n jwt: res.value.data.token,\n refresh: res.value.data.refresh || \"refresh_token\",\n decoded: dec,\n };\n\n await session.save();\n\n return Response.json({\n redirect: \"/\",\n });\n}\n\nasync function getUser(request: NextRequest, config: IConfig) {\n const session = await getSession();\n\n if (session.token === undefined)\n return Response.json({\n error: \"SESSION_ERROR\",\n message: \"Session is required but not found.\",\n });\n\n const force = request.nextUrl.searchParams.get(\"force\") === \"true\";\n\n // * User already exists in session\n if (session.user !== undefined && !force)\n return Response.json({\n data: session.user,\n });\n\n // * User does not exist in session\n const res = await serverSideFetch<DefaultUser>({\n url: `${config.userEndpoint}`,\n config,\n });\n\n if (config.debug) console.log(\"#> getUser\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n session.user = res.value.data;\n await session.save();\n\n return Response.json({\n data: session.user,\n });\n}\n\nasync function oauth(request: NextRequest, config: IConfig) {\n const authUrl = request.nextUrl.searchParams.get(\"authUrl\");\n const state = request.nextUrl.searchParams.get(\"state\");\n\n if (!authUrl) throw new Error(\"No authUrl provided\");\n\n const url = new URL(config.route + \"/oauth_callback\", config.host);\n if (state) url.searchParams.set(\"state\", state);\n\n const oAuthUrl = new URL(authUrl, config.apiUrl);\n oAuthUrl.searchParams.set(\"returnUrl\", url.toString());\n\n const res = await serverSideFetch({\n url: oAuthUrl.pathname + oAuthUrl.search,\n config,\n sessionIsOptional: true,\n });\n\n if (res.isErr()) {\n if (config.debug) console.log(\"#> oauthError\", res.error);\n return Response.json(res.error);\n }\n\n return Response.json(res.value);\n}\n\nasync function oauth_callback(request: NextRequest, config: IConfig) {\n const refresh = request.nextUrl.searchParams.get(\"refresh\");\n\n const token = request.nextUrl.searchParams.get(\"token\");\n if (token === null) throw new Error(\"No token provided\");\n\n const dec = jwtDecode<any>(token);\n\n const session = await getSession();\n session.token = {\n jwt: token,\n refresh: refresh || \"refresh_token\",\n decoded: dec,\n };\n\n await session.save();\n\n // redirect to return url if provided\n const state = request.nextUrl.searchParams.get(\"state\");\n if (state !== null)\n return Response.redirect(\n state.includes(\"http\") ? state : config.host + state\n );\n\n return Response.redirect(config.host);\n}\n\nasync function logout(config: IConfig) {\n const session = await getSession();\n session.destroy();\n\n revalidatePath(config.host, \"layout\");\n\n return Response.json({\n redirect: \"/\",\n });\n}\n\nasync function debug() {\n return Response.json(await getSession());\n}\n\nasync function refresh(request: NextRequest, config: IConfig) {\n const result = await attemptTokenRefresh(config);\n\n if (config.debug) console.log(\"#> refresh result\", result);\n\n if (result.isErr()) {\n return Response.json(result.error, { status: 401 });\n }\n\n const url = request.nextUrl;\n url.pathname = decodeURIComponent(url.searchParams.get(\"r\") || \"\");\n url.searchParams.delete(\"r\");\n\n return Response.redirect(url);\n}\n\nasync function proxyFunction(\n method: \"GET\" | \"POST\" | \"DELETE\",\n request: NextRequest,\n config: IConfig,\n options: string[]\n) {\n options.shift(); // remove the first element which is the endpoint\n\n // Forward custom headers (X-*) from the incoming request\n const forwardHeaders: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n if (key.startsWith(\"x-\") && key !== \"x-forwarded-for\" && key !== \"x-forwarded-host\" && key !== \"x-forwarded-proto\") {\n forwardHeaders[key] = value;\n }\n });\n\n const res = await serverSideFetch({\n method,\n url: `/${options.join(\"/\")}${request.nextUrl.search}`,\n body: method === \"POST\" ? await request.json() : undefined,\n config,\n headers: forwardHeaders,\n });\n\n if (config.debug) console.log(\"#> proxyFunction\", res);\n if (res.isErr()) {\n return Response.json(res.error);\n }\n\n return Response.json(res.value);\n}\n","import { SessionOptions } from \"iron-session\";\nimport { getIronSession } from \"iron-session\";\nimport { cookies } from \"next/headers\";\nimport { ServerSideSession } from \"../types/internal\";\nimport { Session } from \"../types\";\n\ndeclare module \"iron-session\" {\n interface IronSessionData<U, T> {\n user?: U;\n token?: {\n jwt: string;\n refresh: string;\n decoded: T;\n };\n }\n}\n\nexport interface DefaultUser {\n [key: string]: unknown;\n}\n\nexport interface Token {\n token: string;\n}\n\nexport const sessionOptions: SessionOptions = {\n password: process.env.CELESTYA_SECRET || \"PLEASE_SET_PASSWORD\",\n cookieName: process.env.CELESTYA_COOKIE_NAME || \"PLEASE_SET_COOKIE_NAME\",\n cookieOptions: { secure: process.env.SECURE === \"true\" },\n};\n\nexport const getSession = async <U = DefaultUser>(\n sessionOpts?: SessionOptions\n) => {\n if (\n sessionOpts === undefined &&\n (!process.env.CELESTYA_SECRET || !process.env.CELESTYA_COOKIE_NAME)\n ) {\n throw new Error(\n \"CELESTYA_SECRET and CELESTYA_COOKIE_NAME must be set in environment variables.\"\n );\n }\n\n const session: Session<U> = await getIronSession<ServerSideSession<U>>(\n await cookies(),\n sessionOpts || sessionOptions\n );\n\n return session;\n};\n","import { IConfig } from \"../types\";\nimport { BaseError, err, ok, Result, Success } from \"../types/response\";\nimport { getSession } from \"./session\";\n\n// Global refresh lock to prevent multiple simultaneous refresh attempts\nlet refreshPromise: Promise<Result<string, BaseError>> | null = null;\n\nexport const serverSideFetch = async <T>({\n url,\n method = \"GET\",\n config,\n body,\n sessionIsOptional = false,\n skipRefresh = false,\n ...options\n}: {\n url: string;\n method?: \"GET\" | \"POST\" | \"DELETE\";\n body?: object;\n config: IConfig;\n sessionIsOptional?: boolean;\n skipRefresh?: boolean;\n} & Omit<RequestInit, \"body\" | \"method\">): Promise<Result<T, BaseError>> => {\n const headers = new Headers({\n \"Content-Type\": \"application/json\",\n ...(options.headers as Record<string, string>),\n });\n\n const session = await getSession();\n\n if (session.token !== undefined) {\n headers.set(\"Authorization\", `Bearer ${session.token.jwt}`);\n } else if (!sessionIsOptional) {\n return err({\n error: \"SESSION_ERROR\",\n message: \"Session is required but not found.\",\n });\n }\n\n const opts: RequestInit = {\n method,\n ...options,\n headers,\n };\n\n try {\n if (body) opts.body = JSON.stringify(body);\n } catch (e) {\n console.log(\"Error stringifying body: \", e);\n }\n\n try {\n const response: Response = await fetch(`${config.apiUrl}${url}`, opts);\n\n // Handle 401 Unauthorized - try to refresh token\n if (response.status === 401 && !skipRefresh && !sessionIsOptional) {\n if (config.debug)\n console.log(\"#> 401 detected, attempting token refresh\");\n\n // Local reference to the refresh promise to prevent race conditions\n let currentRefresh: Promise<Result<string, BaseError>>;\n\n // Check if refresh is already in progress\n if (refreshPromise === null) {\n // Start new refresh process\n refreshPromise = attemptTokenRefresh(config).finally(() => {\n // Clear the lock when done (success or failure)\n refreshPromise = null;\n });\n currentRefresh = refreshPromise;\n } else {\n if (config.debug)\n console.log(\"#> Refresh already in progress, waiting...\");\n currentRefresh = refreshPromise;\n }\n\n // Wait for the refresh to complete (whether we started it or not)\n const refreshResult = await currentRefresh;\n\n if (refreshResult.isOk()) {\n // Retry the original request with refreshed token\n return serverSideFetch<T>({\n url,\n method,\n config,\n body,\n sessionIsOptional,\n skipRefresh: true, // Prevent infinite loop\n ...options,\n });\n }\n\n // Refresh failed, return the error\n return err(refreshResult.error);\n }\n\n if (!response.ok) {\n return err({\n error: \"RESPONSE_ERROR\",\n message: `HTTP error! status: ${response.status}`,\n });\n }\n\n try {\n const res = await response.json();\n if (res.error !== undefined) {\n return err(res);\n }\n\n return ok(res as Success<T>);\n } catch (e) {\n return err({\n error: \"PARSE_ERROR\",\n message: \"Failed to parse response as JSON\",\n });\n }\n } catch (e) {\n return err({\n error: \"FETCH_ERROR\",\n message: \"Failed to fetch the resource: \" + String(e),\n });\n }\n};\n\nexport async function attemptTokenRefresh(\n config: IConfig\n): Promise<Result<string, BaseError>> {\n if (config.debug) console.log(\"#> attemptTokenRefresh called\");\n\n const { jwtDecode } = await import(\"jwt-decode\");\n const session = await getSession();\n\n if (!session.token?.refresh) {\n return err({\n error: \"NO_REFRESH_TOKEN\",\n message: \"No refresh token available in session\",\n });\n }\n\n const res = await serverSideFetch<{ token: string }>({\n method: \"POST\",\n url: \"/refresh\",\n body: {\n refreshToken: session.token.refresh,\n },\n sessionIsOptional: true,\n skipRefresh: true, // Prevent infinite loop\n config,\n });\n\n if (config.debug) console.log(\"#> attemptTokenRefresh\", res);\n if (res.isErr()) {\n // Refresh token is invalid or expired, destroy session\n session.destroy();\n return err(res.error);\n }\n\n // Update session with new JWT\n const decoded = jwtDecode<any>(res.value.data.token);\n const newToken = {\n jwt: res.value.data.token,\n refresh: session.token.refresh, // Keep the same refresh token\n decoded,\n };\n\n session.token = newToken;\n\n await session.save();\n\n return ok({ data: \"success\" });\n}\n","import { NextRequest } from \"next/server\";\nimport { IConfig, IRequestOptions } from \"../types\";\nimport { Proxy } from \"./api\";\n\nexport const CelestyaProxy = (config: IConfig) => {\n return {\n POST: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"POST\", req, opt, config),\n GET: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"GET\", req, opt, config),\n DELETE: (req: NextRequest, opt: IRequestOptions) =>\n Proxy(\"DELETE\", req, opt, config),\n };\n};\n","import { CallbackOptions, IConfig, WrapperFunction } from \"../types\";\nimport { serverSideFetch } from \"./fetch\";\n\n/**\n * Register new Wrapper for the API.\n *\n * Example Wrapper:\n * ```ts\n * const apiWrapper = (cb: WrapperFunction) => {\n * return {\n * commmands: {\n * get: () => cb<string>({ method: \"GET\", url: \"/command\" }),\n * update: (id: string, body: JSON) =>\n * cb({ method: \"POST\", url: `/command/${id}`, body }),\n * },\n * };\n * };\n * ````\n * @param apiWrapper\n * @param config\n * @returns\n */\nexport const serverAPIWrapper = <T>(\n apiWrapper: (cb: WrapperFunction) => T,\n config: IConfig\n) => {\n return apiWrapper(async (data: CallbackOptions) => {\n const { method, url, body } = data;\n\n return serverSideFetch({\n url,\n method,\n body,\n config,\n });\n });\n};\n"],"mappings":"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","names":["jwtDecode","refresh"]}
|