@data-client/core 0.2.1 → 0.4.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/dist/index.js +49 -0
- package/dist/index.umd.min.js +1 -1
- package/dist/next.js +27 -0
- package/legacy/actionTypes.js +2 -1
- package/legacy/controller/Controller.js +21 -1
- package/legacy/controller/createExpireAll.js +8 -0
- package/legacy/newActions.js +1 -1
- package/legacy/state/reducer/createReducer.js +5 -2
- package/legacy/state/reducer/expireReducer.js +15 -0
- package/lib/actionTypes.d.ts +1 -0
- package/lib/actionTypes.d.ts.map +1 -1
- package/lib/actionTypes.js +2 -1
- package/lib/controller/Controller.d.ts +16 -0
- package/lib/controller/Controller.d.ts.map +1 -1
- package/lib/controller/Controller.js +21 -1
- package/lib/controller/createExpireAll.d.ts +3 -0
- package/lib/controller/createExpireAll.d.ts.map +1 -0
- package/lib/controller/createExpireAll.js +8 -0
- package/lib/newActions.d.ts +6 -2
- package/lib/newActions.d.ts.map +1 -1
- package/lib/newActions.js +1 -1
- package/lib/state/reducer/createReducer.d.ts.map +1 -1
- package/lib/state/reducer/createReducer.js +5 -2
- package/lib/state/reducer/expireReducer.d.ts +36 -0
- package/lib/state/reducer/expireReducer.d.ts.map +1 -0
- package/lib/state/reducer/expireReducer.js +19 -0
- package/package.json +1 -1
- package/src/actionTypes.ts +1 -0
- package/src/controller/Controller.ts +34 -0
- package/src/controller/__tests__/Controller.ts +76 -2
- package/src/controller/createExpireAll.ts +11 -0
- package/src/newActions.ts +8 -0
- package/src/state/reducer/createReducer.ts +5 -0
- package/src/state/reducer/expireReducer.ts +20 -0
- package/ts3.4/actionTypes.d.ts +1 -0
- package/ts3.4/controller/Controller.d.ts +16 -0
- package/ts3.4/controller/createExpireAll.d.ts +3 -0
- package/ts3.4/newActions.d.ts +6 -2
- package/ts3.4/state/reducer/expireReducer.d.ts +36 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Endpoint, Entity } from '@data-client/endpoint';
|
|
2
|
+
import { normalize } from '@data-client/normalizr';
|
|
2
3
|
import { CoolerArticleResource } from '__tests__/new';
|
|
4
|
+
import { createEntityMeta } from '__tests__/utils';
|
|
3
5
|
|
|
4
6
|
import { ExpiryStatus } from '../..';
|
|
5
7
|
import { initialState } from '../../state/reducer/createReducer';
|
|
6
|
-
import
|
|
8
|
+
import Controller from '../Controller';
|
|
7
9
|
|
|
8
10
|
function ignoreError(e: Event) {
|
|
9
11
|
e.preventDefault();
|
|
@@ -19,11 +21,83 @@ afterEach(() => {
|
|
|
19
21
|
|
|
20
22
|
describe('Controller', () => {
|
|
21
23
|
it('warns when dispatching during middleware setup', () => {
|
|
22
|
-
const controller = new
|
|
24
|
+
const controller = new Controller();
|
|
23
25
|
expect(() =>
|
|
24
26
|
controller.fetch(CoolerArticleResource.get, { id: 5 }),
|
|
25
27
|
).toThrowErrorMatchingInlineSnapshot(
|
|
26
28
|
`"Dispatching while constructing your middleware is not allowed. Other middleware would not be applied to this dispatch."`,
|
|
27
29
|
);
|
|
28
30
|
});
|
|
31
|
+
|
|
32
|
+
describe('fetchIfStale', () => {
|
|
33
|
+
it('should NOT fetch if result is NOT stale', () => {
|
|
34
|
+
const payload = {
|
|
35
|
+
id: 5,
|
|
36
|
+
title: 'hi ho',
|
|
37
|
+
content: 'whatever',
|
|
38
|
+
tags: ['a', 'best', 'react'],
|
|
39
|
+
};
|
|
40
|
+
const { entities, result } = normalize(
|
|
41
|
+
payload,
|
|
42
|
+
CoolerArticleResource.get.schema,
|
|
43
|
+
);
|
|
44
|
+
const fetchKey = CoolerArticleResource.get.key({ id: payload.id });
|
|
45
|
+
const state = {
|
|
46
|
+
...initialState,
|
|
47
|
+
entities,
|
|
48
|
+
results: {
|
|
49
|
+
[fetchKey]: result,
|
|
50
|
+
},
|
|
51
|
+
entityMeta: createEntityMeta(entities),
|
|
52
|
+
meta: {
|
|
53
|
+
[fetchKey]: {
|
|
54
|
+
date: Date.now(),
|
|
55
|
+
expiresAt: Date.now() + 10000,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
const getState = () => state;
|
|
60
|
+
const controller = new Controller({
|
|
61
|
+
dispatch: jest.fn(() => Promise.resolve()),
|
|
62
|
+
getState,
|
|
63
|
+
});
|
|
64
|
+
controller.fetchIfStale(CoolerArticleResource.get, { id: payload.id });
|
|
65
|
+
expect(controller.dispatch.mock.calls.length).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
it('should fetch if result stale', () => {
|
|
68
|
+
const payload = {
|
|
69
|
+
id: 5,
|
|
70
|
+
title: 'hi ho',
|
|
71
|
+
content: 'whatever',
|
|
72
|
+
tags: ['a', 'best', 'react'],
|
|
73
|
+
};
|
|
74
|
+
const { entities, result } = normalize(
|
|
75
|
+
payload,
|
|
76
|
+
CoolerArticleResource.get.schema,
|
|
77
|
+
);
|
|
78
|
+
const fetchKey = CoolerArticleResource.get.key({ id: payload.id });
|
|
79
|
+
const state = {
|
|
80
|
+
...initialState,
|
|
81
|
+
entities,
|
|
82
|
+
results: {
|
|
83
|
+
[fetchKey]: result,
|
|
84
|
+
},
|
|
85
|
+
entityMeta: createEntityMeta(entities),
|
|
86
|
+
meta: {
|
|
87
|
+
[fetchKey]: {
|
|
88
|
+
date: 0,
|
|
89
|
+
expiresAt: 0,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
const getState = () => state;
|
|
94
|
+
const controller = new Controller({
|
|
95
|
+
dispatch: jest.fn(() => Promise.resolve()),
|
|
96
|
+
getState,
|
|
97
|
+
});
|
|
98
|
+
controller.fetchIfStale(CoolerArticleResource.get, { id: payload.id });
|
|
99
|
+
|
|
100
|
+
expect(controller.dispatch.mock.calls.length).toBe(1);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
29
103
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { EXPIREALL_TYPE } from '../actionTypes.js';
|
|
2
|
+
import type { ExpireAllAction } from '../types.js';
|
|
3
|
+
|
|
4
|
+
export default function createExpireAll(
|
|
5
|
+
testKey: (key: string) => boolean,
|
|
6
|
+
): ExpireAllAction {
|
|
7
|
+
return {
|
|
8
|
+
type: EXPIREALL_TYPE,
|
|
9
|
+
testKey,
|
|
10
|
+
};
|
|
11
|
+
}
|
package/src/newActions.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
GC_TYPE,
|
|
15
15
|
OPTIMISTIC_TYPE,
|
|
16
16
|
INVALIDATEALL_TYPE,
|
|
17
|
+
EXPIREALL_TYPE,
|
|
17
18
|
} from './actionTypes.js';
|
|
18
19
|
import type { EndpointUpdateFunction } from './controller/types.js';
|
|
19
20
|
|
|
@@ -112,6 +113,12 @@ export interface UnsubscribeAction<
|
|
|
112
113
|
};
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
/* EXPIRY */
|
|
117
|
+
export interface ExpireAllAction {
|
|
118
|
+
type: typeof EXPIREALL_TYPE;
|
|
119
|
+
testKey: (key: string) => boolean;
|
|
120
|
+
}
|
|
121
|
+
|
|
115
122
|
/* INVALIDATE */
|
|
116
123
|
export interface InvalidateAllAction {
|
|
117
124
|
type: typeof INVALIDATEALL_TYPE;
|
|
@@ -146,5 +153,6 @@ export type ActionTypes =
|
|
|
146
153
|
| UnsubscribeAction
|
|
147
154
|
| InvalidateAction
|
|
148
155
|
| InvalidateAllAction
|
|
156
|
+
| ExpireAllAction
|
|
149
157
|
| ResetAction
|
|
150
158
|
| GCAction;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { expireReducer } from './expireReducer.js';
|
|
1
2
|
import { fetchReducer } from './fetchReducer.js';
|
|
2
3
|
import { invalidateReducer } from './invalidateReducer.js';
|
|
3
4
|
import { setReducer } from './setReducer.js';
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
GC_TYPE,
|
|
10
11
|
OPTIMISTIC_TYPE,
|
|
11
12
|
INVALIDATEALL_TYPE,
|
|
13
|
+
EXPIREALL_TYPE,
|
|
12
14
|
} from '../../actionTypes.js';
|
|
13
15
|
import type Controller from '../../controller/Controller.js';
|
|
14
16
|
import type { ActionTypes, State } from '../../types.js';
|
|
@@ -43,6 +45,9 @@ export default function createReducer(controller: Controller): ReducerType {
|
|
|
43
45
|
case INVALIDATE_TYPE:
|
|
44
46
|
return invalidateReducer(state, action);
|
|
45
47
|
|
|
48
|
+
case EXPIREALL_TYPE:
|
|
49
|
+
return expireReducer(state, action);
|
|
50
|
+
|
|
46
51
|
case RESET_TYPE:
|
|
47
52
|
return { ...initialState, lastReset: action.date };
|
|
48
53
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { State, ExpireAllAction } from '../../types.js';
|
|
2
|
+
|
|
3
|
+
export function expireReducer(state: State<unknown>, action: ExpireAllAction) {
|
|
4
|
+
const meta = { ...state.meta };
|
|
5
|
+
|
|
6
|
+
Object.keys(meta).forEach(key => {
|
|
7
|
+
if (action.testKey(key)) {
|
|
8
|
+
meta[key] = {
|
|
9
|
+
...meta[key],
|
|
10
|
+
// 1 instead of 0 so we can do 'falsy' checks to see if it is set
|
|
11
|
+
expiresAt: 1,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
...state,
|
|
18
|
+
meta,
|
|
19
|
+
};
|
|
20
|
+
}
|
package/ts3.4/actionTypes.d.ts
CHANGED
|
@@ -8,5 +8,6 @@ export declare const SUBSCRIBE_TYPE: "rest-hooks/subscribe";
|
|
|
8
8
|
export declare const UNSUBSCRIBE_TYPE: "rest-hook/unsubscribe";
|
|
9
9
|
export declare const INVALIDATE_TYPE: "rest-hooks/invalidate";
|
|
10
10
|
export declare const INVALIDATEALL_TYPE: "rest-hooks/invalidateall";
|
|
11
|
+
export declare const EXPIREALL_TYPE: "rest-hooks/expireall";
|
|
11
12
|
export declare const GC_TYPE: "rest-hooks/gc";
|
|
12
13
|
//# sourceMappingURL=actionTypes.d.ts.map
|
|
@@ -38,6 +38,13 @@ export default class Controller<D extends GenericDispatch = DataClientDispatch>
|
|
|
38
38
|
fetch: <E extends EndpointInterface<FetchFunction, Schema | undefined, boolean | undefined> & {
|
|
39
39
|
update?: EndpointUpdateFunction<E> | undefined;
|
|
40
40
|
}>(endpoint: E, ...args_0: Parameters<E>) => E["schema"] extends null | undefined ? ReturnType<E> : Promise<Denormalize<E["schema"]>>;
|
|
41
|
+
/**
|
|
42
|
+
* Fetches only if endpoint is considered 'stale'; otherwise returns undefined
|
|
43
|
+
* @see https://dataclient.io/docs/api/Controller#fetchIfStale
|
|
44
|
+
*/
|
|
45
|
+
fetchIfStale: <E extends EndpointInterface<FetchFunction, Schema | undefined, boolean | undefined> & {
|
|
46
|
+
update?: EndpointUpdateFunction<E> | undefined;
|
|
47
|
+
}>(endpoint: E, ...args_0: Parameters<E>) => (E["schema"] extends null | undefined ? ReturnType<E> : Promise<Denormalize<E["schema"]>>) | undefined;
|
|
41
48
|
/**
|
|
42
49
|
* Forces refetching and suspense on useSuspense with the same Endpoint and parameters.
|
|
43
50
|
* @see https://resthooks.io/docs/api/Controller#invalidate
|
|
@@ -50,10 +57,19 @@ export default class Controller<D extends GenericDispatch = DataClientDispatch>
|
|
|
50
57
|
/**
|
|
51
58
|
* Forces refetching and suspense on useSuspense on all matching endpoint result keys.
|
|
52
59
|
* @see https://resthooks.io/docs/api/Controller#invalidateAll
|
|
60
|
+
* @returns Promise that resolves when invalidation is commited.
|
|
53
61
|
*/
|
|
54
62
|
invalidateAll: (options: {
|
|
55
63
|
testKey: (key: string) => boolean;
|
|
56
64
|
}) => Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Sets all matching endpoint result keys to be STALE.
|
|
67
|
+
* @see https://dataclient.io/docs/api/Controller#expireAll
|
|
68
|
+
* @returns Promise that resolves when expiry is commited. *NOT* fetch promise
|
|
69
|
+
*/
|
|
70
|
+
expireAll: (options: {
|
|
71
|
+
testKey: (key: string) => boolean;
|
|
72
|
+
}) => Promise<void>;
|
|
57
73
|
/**
|
|
58
74
|
* Resets the entire Rest Hooks cache. All inflight requests will not resolve.
|
|
59
75
|
* @see https://resthooks.io/docs/api/Controller#resetEntireStore
|
package/ts3.4/newActions.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EndpointInterface, ResolveType, UnknownError } from '@data-client/normalizr';
|
|
2
|
-
import { SET_TYPE, RESET_TYPE, FETCH_TYPE, SUBSCRIBE_TYPE, UNSUBSCRIBE_TYPE, INVALIDATE_TYPE, GC_TYPE, OPTIMISTIC_TYPE, INVALIDATEALL_TYPE } from './actionTypes.js';
|
|
2
|
+
import { SET_TYPE, RESET_TYPE, FETCH_TYPE, SUBSCRIBE_TYPE, UNSUBSCRIBE_TYPE, INVALIDATE_TYPE, GC_TYPE, OPTIMISTIC_TYPE, INVALIDATEALL_TYPE, EXPIREALL_TYPE } from './actionTypes.js';
|
|
3
3
|
import { EndpointUpdateFunction } from './controller/types.js';
|
|
4
4
|
type EndpointAndUpdate<E extends EndpointInterface> = EndpointInterface & {
|
|
5
5
|
update?: EndpointUpdateFunction<E>;
|
|
@@ -69,6 +69,10 @@ export interface UnsubscribeAction<E extends EndpointAndUpdate<E> = EndpointDefa
|
|
|
69
69
|
key: string;
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
|
+
export interface ExpireAllAction {
|
|
73
|
+
type: typeof EXPIREALL_TYPE;
|
|
74
|
+
testKey: (key: string) => boolean;
|
|
75
|
+
}
|
|
72
76
|
export interface InvalidateAllAction {
|
|
73
77
|
type: typeof INVALIDATEALL_TYPE;
|
|
74
78
|
testKey: (key: string) => boolean;
|
|
@@ -91,6 +95,6 @@ export interface GCAction {
|
|
|
91
95
|
][];
|
|
92
96
|
results: string[];
|
|
93
97
|
}
|
|
94
|
-
export type ActionTypes = FetchAction | OptimisticAction | SetAction | SubscribeAction | UnsubscribeAction | InvalidateAction | InvalidateAllAction | ResetAction | GCAction;
|
|
98
|
+
export type ActionTypes = FetchAction | OptimisticAction | SetAction | SubscribeAction | UnsubscribeAction | InvalidateAction | InvalidateAllAction | ExpireAllAction | ResetAction | GCAction;
|
|
95
99
|
export {};
|
|
96
100
|
//# sourceMappingURL=newActions.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { State, ExpireAllAction } from '../../types.js';
|
|
2
|
+
export declare function expireReducer(state: State<unknown>, action: ExpireAllAction): {
|
|
3
|
+
meta: {
|
|
4
|
+
[x: string]: {
|
|
5
|
+
readonly date: number;
|
|
6
|
+
readonly error?: import("packages/normalizr/lib/index.js").ErrorTypes | undefined;
|
|
7
|
+
readonly expiresAt: number;
|
|
8
|
+
readonly prevExpiresAt?: number | undefined;
|
|
9
|
+
readonly invalidated?: boolean | undefined;
|
|
10
|
+
readonly errorPolicy?: "hard" | "soft" | undefined;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
entities: {
|
|
14
|
+
readonly [entityKey: string]: {
|
|
15
|
+
readonly [pk: string]: unknown;
|
|
16
|
+
} | undefined;
|
|
17
|
+
};
|
|
18
|
+
indexes: import("packages/normalizr/lib/interface.js").NormalizedIndex;
|
|
19
|
+
results: {
|
|
20
|
+
readonly [key: string]: unknown;
|
|
21
|
+
};
|
|
22
|
+
entityMeta: {
|
|
23
|
+
readonly [entityKey: string]: {
|
|
24
|
+
readonly [pk: string]: {
|
|
25
|
+
readonly date: number;
|
|
26
|
+
readonly expiresAt: number;
|
|
27
|
+
readonly fetchedAt: number;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
optimistic: (import("../../newActions.js").SetAction | import("../../newActions.js").OptimisticAction<import("packages/normalizr/lib/index.js").EndpointInterface<import("packages/normalizr/lib/index.js").FetchFunction, import("packages/normalizr/lib/interface.js").Schema | undefined, boolean | undefined> & {
|
|
32
|
+
update?: import("../../index.js").EndpointUpdateFunction<import("packages/normalizr/lib/index.js").EndpointInterface<import("packages/normalizr/lib/index.js").FetchFunction, import("packages/normalizr/lib/interface.js").Schema | undefined, boolean | undefined>> | undefined;
|
|
33
|
+
}>)[];
|
|
34
|
+
lastReset: number;
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=expireReducer.d.ts.map
|