floppy-disk 2.9.0 → 2.10.0-beta.2

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/esm/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { hashStoreKey } from './utils';
2
+ export * from './utils/fetcher';
2
3
  export * from './vanilla';
3
4
  export * from './react';
package/esm/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { hashStoreKey } from './utils';
2
+ export * from './utils/fetcher';
2
3
  export * from './vanilla';
3
4
  export * from './react';
@@ -67,7 +67,7 @@ export const createQuery = (queryFn, options = {}) => {
67
67
  set({ isWaiting: true });
68
68
  else
69
69
  set({ isWaiting: true, isRefetching: true });
70
- const callQuery = () => {
70
+ const callQuery = (innerResolve = resolve) => {
71
71
  if (get().isGoingToRetry) {
72
72
  if (isLoading)
73
73
  set({ isGoingToRetry: false, isWaiting: true });
@@ -81,7 +81,7 @@ export const createQuery = (queryFn, options = {}) => {
81
81
  .then((response) => {
82
82
  if (preventReplaceResponse.get(keyHash)) {
83
83
  set({ isWaiting: false, isRefetching: false });
84
- return resolve(get());
84
+ return innerResolve(get());
85
85
  }
86
86
  responseAllPages.push(response);
87
87
  const newPageParam = getNextPageParam(response, responseAllPages.length, stateBeforeCallQuery);
@@ -121,11 +121,26 @@ export const createQuery = (queryFn, options = {}) => {
121
121
  }
122
122
  set(nextState);
123
123
  onSuccess(response, stateBeforeCallQuery);
124
+ onSettled(stateBeforeCallQuery);
125
+ innerResolve(get());
124
126
  })
125
127
  .catch((error) => {
126
128
  const prevState = get();
127
129
  const errorUpdatedAt = Date.now();
128
130
  const { shouldRetry, delay } = getRetryProps(error, prevState.retryCount);
131
+ if (shouldRetry) {
132
+ set({
133
+ isWaiting: false,
134
+ isGoingToRetry: true,
135
+ });
136
+ if (isClient) {
137
+ retryTimeoutId.set(keyHash, window.setTimeout(() => {
138
+ set({ retryCount: prevState.retryCount + 1 });
139
+ callQuery(innerResolve);
140
+ }, delay));
141
+ }
142
+ return;
143
+ }
129
144
  set(prevState.isSuccess && !prevState.isPreviousData
130
145
  ? {
131
146
  isWaiting: false,
@@ -138,7 +153,6 @@ export const createQuery = (queryFn, options = {}) => {
138
153
  : prevState.data,
139
154
  error,
140
155
  errorUpdatedAt,
141
- isGoingToRetry: shouldRetry,
142
156
  pageParam,
143
157
  hasNextPage: hasValue(pageParam),
144
158
  }
@@ -150,21 +164,12 @@ export const createQuery = (queryFn, options = {}) => {
150
164
  data: undefined,
151
165
  error,
152
166
  errorUpdatedAt,
153
- isGoingToRetry: shouldRetry,
154
167
  pageParam,
155
168
  hasNextPage: hasValue(pageParam),
156
169
  });
157
- if (shouldRetry && isClient) {
158
- retryTimeoutId.set(keyHash, window.setTimeout(() => {
159
- set({ retryCount: prevState.retryCount + 1 });
160
- callQuery();
161
- }, delay));
162
- }
163
170
  onError(error, stateBeforeCallQuery);
164
- })
165
- .finally(() => {
166
171
  onSettled(stateBeforeCallQuery);
167
- resolve(get());
172
+ innerResolve(get());
168
173
  });
169
174
  };
170
175
  callQuery();
@@ -207,26 +212,30 @@ export const createQuery = (queryFn, options = {}) => {
207
212
  hasNextPage: hasValue(newPageParam),
208
213
  });
209
214
  onSuccess(response, stateBeforeCallQuery);
215
+ onSettled(stateBeforeCallQuery);
216
+ resolve(get());
210
217
  })
211
218
  .catch((error) => {
212
219
  const prevState = get();
213
220
  const { shouldRetry, delay } = getRetryProps(error, prevState.retryNextPageCount);
221
+ if (shouldRetry) {
222
+ set({
223
+ isWaitingNextPage: false,
224
+ isGoingToRetryNextPage: true,
225
+ });
226
+ retryNextPageTimeoutId.set(keyHash, window.setTimeout(() => {
227
+ set({ retryNextPageCount: prevState.retryNextPageCount + 1 });
228
+ resolve(fetchNextPage());
229
+ }, delay));
230
+ return;
231
+ }
214
232
  set({
215
233
  isWaitingNextPage: false,
216
234
  isError: true,
217
235
  error,
218
236
  errorUpdatedAt: Date.now(),
219
- isGoingToRetryNextPage: shouldRetry,
220
237
  });
221
- if (shouldRetry) {
222
- retryNextPageTimeoutId.set(keyHash, window.setTimeout(() => {
223
- set({ retryNextPageCount: prevState.retryNextPageCount + 1 });
224
- fetchNextPage();
225
- }, delay));
226
- }
227
238
  onError(error, stateBeforeCallQuery);
228
- })
229
- .finally(() => {
230
239
  onSettled(stateBeforeCallQuery);
231
240
  resolve(get());
232
241
  });
@@ -0,0 +1,25 @@
1
+ type FetcherOptions<TResponse = any> = {
2
+ url: string;
3
+ query?: string;
4
+ params?: Record<string, string | number | boolean> | null;
5
+ payload?: any;
6
+ interceptRequest?: (requestOptions: RequestInit & {
7
+ url: string;
8
+ }) => (RequestInit & {
9
+ url: string;
10
+ }) | Promise<RequestInit & {
11
+ url: string;
12
+ }>;
13
+ interceptResponse?: (response: TResponse) => TResponse | Promise<TResponse>;
14
+ } & RequestInit;
15
+ /**
16
+ * Experimental fetcher - abstraction layer for query/mutation function creator.
17
+ *
18
+ * Can be used for REST or GraphQL.
19
+ *
20
+ * @see https://floppy-disk.vercel.app/docs/experimental
21
+ *
22
+ * @returns A function to fetch data
23
+ */
24
+ export declare const fetcher: <TResponse = any, TInput extends any[] = any[]>(options: FetcherOptions<TResponse> | ((...args: TInput) => FetcherOptions<TResponse>)) => (...args: TInput) => Promise<TResponse>;
25
+ export {};
@@ -0,0 +1,74 @@
1
+ /* eslint-disable no-throw-literal */
2
+ import { getValueOrComputedValue, identityFn } from '.';
3
+ const encodeParams = (params) => Object.entries(params)
4
+ .map((kv) => kv.map(encodeURIComponent).join('='))
5
+ .join('&');
6
+ /**
7
+ * Experimental fetcher - abstraction layer for query/mutation function creator.
8
+ *
9
+ * Can be used for REST or GraphQL.
10
+ *
11
+ * @see https://floppy-disk.vercel.app/docs/experimental
12
+ *
13
+ * @returns A function to fetch data
14
+ */
15
+ export const fetcher = (options) => async (...args) => {
16
+ const { url, query, params, payload, headers, interceptRequest = identityFn, interceptResponse, ...rest } = getValueOrComputedValue(options, ...args);
17
+ let autoOptions = {};
18
+ let searchParams = params;
19
+ if (query) {
20
+ // GraphQL
21
+ autoOptions = {
22
+ method: 'POST',
23
+ body: JSON.stringify({ query, variables: payload || args[0] }),
24
+ };
25
+ }
26
+ else if (rest.method && rest.method.toLowerCase() !== 'get') {
27
+ // REST - Mutation
28
+ autoOptions = {
29
+ body: JSON.stringify(payload === undefined ? args[0] : payload),
30
+ };
31
+ }
32
+ else {
33
+ // REST - Query
34
+ if (typeof options === 'object' && params === undefined)
35
+ searchParams = args[0];
36
+ }
37
+ const interceptedOptions = await interceptRequest({
38
+ url: searchParams ? [url, encodeParams(searchParams)].join('?') : url,
39
+ headers: { 'Content-Type': 'application/json', ...headers },
40
+ ...autoOptions,
41
+ ...rest,
42
+ });
43
+ const { url: finalUrl, ...finalOptions } = interceptedOptions;
44
+ const res = await fetch(finalUrl, finalOptions);
45
+ const contentType = res.headers.get('content-type');
46
+ if (contentType && contentType.includes('application/json')) {
47
+ const resJson = await res.json().catch(() => undefined);
48
+ if (resJson !== undefined) {
49
+ if (query && resJson.errors) {
50
+ throw { status: res.status, statusText: res.statusText, response: resJson };
51
+ }
52
+ if (res.ok) {
53
+ if (interceptResponse) {
54
+ try {
55
+ const finalResponse = await interceptResponse(resJson);
56
+ return finalResponse;
57
+ }
58
+ catch (error) {
59
+ throw {
60
+ status: res.status,
61
+ statusText: res.statusText,
62
+ response: resJson,
63
+ error,
64
+ };
65
+ }
66
+ }
67
+ return resJson;
68
+ }
69
+ throw { status: res.status, statusText: res.statusText, response: resJson };
70
+ }
71
+ }
72
+ const resText = await res.text().catch(() => undefined);
73
+ throw { status: res.status, statusText: res.statusText, response: resText };
74
+ };
package/lib/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { hashStoreKey } from './utils';
2
+ export * from './utils/fetcher';
2
3
  export * from './vanilla';
3
4
  export * from './react';
package/lib/index.js CHANGED
@@ -4,5 +4,6 @@ exports.hashStoreKey = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  var utils_1 = require("./utils");
6
6
  Object.defineProperty(exports, "hashStoreKey", { enumerable: true, get: function () { return utils_1.hashStoreKey; } });
7
+ tslib_1.__exportStar(require("./utils/fetcher"), exports);
7
8
  tslib_1.__exportStar(require("./vanilla"), exports);
8
9
  tslib_1.__exportStar(require("./react"), exports);
@@ -70,7 +70,7 @@ const createQuery = (queryFn, options = {}) => {
70
70
  set({ isWaiting: true });
71
71
  else
72
72
  set({ isWaiting: true, isRefetching: true });
73
- const callQuery = () => {
73
+ const callQuery = (innerResolve = resolve) => {
74
74
  if (get().isGoingToRetry) {
75
75
  if (isLoading)
76
76
  set({ isGoingToRetry: false, isWaiting: true });
@@ -84,7 +84,7 @@ const createQuery = (queryFn, options = {}) => {
84
84
  .then((response) => {
85
85
  if (preventReplaceResponse.get(keyHash)) {
86
86
  set({ isWaiting: false, isRefetching: false });
87
- return resolve(get());
87
+ return innerResolve(get());
88
88
  }
89
89
  responseAllPages.push(response);
90
90
  const newPageParam = getNextPageParam(response, responseAllPages.length, stateBeforeCallQuery);
@@ -124,11 +124,26 @@ const createQuery = (queryFn, options = {}) => {
124
124
  }
125
125
  set(nextState);
126
126
  onSuccess(response, stateBeforeCallQuery);
127
+ onSettled(stateBeforeCallQuery);
128
+ innerResolve(get());
127
129
  })
128
130
  .catch((error) => {
129
131
  const prevState = get();
130
132
  const errorUpdatedAt = Date.now();
131
133
  const { shouldRetry, delay } = getRetryProps(error, prevState.retryCount);
134
+ if (shouldRetry) {
135
+ set({
136
+ isWaiting: false,
137
+ isGoingToRetry: true,
138
+ });
139
+ if (utils_1.isClient) {
140
+ retryTimeoutId.set(keyHash, window.setTimeout(() => {
141
+ set({ retryCount: prevState.retryCount + 1 });
142
+ callQuery(innerResolve);
143
+ }, delay));
144
+ }
145
+ return;
146
+ }
132
147
  set(prevState.isSuccess && !prevState.isPreviousData
133
148
  ? {
134
149
  isWaiting: false,
@@ -141,7 +156,6 @@ const createQuery = (queryFn, options = {}) => {
141
156
  : prevState.data,
142
157
  error,
143
158
  errorUpdatedAt,
144
- isGoingToRetry: shouldRetry,
145
159
  pageParam,
146
160
  hasNextPage: (0, utils_1.hasValue)(pageParam),
147
161
  }
@@ -153,21 +167,12 @@ const createQuery = (queryFn, options = {}) => {
153
167
  data: undefined,
154
168
  error,
155
169
  errorUpdatedAt,
156
- isGoingToRetry: shouldRetry,
157
170
  pageParam,
158
171
  hasNextPage: (0, utils_1.hasValue)(pageParam),
159
172
  });
160
- if (shouldRetry && utils_1.isClient) {
161
- retryTimeoutId.set(keyHash, window.setTimeout(() => {
162
- set({ retryCount: prevState.retryCount + 1 });
163
- callQuery();
164
- }, delay));
165
- }
166
173
  onError(error, stateBeforeCallQuery);
167
- })
168
- .finally(() => {
169
174
  onSettled(stateBeforeCallQuery);
170
- resolve(get());
175
+ innerResolve(get());
171
176
  });
172
177
  };
173
178
  callQuery();
@@ -210,26 +215,30 @@ const createQuery = (queryFn, options = {}) => {
210
215
  hasNextPage: (0, utils_1.hasValue)(newPageParam),
211
216
  });
212
217
  onSuccess(response, stateBeforeCallQuery);
218
+ onSettled(stateBeforeCallQuery);
219
+ resolve(get());
213
220
  })
214
221
  .catch((error) => {
215
222
  const prevState = get();
216
223
  const { shouldRetry, delay } = getRetryProps(error, prevState.retryNextPageCount);
224
+ if (shouldRetry) {
225
+ set({
226
+ isWaitingNextPage: false,
227
+ isGoingToRetryNextPage: true,
228
+ });
229
+ retryNextPageTimeoutId.set(keyHash, window.setTimeout(() => {
230
+ set({ retryNextPageCount: prevState.retryNextPageCount + 1 });
231
+ resolve(fetchNextPage());
232
+ }, delay));
233
+ return;
234
+ }
217
235
  set({
218
236
  isWaitingNextPage: false,
219
237
  isError: true,
220
238
  error,
221
239
  errorUpdatedAt: Date.now(),
222
- isGoingToRetryNextPage: shouldRetry,
223
240
  });
224
- if (shouldRetry) {
225
- retryNextPageTimeoutId.set(keyHash, window.setTimeout(() => {
226
- set({ retryNextPageCount: prevState.retryNextPageCount + 1 });
227
- fetchNextPage();
228
- }, delay));
229
- }
230
241
  onError(error, stateBeforeCallQuery);
231
- })
232
- .finally(() => {
233
242
  onSettled(stateBeforeCallQuery);
234
243
  resolve(get());
235
244
  });
@@ -0,0 +1,25 @@
1
+ type FetcherOptions<TResponse = any> = {
2
+ url: string;
3
+ query?: string;
4
+ params?: Record<string, string | number | boolean> | null;
5
+ payload?: any;
6
+ interceptRequest?: (requestOptions: RequestInit & {
7
+ url: string;
8
+ }) => (RequestInit & {
9
+ url: string;
10
+ }) | Promise<RequestInit & {
11
+ url: string;
12
+ }>;
13
+ interceptResponse?: (response: TResponse) => TResponse | Promise<TResponse>;
14
+ } & RequestInit;
15
+ /**
16
+ * Experimental fetcher - abstraction layer for query/mutation function creator.
17
+ *
18
+ * Can be used for REST or GraphQL.
19
+ *
20
+ * @see https://floppy-disk.vercel.app/docs/experimental
21
+ *
22
+ * @returns A function to fetch data
23
+ */
24
+ export declare const fetcher: <TResponse = any, TInput extends any[] = any[]>(options: FetcherOptions<TResponse> | ((...args: TInput) => FetcherOptions<TResponse>)) => (...args: TInput) => Promise<TResponse>;
25
+ export {};
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetcher = void 0;
4
+ /* eslint-disable no-throw-literal */
5
+ const _1 = require(".");
6
+ const encodeParams = (params) => Object.entries(params)
7
+ .map((kv) => kv.map(encodeURIComponent).join('='))
8
+ .join('&');
9
+ /**
10
+ * Experimental fetcher - abstraction layer for query/mutation function creator.
11
+ *
12
+ * Can be used for REST or GraphQL.
13
+ *
14
+ * @see https://floppy-disk.vercel.app/docs/experimental
15
+ *
16
+ * @returns A function to fetch data
17
+ */
18
+ const fetcher = (options) => async (...args) => {
19
+ const { url, query, params, payload, headers, interceptRequest = _1.identityFn, interceptResponse, ...rest } = (0, _1.getValueOrComputedValue)(options, ...args);
20
+ let autoOptions = {};
21
+ let searchParams = params;
22
+ if (query) {
23
+ // GraphQL
24
+ autoOptions = {
25
+ method: 'POST',
26
+ body: JSON.stringify({ query, variables: payload || args[0] }),
27
+ };
28
+ }
29
+ else if (rest.method && rest.method.toLowerCase() !== 'get') {
30
+ // REST - Mutation
31
+ autoOptions = {
32
+ body: JSON.stringify(payload === undefined ? args[0] : payload),
33
+ };
34
+ }
35
+ else {
36
+ // REST - Query
37
+ if (typeof options === 'object' && params === undefined)
38
+ searchParams = args[0];
39
+ }
40
+ const interceptedOptions = await interceptRequest({
41
+ url: searchParams ? [url, encodeParams(searchParams)].join('?') : url,
42
+ headers: { 'Content-Type': 'application/json', ...headers },
43
+ ...autoOptions,
44
+ ...rest,
45
+ });
46
+ const { url: finalUrl, ...finalOptions } = interceptedOptions;
47
+ const res = await fetch(finalUrl, finalOptions);
48
+ const contentType = res.headers.get('content-type');
49
+ if (contentType && contentType.includes('application/json')) {
50
+ const resJson = await res.json().catch(() => undefined);
51
+ if (resJson !== undefined) {
52
+ if (query && resJson.errors) {
53
+ throw { status: res.status, statusText: res.statusText, response: resJson };
54
+ }
55
+ if (res.ok) {
56
+ if (interceptResponse) {
57
+ try {
58
+ const finalResponse = await interceptResponse(resJson);
59
+ return finalResponse;
60
+ }
61
+ catch (error) {
62
+ throw {
63
+ status: res.status,
64
+ statusText: res.statusText,
65
+ response: resJson,
66
+ error,
67
+ };
68
+ }
69
+ }
70
+ return resJson;
71
+ }
72
+ throw { status: res.status, statusText: res.statusText, response: resJson };
73
+ }
74
+ }
75
+ const resText = await res.text().catch(() => undefined);
76
+ throw { status: res.status, statusText: res.statusText, response: resText };
77
+ };
78
+ exports.fetcher = fetcher;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "floppy-disk",
3
- "version": "2.9.0",
3
+ "version": "2.10.0-beta.2",
4
4
  "description": "FloppyDisk - lightweight, simple, and powerful state management library",
5
5
  "keywords": [
6
6
  "state",