shelflife-react-hooks 1.0.18 → 1.0.20

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.
Files changed (44) hide show
  1. package/dist/index.cjs.js +23 -0
  2. package/dist/index.cjs.js.map +1 -1
  3. package/dist/index.d.cts +2 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.esm.js +23 -0
  6. package/dist/index.esm.js.map +1 -1
  7. package/package.json +36 -36
  8. package/src/context/AuthContext.tsx +161 -161
  9. package/src/context/InviteContext.tsx +74 -74
  10. package/src/context/ProductContext.tsx +131 -121
  11. package/src/context/RunningLowContext.tsx +100 -100
  12. package/src/context/ShoppingListContext.tsx +76 -76
  13. package/src/context/StorageContext.tsx +105 -105
  14. package/src/context/StorageItemContext.tsx +157 -157
  15. package/src/context/StorageMemberContext.tsx +84 -84
  16. package/src/context/UserContext.tsx +109 -109
  17. package/src/context/__tests__/contexts.test.tsx +370 -370
  18. package/src/context/api/authApi.ts +155 -155
  19. package/src/context/api/inviteApi.ts +65 -65
  20. package/src/context/api/productApi.ts +223 -201
  21. package/src/context/api/requestState.ts +24 -24
  22. package/src/context/api/runningLowApi.ts +141 -141
  23. package/src/context/api/shoppingListApi.ts +161 -159
  24. package/src/context/api/storageApi.ts +166 -166
  25. package/src/context/api/storageItemApi.ts +260 -260
  26. package/src/context/api/storageMemberApi.ts +84 -84
  27. package/src/context/api/userApi.ts +161 -161
  28. package/src/context/http.ts +22 -22
  29. package/src/index.ts +21 -21
  30. package/src/type/PaginatedResponse.ts +8 -8
  31. package/src/type/auth.ts +79 -79
  32. package/src/type/base.ts +21 -21
  33. package/src/type/item.ts +12 -12
  34. package/src/type/member.ts +6 -6
  35. package/src/type/models.ts +56 -56
  36. package/src/type/product.ts +11 -11
  37. package/src/type/requests.ts +60 -60
  38. package/src/type/runninglow.ts +13 -13
  39. package/src/type/shoppingList.ts +13 -13
  40. package/src/type/storage.ts +7 -7
  41. package/src/type/user.ts +11 -11
  42. package/tsconfig.json +46 -46
  43. package/tsup.config.ts +10 -10
  44. package/vitest.config.ts +8 -8
