@qoretechnologies/reqraft 0.10.11 → 0.10.12

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.
@@ -1 +1 @@
1
- {"version":3,"file":"StorageProvider.d.ts","sourceRoot":"","sources":["../../src/providers/StorageProvider.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAwB,MAAM,OAAO,CAAC;AAQxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,WAAW,4BACf,SAAQ,IAAI,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;IACrD,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,eAAO,MAAM,mBAAmB,iCAAkC,4BAA4B,4CAyE7F,CAAC"}
1
+ {"version":3,"file":"StorageProvider.d.ts","sourceRoot":"","sources":["../../src/providers/StorageProvider.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAwB,MAAM,OAAO,CAAC;AAQxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,WAAW,4BACf,SAAQ,IAAI,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;IACrD,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,eAAO,MAAM,mBAAmB,iCAAkC,4BAA4B,4CAiF7F,CAAC"}
@@ -28,19 +28,29 @@ var ReqraftUserProvider = function (_a) {
28
28
  return (_a = (0, lodash_1.get)(currentUser === null || currentUser === void 0 ? void 0 : currentUser.storage, _path)) !== null && _a !== void 0 ? _a : defaultValue;
29
29
  }, [appName, currentUser === null || currentUser === void 0 ? void 0 : currentUser.storage]);
30
30
  var updateStorage = (0, react_1.useCallback)(function (path, value, includeAppPrefix) {
31
+ var _a;
31
32
  if (includeAppPrefix === void 0) { includeAppPrefix = true; }
32
33
  var _path = includeAppPrefix ? "".concat(appName, ".").concat(path) : path;
33
- var updatedStorage = (0, lodash_1.set)((0, lodash_1.cloneDeep)(currentUser === null || currentUser === void 0 ? void 0 : currentUser.storage), _path, value);
34
+ // Base the write on the LATEST storage read straight from the store, not
35
+ // the blob captured in this callback's closure. A caller holding a stale
36
+ // updater — e.g. an imperative store subscription created on mount, before
37
+ // other keys had loaded — would otherwise persist an out-of-date blob and
38
+ // wipe every key written to storage since its closure was captured.
39
+ var latestStorage = (_a = currentUser_1.currentUserStore.getState().currentUser) === null || _a === void 0 ? void 0 : _a.storage;
40
+ var updatedStorage = (0, lodash_1.set)((0, lodash_1.cloneDeep)(latestStorage), _path, value);
34
41
  updateCurrentUserStorage(updatedStorage);
35
42
  load({ body: { storage: updatedStorage } });
36
- }, [appName, currentUser === null || currentUser === void 0 ? void 0 : currentUser.storage, load, updateCurrentUserStorage]);
43
+ }, [appName, load, updateCurrentUserStorage]);
37
44
  var removeStorageValue = (0, react_1.useCallback)(function (path, includeAppPrefix) {
45
+ var _a;
38
46
  if (includeAppPrefix === void 0) { includeAppPrefix = true; }
39
47
  var _path = includeAppPrefix ? "".concat(appName, ".").concat(path) : path;
40
- var updatedStorage = (0, lodash_1.set)((0, lodash_1.cloneDeep)(currentUser === null || currentUser === void 0 ? void 0 : currentUser.storage), _path, null);
48
+ // Same as `updateStorage`: mutate the latest blob, never a stale closure copy.
49
+ var latestStorage = (_a = currentUser_1.currentUserStore.getState().currentUser) === null || _a === void 0 ? void 0 : _a.storage;
50
+ var updatedStorage = (0, lodash_1.set)((0, lodash_1.cloneDeep)(latestStorage), _path, null);
41
51
  updateCurrentUserStorage(updatedStorage);
42
52
  load({ body: { storage_path: _path } });
43
- }, [appName, currentUser === null || currentUser === void 0 ? void 0 : currentUser.storage, load, updateCurrentUserStorage]);
53
+ }, [appName, load, updateCurrentUserStorage]);
44
54
  var contextValue = (0, react_1.useMemo)(function () { return ({ storage: currentUser === null || currentUser === void 0 ? void 0 : currentUser.storage, getStorage: getStorage, updateStorage: updateStorage, removeStorageValue: removeStorageValue }); }, [currentUser === null || currentUser === void 0 ? void 0 : currentUser.storage, getStorage, updateStorage, removeStorageValue]);
