@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.
Files changed (39) hide show
  1. package/dist/index.js +49 -0
  2. package/dist/index.umd.min.js +1 -1
  3. package/dist/next.js +27 -0
  4. package/legacy/actionTypes.js +2 -1
  5. package/legacy/controller/Controller.js +21 -1
  6. package/legacy/controller/createExpireAll.js +8 -0
  7. package/legacy/newActions.js +1 -1
  8. package/legacy/state/reducer/createReducer.js +5 -2
  9. package/legacy/state/reducer/expireReducer.js +15 -0
  10. package/lib/actionTypes.d.ts +1 -0
  11. package/lib/actionTypes.d.ts.map +1 -1
  12. package/lib/actionTypes.js +2 -1
  13. package/lib/controller/Controller.d.ts +16 -0
  14. package/lib/controller/Controller.d.ts.map +1 -1
  15. package/lib/controller/Controller.js +21 -1
  16. package/lib/controller/createExpireAll.d.ts +3 -0
  17. package/lib/controller/createExpireAll.d.ts.map +1 -0
  18. package/lib/controller/createExpireAll.js +8 -0
  19. package/lib/newActions.d.ts +6 -2
  20. package/lib/newActions.d.ts.map +1 -1
  21. package/lib/newActions.js +1 -1
  22. package/lib/state/reducer/createReducer.d.ts.map +1 -1
  23. package/lib/state/reducer/createReducer.js +5 -2
  24. package/lib/state/reducer/expireReducer.d.ts +36 -0
  25. package/lib/state/reducer/expireReducer.d.ts.map +1 -0
  26. package/lib/state/reducer/expireReducer.js +19 -0
  27. package/package.json +1 -1
  28. package/src/actionTypes.ts +1 -0
  29. package/src/controller/Controller.ts +34 -0
  30. package/src/controller/__tests__/Controller.ts +76 -2
  31. package/src/controller/createExpireAll.ts +11 -0
  32. package/src/newActions.ts +8 -0
  33. package/src/state/reducer/createReducer.ts +5 -0
  34. package/src/state/reducer/expireReducer.ts +20 -0
  35. package/ts3.4/actionTypes.d.ts +1 -0
  36. package/ts3.4/controller/Controller.d.ts +16 -0
  37. package/ts3.4/controller/createExpireAll.d.ts +3 -0
  38. package/ts3.4/newActions.d.ts +6 -2
  39. 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 Contoller from '../Controller';
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 Contoller();
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
+ }
@@ -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
@@ -0,0 +1,3 @@
1
+ import { ExpireAllAction } from '../types.js';
2
+ export default function createExpireAll(testKey: (key: string) => boolean): ExpireAllAction;
3
+ //# sourceMappingURL=createExpireAll.d.ts.map
@@ -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