@@ -1,84 +1,84 @@
1
- import type { InviteMemberError } from '../../type/member.js';
2
- import type { StorageMember } from '../../type/models.js';
3
- import type { InviteMemberRequest } from '../../type/requests.js';
4
- import { buildAuthHeaders, normalizeBaseUrl, readJson } from '../http.js';
5
- import { runWithRequestState, type RequestStateHandlers } from './requestState.js';
6
-
7
- type StorageMemberApiConfig = RequestStateHandlers & {
8
- baseUrl: string;
9
- token: string | null;
10
- setMembers: (value: StorageMember[] | ((items: StorageMember[]) => StorageMember[])) => void;
11
- };
12
-
13
- export const fetchMembersRequest = async (
14
- config: StorageMemberApiConfig,
15
- storageId: number
16
- ): Promise<StorageMember[]> => runWithRequestState(config, async () => {
17
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
18
- const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/members`, {
19
- headers: buildAuthHeaders(config.token)
20
- });
21
-
22
- if (!response.ok) {
23
- throw new Error('Failed to fetch storage members');
24
- }
25
-
26
- const payload = await readJson<StorageMember[]>(response);
27
- if (payload) {
28
- config.setMembers(payload);
29
- return payload;
30
- }
31
-
32
- return [];
33
- });
34
-
35
- export const inviteMemberRequest = async (
36
- config: StorageMemberApiConfig,
37
- storageId: number,
38
- dto: InviteMemberRequest
39
- ): Promise<StorageMember> => runWithRequestState(config, async () => {
40
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
41
- const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/members`, {
42
- method: 'POST',
43
- headers: {
44
- ...buildAuthHeaders(config.token),
45
- 'Content-Type': 'application/json'
46
- },
47
- body: JSON.stringify(dto)
48
- });
49
-
50
- if (!response.ok) {
51
- const err = await readJson<InviteMemberError>(response);
52
-
53
- if(err?.email)
54
- throw err;
55
-
56
- throw new Error('Failed to invite member');
57
- }
58
-
59
- const payload = await readJson<StorageMember>(response);
60
- if (!payload) {
61
- throw new Error('Invite member response missing data');
62
- }
63
-
64
- config.setMembers((previous) => [payload, ...previous]);
65
- return payload;
66
- });
67
-
68
- export const removeMemberRequest = async (
69
- config: StorageMemberApiConfig,
70
- storageId: number,
71
- userId: number
72
- ): Promise<void> => runWithRequestState(config, async () => {
73
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
74
- const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/members/${userId}`, {
75
- method: 'DELETE',
76
- headers: buildAuthHeaders(config.token)
77
- });
78
-
79
- if (!response.ok) {
80
- throw new Error('Failed to remove member');
81
- }
82
-
83
- config.setMembers((previous) => previous.filter((member) => member.user.id !== userId));
84
- });
1
+ import type { InviteMemberError } from '../../type/member.js';
2
+ import type { StorageMember } from '../../type/models.js';
3
+ import type { InviteMemberRequest } from '../../type/requests.js';
4
+ import { buildAuthHeaders, normalizeBaseUrl, readJson } from '../http.js';
5
+ import { runWithRequestState, type RequestStateHandlers } from './requestState.js';
6
+
7
+ type StorageMemberApiConfig = RequestStateHandlers & {
8
+ baseUrl: string;
9
+ token: string | null;
10
+ setMembers: (value: StorageMember[] | ((items: StorageMember[]) => StorageMember[])) => void;
11
+ };
12
+
13
+ export const fetchMembersRequest = async (
14
+ config: StorageMemberApiConfig,
15
+ storageId: number
16
+ ): Promise<StorageMember[]> => runWithRequestState(config, async () => {
17
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
18
+ const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/members`, {
19
+ headers: buildAuthHeaders(config.token)
20
+ });
21
+
22
+ if (!response.ok) {
23
+ throw new Error('Failed to fetch storage members');
24
+ }
25
+
26
+ const payload = await readJson<StorageMember[]>(response);
27
+ if (payload) {
28
+ config.setMembers(payload);
29
+ return payload;
30
+ }
31
+
32
+ return [];
33
+ });
34
+
35
+ export const inviteMemberRequest = async (
36
+ config: StorageMemberApiConfig,
37
+ storageId: number,
38
+ dto: InviteMemberRequest
39
+ ): Promise<StorageMember> => runWithRequestState(config, async () => {
40
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
41
+ const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/members`, {
42
+ method: 'POST',
43
+ headers: {
44
+ ...buildAuthHeaders(config.token),
45
+ 'Content-Type': 'application/json'
46
+ },
47
+ body: JSON.stringify(dto)
48
+ });
49
+
50
+ if (!response.ok) {
51
+ const err = await readJson<InviteMemberError>(response);
52
+
53
+ if(err?.email)
54
+ throw err;
55
+
56
+ throw new Error('Failed to invite member');
57
+ }
58
+
59
+ const payload = await readJson<StorageMember>(response);
60
+ if (!payload) {
61
+ throw new Error('Invite member response missing data');
62
+ }
63
+
64
+ config.setMembers((previous) => [payload, ...previous]);
65
+ return payload;
66
+ });
67
+
68
+ export const removeMemberRequest = async (
69
+ config: StorageMemberApiConfig,
70
+ storageId: number,
71
+ userId: number
72
+ ): Promise<void> => runWithRequestState(config, async () => {
73
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
74
+ const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/members/${userId}`, {
75
+ method: 'DELETE',
76
+ headers: buildAuthHeaders(config.token)
77
+ });
78
+
79
+ if (!response.ok) {
80
+ throw new Error('Failed to remove member');
81
+ }
82
+
83
+ config.setMembers((previous) => previous.filter((member) => member.user.id !== userId));
84
+ });
@@ -1,161 +1,161 @@
1
- import type { User } from '../../type/models.js';
2
- import type { ChangeUserDataRequest } from '../../type/requests.js';
3
- import type { UpdateUserError } from '../../type/user.js';
4
- import { buildAuthHeaders, normalizeBaseUrl, readJson } from '../http.js';
5
- import { runWithRequestState, type RequestStateHandlers } from './requestState.js';
6
-
7
- type UserApiConfig = RequestStateHandlers & {
8
- baseUrl: string;
9
- token: string | null;
10
- setUsers: (value: User[] | ((items: User[]) => User[])) => void;
11
- setUser: (value: User | null | ((prev: User | null) => User | null)) => void;
12
- };
13
-
14
- const updateById = (items: User[], updated: User): User[] => {
15
- const index = items.findIndex((item) => item.id === updated.id);
16
- if (index === -1) {
17
- return [updated, ...items];
18
- }
19
-
20
- const next = [...items];
21
- next[index] = updated;
22
- return next;
23
- };
24
-
25
- export const fetchUsersRequest = async (config: UserApiConfig): Promise<User[]> => runWithRequestState(
26
- config,
27
- async () => {
28
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
29
- const response = await fetch(`${normalizedBaseUrl}/api/users`, {
30
- headers: buildAuthHeaders(config.token)
31
- });
32
-
33
- if (!response.ok) {
34
- throw new Error('Failed to fetch users');
35
- }
36
-
37
- const payload = await readJson<User[]>(response);
38
- if (payload) {
39
- config.setUsers(payload);
40
- return payload;
41
- }
42
-
43
- return [];
44
- }
45
- );
46
-
47
- export const fetchUserRequest = async (
48
- config: UserApiConfig,
49
- id: number
50
- ): Promise<User | null> => runWithRequestState(config, async () => {
51
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
52
- const response = await fetch(`${normalizedBaseUrl}/api/users/${id}`, {
53
- headers: buildAuthHeaders(config.token)
54
- });
55
-
56
- if (response.status === 404) {
57
- config.setUser(null);
58
- return null;
59
- }
60
-
61
- if (!response.ok) {
62
- throw new Error('Failed to fetch user');
63
- }
64
-
65
- const payload = await readJson<User>(response);
66
- if (!payload) {
67
- throw new Error('User response missing data');
68
- }
69
-
70
- config.setUser(payload);
71
- config.setUsers((previous) => updateById(previous, payload));
72
- return payload;
73
- });
74
-
75
- export const updateUserRequest = async (
76
- config: UserApiConfig,
77
- id: number,
78
- dto: ChangeUserDataRequest
79
- ): Promise<User> => runWithRequestState(config, async () => {
80
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
81
- const response = await fetch(`${normalizedBaseUrl}/api/users/${id}`, {
82
- method: 'PATCH',
83
- headers: {
84
- ...buildAuthHeaders(config.token),
85
- 'Content-Type': 'application/json'
86
- },
87
- body: JSON.stringify(dto)
88
- });
89
-
90
- if (!response.ok) {
91
- const update = await readJson<UpdateUserError>(response);
92
-
93
- if (update?.email || update?.username)
94
- throw update;
95
-
96
- throw new Error('Failed to update user');
97
- }
98
-
99
- const payload = await readJson<User>(response);
100
- if (!payload) {
101
- throw new Error('Update user response missing data');
102
- }
103
-
104
- config.setUsers((previous) => updateById(previous, payload));
105
- config.setUser(payload);
106
- return payload;
107
- });
108
-
109
- export const deleteUserRequest = async (
110
- config: UserApiConfig,
111
- id: number
112
- ): Promise<void> => runWithRequestState(config, async () => {
113
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
114
- const response = await fetch(`${normalizedBaseUrl}/api/users/${id}`, {
115
- method: 'DELETE',
116
- headers: buildAuthHeaders(config.token)
117
- });
118
-
119
- if (!response.ok) {
120
- throw new Error('Failed to delete user');
121
- }
122
-
123
- config.setUsers((previous) => previous.filter((item) => item.id !== id));
124
- config.setUser((current) => (current?.id === id ? null : current));
125
- });
126
-
127
- export const getUserPfpRequest = async (
128
- config: UserApiConfig,
129
- id: number
130
- ): Promise<Blob> => runWithRequestState(config, async () => {
131
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
132
- const response = await fetch(`${normalizedBaseUrl}/api/users/${id}/pfp`, {
133
- headers: buildAuthHeaders(config.token)
134
- });
135
-
136
- if (!response.ok) {
137
- throw new Error('Failed to fetch user pfp');
138
- }
139
-
140
- return response.blob();
141
- });
142
-
143
- export const uploadUserPfpRequest = async (
144
- config: UserApiConfig,
145
- id: number,
146
- file: Blob
147
- ): Promise<void> => runWithRequestState(config, async () => {
148
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
149
- const formData = new FormData();
150
- formData.append('pfp', file);
151
-
152
- const response = await fetch(`${normalizedBaseUrl}/api/users/${id}/pfp`, {
153
- method: 'POST',
154
- headers: buildAuthHeaders(config.token),
155
- body: formData
156
- });
157
-
158
- if (!response.ok) {
159
- throw new Error('Failed to upload user pfp');
160
- }
161
- });
1
+ import type { User } from '../../type/models.js';
2
+ import type { ChangeUserDataRequest } from '../../type/requests.js';
3
+ import type { UpdateUserError } from '../../type/user.js';
4
+ import { buildAuthHeaders, normalizeBaseUrl, readJson } from '../http.js';
5
+ import { runWithRequestState, type RequestStateHandlers } from './requestState.js';
6
+
7
+ type UserApiConfig = RequestStateHandlers & {
8
+ baseUrl: string;
9
+ token: string | null;
10
+ setUsers: (value: User[] | ((items: User[]) => User[])) => void;
11
+ setUser: (value: User | null | ((prev: User | null) => User | null)) => void;
12
+ };
13
+
14
+ const updateById = (items: User[], updated: User): User[] => {
15
+ const index = items.findIndex((item) => item.id === updated.id);
16
+ if (index === -1) {
17
+ return [updated, ...items];
18
+ }
19
+
20
+ const next = [...items];
21
+ next[index] = updated;
22
+ return next;
23
+ };
24
+
25
+ export const fetchUsersRequest = async (config: UserApiConfig): Promise<User[]> => runWithRequestState(
26
+ config,
27
+ async () => {
28
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
29
+ const response = await fetch(`${normalizedBaseUrl}/api/users`, {
30
+ headers: buildAuthHeaders(config.token)
31
+ });
32
+
33
+ if (!response.ok) {
34
+ throw new Error('Failed to fetch users');
35
+ }
36
+
37
+ const payload = await readJson<User[]>(response);
38
+ if (payload) {
39
+ config.setUsers(payload);
40
+ return payload;
41
+ }
42
+
43
+ return [];
44
+ }
45
+ );
46
+
47
+ export const fetchUserRequest = async (
48
+ config: UserApiConfig,
49
+ id: number
50
+ ): Promise<User | null> => runWithRequestState(config, async () => {
51
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
52
+ const response = await fetch(`${normalizedBaseUrl}/api/users/${id}`, {
53
+ headers: buildAuthHeaders(config.token)
54
+ });
55
+
56
+ if (response.status === 404) {
57
+ config.setUser(null);
58
+ return null;
59
+ }
60
+
61
+ if (!response.ok) {
62
+ throw new Error('Failed to fetch user');
63
+ }
64
+
65
+ const payload = await readJson<User>(response);
66
+ if (!payload) {
67
+ throw new Error('User response missing data');
68
+ }
69
+
70
+ config.setUser(payload);
71
+ config.setUsers((previous) => updateById(previous, payload));
72
+ return payload;
73
+ });
74
+
75
+ export const updateUserRequest = async (
76
+ config: UserApiConfig,
77
+ id: number,
78
+ dto: ChangeUserDataRequest
79
+ ): Promise<User> => runWithRequestState(config, async () => {
80
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
81
+ const response = await fetch(`${normalizedBaseUrl}/api/users/${id}`, {
82
+ method: 'PATCH',
83
+ headers: {
84
+ ...buildAuthHeaders(config.token),
85
+ 'Content-Type': 'application/json'
86
+ },
87
+ body: JSON.stringify(dto)
88
+ });
89
+
90
+ if (!response.ok) {
91
+ const update = await readJson<UpdateUserError>(response);
92
+
93
+ if (update?.email || update?.username)
94
+ throw update;
95
+
96
+ throw new Error('Failed to update user');
97
+ }
98
+
99
+ const payload = await readJson<User>(response);
100
+ if (!payload) {
101
+ throw new Error('Update user response missing data');
102
+ }
103
+
104
+ config.setUsers((previous) => updateById(previous, payload));
105
+ config.setUser(payload);
106
+ return payload;
107
+ });
108
+
109
+ export const deleteUserRequest = async (
110
+ config: UserApiConfig,
111
+ id: number
112
+ ): Promise<void> => runWithRequestState(config, async () => {
113
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
114
+ const response = await fetch(`${normalizedBaseUrl}/api/users/${id}`, {
115
+ method: 'DELETE',
116
+ headers: buildAuthHeaders(config.token)
117
+ });
118
+
119
+ if (!response.ok) {
120
+ throw new Error('Failed to delete user');
121
+ }
122
+
123
+ config.setUsers((previous) => previous.filter((item) => item.id !== id));
124
+ config.setUser((current) => (current?.id === id ? null : current));
125
+ });
126
+
127
+ export const getUserPfpRequest = async (
128
+ config: UserApiConfig,
129
+ id: number
130
+ ): Promise<Blob> => runWithRequestState(config, async () => {
131
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
132
+ const response = await fetch(`${normalizedBaseUrl}/api/users/${id}/pfp`, {
133
+ headers: buildAuthHeaders(config.token)
134
+ });
135
+
136
+ if (!response.ok) {
137
+ throw new Error('Failed to fetch user pfp');
138
+ }
139
+
140
+ return response.blob();
141
+ });
142
+
143
+ export const uploadUserPfpRequest = async (
144
+ config: UserApiConfig,
145
+ id: number,
146
+ file: Blob
147
+ ): Promise<void> => runWithRequestState(config, async () => {
148
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
149
+ const formData = new FormData();
150
+ formData.append('pfp', file);
151
+
152
+ const response = await fetch(`${normalizedBaseUrl}/api/users/${id}/pfp`, {
153
+ method: 'POST',
154
+ headers: buildAuthHeaders(config.token),
155
+ body: formData
156
+ });
157
+
158
+ if (!response.ok) {
159
+ throw new Error('Failed to upload user pfp');
160
+ }
161
+ });
@@ -1,22 +1,22 @@
1
- export const normalizeBaseUrl = (baseUrl: string): string => baseUrl.replace(/\/+$/, '');
2
-
3
- export const buildAuthHeaders = (token: string | null): HeadersInit => {
4
- if (!token) {
5
- return {};
6
- }
7
-
8
- return {
9
- Authorization: `Bearer ${token}`
10
- };
11
- };
12
-
13
- export const readJson = async <T,>(response: Response): Promise<T | null> => {
14
- const contentType = response.headers.get('content-type');
15
- if (!contentType || !contentType.includes('application/json')) {
16
- return null;
17
- }
18
-
19
- return (await response.json()) as T;
20
- };
21
-
22
- export const readArrayBuffer = async (response: Response): Promise<ArrayBuffer> => response.arrayBuffer();
1
+ export const normalizeBaseUrl = (baseUrl: string): string => baseUrl.replace(/\/+$/, '');
2
+
3
+ export const buildAuthHeaders = (token: string | null): HeadersInit => {
4
+ if (!token) {
5
+ return {};
6
+ }
7
+
8
+ return {
9
+ Authorization: `Bearer ${token}`
10
+ };
11
+ };
12
+
13
+ export const readJson = async <T,>(response: Response): Promise<T | null> => {
14
+ const contentType = response.headers.get('content-type');
15
+ if (!contentType || !contentType.includes('application/json')) {
16
+ return null;
17
+ }
18
+
19
+ return (await response.json()) as T;
20
+ };
21
+
22
+ export const readArrayBuffer = async (response: Response): Promise<ArrayBuffer> => response.arrayBuffer();
package/src/index.ts CHANGED
@@ -1,22 +1,22 @@
1
- export * from './type/auth.js';
2
- export * from './type/base.js';
3
- export * from './type/item.js';
4
- export * from './type/member.js';
5
- export * from './type/models.js';
6
- export * from './type/PaginatedResponse.js';
7
- export * from './type/product.js';
8
- export * from './type/requests.js';
9
- export * from './type/runninglow.js';
10
- export * from './type/shoppingList.js';
11
- export * from './type/storage.js';
12
- export * from './type/user.js';
13
- export * from './context/AuthContext.js';
14
- export * from './context/http.js';
15
- export * from './context/InviteContext.js';
16
- export * from './context/ProductContext.js';
17
- export * from './context/RunningLowContext.js';
18
- export * from './context/StorageContext.js';
19
- export * from './context/StorageItemContext.js';
20
- export * from './context/StorageMemberContext.js';
21
- export * from './context/UserContext.js';
1
+ export * from './type/auth.js';
2
+ export * from './type/base.js';
3
+ export * from './type/item.js';
4
+ export * from './type/member.js';
5
+ export * from './type/models.js';
6
+ export * from './type/PaginatedResponse.js';
7
+ export * from './type/product.js';
8
+ export * from './type/requests.js';
9
+ export * from './type/runninglow.js';
10
+ export * from './type/shoppingList.js';
11
+ export * from './type/storage.js';
12
+ export * from './type/user.js';
13
+ export * from './context/AuthContext.js';
14
+ export * from './context/http.js';
15
+ export * from './context/InviteContext.js';
16
+ export * from './context/ProductContext.js';
17
+ export * from './context/RunningLowContext.js';
18
+ export * from './context/StorageContext.js';
19
+ export * from './context/StorageItemContext.js';
20
+ export * from './context/StorageMemberContext.js';
21
+ export * from './context/UserContext.js';
22
22
  export * from './context/ShoppingListContext.js';
@@ -1,9 +1,9 @@
1
- export type PaginatedResponse<T> = {
2
- data: T[];
3
- currentPage: number;
4
- totalPages: number;
5
- totalItems: number;
6
- pageSize: number;
7
- hasNext: boolean;
8
- hasPrevious: boolean;
1
+ export type PaginatedResponse<T> = {
2
+ data: T[];
3
+ currentPage: number;
4
+ totalPages: number;
5
+ totalItems: number;
6
+ pageSize: number;
7
+ hasNext: boolean;
8
+ hasPrevious: boolean;
9
9
  }