@dr.pogodin/react-global-state 0.10.0-alpha.2 → 0.10.0-alpha.3

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 (67) hide show
  1. package/README.md +2 -0
  2. package/build/common/GlobalState.js +219 -0
  3. package/build/common/GlobalState.js.map +1 -0
  4. package/build/common/GlobalStateProvider.js +101 -0
  5. package/build/common/GlobalStateProvider.js.map +1 -0
  6. package/build/common/SsrContext.js +15 -0
  7. package/build/common/SsrContext.js.map +1 -0
  8. package/build/common/index.js +76 -0
  9. package/build/common/index.js.map +1 -0
  10. package/build/common/useAsyncCollection.js +61 -0
  11. package/build/common/useAsyncCollection.js.map +1 -0
  12. package/build/common/useAsyncData.js +204 -0
  13. package/build/common/useAsyncData.js.map +1 -0
  14. package/build/common/useGlobalState.js +113 -0
  15. package/build/common/useGlobalState.js.map +1 -0
  16. package/build/common/utils.js +44 -0
  17. package/build/common/utils.js.map +1 -0
  18. package/build/common/withGlobalStateType.js +42 -0
  19. package/build/common/withGlobalStateType.js.map +1 -0
  20. package/build/module/GlobalState.js +230 -0
  21. package/build/module/GlobalState.js.map +1 -0
  22. package/build/module/GlobalStateProvider.js +94 -0
  23. package/build/module/GlobalStateProvider.js.map +1 -0
  24. package/build/module/SsrContext.js +9 -0
  25. package/build/module/SsrContext.js.map +1 -0
  26. package/build/module/index.js +8 -0
  27. package/build/module/index.js.map +1 -0
  28. package/build/module/useAsyncCollection.js +56 -0
  29. package/build/module/useAsyncCollection.js.map +1 -0
  30. package/build/module/useAsyncData.js +199 -0
  31. package/build/module/useAsyncData.js.map +1 -0
  32. package/build/module/useGlobalState.js +108 -0
  33. package/build/module/useGlobalState.js.map +1 -0
  34. package/build/module/utils.js +35 -0
  35. package/build/module/utils.js.map +1 -0
  36. package/build/module/withGlobalStateType.js +35 -0
  37. package/build/module/withGlobalStateType.js.map +1 -0
  38. package/build/{GlobalState.d.ts → types/GlobalState.d.ts} +6 -6
  39. package/build/{GlobalStateProvider.d.ts → types/GlobalStateProvider.d.ts} +8 -8
  40. package/build/{index.d.ts → types/index.d.ts} +1 -1
  41. package/build/{useAsyncCollection.d.ts → types/useAsyncCollection.d.ts} +2 -2
  42. package/build/{useAsyncData.d.ts → types/useAsyncData.d.ts} +2 -3
  43. package/build/{useGlobalState.d.ts → types/useGlobalState.d.ts} +2 -2
  44. package/build/{utils.d.ts → types/utils.d.ts} +4 -1
  45. package/build/types/withGlobalStateType.d.ts +29 -0
  46. package/package.json +43 -38
  47. package/src/GlobalState.ts +14 -10
  48. package/src/GlobalStateProvider.tsx +26 -15
  49. package/src/index.ts +1 -1
  50. package/src/useAsyncCollection.ts +5 -5
  51. package/src/useAsyncData.ts +27 -19
  52. package/src/useGlobalState.ts +9 -11
  53. package/src/utils.ts +8 -3
  54. package/src/withGlobalStateType.ts +77 -25
  55. package/tsconfig.json +9 -3
  56. package/tsconfig.types.json +14 -0
  57. package/build/GlobalState.js +0 -193
  58. package/build/GlobalStateProvider.js +0 -103
  59. package/build/SsrContext.js +0 -10
  60. package/build/index.js +0 -23
  61. package/build/useAsyncCollection.js +0 -14
  62. package/build/useAsyncData.js +0 -150
  63. package/build/useGlobalState.js +0 -49
  64. package/build/utils.js +0 -25
  65. package/build/withGlobalStateType.d.ts +0 -39
  66. package/build/withGlobalStateType.js +0 -55
  67. /package/build/{SsrContext.d.ts → types/SsrContext.d.ts} +0 -0
