@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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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":";;;;AAAA,iCAA6C;AAC7C,+BAAwD;AACxD,uCAA0C;AAE1C,6DAAoF;AACpF,uDAAsD;AACtD,kEAAiE;AAEjE,iEAAqE;AAQ9D,IAAM,mBAAmB,GAAG,UAAC,EAA0D;QAAxD,QAAQ,cAAA,EAAE,cAAc,oBAAA;IAC5D,IAAM,OAAO,GAAG,IAAA,uCAAkB,EAAC,SAAS,CAAC,CAAC;IACxC,IAAA,KAKF,IAAA,8BAAgB,GAAE,EAJpB,WAAW,iBAAA,EACI,wBAAwB,mBAAA,EACjC,eAAe,UAAA,EACrB,OAAO,aACa,CAAC;IAEvB,IAAA,yBAAa,EAAC;QACZ,eAAe,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEK,IAAA,IAAI,GAAK,IAAA,mBAAQ,EAAC;QACxB,GAAG,EAAE,kBAAkB;QACvB,MAAM,EAAE,KAAK;QACb,KAAK,EAAE,KAAK;KACb,CAAC,KAJU,CAIT;IAEH,IAAM,UAAU,GAAG,IAAA,mBAAW,EAC5B,UACE,IAAY,EACZ,YAAe,EACf,gBAAgC;;QAAhC,iCAAA,EAAA,uBAAgC;QAEhC,IAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,CAAC,UAAG,OAAO,cAAI,IAAI,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAE7D,OAAO,MAAA,IAAA,YAAG,EAAC,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,OAAO,EAAE,KAAK,CAAC,mCAAI,YAAY,CAAC;IAC1D,CAAC,EACD,CAAC,OAAO,EAAE,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,OAAO,CAAC,CAChC,CAAC;IAEF,IAAM,aAAa,GAAG,IAAA,mBAAW,EAC/B,UACE,IAAY,EACZ,KAAQ,EACR,gBAAgC
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
83
|
+
[appName, load, updateCurrentUserStorage]
|
|
76
84
|
);
|
|
77
85
|
|
|
78
86
|
const contextValue = useMemo(
|