@veams/status-quo 1.5.0 → 1.6.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 (126) hide show
  1. package/.turbo/turbo-build.log +12 -0
  2. package/.turbo/turbo-check$colon$types.log +4 -0
  3. package/.turbo/turbo-docs$colon$build.log +14 -0
  4. package/.turbo/turbo-lint.log +8 -0
  5. package/.turbo/turbo-test.log +15 -0
  6. package/CHANGELOG.md +24 -3
  7. package/README.md +217 -41
  8. package/dist/config/status-quo-config.d.ts +13 -0
  9. package/dist/config/status-quo-config.js +14 -0
  10. package/dist/config/status-quo-config.js.map +1 -1
  11. package/dist/hooks/__tests__/state-provider.spec.d.ts +4 -0
  12. package/dist/hooks/__tests__/state-provider.spec.js +179 -0
  13. package/dist/hooks/__tests__/state-provider.spec.js.map +1 -0
  14. package/dist/hooks/__tests__/state-selector.spec.js +11 -12
  15. package/dist/hooks/__tests__/state-selector.spec.js.map +1 -1
  16. package/dist/hooks/__tests__/state-singleton.spec.js +10 -11
  17. package/dist/hooks/__tests__/state-singleton.spec.js.map +1 -1
  18. package/dist/hooks/index.d.ts +1 -0
  19. package/dist/hooks/index.js +1 -0
  20. package/dist/hooks/index.js.map +1 -1
  21. package/dist/hooks/state-factory.js.map +1 -1
  22. package/dist/hooks/state-provider.d.ts +14 -0
  23. package/dist/hooks/state-provider.js +24 -0
  24. package/dist/hooks/state-provider.js.map +1 -0
  25. package/dist/hooks/state-subscription-selector.js +6 -2
  26. package/dist/hooks/state-subscription-selector.js.map +1 -1
  27. package/dist/hooks/state-subscription.js +1 -1
  28. package/dist/hooks/state-subscription.js.map +1 -1
  29. package/dist/index.d.ts +4 -5
  30. package/dist/index.js +2 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/react/hooks/__tests__/state-provider.spec.d.ts +4 -0
  33. package/dist/react/hooks/__tests__/state-provider.spec.js +179 -0
  34. package/dist/react/hooks/__tests__/state-provider.spec.js.map +1 -0
  35. package/dist/react/hooks/__tests__/state-selector.spec.d.ts +4 -0
  36. package/dist/react/hooks/__tests__/state-selector.spec.js +499 -0
  37. package/dist/react/hooks/__tests__/state-selector.spec.js.map +1 -0
  38. package/dist/react/hooks/__tests__/state-singleton.spec.d.ts +4 -0
  39. package/dist/react/hooks/__tests__/state-singleton.spec.js +96 -0
  40. package/dist/react/hooks/__tests__/state-singleton.spec.js.map +1 -0
  41. package/{src/hooks/index.ts → dist/react/hooks/index.d.ts} +1 -0
  42. package/dist/react/hooks/index.js +7 -0
  43. package/dist/react/hooks/index.js.map +1 -0
  44. package/dist/react/hooks/state-actions.d.ts +2 -0
  45. package/dist/react/hooks/state-actions.js +5 -0
  46. package/dist/react/hooks/state-actions.js.map +1 -0
  47. package/dist/react/hooks/state-factory.d.ts +7 -0
  48. package/dist/react/hooks/state-factory.js +13 -0
  49. package/dist/react/hooks/state-factory.js.map +1 -0
  50. package/dist/react/hooks/state-handler.d.ts +2 -0
  51. package/dist/react/hooks/state-handler.js +9 -0
  52. package/dist/react/hooks/state-handler.js.map +1 -0
  53. package/dist/react/hooks/state-provider.d.ts +14 -0
  54. package/dist/react/hooks/state-provider.js +24 -0
  55. package/dist/react/hooks/state-provider.js.map +1 -0
  56. package/dist/react/hooks/state-singleton.d.ts +6 -0
  57. package/dist/react/hooks/state-singleton.js +7 -0
  58. package/dist/react/hooks/state-singleton.js.map +1 -0
  59. package/dist/react/hooks/state-subscription-selector.d.ts +3 -0
  60. package/dist/react/hooks/state-subscription-selector.js +63 -0
  61. package/dist/react/hooks/state-subscription-selector.js.map +1 -0
  62. package/dist/react/hooks/state-subscription.d.ts +9 -0
  63. package/dist/react/hooks/state-subscription.js +53 -0
  64. package/dist/react/hooks/state-subscription.js.map +1 -0
  65. package/dist/react/index.d.ts +1 -0
  66. package/dist/react/index.js +2 -0
  67. package/dist/react/index.js.map +1 -0
  68. package/dist/store/__tests__/observable-state-handler.spec.js +66 -11
  69. package/dist/store/__tests__/observable-state-handler.spec.js.map +1 -1
  70. package/dist/store/__tests__/signal-state-handler.spec.js.map +1 -1
  71. package/dist/store/base-state-handler.d.ts +4 -6
  72. package/dist/store/base-state-handler.js +10 -9
  73. package/dist/store/base-state-handler.js.map +1 -1
  74. package/dist/store/dev-tools.js +0 -3
  75. package/dist/store/dev-tools.js.map +1 -1
  76. package/dist/store/observable-state-handler.d.ts +4 -10
  77. package/dist/store/observable-state-handler.js +4 -11
  78. package/dist/store/observable-state-handler.js.map +1 -1
  79. package/dist/store/signal-state-handler.d.ts +2 -5
  80. package/dist/store/signal-state-handler.js +3 -2
  81. package/dist/store/signal-state-handler.js.map +1 -1
  82. package/dist/store/state-singleton.js +1 -1
  83. package/dist/store/state-singleton.js.map +1 -1
  84. package/eslint.config.mjs +75 -0
  85. package/package.json +18 -18
  86. package/src/config/status-quo-config.ts +31 -1
  87. package/src/index.ts +11 -15
  88. package/src/react/hooks/__tests__/state-provider.spec.tsx +286 -0
  89. package/src/{hooks → react/hooks}/__tests__/state-selector.spec.tsx +52 -44
  90. package/src/{hooks → react/hooks}/__tests__/state-singleton.spec.tsx +21 -20
  91. package/src/react/hooks/index.ts +11 -0
  92. package/src/{hooks → react/hooks}/state-actions.tsx +1 -1
  93. package/src/{hooks → react/hooks}/state-factory.tsx +2 -2
  94. package/src/{hooks → react/hooks}/state-handler.tsx +1 -1
  95. package/src/react/hooks/state-provider.tsx +56 -0
  96. package/src/{hooks → react/hooks}/state-singleton.tsx +1 -1
  97. package/src/{hooks → react/hooks}/state-subscription-selector.tsx +15 -6
  98. package/src/{hooks → react/hooks}/state-subscription.tsx +5 -9
  99. package/src/react/index.ts +1 -0
  100. package/src/store/__tests__/observable-state-handler.spec.ts +92 -13
  101. package/src/store/__tests__/signal-state-handler.spec.ts +5 -1
  102. package/src/store/base-state-handler.ts +17 -24
  103. package/src/store/dev-tools.ts +3 -3
  104. package/src/store/observable-state-handler.ts +12 -22
  105. package/src/store/signal-state-handler.ts +11 -8
  106. package/src/store/state-singleton.ts +1 -1
  107. package/tsconfig.json +2 -3
  108. package/.eslintrc.cjs +0 -132
  109. package/.github/workflows/pages.yml +0 -46
  110. package/.github/workflows/release.yml +0 -33
  111. package/.nvmrc +0 -1
  112. package/.prettierrc +0 -7
  113. package/docs/assets/index-BBmpszOW.css +0 -1
  114. package/docs/assets/index-Cf8El_RO.js +0 -194
  115. package/docs/assets/statusquo-logo-8GVRbxpc.png +0 -0
  116. package/docs/index.html +0 -13
  117. package/playground/index.html +0 -12
  118. package/playground/src/App.tsx +0 -699
  119. package/playground/src/assets/philosophy-agnostic.svg +0 -18
  120. package/playground/src/assets/philosophy-separation.svg +0 -13
  121. package/playground/src/assets/philosophy-swap.svg +0 -17
  122. package/playground/src/assets/statusquo-logo.png +0 -0
  123. package/playground/src/main.tsx +0 -19
  124. package/playground/src/styles.css +0 -534
  125. package/playground/tsconfig.json +0 -12
  126. package/playground/vite.config.ts +0 -18