@@ -0,0 +1,29 @@
1
+ import { type ForceT, type TypeLock, type ValueAtPathT, type ValueOrInitializerT } from './utils';
2
+ import GlobalStateProvider, { getGlobalState, getSsrContext } from './GlobalStateProvider';
3
+ import SsrContext from './SsrContext';
4
+ import { type UseGlobalStateResT } from './useGlobalState';
5
+ import { type AsyncCollectionLoaderT } from './useAsyncCollection';
6
+ import { type AsyncDataLoaderT, type DataInEnvelopeAtPathT, type UseAsyncDataOptionsT, type UseAsyncDataResT } from './useAsyncData';
7
+ interface NarrowedUseGlobalStateI<StateT> {
8
+ (): UseGlobalStateResT<StateT>;
9
+ <PathT extends null | string | undefined>(path: PathT, initialValue?: ValueOrInitializerT<ValueAtPathT<StateT, PathT, never>>): UseGlobalStateResT<ValueAtPathT<StateT, PathT, void>>;
10
+ <Forced extends ForceT | false = false, ValueT = unknown>(path: null | string | undefined, initialValue?: ValueOrInitializerT<TypeLock<Forced, never, ValueT>>): UseGlobalStateResT<TypeLock<Forced, void, ValueT>>;
11
+ }
12
+ interface NarrowedUseAsyncDataI<StateT> {
13
+ <PathT extends null | string | undefined>(path: PathT, loader: AsyncDataLoaderT<DataInEnvelopeAtPathT<StateT, PathT>>, options?: UseAsyncDataOptionsT): UseAsyncDataResT<DataInEnvelopeAtPathT<StateT, PathT>>;
14
+ <Forced extends ForceT | false = false, DataT = unknown>(path: null | string | undefined, loader: AsyncDataLoaderT<TypeLock<Forced, void, DataT>>, options?: UseAsyncDataOptionsT): UseAsyncDataResT<TypeLock<Forced, never, DataT>>;
15
+ }
16
+ interface NarrowedUseAsyncCollectionI<StateT> {
17
+ <PathT extends null | string | undefined>(id: string, path: PathT, loader: AsyncCollectionLoaderT<DataInEnvelopeAtPathT<StateT, PathT>>, options?: UseAsyncDataOptionsT): UseAsyncDataResT<DataInEnvelopeAtPathT<StateT, PathT>>;
18
+ <Forced extends ForceT | false = false, DataT = unknown>(id: string, path: null | string | undefined, loader: AsyncCollectionLoaderT<TypeLock<Forced, void, DataT>>, options?: UseAsyncDataOptionsT): UseAsyncDataResT<DataT>;
19
+ }
20
+ type WithGlobalStateTypeResT<StateT, SsrContextT extends SsrContext<StateT> = SsrContext<StateT>> = {
21
+ getGlobalState: typeof getGlobalState<StateT, SsrContextT>;
22
+ getSsrContext: typeof getSsrContext<SsrContextT>;
23
+ GlobalStateProvider: typeof GlobalStateProvider<StateT, SsrContextT>;
24
+ useAsyncCollection: NarrowedUseAsyncCollectionI<StateT>;
25
+ useAsyncData: NarrowedUseAsyncDataI<StateT>;
26
+ useGlobalState: NarrowedUseGlobalStateI<StateT>;
27
+ };
28
+ export default function withGlobalStateType<StateT, SsrContextT extends SsrContext<StateT> = SsrContext<StateT>>(): WithGlobalStateTypeResT<StateT, SsrContextT>;
29
+ export {};
package/package.json CHANGED
@@ -1,22 +1,27 @@
1
1
  {
2
2
  "name": "@dr.pogodin/react-global-state",
3
- "version": "0.10.0-alpha.2",
3
+ "version": "0.10.0-alpha.3",
4
4
  "description": "Hook-based global state for React",
5
- "main": "./build/index.js",
5
+ "main": "./build/common/index.js",
6
6
  "react-native": "src/index.ts",
7
7
  "source": "src/index.ts",
8
+ "types": "./build/types/index.d.ts",
8
9
  "exports": {
9
- "types": "./index.d.ts",
10
- "default": "./build/index.js"
10
+ "module": "./build/module/index.js",
11
+ "node": "./build/common/index.js",
12
+ "default": "./build/common/index.js"
11
13
  },
12
14
  "scripts": {
13
- "build": "rimraf build && tsc",
15
+ "build": "rimraf build && npm run build:types && npm run build:common && npm run build:module",
16
+ "build:common": "rimraf build/common && babel src -x .ts,.tsx --out-dir build/common --source-maps",
17
+ "build:module": "rimraf build/module && babel src -x .ts,.tsx --out-dir build/module --source-maps --config-file ./babel.module.config.js",
18
+ "build:types": "rimraf build/types && tsc --project tsconfig.types.json",
14
19
  "jest": "npm run jest:types && npm run jest:logic",
15
- "jest:types": "jest --config jest/config-types.json",
16
20
  "jest:logic": "jest --config jest/config.json -w 1",
21
+ "jest:types": "jest --config jest/config-types.json",
17
22
  "lint": "eslint --ext .js,.jsx,.ts,.tsx .",
18
23
  "test": "npm run lint && npm run typecheck && npm run jest",
19
- "typecheck": "tsc --noEmit && tsc --project __tests__/tsconfig.typecheck.json"
24
+ "typecheck": "tsc --project __tests__/tsconfig.json"
20
25
  },
21
26
  "repository": "github:birdofpreyru/react-global-state",
22
27
  "keywords": [
@@ -35,53 +40,53 @@
35
40
  },
36
41
  "homepage": "https://dr.pogodin.studio/docs/react-global-state/index.html",
37
42
  "engines": {
38
- "node": ">=16"
43
+ "node": ">=18"
39
44
  },
40
45
  "dependencies": {
41
- "@babel/runtime": "^7.22.6",
46
+ "@babel/runtime": "^7.23.2",
42
47
  "@dr.pogodin/js-utils": "^0.0.6",
43
48
  "lodash": "^4.17.21",
44
- "uuid": "^9.0.0"
49
+ "uuid": "^9.0.1"
45
50
  },
46
51
  "devDependencies": {
47
- "@babel/cli": "^7.22.6",
48
- "@babel/core": "^7.22.8",
49
- "@babel/eslint-parser": "^7.22.7",
50
- "@babel/eslint-plugin": "^7.22.5",
51
- "@babel/node": "^7.22.6",
52
- "@babel/plugin-transform-runtime": "^7.22.7",
53
- "@babel/preset-env": "^7.22.7",
54
- "@babel/preset-react": "^7.22.5",
55
- "@babel/preset-typescript": "^7.22.5",
56
- "@tsconfig/recommended": "^1.0.2",
57
- "@tsd/typescript": "^5.1.6",
58
- "@types/jest": "^29.5.3",
59
- "@types/lodash": "^4.14.195",
60
- "@types/pretty": "^2.0.1",
61
- "@types/react": "^18.2.14",
62
- "@types/react-dom": "^18.2.6",
63
- "@types/uuid": "^9.0.2",
64
- "@typescript-eslint/eslint-plugin": "^5.61.0",
65
- "@typescript-eslint/parser": "^5.61.0",
66
- "babel-jest": "^29.6.1",
52
+ "@babel/cli": "^7.23.0",
53
+ "@babel/core": "^7.23.2",
54
+ "@babel/eslint-parser": "^7.22.15",
55
+ "@babel/eslint-plugin": "^7.22.10",
56
+ "@babel/node": "^7.22.19",
57
+ "@babel/plugin-transform-runtime": "^7.23.2",
58
+ "@babel/preset-env": "^7.23.2",
59
+ "@babel/preset-react": "^7.22.15",
60
+ "@babel/preset-typescript": "^7.23.2",
61
+ "@tsconfig/recommended": "^1.0.3",
62
+ "@tsd/typescript": "^5.2.2",
63
+ "@types/jest": "^29.5.6",
64
+ "@types/lodash": "^4.14.200",
65
+ "@types/pretty": "^2.0.2",
66
+ "@types/react": "^18.2.31",
67
+ "@types/react-dom": "^18.2.14",
68
+ "@types/uuid": "^9.0.6",
69
+ "@typescript-eslint/eslint-plugin": "^6.8.0",
70
+ "@typescript-eslint/parser": "^6.8.0",
71
+ "babel-jest": "^29.7.0",
67
72
  "babel-plugin-module-resolver": "^5.0.0",
68
- "eslint": "^8.44.0",
73
+ "eslint": "^8.52.0",
69
74
  "eslint-config-airbnb": "^19.0.4",
70
- "eslint-config-airbnb-typescript": "^17.0.0",
75
+ "eslint-config-airbnb-typescript": "^17.1.0",
71
76
  "eslint-import-resolver-babel-module": "^5.3.2",
72
- "eslint-plugin-import": "^2.27.5",
73
- "eslint-plugin-jest": "^27.2.2",
77
+ "eslint-plugin-import": "^2.28.1",
78
+ "eslint-plugin-jest": "^27.4.3",
74
79
  "eslint-plugin-jsx-a11y": "^6.7.1",
75
- "eslint-plugin-react": "^7.32.2",
80
+ "eslint-plugin-react": "^7.33.2",
76
81
  "eslint-plugin-react-hooks": "^4.6.0",
77
- "jest": "^29.6.1",
78
- "jest-environment-jsdom": "^29.6.1",
82
+ "jest": "^29.7.0",
83
+ "jest-environment-jsdom": "^29.7.0",
79
84
  "jest-runner-tsd": "^6.0.0",
80
85
  "mockdate": "^3.0.5",
81
86
  "pretty": "^2.0.0",
82
87
  "rimraf": "^5.0.1",
83
88
  "tsd-lite": "^0.8.0",
84
- "typescript": "^5.1.6"
89
+ "typescript": "^5.2.2"
85
90
  },
86
91
  "peerDependencies": {
87
92
  "react": "^18.2.0",
@@ -12,6 +12,7 @@ import SsrContext from './SsrContext';
12
12
 
13
13
  import {
14
14
  type CallbackT,
15
+ type ForceT,
15
16
  type TypeLock,
16
17
  type ValueAtPathT,
17
18
  type ValueOrInitializerT,
@@ -25,8 +26,11 @@ type GetOptsT<T> = {
25
26
  initialValue?: ValueOrInitializerT<T>;
26
27
  };
27
28
 
28
- export default class GlobalState<StateT> {
29
- readonly ssrContext?: SsrContext<StateT>;
29
+ export default class GlobalState<
30
+ StateT,
31
+ SsrContextT extends SsrContext<StateT> = SsrContext<StateT>,
32
+ > {
33
+ readonly ssrContext?: SsrContextT;
30
34
 
31
35
  #initialState: StateT;
32
36
 
@@ -47,7 +51,7 @@ export default class GlobalState<StateT> {
47
51
  */
48
52
  constructor(
49
53
  initialState: StateT,
50
- ssrContext?: SsrContext<StateT>,
54
+ ssrContext?: SsrContextT,
51
55
  ) {
52
56
  this.#currentState = initialState;
53
57
  this.#initialState = initialState;
@@ -158,10 +162,10 @@ export default class GlobalState<StateT> {
158
162
 
159
163
  // This variant is not callable by default (without generic arguments),
160
164
  // otherwise it allows to set the correct ValueT directly.
161
- get<Unlocked extends 0 | 1 = 0, ValueT = void>(
165
+ get<Forced extends ForceT | false = false, ValueT = void>(
162
166
  path?: null | string,
163
- opts?: GetOptsT<TypeLock<Unlocked, never, ValueT>>,
164
- ): TypeLock<Unlocked, void, ValueT>;
167
+ opts?: GetOptsT<TypeLock<Forced, never, ValueT>>,
168
+ ): TypeLock<Forced, void, ValueT>;
165
169
 
166
170
  get<ValueT>(path?: null | string, opts?: GetOptsT<ValueT>): ValueT {
167
171
  if (isNil(path)) {
@@ -178,7 +182,7 @@ export default class GlobalState<StateT> {
178
182
  res = isFunction(iv) ? iv() : iv;
179
183
 
180
184
  if (!opts?.initialState || this.get(path) === undefined) {
181
- this.set<1, unknown>(path, res);
185
+ this.set<ForceT, unknown>(path, res);
182
186
  }
183
187
 
184
188
  return res;
@@ -201,10 +205,10 @@ export default class GlobalState<StateT> {
201
205
 
202
206
  // This variant is disabled by default, otherwise allows to give
203
207
  // expected value type explicitly.
204
- set<Unlocked extends 0 | 1 = 0, ValueT = never>(
208
+ set<Forced extends ForceT | false = false, ValueT = never>(
205
209
  path: null | string | undefined,
206
- value: TypeLock<Unlocked, never, ValueT>,
207
- ): TypeLock<Unlocked, void, ValueT>;
210
+ value: TypeLock<Forced, never, ValueT>,
211
+ ): TypeLock<Forced, void, ValueT>;
208
212
 
209
213
  set(path: null | string | undefined, value: unknown): unknown {
210
214
  if (isNil(path)) return this.setEntireState(value as StateT);
@@ -22,7 +22,10 @@ const context = createContext<GlobalState<unknown> | null>(null);
22
22
  * the global state, instead of accessing it directly.
23
23
  * @return
24
24
  */
25
- export function getGlobalState<StateT>(): GlobalState<StateT> {
25
+ export function getGlobalState<
26
+ StateT,
27
+ SsrContextT extends SsrContext<StateT> = SsrContext<StateT>,
28
+ >(): GlobalState<StateT, SsrContextT> {
26
29
  // Here Rules of Hooks are violated because "getGlobalState()" does not follow
27
30
  // convention that hook names should start with use... This is intentional in
28
31
  // our case, as getGlobalState() hook is intended for advance scenarious,
@@ -32,7 +35,7 @@ export function getGlobalState<StateT>(): GlobalState<StateT> {
32
35
  const globalState = useContext(context);
33
36
  /* eslint-enable react-hooks/rules-of-hooks */
34
37
  if (!globalState) throw new Error('Missing GlobalStateProvider');
35
- return globalState as GlobalState<StateT>;
38
+ return globalState as GlobalState<StateT, SsrContextT>;
36
39
  }
37
40
 
38
41
  /**
@@ -49,25 +52,30 @@ export function getGlobalState<StateT>(): GlobalState<StateT> {
49
52
  * - If `throwWithoutSsrContext` is `true`, and there is no SSR context attached
50
53
  * to the global state provided by {@link &lt;GlobalStateProvider&gt;}.
51
54
  */
52
- export function getSsrContext<StateT>(
55
+ export function getSsrContext<
56
+ SsrContextT extends SsrContext<unknown>,
57
+ >(
53
58
  throwWithoutSsrContext = true,
54
- ): SsrContext<StateT> | undefined {
55
- const { ssrContext } = getGlobalState<StateT>();
59
+ ): SsrContextT | undefined {
60
+ const { ssrContext } = getGlobalState<SsrContextT['state'], SsrContextT>();
56
61
  if (!ssrContext && throwWithoutSsrContext) {
57
62
  throw new Error('No SSR context found');
58
63
  }
59
64
  return ssrContext;
60
65
  }
61
66
 
62
- type NewStateProps<StateT> = {
67
+ type NewStateProps<StateT, SsrContextT extends SsrContext<StateT>> = {
63
68
  initialState: ValueOrInitializerT<StateT>,
64
- ssrContext?: SsrContext<StateT>;
69
+ ssrContext?: SsrContextT;
65
70
  };
66
71
 
67
- type GlobalStateProviderProps<StateT> = {
72
+ type GlobalStateProviderProps<
73
+ StateT,
74
+ SsrContextT extends SsrContext<StateT>,
75
+ > = {
68
76
  children?: ReactNode;
69
- } & (NewStateProps<StateT> | {
70
- stateProxy: true | GlobalState<StateT>;
77
+ } & (NewStateProps<StateT, SsrContextT> | {
78
+ stateProxy: true | GlobalState<StateT, SsrContextT>;
71
79
  });
72
80
 
73
81
  /**
@@ -83,10 +91,13 @@ type GlobalStateProviderProps<StateT> = {
83
91
  * - If `GlobalState` instance, it will be used by this provider.
84
92
  * - If not given, a new `GlobalState` instance will be created and used.
85
93
  */
86
- export default function GlobalStateProvider<StateT>(
87
- { children, ...rest }: GlobalStateProviderProps<StateT>,
94
+ export default function GlobalStateProvider<
95
+ StateT,
96
+ SsrContextT extends SsrContext<StateT> = SsrContext<StateT>,
97
+ >(
98
+ { children, ...rest }: GlobalStateProviderProps<StateT, SsrContextT>,
88
99
  ) {
89
- const state = useRef<GlobalState<StateT>>();
100
+ const state = useRef<GlobalState<StateT, SsrContextT>>();
90
101
  if (!state.current) {
91
102
  // NOTE: The last part of condition, "&& rest.stateProxy", is needed for
92
103
  // graceful compatibility with JavaScript - if "undefined" stateProxy value
@@ -96,9 +107,9 @@ export default function GlobalStateProvider<StateT>(
96
107
  if (rest.stateProxy === true) state.current = getGlobalState();
97
108
  else state.current = rest.stateProxy;
98
109
  } else {
99
- const { initialState, ssrContext } = rest as NewStateProps<StateT>;
110
+ const { initialState, ssrContext } = rest as NewStateProps<StateT, SsrContextT>;
100
111
 
101
- state.current = new GlobalState<StateT>(
112
+ state.current = new GlobalState<StateT, SsrContextT>(
102
113
  isFunction(initialState) ? initialState() : initialState,
103
114
  ssrContext,
104
115
  );
package/src/index.ts CHANGED
@@ -28,6 +28,6 @@ export {
28
28
  default as useGlobalState,
29
29
  } from './useGlobalState';
30
30
 
31
- export { type ValueOrInitializerT } from './utils';
31
+ export { type ForceT, type ValueOrInitializerT } from './utils';
32
32
 
33
33
  export { default as withGlobalStateType } from './withGlobalStateType';
@@ -8,7 +8,7 @@ import useAsyncData, {
8
8
  type UseAsyncDataResT,
9
9
  } from './useAsyncData';
10
10
 
11
- import { TypeLock } from './utils';
11
+ import { type ForceT, type TypeLock } from './utils';
12
12
 
13
13
  export type AsyncCollectionLoaderT<DataT> =
14
14
  (id: string, oldData: null | DataT) => DataT | Promise<DataT>;
@@ -70,14 +70,14 @@ function useAsyncCollection<
70
70
  ): UseAsyncDataResT<DataInEnvelopeAtPathT<StateT, `${PathT}.${IdT}`>>;
71
71
 
72
72
  function useAsyncCollection<
73
- Unlocked extends 0 | 1 = 0,
73
+ Forced extends ForceT | false = false,
74
74
  DataT = unknown,
75
75
  >(
76
76
  id: string,
77
77
  path: null | string | undefined,
78
- loader: AsyncCollectionLoaderT<TypeLock<Unlocked, void, DataT>>,
78
+ loader: AsyncCollectionLoaderT<TypeLock<Forced, void, DataT>>,
79
79
  options?: UseAsyncDataOptionsT,
80
- ): UseAsyncDataResT<TypeLock<Unlocked, void, DataT>>;
80
+ ): UseAsyncDataResT<TypeLock<Forced, void, DataT>>;
81
81
 
82
82
  function useAsyncCollection<DataT>(
83
83
  id: string,
@@ -86,7 +86,7 @@ function useAsyncCollection<DataT>(
86
86
  options: UseAsyncDataOptionsT = {},
87
87
  ): UseAsyncDataResT<DataT> {
88
88
  const itemPath = path ? `${path}.${id}` : id;
89
- return useAsyncData<1, DataT>(
89
+ return useAsyncData<ForceT, DataT>(
90
90
  itemPath,
91
91
  (oldData: null | DataT) => loader(id, oldData),
92
92
  options,
@@ -10,11 +10,16 @@ import { MIN_MS } from '@dr.pogodin/js-utils';
10
10
 
11
11
  import { getGlobalState } from './GlobalStateProvider';
12
12
  import useGlobalState from './useGlobalState';
13
- import { TypeLock, isDebugMode } from './utils';
14
13
 
15
- import GlobalState from './GlobalState';
14
+ import {
15
+ type ForceT,
16
+ type TypeLock,
17
+ type ValueAtPathT,
18
+ isDebugMode,
19
+ } from './utils';
16
20
 
17
- import { type ValueAtPathT } from './utils';
21
+ import GlobalState from './GlobalState';
22
+ import SsrContext from './SsrContext';
18
23
 
19
24
  const DEFAULT_MAXAGE = 5 * MIN_MS; // 5 minutes.
20
25
 
@@ -70,7 +75,7 @@ export type UseAsyncDataResT<DataT> = {
70
75
  async function load<DataT>(
71
76
  path: null | string | undefined,
72
77
  loader: AsyncDataLoaderT<DataT>,
73
- globalState: GlobalState<unknown>,
78
+ globalState: GlobalState<unknown, SsrContext<unknown>>,
74
79
  oldData: DataT | null,
75
80
  opIdPrefix: 'C' | 'S' = 'C',
76
81
  ): Promise<void> {
@@ -83,11 +88,11 @@ async function load<DataT>(
83
88
  }
84
89
  const operationId = opIdPrefix + uuid();
85
90
  const operationIdPath = path ? `${path}.operationId` : 'operationId';
86
- globalState.set<1, string>(operationIdPath, operationId);
91
+ globalState.set<ForceT, string>(operationIdPath, operationId);
87
92
  const data = await loader(
88
- oldData || (globalState.get<1, AsyncDataEnvelopeT<DataT>>(path)).data,
93
+ oldData || (globalState.get<ForceT, AsyncDataEnvelopeT<DataT>>(path)).data,
89
94
  );
90
- const state: AsyncDataEnvelopeT<DataT> = globalState.get<1, AsyncDataEnvelopeT<DataT>>(path);
95
+ const state: AsyncDataEnvelopeT<DataT> = globalState.get<ForceT, AsyncDataEnvelopeT<DataT>>(path);
91
96
  if (operationId === state.operationId) {
92
97
  if (process.env.NODE_ENV !== 'production' && isDebugMode()) {
93
98
  /* eslint-disable no-console */
@@ -99,7 +104,7 @@ async function load<DataT>(
99
104
  console.log('Data:', cloneDeep(data));
100
105
  /* eslint-enable no-console */
101
106
  }
102
- globalState.set<1, AsyncDataEnvelopeT<DataT>>(path, {
107
+ globalState.set<ForceT, AsyncDataEnvelopeT<DataT>>(path, {
103
108
  ...state,
104
109
  data,
105
110
  operationId: '',
@@ -176,13 +181,13 @@ function useAsyncData<
176
181
  ): UseAsyncDataResT<DataInEnvelopeAtPathT<StateT, PathT>>;
177
182
 
178
183
  function useAsyncData<
179
- Unlocked extends 0 | 1 = 0,
184
+ Forced extends ForceT | false = false,
180
185
  DataT = unknown,
181
186
  >(
182
187
  path: null | string | undefined,
183
- loader: AsyncDataLoaderT<TypeLock<Unlocked, void, DataT>>,
188
+ loader: AsyncDataLoaderT<TypeLock<Forced, void, DataT>>,
184
189
  options?: UseAsyncDataOptionsT,
185
- ): UseAsyncDataResT<TypeLock<Unlocked, void, DataT>>;
190
+ ): UseAsyncDataResT<TypeLock<Forced, void, DataT>>;
186
191
 
187
192
  function useAsyncData<DataT>(
188
193
  path: null | string | undefined,
@@ -201,7 +206,7 @@ function useAsyncData<DataT>(
201
206
  // Note: here we can't depend on useGlobalState() to init the initial value,
202
207
  // because that way we'll have issues with SSR (see details below).
203
208
  const globalState = getGlobalState();
204
- const state = globalState.get<1, AsyncDataEnvelopeT<DataT>>(path, {
209
+ const state = globalState.get<ForceT, AsyncDataEnvelopeT<DataT>>(path, {
205
210
  initialValue: newAsyncDataEnvelope<DataT>(),
206
211
  });
207
212
 
@@ -223,10 +228,11 @@ function useAsyncData<DataT>(
223
228
  // The same applies to other useEffect() hooks below.
224
229
  useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks
225
230
  const numRefsPath = path ? `${path}.numRefs` : 'numRefs';
226
- const numRefs = globalState.get<1, number>(numRefsPath);
227
- globalState.set<1, number>(numRefsPath, numRefs + 1);
231
+ const numRefs = globalState.get<ForceT, number>(numRefsPath);
232
+ globalState.set<ForceT, number>(numRefsPath, numRefs + 1);
228
233
  return () => {
229
- const state2: AsyncDataEnvelopeT<DataT> = globalState.get<1, AsyncDataEnvelopeT<DataT>>(
234
+ const state2: AsyncDataEnvelopeT<DataT> = globalState.get<
235
+ ForceT, AsyncDataEnvelopeT<DataT>>(
230
236
  path,
231
237
  );
232
238
  if (
@@ -242,13 +248,13 @@ function useAsyncData<DataT>(
242
248
  );
243
249
  /* eslint-enable no-console */
244
250
  }
245
- globalState.set<1, AsyncDataEnvelopeT<DataT>>(path, {
251
+ globalState.set<ForceT, AsyncDataEnvelopeT<DataT>>(path, {
246
252
  ...state2,
247
253
  data: null,
248
254
  numRefs: 0,
249
255
  timestamp: 0,
250
256
  });
251
- } else globalState.set<1, number>(numRefsPath, state2.numRefs - 1);
257
+ } else globalState.set<ForceT, number>(numRefsPath, state2.numRefs - 1);
252
258
  };
253
259
  }, [garbageCollectAge, globalState, path]);
254
260
 
@@ -258,7 +264,9 @@ function useAsyncData<DataT>(
258
264
  // Data loading and refreshing.
259
265
  let loadTriggered = false;
260
266
  useEffect(() => { // eslint-disable-line react-hooks/rules-of-hooks
261
- const state2: AsyncDataEnvelopeT<DataT> = globalState.get<1, AsyncDataEnvelopeT<DataT>>(path);
267
+ const state2: AsyncDataEnvelopeT<DataT> = globalState.get<
268
+ ForceT, AsyncDataEnvelopeT<DataT>>(path);
269
+
262
270
  if (refreshAge < Date.now() - state2.timestamp
263
271
  && (!state2.operationId || state2.operationId.charAt(0) === 'S')) {
264
272
  load(path, loader, globalState, state2.data);
@@ -272,7 +280,7 @@ function useAsyncData<DataT>(
272
280
  }, deps); // eslint-disable-line react-hooks/exhaustive-deps
273
281
  }
274
282
 
275
- const [localState] = useGlobalState<1, AsyncDataEnvelopeT<DataT>>(
283
+ const [localState] = useGlobalState<ForceT, AsyncDataEnvelopeT<DataT>>(
276
284
  path,
277
285
  newAsyncDataEnvelope<DataT>(),
278
286
  );
@@ -10,6 +10,7 @@ import { getGlobalState } from './GlobalStateProvider';
10
10
 
11
11
  import {
12
12
  type CallbackT,
13
+ type ForceT,
13
14
  type TypeLock,
14
15
  type ValueAtPathT,
15
16
  type ValueOrInitializerT,
@@ -82,6 +83,11 @@ export type UseGlobalStateResT<T> = [T, SetterT<T>];
82
83
 
83
84
  function useGlobalState<StateT>(): UseGlobalStateResT<StateT>;
84
85
 
86
+ function useGlobalState<Forced extends ForceT | false = false, ValueT = unknown>(
87
+ path: null | string | undefined,
88
+ initialValue?: ValueOrInitializerT<TypeLock<Forced, never, ValueT>>,
89
+ ): UseGlobalStateResT<TypeLock<Forced, void, ValueT>>;
90
+
85
91
  function useGlobalState<
86
92
  StateT,
87
93
  PathT extends null | string | undefined,
@@ -90,14 +96,6 @@ function useGlobalState<
90
96
  initialValue?: ValueOrInitializerT<ValueAtPathT<StateT, PathT, never>>
91
97
  ): UseGlobalStateResT<ValueAtPathT<StateT, PathT, void>>;
92
98
 
93
- function useGlobalState<
94
- Unlocked extends 0 | 1 = 0,
95
- ValueT = void,
96
- >(
97
- path: null | string | undefined,
98
- initialValue?: ValueOrInitializerT<TypeLock<Unlocked, never, ValueT>>,
99
- ): UseGlobalStateResT<TypeLock<Unlocked, void, ValueT>>;
100
-
101
99
  function useGlobalState(
102
100
  path?: null | string,
103
101
  initialValue?: ValueOrInitializerT<unknown>,
@@ -122,7 +120,7 @@ function useGlobalState(
122
120
  console.groupEnd();
123
121
  /* eslint-enable no-console */
124
122
  }
125
- rc.globalState.set<1, unknown>(rc.path, newState);
123
+ rc.globalState.set<ForceT, unknown>(rc.path, newState);
126
124
  },
127
125
  state: isFunction(initialValue) ? initialValue() : initialValue,
128
126
  watcher: () => {
@@ -137,8 +135,8 @@ function useGlobalState(
137
135
 
138
136
  rc.state = useSyncExternalStore(
139
137
  (cb) => rc.emitter.addListener(cb),
140
- () => rc.globalState.get<1, unknown>(rc.path, { initialValue }),
141
- () => rc.globalState.get<1, unknown>(rc.path, { initialValue, initialState: true }),
138
+ () => rc.globalState.get<ForceT, unknown>(rc.path, { initialValue }),
139
+ () => rc.globalState.get<ForceT, unknown>(rc.path, { initialValue, initialState: true }),
142
140
  );
143
141
 
144
142
  useEffect(() => {
package/src/utils.ts CHANGED
@@ -2,12 +2,17 @@ import { type GetFieldType } from 'lodash';
2
2
 
3
3
  export type CallbackT = () => void;
4
4
 
5
- // TODO: This probably should be moved to JS Utils lib.
5
+ // TODO: This (ForceT & TypeLock) probably should be moved to JS Utils lib.
6
+
7
+ // This type is used to "unlocked" special generic overrides around the library.
8
+ declare const force: unique symbol;
9
+ export type ForceT = typeof force;
10
+
6
11
  export type TypeLock<
7
- Unlocked extends 0 | 1,
12
+ Unlocked extends ForceT | false,
8
13
  LockedT extends never | void,
9
14
  UnlockedT,
10
- > = Unlocked extends 0 ? LockedT : UnlockedT;
15
+ > = Unlocked extends ForceT ? UnlockedT : LockedT;
11
16
 
12
17
  /**
13
18
  * Given the type of state, `StateT`, and the type of state path, `PathT`,