45
55
  if (loading && waitForStorage) {
46
56
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"StorageProvider.js","sourceRoot":"","sources":["../../src/providers/StorageProvider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StorageProvider.js","sourceRoot":"","sources":["../../src/providers/StorageProvider.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qoretechnologies/reqraft",
3
- "version": "0.10.11",
3
+ "version": "0.10.12",
4
4
  "description": "ReQraft is a collection of React components and hooks that are used across Qore Technologies' products made using the ReQore component library from Qore.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,5 +1,6 @@
1
1
  import { ReqoreButton, ReqoreControlGroup, ReqoreP } from '@qoretechnologies/reqore';
2
2
  import { StoryObj } from '@storybook/react-vite';
3
+ import { useRef } from 'react';
3
4
  import { fireEvent, within } from 'storybook/test';
4
5
  import { storiesStorageMock } from '../../../__tests__/ mock';
5
6
  import { testsWaitForText } from '../../../__tests__/utils';
@@ -93,6 +94,57 @@ export const ValueCanBeUpdated: Story = {
93
94
  },
94
95
  };
95
96
 
97
+ /**
98
+ * Regression for the "stale updater clobbers other keys" bug: the storage
99
+ * updater must persist onto the LATEST storage blob, not the one captured in
100
+ * its closure. Long-lived callers (an imperative store subscription, a ref to
101
+ * the mount-time setter) hold an old updater; writing through it must not wipe
102
+ * keys other hooks wrote in the meantime.
103
+ */
104
+ export const StaleUpdaterDoesNotClobberOtherKeys: Story = {
105
+ args: { reqraftOptions: { waitForStorage: true } },
106
+ parameters: {
107
+ docs: {
108
+ description: {
109
+ story:
110
+ "Renders two useReqraftStorage keys and captures the mount-time setter for the first one (a deliberately STALE updater). After a second key is written, writing through the stale updater must keep the second key intact — proving storage writes merge onto the latest blob rather than a closure copy. This is the regression test for page-visit tracking wiping other stored settings.",
111
+ },
112
+ },
113
+ mockData: [...storiesStorageMock],
114
+ },
115
+ render: () => {
116
+ const [someVal, setSomeVal] = useReqraftStorage<string>('some-path', 'default');
117
+ const [keyB] = useReqraftStorage<string>('key-b', 'b-default');
118
+ const [, setKeyB] = useReqraftStorage<string>('key-b', 'b-default');
119
+ // Capture the mount-time setter — it stays stale once other keys change.
120
+ const staleSetSomeVal = useRef(setSomeVal);
121
+
122
+ return (
123
+ <ReqoreControlGroup vertical>
124
+ <ReqoreP>{`some-path: ${someVal}`}</ReqoreP>
125
+ <ReqoreP>{`key-b: ${keyB}`}</ReqoreP>
126
+ <ReqoreButton onClick={() => setKeyB('b-written')}>Write B</ReqoreButton>
127
+ <ReqoreButton onClick={() => staleSetSomeVal.current('a-written')}>
128
+ Write A (stale)
129
+ </ReqoreButton>
130
+ </ReqoreControlGroup>
131
+ );
132
+ },
133
+ play: async ({ canvasElement }) => {
134
+ const canvas = within(canvasElement);
135
+
136
+ await testsWaitForText('some-path: This is a storage value');
137
+ // Write a second key through its own (fresh) updater.
138
+ await fireEvent.click(canvas.getByText('Write B'));
139
+ await testsWaitForText('key-b: b-written');
140
+ // Write the first key through the STALE mount-time updater.
141
+ await fireEvent.click(canvas.getByText('Write A (stale)'));
142
+ await testsWaitForText('some-path: a-written');
143
+ // The stale write must NOT have wiped the second key.
144
+ await testsWaitForText('key-b: b-written');
145
+ },
146
+ };
147
+
96
148
  export const ValueCanBeRemoved: Story = {
97
149
  ...StorageValue,
98
150
  parameters: {
@@ -53,26 +53,34 @@ export const ReqraftUserProvider = ({ children, waitForStorage }: IReqraftStorag
53
53
  includeAppPrefix: boolean = true
54
54
  ) {
55
55
  const _path = includeAppPrefix ? `${appName}.${path}` : path;
56
- const updatedStorage = set(cloneDeep(currentUser?.storage), _path, value);
56
+ // Base the write on the LATEST storage read straight from the store, not
57
+ // the blob captured in this callback's closure. A caller holding a stale
58
+ // updater — e.g. an imperative store subscription created on mount, before
59
+ // other keys had loaded — would otherwise persist an out-of-date blob and
60
+ // wipe every key written to storage since its closure was captured.
61
+ const latestStorage = currentUserStore.getState().currentUser?.storage;
62
+ const updatedStorage = set(cloneDeep(latestStorage), _path, value);
57
63
 
58
64
  updateCurrentUserStorage(updatedStorage);
59
65
 
60
66
  load({ body: { storage: updatedStorage } });
61
67
  },
62
- [appName, currentUser?.storage, load, updateCurrentUserStorage]
68
+ [appName, load, updateCurrentUserStorage]
63
69
  );
64
70
 
65
71
  const removeStorageValue = useCallback(
66
72
  function (path: string, includeAppPrefix: boolean = true) {
67
73
  const _path = includeAppPrefix ? `${appName}.${path}` : path;
68
74
 
69
- const updatedStorage = set(cloneDeep(currentUser?.storage), _path, null);
75
+ // Same as `updateStorage`: mutate the latest blob, never a stale closure copy.
76
+ const latestStorage = currentUserStore.getState().currentUser?.storage;
77
+ const updatedStorage = set(cloneDeep(latestStorage), _path, null);
70
78
 
71
79
  updateCurrentUserStorage(updatedStorage);
72
80
 
73
81
  load({ body: { storage_path: _path } });
74
82
  },
75
- [appName, currentUser?.storage, load, updateCurrentUserStorage]
83
+ [appName, load, updateCurrentUserStorage]
76
84
  );
77
85
 
78
86
  const contextValue = useMemo(