@@ -2,30 +2,30 @@ import { lastValueFrom, Subject, take } from 'rxjs';
2
2
 
3
3
  import { resetStatusQuoForTests, setupStatusQuo } from '../../config/status-quo-config.js';
4
4
  import { ObservableStateHandler } from '../observable-state-handler.js';
5
- import type { DistinctOptions } from '../../config/status-quo-config.js';
5
+
6
+ import type { DevToolsOptions, DistinctOptions } from '../../config/status-quo-config.js';
6
7
 
7
8
  type TestState = { test: string; test2: string };
8
9
  type TestActions = { testAction: () => void };
9
10
  type TestObservableHandlerOptions = {
10
- withDevTools?: boolean;
11
+ devTools?: DevToolsOptions;
11
12
  distinct?: DistinctOptions<TestState>;
12
13
  useDistinctUntilChanged?: boolean;
13
14
  };
14
15
 
15
16
  class TestObservableStateHandler extends ObservableStateHandler<TestState, TestActions> {
16
- constructor({ withDevTools, distinct, useDistinctUntilChanged }: TestObservableHandlerOptions = {}) {
17
+ constructor({
18
+ devTools,
19
+ distinct,
20
+ useDistinctUntilChanged,
21
+ }: TestObservableHandlerOptions = {}) {
17
22
  super({
18
23
  initialState: {
19
24
  test: 'testValue',
20
25
  test2: 'testValue2',
21
26
  },
22
27
  options: {
23
- ...(withDevTools && {
24
- devTools: {
25
- enabled: true,
26
- namespace: 'TestObservableStateHandler',
27
- },
28
- }),
28
+ ...(devTools && { devTools }),
29
29
  ...(distinct && {
30
30
  distinct,
31
31
  }),
@@ -48,12 +48,26 @@ class TestObservableStateHandler extends ObservableStateHandler<TestState, TestA
48
48
  describe('Observable State Handler', () => {
49
49
  let stateHandler: TestObservableStateHandler;
50
50
 
51
+ function mockDevToolsExtension() {
52
+ const devTools = {
53
+ init: jest.fn(),
54
+ send: jest.fn(),
55
+ subscribe: jest.fn(),
56
+ };
57
+ const connect = jest.fn(() => devTools);
58
+
59
+ window.__REDUX_DEVTOOLS_EXTENSION__ = { connect };
60
+
61
+ return { connect, devTools };
62
+ }
63
+
51
64
  beforeEach(() => {
52
65
  resetStatusQuoForTests();
53
66
  stateHandler = new TestObservableStateHandler();
54
67
  });
55
68
 
56
69
  afterEach(() => {
70
+ delete window.__REDUX_DEVTOOLS_EXTENSION__;
57
71
  resetStatusQuoForTests();
58
72
  });
59
73
 
@@ -79,7 +93,7 @@ describe('Observable State Handler', () => {
79
93
 
80
94
  stateHandler.setState(expected);
81
95
 
82
- const state = await lastValueFrom(stateHandler.getStateAsObservable().pipe(take(1)));
96
+ const state = await lastValueFrom(stateHandler.getObservable().pipe(take(1)));
83
97
 
84
98
  expect(state).toStrictEqual(expected);
85
99
  expect(stateHandler.getState()).toStrictEqual(expected);
@@ -102,13 +116,13 @@ describe('Observable State Handler', () => {
102
116
  expect(spy).toHaveBeenCalledTimes(1);
103
117
  });
104
118
 
105
- it('should expose state item observable via getObservable', async () => {
106
- const observableValue = await lastValueFrom(stateHandler.getObservable('test').pipe(take(1)));
119
+ it('should expose state item observable via getObservableItem', async () => {
120
+ const observableValue = await lastValueFrom(stateHandler.getObservableItem('test').pipe(take(1)));
107
121
 
108
122
  expect(observableValue).toBe('testValue');
109
123
  });
110
124
 
111
- it('should only call subscriber when object state has changed', async () => {
125
+ it('should only call subscriber when object state has changed', () => {
112
126
  const spy = jest.fn();
113
127
 
114
128
  const unsubscribe = stateHandler.subscribe(spy);
@@ -192,4 +206,69 @@ describe('Observable State Handler', () => {
192
206
 
193
207
  expect(spy).toHaveBeenCalledTimes(2);
194
208
  });
209
+
210
+ it('should enable devtools from global setup and fall back to the class name as namespace', () => {
211
+ const { connect, devTools } = mockDevToolsExtension();
212
+
213
+ setupStatusQuo({
214
+ devTools: {
215
+ enabled: true,
216
+ },
217
+ });
218
+
219
+ new TestObservableStateHandler();
220
+
221
+ expect(connect).toHaveBeenCalledWith(
222
+ expect.objectContaining({
223
+ instanceId: 'testobservablestatehandler',
224
+ name: 'TestObservableStateHandler',
225
+ })
226
+ );
227
+ expect(devTools.init).toHaveBeenCalledWith({
228
+ test: 'testValue',
229
+ test2: 'testValue2',
230
+ });
231
+ expect(devTools.subscribe).toHaveBeenCalledTimes(1);
232
+ });
233
+
234
+ it('should prefer per-handler devtools options over global setup', () => {
235
+ const { connect } = mockDevToolsExtension();
236
+
237
+ setupStatusQuo({
238
+ devTools: {
239
+ enabled: true,
240
+ },
241
+ });
242
+
243
+ new TestObservableStateHandler({
244
+ devTools: {
245
+ namespace: 'LocalHandler',
246
+ },
247
+ });
248
+
249
+ expect(connect).toHaveBeenCalledWith(
250
+ expect.objectContaining({
251
+ instanceId: 'localhandler',
252
+ name: 'LocalHandler',
253
+ })
254
+ );
255
+ });
256
+
257
+ it('should allow per-handler devtools settings to disable a global devtools setup', () => {
258
+ const { connect } = mockDevToolsExtension();
259
+
260
+ setupStatusQuo({
261
+ devTools: {
262
+ enabled: true,
263
+ },
264
+ });
265
+
266
+ new TestObservableStateHandler({
267
+ devTools: {
268
+ enabled: false,
269
+ },
270
+ });
271
+
272
+ expect(connect).not.toHaveBeenCalled();
273
+ });
195
274
  });
@@ -1,6 +1,7 @@
1
1
  import { resetStatusQuoForTests, setupStatusQuo } from '../../config/status-quo-config.js';
2
2
  import { SignalStateHandler } from '../signal-state-handler.js';
3
3
  import { makeStateSingleton } from '../state-singleton.js';
4
+
4
5
  import type { DistinctOptions } from '../../config/status-quo-config.js';
5
6
 
6
7
  type TestState = { test: string; test2: string };
@@ -67,7 +68,10 @@ class CounterSignalStateHandler extends SignalStateHandler<CounterState, Counter
67
68
  }
68
69
  }
69
70
 
70
- class CounterSignalBridgeStateHandler extends SignalStateHandler<CounterState, { noop: () => void }> {
71
+ class CounterSignalBridgeStateHandler extends SignalStateHandler<
72
+ CounterState,
73
+ { noop: () => void }
74
+ > {
71
75
  constructor(
72
76
  counterSingleton: ReturnType<typeof makeStateSingleton<CounterState, CounterActions>>,
73
77
  onCounterSync: (counterState: CounterState) => void
@@ -1,22 +1,17 @@
1
- import { withDevTools } from './dev-tools.js';
1
+ import { resolveDevToolsOptions } from '../config/status-quo-config.js';
2
2
  import { createSelectorCache, selectWithCache } from '../utils/selector-cache.js';
3
+ import { withDevTools } from './dev-tools.js';
3
4
 
4
5
  import type { StateSubscriptionHandler } from '../types/types.js';
5
- import type { DevTools, MessagePayload } from './dev-tools.js';
6
6
  import type { EqualityFn, Selector } from '../utils/selector-cache.js';
7
-
8
- type DevToolsOptions = {
9
- enabled?: boolean;
10
- namespace: string;
11
- };
7
+ import type { DevTools, MessagePayload } from './dev-tools.js';
8
+ import type { DevToolsOptions } from '../config/status-quo-config.js';
12
9
 
13
10
  type Subscribable<T> = {
14
11
  subscribe: (listener: (value: T) => void) => () => void;
15
12
  getSnapshot?: () => T;
16
13
  };
17
14
 
18
- const defaultDevToolsOptions = { enabled: false, namespace: 'Store' };
19
-
20
15
  const devToolsFeatures = {
21
16
  pause: true,
22
17
  lock: true,
@@ -41,19 +36,18 @@ export abstract class BaseStateHandler<S, A> implements StateSubscriptionHandler
41
36
  }
42
37
 
43
38
  protected initDevTools(devToolsOptions?: DevToolsOptions) {
44
- const mergedOptions = {
45
- ...defaultDevToolsOptions,
46
- ...devToolsOptions,
47
- };
39
+ const resolvedOptions = resolveDevToolsOptions(devToolsOptions);
48
40
 
49
- if (!mergedOptions.enabled) {
41
+ if (!resolvedOptions.enabled) {
50
42
  this.devTools = null;
51
43
  return;
52
44
  }
53
45
 
46
+ const namespace = devToolsOptions?.namespace ?? this.getDevToolsNamespace();
47
+
54
48
  this.devTools = withDevTools(this.initialState, {
55
- name: mergedOptions.namespace,
56
- instanceId: mergedOptions.namespace.toLowerCase().replaceAll(' ', '-'),
49
+ name: namespace,
50
+ instanceId: namespace.toLowerCase().replaceAll(' ', '-'),
57
51
  actionCreators: this.getActions(),
58
52
  features: devToolsFeatures,
59
53
  });
@@ -86,25 +80,24 @@ export abstract class BaseStateHandler<S, A> implements StateSubscriptionHandler
86
80
  protected abstract getStateValue(): S;
87
81
  protected abstract setStateValue(nextState: S): void;
88
82
 
89
- protected bindSubscribable<T>(
90
- service: Subscribable<T>,
91
- onChange: (value: T) => void,
92
- selector?: Selector<T, T>,
93
- isEqual?: EqualityFn<T>
94
- ): void;
83
+ protected getDevToolsNamespace() {
84
+ return this.constructor.name || 'Store';
85
+ }
86
+
95
87
  protected bindSubscribable<T, Sel>(
96
88
  service: Subscribable<T>,
97
89
  onChange: (value: Sel) => void,
98
90
  selector: Selector<T, Sel>,
99
91
  isEqual?: EqualityFn<Sel>
100
92
  ): void;
93
+ protected bindSubscribable<T>(service: Subscribable<T>, onChange: (value: T) => void): void;
101
94
  protected bindSubscribable<T, Sel = T>(
102
95
  service: Subscribable<T>,
103
96
  onChange: (value: Sel) => void,
104
97
  selector?: Selector<T, Sel>,
105
98
  isEqual: EqualityFn<Sel> = Object.is
106
99
  ) {
107
- const selectorFn = (selector ?? ((value: T) => value as unknown as Sel)) as Selector<T, Sel>;
100
+ const selectorFn = (selector ?? ((value: T) => value as unknown as Sel));
108
101
  const selectorCache = createSelectorCache<Sel>();
109
102
  let receivedSyncValue = false;
110
103
 
@@ -152,7 +145,7 @@ export abstract class BaseStateHandler<S, A> implements StateSubscriptionHandler
152
145
 
153
146
  case 'JUMP_TO_STATE':
154
147
  case 'JUMP_TO_ACTION':
155
- this.setStateValue(JSON.parse(message.state));
148
+ this.setStateValue(JSON.parse(message.state) as S);
156
149
  break;
157
150
 
158
151
  default:
@@ -28,17 +28,17 @@ export function withDevTools<S>(initialState: S, options = {}): DevTools | null
28
28
  return null;
29
29
  }
30
30
 
31
- // eslint-disable-next-line no-underscore-dangle
31
+
32
32
  if (!window.__REDUX_DEVTOOLS_EXTENSION__) {
33
33
  console.error('Status Quo :: Devtools Extension is not installed!');
34
34
  return null;
35
35
  }
36
36
 
37
- // eslint-disable-next-line no-underscore-dangle
37
+
38
38
  const devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect(options);
39
39
 
40
40
  devTools.init(initialState);
41
41
 
42
- // eslint-disable-next-line consistent-return
42
+
43
43
  return devTools;
44
44
  }
@@ -1,18 +1,15 @@
1
1
  import { BehaviorSubject, distinctUntilChanged, distinctUntilKeyChanged, map } from 'rxjs';
2
2
 
3
- import { BaseStateHandler } from './base-state-handler.js';
4
3
  import { resolveDistinctOptions } from '../config/status-quo-config.js';
4
+ import { BaseStateHandler } from './base-state-handler.js';
5
5
 
6
+ import type { DevToolsOptions, DistinctOptions } from '../config/status-quo-config.js';
6
7
  import type { Observable } from 'rxjs';
7
- import type { DistinctOptions } from '../config/status-quo-config.js';
8
8
 
9
9
  type ObservableStateHandlerProps<S> = {
10
10
  initialState: S;
11
11
  options?: {
12
- devTools?: {
13
- enabled?: boolean;
14
- namespace: string;
15
- };
12
+ devTools?: DevToolsOptions;
16
13
  distinct?: DistinctOptions<S>;
17
14
  useDistinctUntilChanged?: boolean;
18
15
  };
@@ -27,7 +24,10 @@ export abstract class ObservableStateHandler<S, A> extends BaseStateHandler<S, A
27
24
  protected constructor({ initialState, options }: ObservableStateHandlerProps<S>) {
28
25
  super(initialState);
29
26
  this.state$ = new BehaviorSubject<S>(initialState);
30
- this.distinctOptions = resolveDistinctOptions(options?.distinct, options?.useDistinctUntilChanged);
27
+ this.distinctOptions = resolveDistinctOptions(
28
+ options?.distinct,
29
+ options?.useDistinctUntilChanged
30
+ );
31
31
  this.initDevTools(options?.devTools);
32
32
  }
33
33
 
@@ -39,16 +39,15 @@ export abstract class ObservableStateHandler<S, A> extends BaseStateHandler<S, A
39
39
  this.state$.next(nextState);
40
40
  }
41
41
 
42
- getStateItemAsObservable(key: keyof S) {
42
+ getObservableItem<K extends keyof S>(key: K): Observable<S[K]> {
43
43
  return this.state$.pipe(
44
44
  distinctUntilKeyChanged(key),
45
45
  map((state) => state[key])
46
46
  );
47
47
  }
48
48
 
49
- getStateAsObservable(options: StateObservableOptions = {}) {
50
- const useDistinctUntilChanged =
51
- options.useDistinctUntilChanged ?? this.distinctOptions.enabled;
49
+ getObservable(options: StateObservableOptions = {}): Observable<S> {
50
+ const useDistinctUntilChanged = options.useDistinctUntilChanged ?? this.distinctOptions.enabled;
52
51
 
53
52
  if (!useDistinctUntilChanged) {
54
53
  return this.state$;
@@ -58,22 +57,13 @@ export abstract class ObservableStateHandler<S, A> extends BaseStateHandler<S, A
58
57
  distinctUntilChanged((previous, next) => {
59
58
  return this.distinctOptions.comparator(previous, next);
60
59
  })
61
- ) as Observable<S>;
62
- }
63
-
64
- getObservable(key: keyof S) {
65
- return this.getStateItemAsObservable(key);
66
- }
67
-
68
- /** @deprecated Use getObservable instead. */
69
- getObservableItem(key: keyof S) {
70
- return this.getObservable(key);
60
+ );
71
61
  }
72
62
 
73
63
  subscribe(listener: () => void): () => void;
74
64
  subscribe(listener: (value: S) => void): () => void;
75
65
  subscribe(listener: (value: S) => void) {
76
- const subscription = this.getStateAsObservable().subscribe((nextState) => {
66
+ const subscription = this.getObservable().subscribe((nextState) => {
77
67
  listener(nextState);
78
68
  });
79
69
  return () => subscription.unsubscribe();
@@ -1,18 +1,15 @@
1
1
  import { signal } from '@preact/signals-core';
2
2
 
3
- import { BaseStateHandler } from './base-state-handler.js';
4
3
  import { resolveDistinctOptions } from '../config/status-quo-config.js';
4
+ import { BaseStateHandler } from './base-state-handler.js';
5
5
 
6
+ import type { DevToolsOptions, DistinctOptions } from '../config/status-quo-config.js';
6
7
  import type { Signal } from '@preact/signals-core';
7
- import type { DistinctOptions } from '../config/status-quo-config.js';
8
8
 
9
9
  type SignalStateHandlerProps<S> = {
10
10
  initialState: S;
11
11
  options?: {
12
- devTools?: {
13
- enabled?: boolean;
14
- namespace: string;
15
- };
12
+ devTools?: DevToolsOptions;
16
13
  distinct?: DistinctOptions<S>;
17
14
  useDistinctUntilChanged?: boolean;
18
15
  };
@@ -26,7 +23,10 @@ export abstract class SignalStateHandler<S, A> extends BaseStateHandler<S, A> {
26
23
  super(initialState);
27
24
 
28
25
  this.state = signal<S>(initialState);
29
- this.distinctOptions = resolveDistinctOptions(options?.distinct, options?.useDistinctUntilChanged);
26
+ this.distinctOptions = resolveDistinctOptions(
27
+ options?.distinct,
28
+ options?.useDistinctUntilChanged
29
+ );
30
30
  this.initDevTools(options?.devTools);
31
31
  }
32
32
 
@@ -48,7 +48,10 @@ export abstract class SignalStateHandler<S, A> extends BaseStateHandler<S, A> {
48
48
  return;
49
49
  }
50
50
 
51
- if (this.distinctOptions.enabled && this.distinctOptions.comparator(previousSnapshot, nextState)) {
51
+ if (
52
+ this.distinctOptions.enabled &&
53
+ this.distinctOptions.comparator(previousSnapshot, nextState)
54
+ ) {
52
55
  previousSnapshot = nextState;
53
56
  return;
54
57
  }
@@ -10,7 +10,7 @@ export interface StateSingletonOptions {
10
10
 
11
11
  export function makeStateSingleton<S, A>(
12
12
  stateHandlerFactory: () => StateSubscriptionHandler<S, A>,
13
- { destroyOnNoConsumers = true }: StateSingletonOptions = {}
13
+ { destroyOnNoConsumers = false }: StateSingletonOptions = {}
14
14
  ): StateSingleton<S, A> {
15
15
  let instance: StateSubscriptionHandler<S, A> | null = null;
16
16
  const singleton: StateSingleton<S, A> & {
package/tsconfig.json CHANGED
@@ -24,9 +24,8 @@
24
24
  "declarationDir": "dist",
25
25
  "outDir": "dist",
26
26
  "typeRoots": [
27
- "node",
28
- "node_modules/@types",
29
- "node_modules/@redux-devtools/extension"
27
+ "../../node_modules/@types",
28
+ "../../node_modules/@redux-devtools/extension"
30
29
  ],
31
30
  "jsx": "react"
32
31
  },
package/.eslintrc.cjs DELETED
@@ -1,132 +0,0 @@
1
- module.exports = {
2
- root: true,
3
- parser: '@typescript-eslint/parser', // Specifies the ESLint parser
4
- plugins: ['react', '@typescript-eslint', 'simple-import-sort'],
5
- ignorePatterns: [
6
- '.eslintrc.js',
7
- '*.config.js',
8
- 'setupTests.js',
9
- 'setupTests.ts',
10
- 'env.js',
11
- 'env.local.js',
12
- ],
13
- extends: [
14
- 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from @typescript-eslint/eslint-plugin
15
- 'airbnb-base',
16
- 'airbnb-typescript/base',
17
- 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
18
- 'plugin:react/recommended',
19
- 'airbnb',
20
- 'airbnb/hooks',
21
- 'airbnb-typescript',
22
- // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors.
23
- // Make sure this is always the last configuration in the extends array.
24
- 'plugin:prettier/recommended',
25
- ],
26
- parserOptions: {
27
- project: './tsconfig.json',
28
- ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
29
- sourceType: 'module', // Allows for the use of imports
30
- ecmaFeatures: {
31
- jsx: true, // Allows for the parsing of JSX
32
- },
33
- },
34
- settings: {
35
- react: {
36
- version: 'detect', // React version. "detect" automatically picks the version you have installed.
37
- },
38
- },
39
- // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
40
- rules: {
41
- 'max-classes-per-file': ['error', 2],
42
- 'no-console': 'off',
43
- '@typescript-eslint/consistent-type-imports': 'error',
44
- 'no-param-reassign': ['error', {props: false}], // for reducer and simple reference changes
45
- 'import/order': 'off', // Is handled by simple-import-sort
46
- 'import/prefer-default-export': 'off', // This is not really useful, because named exports are easier to import (IDE)
47
- 'import/no-default-export': 'error', // Prefer named exports over default exports since they are easier to find and refactor
48
- 'import/extensions': [
49
- 'error',
50
- 'always',
51
- {
52
- ignorePackages: true,
53
- js: 'always',
54
- },
55
- ],
56
- 'simple-import-sort/exports': 'error',
57
- 'simple-import-sort/imports': [
58
- 'error',
59
- {
60
- /**
61
- * The default grouping, but with type imports last as a separate group.
62
- * From https://github.com/lydell/eslint-plugin-simple-import-sort/blob/37f9448cdfed85dacf27e34c515653ff96f0377a/examples/.eslintrc.js.
63
- */
64
- groups: [['^\\u0000'], ['^@?\\w'], ['^'], ['^\\.'], ['^.+\\u0000$']],
65
- },
66
- ],
67
- '@typescript-eslint/no-use-before-define': ['error', {functions: false}], // function declarations are always
68
- // hoisted so it's safe
69
- '@typescript-eslint/lines-between-class-members': [
70
- 'error',
71
- 'always',
72
- {exceptAfterSingleLine: true},
73
- ], // Avoid blowing up classes
74
-
75
- // Forbid the use of extraneous packages
76
- // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-extraneous-dependencies.md
77
- // paths are treated both as absolute paths, and relative to process.cwd()
78
- 'import/no-extraneous-dependencies': [
79
- 'error',
80
- {
81
- devDependencies: [
82
- '**/setupTests.{js,ts}', // test setup files
83
- 'test/**', // tape, common npm pattern
84
- 'tests/**', // also common npm pattern
85
- 'spec/**', // mocha, rspec-like pattern
86
- '**/__tests__/**', // jest pattern
87
- '**/__mocks__/**', // jest pattern
88
- 'test.{js,jsx}', // repos with a single test file
89
- 'test-*.{js,jsx}', // repos with multiple top-level test files
90
- '**/*{.,_}{test,spec}.{js,jsx}', // tests where the extension or filename suffix denotes that it is a test
91
- '**/jest.config.js', // jest config
92
- '**/jest.setup.js', // jest setup
93
- '**/vue.config.js', // vue-cli config
94
- '**/webpack.config.js', // webpack config
95
- '**/webpack.config.*.js', // webpack config
96
- '**/rollup.config.js', // rollup config
97
- '**/rollup.config.*.js', // rollup config
98
- '**/gulpfile.js', // gulp config
99
- '**/gulpfile.*.js', // gulp config
100
- '**/Gruntfile{,.js}', // grunt config
101
- '**/protractor.conf.js', // protractor config
102
- '**/protractor.conf.*.js', // protractor config
103
- '**/karma.conf.js', // karma config
104
- '**/.eslintrc.js', // eslint config
105
- ],
106
- optionalDependencies: false,
107
- },
108
- ],
109
- 'react/prop-types': 'off', // Since we do not use prop-types
110
- 'react/require-default-props': 'off', // Since we do not use prop-types
111
- // Many of our loops are server side rendered, so we can rely on the index in general
112
- 'react/no-array-index-key': 0,
113
- // To support hydration of components, a string is necessary so that the minification of bundles
114
- // do not affect our markup generation on the server.
115
- 'react/display-name': [2, {ignoreTranspilerName: true}],
116
- // aria roles ignored (0) instead of warning (1) / errors (2).
117
- 'jsx-a11y/role-supports-aria-props': 0,
118
- 'react/function-component-definition': [2, {namedComponents: 'arrow-function'}],
119
- // Conditional spreads are easier to do so we can deactivate this rule
120
- 'react/jsx-props-no-spreading': 0,
121
- // Enforce the definition of Fragment instead of shorthand syntax.
122
- // The thing is, that keys can only be applied to the long version. So we should stick to one version.
123
- 'react/jsx-fragments': [2, 'element'],
124
- // We need to use setDangerouslyInnerHtml for article and server side rendered markup prepared by external helpers.
125
- // So it makes no sense to have this rule in place.
126
- 'react/no-danger': 0,
127
- // strict null-checking is not necessary.
128
- // The syntax itself should be avoided for sure but in some cases where we know we get the data,
129
- // we can use this functionality
130
- '@typescript-eslint/no-non-null-assertion': 0,
131
- },
132
- };
@@ -1,46 +0,0 @@
1
- name: Deploy Docs
2
-
3
- on:
4
- push:
5
- branches: [master]
6
- workflow_dispatch:
7
-
8
- permissions:
9
- contents: read
10
- pages: write
11
- id-token: write
12
-
13
- concurrency:
14
- group: pages
15
- cancel-in-progress: true
16
-
17
- jobs:
18
- build:
19
- runs-on: ubuntu-latest
20
- steps:
21
- - name: Checkout
22
- uses: actions/checkout@v4
23
- - name: Setup Node
24
- uses: actions/setup-node@v4
25
- with:
26
- node-version: 20
27
- cache: npm
28
- - name: Install dependencies
29
- run: npm ci
30
- - name: Build docs
31
- run: npm run docs:build
32
- - name: Upload artifact
33
- uses: actions/upload-pages-artifact@v3
34
- with:
35
- path: ./docs
36
-
37
- deploy:
38
- runs-on: ubuntu-latest
39
- needs: build
40
- environment:
41
- name: github-pages
42
- url: ${{ steps.deployment.outputs.page_url }}
43
- steps:
44
- - name: Deploy
45
- id: deployment
46
- uses: actions/deploy-pages@v4
@@ -1,33 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- push:
5
- tags:
6
- - 'v*'
7
-
8
- permissions:
9
- contents: write
10
-
11
- jobs:
12
- release:
13
- runs-on: ubuntu-latest
14
- steps:
15
- - name: Checkout
16
- uses: actions/checkout@v4
17
- - name: Setup Node
18
- uses: actions/setup-node@v4
19
- with:
20
- node-version: 20
21
- cache: npm
22
- - name: Install dependencies
23
- run: npm ci
24
- - name: Build dist
25
- run: npm run build
26
- - name: Package dist
27
- run: |
28
- zip -r "dist-${{ github.ref_name }}.zip" dist
29
- - name: Create GitHub release
30
- uses: softprops/action-gh-release@v2
31
- with:
32
- files: dist-${{ github.ref_name }}.zip
33
- generate_release_notes: true
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- v22
package/.prettierrc DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "useTabs": false,
3
- "printWidth": 100,
4
- "singleQuote": true,
5
- "trailingComma": "es5",
6
- "semi": true
7
- }