celestya 0.1.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 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
@@ -143,6 +147,6 @@ export default Navbar;
143
147
  - [x]: POST request with auth
144
148
  - [x]: Fix issue with getSession serverside and config set at layout (If used at api/\_/route.tsx)
145
149
  - [x]: Fix issue with api endpoints if no layout has been loaded (if accessing api directly)
150
+ - [X]: Refresh logic
151
+ - [X]: Fix Response types
146
152
  - [ ]: Upload request with worker as helper (?)
147
- - [ ]: Refresh logic
148
- - [ ]: Fix Response types
@@ -1,5 +1,62 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
 
3
+ type BaseError = {
4
+ error: string;
5
+ message: string;
6
+ };
7
+ type Success<S> = {
8
+ data: S;
9
+ };
10
+ /**
11
+ * A Result type that represents either a successful value (Ok) or an error (Err).
12
+ * This is a discriminated union type that helps handle errors in a type-safe way.
13
+ *
14
+ * @template T - The type of the successful value
15
+ * @template E - The type of the error, must extend BaseError
16
+ */
17
+ type Result<T, E extends BaseError> = Ok<T, E> | Err<T, E>;
18
+ type IResult<T, E extends BaseError> = {
19
+ /**
20
+ * Checks if the `Result` is an `Ok` instance
21
+ */
22
+ isOk: () => this is Ok<T, E>;
23
+ /**
24
+ * Checks if the `Result` is an `Err` instance.
25
+ */
26
+ isErr: () => this is Err<T, E>;
27
+ };
28
+ /**
29
+ * Represents a successful `Result` value.
30
+ *
31
+ * @template T - The type of the successful value.
32
+ * @template E - The type of the error, must extend `BaseError`.
33
+ */
34
+ declare class Ok<T, E extends BaseError> implements IResult<T, E> {
35
+ value: Success<T>;
36
+ constructor(value: Success<T>);
37
+ isOk(): this is Ok<T, E>;
38
+ isErr(): this is Err<T, E>;
39
+ }
40
+ /**
41
+ * Represents an error `Result` value.
42
+ *
43
+ * @template T - The type of the successful value.
44
+ * @template E - The type of the error, must extend `BaseError`.
45
+ */
46
+ declare class Err<T, E extends BaseError> implements IResult<T, E> {
47
+ error: E;
48
+ constructor(error: E);
49
+ isOk(): this is Ok<T, E>;
50
+ isErr(): this is Err<T, E>;
51
+ }
52
+
53
+ type CallbackOptions = {
54
+ method: "GET" | "POST" | "DELETE";
55
+ url: string;
56
+ body?: object;
57
+ };
58
+ type WrapperFunction = <T>(data: CallbackOptions) => Promise<Result<T, BaseError>>;
59
+
3
60
  declare module "iron-session" {
4
61
  interface IronSessionData<U, T> {
5
62
  user?: U;
@@ -26,15 +83,6 @@ interface IOAuthData {
26
83
  oAuthUrl: string;
27
84
  onErrorUrl?: string;
28
85
  }
29
- interface IResponseError {
30
- error: string;
31
- message: string;
32
- }
33
- interface IResponseSuccess<T, U> {
34
- error: null;
35
- data: T;
36
- details: U;
37
- }
38
86
  interface IAuthContext<U> {
39
87
  isLoggedIn: boolean;
40
88
  ready: boolean;
@@ -44,9 +92,21 @@ interface IAuthContext<U> {
44
92
  oAuth: (data: IOAuthData) => Promise<string>;
45
93
  logout: () => Promise<string>;
46
94
  refreshUser: (force?: boolean) => Promise<void>;
47
- get: <T, U = any>(url: string) => Promise<ResponseType<T, U>>;
48
- post: <T, U = any>(url: string, body: any) => Promise<ResponseType<T, U>>;
49
- del: <T, U = any>(url: string) => Promise<ResponseType<T, U>>;
95
+ get: <T>(props: {
96
+ url: string;
97
+ headers?: Record<string, string>;
98
+ }) => Promise<Result<T, BaseError>>;
99
+ post: <T>(props: {
100
+ url: string;
101
+ body: object;
102
+ headers?: Record<string, string>;
103
+ }) => Promise<Result<T, BaseError>>;
104
+ del: <T>(props: {
105
+ url: string;
106
+ headers?: Record<string, string>;
107
+ }) => Promise<Result<T, BaseError>>;
108
+ setHeader: (key: string, value: string) => void;
109
+ removeHeader: (key: string) => void;
50
110
  }
51
111
  interface IAuthContextOptions {
52
112
  children: React.ReactNode;
@@ -55,12 +115,14 @@ interface IAuthContextOptions {
55
115
  interface IChildProps {
56
116
  children?: React.ReactNode;
57
117
  }
58
- type ResponseType<T, U = any> = IResponseError | IResponseSuccess<T, U>;
59
118
 
60
- type LogoutProps = React.FC<React.ComponentProps<'div'> & IChildProps>;
119
+ type LogoutProps = React.FC<React.ComponentProps<"div"> & IChildProps>;
120
+
121
+ declare const useAuth: () => IAuthContext<any>;
122
+
123
+ declare const APIWrapper: <T>(wrapperFunction: (cb: WrapperFunction) => T) => () => T;
61
124
 
62
125
  declare const AuthProvider: <IU>({ children, routePrefix, }: IAuthContextOptions) => react_jsx_runtime.JSX.Element;
63
126
  declare const Logout: LogoutProps;
64
- declare const useAuth: () => IAuthContext<any>;
65
127
 
66
- export { AuthProvider, Logout, useAuth };
128
+ export { APIWrapper, AuthProvider, Logout, useAuth };
@@ -1,5 +1,62 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
 
3
+ type BaseError = {
4
+ error: string;
5
+ message: string;
6
+ };
7
+ type Success<S> = {
8
+ data: S;
9
+ };
10
+ /**
11
+ * A Result type that represents either a successful value (Ok) or an error (Err).
12
+ * This is a discriminated union type that helps handle errors in a type-safe way.
13
+ *
14
+ * @template T - The type of the successful value
15
+ * @template E - The type of the error, must extend BaseError
16
+ */
17
+ type Result<T, E extends BaseError> = Ok<T, E> | Err<T, E>;
18
+ type IResult<T, E extends BaseError> = {
19
+ /**
20
+ * Checks if the `Result` is an `Ok` instance
21
+ */
22
+ isOk: () => this is Ok<T, E>;
23
+ /**
24
+ * Checks if the `Result` is an `Err` instance.
25
+ */
26
+ isErr: () => this is Err<T, E>;
27
+ };
28
+ /**
29
+ * Represents a successful `Result` value.
30
+ *
31
+ * @template T - The type of the successful value.
32
+ * @template E - The type of the error, must extend `BaseError`.
33
+ */
34
+ declare class Ok<T, E extends BaseError> implements IResult<T, E> {
35
+ value: Success<T>;
36
+ constructor(value: Success<T>);
37
+ isOk(): this is Ok<T, E>;
38
+ isErr(): this is Err<T, E>;
39
+ }
40
+ /**
41
+ * Represents an error `Result` value.
42
+ *
43
+ * @template T - The type of the successful value.
44
+ * @template E - The type of the error, must extend `BaseError`.
45
+ */
46
+ declare class Err<T, E extends BaseError> implements IResult<T, E> {
47
+ error: E;
48
+ constructor(error: E);
49
+ isOk(): this is Ok<T, E>;
50
+ isErr(): this is Err<T, E>;
51
+ }
52
+
53
+ type CallbackOptions = {
54
+ method: "GET" | "POST" | "DELETE";
55
+ url: string;
56
+ body?: object;
57
+ };
58
+ type WrapperFunction = <T>(data: CallbackOptions) => Promise<Result<T, BaseError>>;
59
+
3
60
  declare module "iron-session" {
4
61
  interface IronSessionData<U, T> {
5
62
  user?: U;
@@ -26,15 +83,6 @@ interface IOAuthData {
26
83
  oAuthUrl: string;
27
84
  onErrorUrl?: string;
28
85
  }
29
- interface IResponseError {
30
- error: string;
31
- message: string;
32
- }
33
- interface IResponseSuccess<T, U> {
34
- error: null;
35
- data: T;
36
- details: U;
37
- }
38
86
  interface IAuthContext<U> {
39
87
  isLoggedIn: boolean;
40
88
  ready: boolean;
@@ -44,9 +92,21 @@ interface IAuthContext<U> {
44
92
  oAuth: (data: IOAuthData) => Promise<string>;
45
93
  logout: () => Promise<string>;
46
94
  refreshUser: (force?: boolean) => Promise<void>;
47
- get: <T, U = any>(url: string) => Promise<ResponseType<T, U>>;
48
- post: <T, U = any>(url: string, body: any) => Promise<ResponseType<T, U>>;
49
- del: <T, U = any>(url: string) => Promise<ResponseType<T, U>>;
95
+ get: <T>(props: {
96
+ url: string;
97
+ headers?: Record<string, string>;
98
+ }) => Promise<Result<T, BaseError>>;
99
+ post: <T>(props: {
100
+ url: string;
101
+ body: object;
102
+ headers?: Record<string, string>;
103
+ }) => Promise<Result<T, BaseError>>;
104
+ del: <T>(props: {
105
+ url: string;
106
+ headers?: Record<string, string>;
107
+ }) => Promise<Result<T, BaseError>>;
108
+ setHeader: (key: string, value: string) => void;
109
+ removeHeader: (key: string) => void;
50
110
  }
51
111
  interface IAuthContextOptions {
52
112
  children: React.ReactNode;
@@ -55,12 +115,14 @@ interface IAuthContextOptions {
55
115
  interface IChildProps {
56
116
  children?: React.ReactNode;
57
117
  }
58
- type ResponseType<T, U = any> = IResponseError | IResponseSuccess<T, U>;
59
118
 
60
- type LogoutProps = React.FC<React.ComponentProps<'div'> & IChildProps>;
119
+ type LogoutProps = React.FC<React.ComponentProps<"div"> & IChildProps>;
120
+
121
+ declare const useAuth: () => IAuthContext<any>;
122
+
123
+ declare const APIWrapper: <T>(wrapperFunction: (cb: WrapperFunction) => T) => () => T;
61
124
 
62
125
  declare const AuthProvider: <IU>({ children, routePrefix, }: IAuthContextOptions) => react_jsx_runtime.JSX.Element;
63
126
  declare const Logout: LogoutProps;
64
- declare const useAuth: () => IAuthContext<any>;
65
127
 
66
- export { AuthProvider, Logout, useAuth };
128
+ export { APIWrapper, AuthProvider, Logout, useAuth };
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/client/index.ts
22
22
  var client_exports = {};
23
23
  __export(client_exports, {
24
+ APIWrapper: () => APIWrapper,
24
25
  AuthProvider: () => AuthProvider,
25
26
  Logout: () => Logout,
26
27
  useAuth: () => useAuth
@@ -30,38 +31,86 @@ module.exports = __toCommonJS(client_exports);
30
31
  // src/client/contextProvider.tsx
31
32
  var import_react = require("react");
32
33
 
33
- // src/client/request.ts
34
- async function gFetch({
35
- url,
36
- options
37
- }) {
38
- const response = await fetch(url, {
39
- method: "GET",
40
- ...options
41
- });
42
- return await response.json();
34
+ // src/types/response.ts
35
+ var Ok = class {
36
+ constructor(value) {
37
+ this.value = value;
38
+ }
39
+ isOk() {
40
+ return true;
41
+ }
42
+ isErr() {
43
+ return false;
44
+ }
45
+ };
46
+ var Err = class {
47
+ constructor(error) {
48
+ this.error = error;
49
+ }
50
+ isOk() {
51
+ return false;
52
+ }
53
+ isErr() {
54
+ return true;
55
+ }
56
+ };
57
+ function ok(value) {
58
+ return new Ok(value);
43
59
  }
44
- async function pFetch({
45
- url,
46
- body
47
- }) {
48
- const response = await fetch(url, {
49
- method: "POST",
50
- headers: { "Content-Type": "application/json" },
51
- body: JSON.stringify(body)
52
- });
53
- return await response.json();
60
+ function err(error) {
61
+ return new Err(error);
54
62
  }
55
- async function dFetch({
63
+
64
+ // src/client/request.ts
65
+ var clientSideFetch = async ({
56
66
  url,
57
- options
58
- }) {
59
- const response = await fetch(url, {
60
- method: "DELETE",
67
+ method = "GET",
68
+ body,
69
+ ...options
70
+ }) => {
71
+ const opts = {
72
+ method,
73
+ headers: {
74
+ "Content-Type": "application/json",
75
+ ...options.headers
76
+ },
61
77
  ...options
62
- });
63
- return await response.json();
64
- }
78
+ };
79
+ try {
80
+ if (body) opts.body = JSON.stringify(body);
81
+ } catch (e) {
82
+ return err({
83
+ error: "PARSE_ERROR",
84
+ message: "Failed to parse request body as JSON"
85
+ });
86
+ }
87
+ try {
88
+ const response = await fetch(url, opts);
89
+ if (!response.ok) {
90
+ return err({
91
+ error: "RESPONSE_ERROR",
92
+ message: `HTTP error! status: ${response.status}`
93
+ });
94
+ }
95
+ try {
96
+ const res = await response.json();
97
+ if (res.error !== void 0) {
98
+ return err(res);
99
+ }
100
+ return ok(res);
101
+ } catch (e) {
102
+ return err({
103
+ error: "PARSE_ERROR",
104
+ message: "Failed to parse response as JSON"
105
+ });
106
+ }
107
+ } catch (e) {
108
+ return err({
109
+ error: "FETCH_ERROR",
110
+ message: "Failed to fetch the resource: " + String(e)
111
+ });
112
+ }
113
+ };
65
114
 
66
115
  // src/client/contextProvider.tsx
67
116
  var import_navigation = require("next/navigation");
@@ -75,6 +124,13 @@ var AuthContextProvider = ({
75
124
  const [ready, setReady] = (0, import_react.useState)(false);
76
125
  const [user, setUser] = (0, import_react.useState)({});
77
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
+ }, []);
78
134
  const loginRoute = routePrefix + "/login";
79
135
  const registerRoute = routePrefix + "/register";
80
136
  const logoutRoute = routePrefix + "/logout";
@@ -82,104 +138,98 @@ var AuthContextProvider = ({
82
138
  const oAuthRoute = routePrefix + "/oauth";
83
139
  const proxyRoute = routePrefix + "/proxy";
84
140
  const login = async (loginData) => {
85
- try {
86
- const res = await pFetch({
87
- url: loginRoute,
88
- body: loginData.data
89
- });
90
- setIsLoggedIn(true);
91
- if (res.error) throw new Error(res.message);
92
- return loginData.redirect || "/";
93
- } catch (e) {
94
- return `${loginData.onErrorUrl || "/"}?error=${e.message}`;
141
+ const res = await clientSideFetch({
142
+ url: loginRoute,
143
+ body: loginData.data
144
+ });
145
+ if (res.isErr()) {
146
+ return `${loginData.onErrorUrl || "/"}?error=${res.error.error}`;
95
147
  }
148
+ setIsLoggedIn(true);
149
+ return loginData.redirect || "/";
96
150
  };
97
151
  const register = async (registerData) => {
98
- try {
99
- const res = await pFetch({
100
- url: registerRoute,
101
- body: registerData.data
102
- });
103
- if (res.error) throw new Error(res.message);
104
- return `${registerData.redirect || "/"}?success=true`;
105
- } catch (e) {
106
- return `${registerData.onErrorUrl || "/"}?error=${e.message}`;
152
+ const res = await clientSideFetch({
153
+ url: registerRoute,
154
+ body: registerData.data
155
+ });
156
+ if (res.isErr()) {
157
+ return `${registerData.onErrorUrl || "/"}?error=${res.error.error}`;
107
158
  }
159
+ return `${registerData.redirect || "/"}?success=true`;
108
160
  };
109
161
  const oAuth = async ({
110
162
  state,
111
163
  oAuthUrl,
112
164
  onErrorUrl
113
165
  }) => {
114
- try {
115
- const url = new URL(oAuthRoute, "http://localhost/");
116
- url.searchParams.set("authUrl", oAuthUrl);
117
- if (state && state !== "/") url.searchParams.set("state", state);
118
- const response = await gFetch({ url: url.pathname + url.search });
119
- return response.data;
120
- } catch (e) {
121
- return `${onErrorUrl || "/"}?error=${e.message}`;
166
+ const url = new URL(oAuthRoute, "http://localhost/");
167
+ url.searchParams.set("authUrl", oAuthUrl);
168
+ if (state && state !== "/") url.searchParams.set("state", state);
169
+ const response = await clientSideFetch({
170
+ url: url.pathname + url.search
171
+ });
172
+ if (response.isErr()) {
173
+ return `${onErrorUrl || "/"}?error=${response.error.message}`;
122
174
  }
175
+ return response.value.data;
123
176
  };
124
177
  const logout = async () => {
125
- try {
126
- const data = await gFetch({
127
- url: logoutRoute,
128
- options: { cache: "no-store" }
129
- });
130
- setIsLoggedIn(false);
131
- setUser({});
132
- return data.data;
133
- } catch (e) {
134
- console.log(e);
178
+ const data = await clientSideFetch({
179
+ url: logoutRoute,
180
+ cache: "no-store"
181
+ });
182
+ if (data.isErr()) {
135
183
  return "/";
136
184
  }
185
+ setIsLoggedIn(false);
186
+ setUser({});
187
+ return data.value.data;
137
188
  };
138
189
  const refreshUser = async (force) => {
139
- try {
140
- const data = await gFetch({
141
- url: `${userRoute}${force ? "?force=true" : ""}`,
142
- options: { cache: "no-store" }
143
- });
144
- if (!data.error) {
145
- setUser(data.data);
146
- setIsLoggedIn(true);
147
- } else {
148
- setUser({});
149
- setIsLoggedIn(false);
150
- }
151
- setReady(true);
152
- router.refresh();
153
- } catch (e) {
154
- console.log("refreshUser error: ", e);
190
+ const data = await clientSideFetch({
191
+ url: `${userRoute}${force ? "?force=true" : ""}`,
192
+ cache: "no-store"
193
+ });
194
+ if (data.isErr()) {
195
+ setUser({});
196
+ setIsLoggedIn(false);
197
+ } else {
198
+ setUser(data.value.data);
155
199
  }
200
+ setReady(true);
201
+ router.refresh();
156
202
  };
157
- const get = async (url) => {
158
- try {
159
- const r = await gFetch({ url: `${proxyRoute}${url}` });
160
- return r;
161
- } catch (e) {
162
- return { error: "getRequestError", message: e.message };
163
- }
203
+ const get = async ({
204
+ url,
205
+ headers
206
+ }) => {
207
+ return clientSideFetch({
208
+ url: `${proxyRoute}${url}`,
209
+ headers: { ...customHeaders.current, ...headers }
210
+ });
164
211
  };
165
- const post = async (url, body) => {
166
- try {
167
- const r = await pFetch({
168
- url: `${proxyRoute}${url}`,
169
- body
170
- });
171
- return r;
172
- } catch (e) {
173
- return { error: "getRequestError", message: e.message };
174
- }
212
+ const post = async ({
213
+ url,
214
+ body,
215
+ headers
216
+ }) => {
217
+ return clientSideFetch({
218
+ method: "POST",
219
+ url: `${proxyRoute}${url}`,
220
+ body,
221
+ headers: { ...customHeaders.current, ...headers }
222
+ });
175
223
  };
176
- const del = async (url) => {
177
- try {
178
- const r = await dFetch({ url: `${proxyRoute}${url}` });
179
- return r;
180
- } catch (e) {
181
- return { error: "getRequestError", message: e.message };
182
- }
224
+ const del = async ({
225
+ url,
226
+ headers
227
+ }) => {
228
+ return clientSideFetch({
229
+ method: "DELETE",
230
+ url: `${proxyRoute}${url}`,
231
+ headers: { ...customHeaders.current, ...headers }
232
+ });
183
233
  };
184
234
  (0, import_react.useEffect)(() => {
185
235
  if (!ready) refreshUser();
@@ -195,24 +245,19 @@ var AuthContextProvider = ({
195
245
  oAuth,
196
246
  get,
197
247
  post,
198
- del
248
+ del,
249
+ setHeader,
250
+ removeHeader
199
251
  /* upload, */
200
252
  };
201
253
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AuthContext.Provider, { value: provider, children });
202
254
  };
203
255
  var contextProvider_default = AuthContextProvider;
204
256
 
205
- // src/client/useAuth.ts
206
- var import_react2 = require("react");
207
- var useAuthContext = () => {
208
- return (0, import_react2.useContext)(AuthContext);
209
- };
210
- var useAuth_default = useAuthContext;
211
-
212
257
  // src/client/Logout.tsx
213
258
  var import_jsx_runtime2 = require("react/jsx-runtime");
214
259
  var LogoutComponent = ({ children, ...props }) => {
215
- const { logout } = useAuth_default();
260
+ const { logout } = useAuth();
216
261
  const handleLogout = async (e) => {
217
262
  e.preventDefault();
218
263
  await logout();
@@ -221,12 +266,39 @@ var LogoutComponent = ({ children, ...props }) => {
221
266
  };
222
267
  var Logout_default = LogoutComponent;
223
268
 
269
+ // src/client/useAuth.ts
270
+ var import_react2 = require("react");
271
+ var useAuth = () => {
272
+ return (0, import_react2.useContext)(AuthContext);
273
+ };
274
+
275
+ // src/client/useAPIWrapper.tsx
276
+ var import_react3 = require("react");
277
+ var APIWrapper = (wrapperFunction) => {
278
+ return () => {
279
+ const { get, post, del } = (0, import_react3.useContext)(AuthContext);
280
+ return wrapperFunction(async (data) => {
281
+ const { method, url, body } = data;
282
+ switch (method) {
283
+ case "GET":
284
+ return get({ url });
285
+ case "POST":
286
+ return post({ url, body: body != null ? body : {} });
287
+ case "DELETE":
288
+ return del({ url });
289
+ default:
290
+ throw new Error("Unsupported method type");
291
+ }
292
+ });
293
+ };
294
+ };
295
+
224
296
  // src/client/index.ts
225
297
  var AuthProvider = contextProvider_default;
226
298
  var Logout = Logout_default;
227
- var useAuth = useAuth_default;
228
299
  // Annotate the CommonJS export names for ESM import in node:
229
300
  0 && (module.exports = {
301
+ APIWrapper,
230
302
  AuthProvider,
231
303
  Logout,
232
304
  useAuth