@okyrychenko-dev/react-action-guard 0.4.0 → 0.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.
- package/README.md +276 -63
- package/dist/index.cjs +178 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -53
- package/dist/index.d.ts +23 -53
- package/dist/index.js +176 -78
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import {
|
|
1
|
+
import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { createStoreProvider, createShallowStore } from '@okyrychenko-dev/react-zustand-toolkit';
|
|
3
|
+
export { createShallowStore } from '@okyrychenko-dev/react-zustand-toolkit';
|
|
4
|
+
import { useStore } from 'zustand';
|
|
5
|
+
import 'zustand/react/shallow';
|
|
5
6
|
import { jsx } from 'react/jsx-runtime';
|
|
7
|
+
import { useShallow } from 'zustand/shallow';
|
|
8
|
+
import { devtools } from 'zustand/middleware';
|
|
6
9
|
|
|
7
10
|
// src/hooks/useAsyncAction/useAsyncAction.ts
|
|
8
11
|
|
|
@@ -12,13 +15,6 @@ var DEFAULT_REASON = "Unknown";
|
|
|
12
15
|
var DEFAULT_PRIORITY = 0;
|
|
13
16
|
var ASYNC_ACTION_PRIORITY = 20;
|
|
14
17
|
var DEVTOOLS_NAME = "UIBlocking";
|
|
15
|
-
function createShallowStore(storeCreator) {
|
|
16
|
-
const storeApi = createStore(storeCreator);
|
|
17
|
-
const useScopedStore = (selector) => {
|
|
18
|
-
return useStore(storeApi, useShallow(selector));
|
|
19
|
-
};
|
|
20
|
-
return { useStore: useScopedStore, useStoreApi: storeApi };
|
|
21
|
-
}
|
|
22
18
|
function normalizeScopeToArray(scope) {
|
|
23
19
|
if (typeof scope === "string") {
|
|
24
20
|
return [scope];
|
|
@@ -27,6 +23,16 @@ function normalizeScopeToArray(scope) {
|
|
|
27
23
|
}
|
|
28
24
|
|
|
29
25
|
// src/store/uiBlockingStore.actions.ts
|
|
26
|
+
var normalizePriority = (priority, fallback = DEFAULT_PRIORITY) => {
|
|
27
|
+
return priority !== void 0 ? Math.max(0, priority) : fallback;
|
|
28
|
+
};
|
|
29
|
+
var createMiddlewareContext = (action, blockerId, config, prevState) => ({
|
|
30
|
+
action,
|
|
31
|
+
blockerId,
|
|
32
|
+
config,
|
|
33
|
+
timestamp: Date.now(),
|
|
34
|
+
...prevState && { prevState }
|
|
35
|
+
});
|
|
30
36
|
var createUIBlockingActions = (set, get) => ({
|
|
31
37
|
// State
|
|
32
38
|
activeBlockers: /* @__PURE__ */ new Map(),
|
|
@@ -37,7 +43,6 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
37
43
|
newMiddlewares.set(name, middleware);
|
|
38
44
|
return { middlewares: newMiddlewares };
|
|
39
45
|
});
|
|
40
|
-
console.log(`[UIBlocking] Registered middleware: ${name}`);
|
|
41
46
|
},
|
|
42
47
|
unregisterMiddleware: (name) => {
|
|
43
48
|
set((state) => {
|
|
@@ -45,7 +50,6 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
45
50
|
newMiddlewares.delete(name);
|
|
46
51
|
return { middlewares: newMiddlewares };
|
|
47
52
|
});
|
|
48
|
-
console.log(`[UIBlocking] Unregistered middleware: ${name}`);
|
|
49
53
|
},
|
|
50
54
|
runMiddlewares: async (context) => {
|
|
51
55
|
const { middlewares } = get();
|
|
@@ -75,17 +79,14 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
75
79
|
timeoutId = setTimeout(() => {
|
|
76
80
|
if (get().activeBlockers.has(id)) {
|
|
77
81
|
config.onTimeout?.(id);
|
|
78
|
-
void get().runMiddlewares(
|
|
79
|
-
|
|
80
|
-
blockerId: id,
|
|
81
|
-
config: {
|
|
82
|
+
void get().runMiddlewares(
|
|
83
|
+
createMiddlewareContext("timeout", id, {
|
|
82
84
|
scope: config.scope,
|
|
83
85
|
reason: config.reason,
|
|
84
86
|
priority: config.priority,
|
|
85
87
|
timeout: config.timeout
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
});
|
|
88
|
+
})
|
|
89
|
+
);
|
|
89
90
|
get().removeBlocker(id);
|
|
90
91
|
}
|
|
91
92
|
}, config.timeout);
|
|
@@ -95,7 +96,7 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
95
96
|
const storedBlocker = {
|
|
96
97
|
scope: config.scope ?? DEFAULT_SCOPE,
|
|
97
98
|
reason: config.reason ?? DEFAULT_REASON,
|
|
98
|
-
priority: config.priority
|
|
99
|
+
priority: normalizePriority(config.priority),
|
|
99
100
|
timestamp: config.timestamp ?? Date.now(),
|
|
100
101
|
timeout: config.timeout,
|
|
101
102
|
timeoutId,
|
|
@@ -104,17 +105,92 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
104
105
|
newBlockers.set(id, storedBlocker);
|
|
105
106
|
return { activeBlockers: newBlockers };
|
|
106
107
|
});
|
|
107
|
-
void get().runMiddlewares(
|
|
108
|
-
|
|
109
|
-
blockerId: id,
|
|
110
|
-
config: {
|
|
108
|
+
void get().runMiddlewares(
|
|
109
|
+
createMiddlewareContext("add", id, {
|
|
111
110
|
scope: config.scope,
|
|
112
111
|
reason: config.reason,
|
|
113
112
|
priority: config.priority,
|
|
114
113
|
timeout: config.timeout
|
|
115
|
-
}
|
|
116
|
-
|
|
114
|
+
})
|
|
115
|
+
);
|
|
116
|
+
},
|
|
117
|
+
/**
|
|
118
|
+
* Update blocker metadata. If timeout is updated, the timer will be restarted.
|
|
119
|
+
*
|
|
120
|
+
* @param id - Unique blocker identifier
|
|
121
|
+
* @param config - Partial blocker configuration to merge
|
|
122
|
+
*
|
|
123
|
+
*/
|
|
124
|
+
updateBlocker: (id, config = {}) => {
|
|
125
|
+
const existingBlocker = get().activeBlockers.get(id);
|
|
126
|
+
if (!existingBlocker) {
|
|
127
|
+
get().addBlocker(id, config);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let timeoutId = existingBlocker.timeoutId;
|
|
131
|
+
const timeoutChanged = config.timeout !== void 0 && config.timeout !== existingBlocker.timeout;
|
|
132
|
+
if (timeoutChanged) {
|
|
133
|
+
if (existingBlocker.timeoutId) {
|
|
134
|
+
clearTimeout(existingBlocker.timeoutId);
|
|
135
|
+
}
|
|
136
|
+
if (config.timeout && config.timeout > 0) {
|
|
137
|
+
const newTimeout = config.timeout;
|
|
138
|
+
const newOnTimeout = config.onTimeout ?? existingBlocker.onTimeout;
|
|
139
|
+
timeoutId = setTimeout(() => {
|
|
140
|
+
if (get().activeBlockers.has(id)) {
|
|
141
|
+
newOnTimeout?.(id);
|
|
142
|
+
void get().runMiddlewares(
|
|
143
|
+
createMiddlewareContext("timeout", id, {
|
|
144
|
+
scope: config.scope ?? existingBlocker.scope,
|
|
145
|
+
reason: config.reason ?? existingBlocker.reason,
|
|
146
|
+
priority: config.priority ?? existingBlocker.priority,
|
|
147
|
+
timeout: newTimeout
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
get().removeBlocker(id);
|
|
151
|
+
}
|
|
152
|
+
}, newTimeout);
|
|
153
|
+
} else {
|
|
154
|
+
timeoutId = void 0;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const updatedBlocker = {
|
|
158
|
+
...existingBlocker,
|
|
159
|
+
scope: config.scope ?? existingBlocker.scope,
|
|
160
|
+
reason: config.reason ?? existingBlocker.reason,
|
|
161
|
+
priority: normalizePriority(config.priority, existingBlocker.priority),
|
|
162
|
+
timestamp: config.timestamp ?? existingBlocker.timestamp,
|
|
163
|
+
timeout: config.timeout ?? existingBlocker.timeout,
|
|
164
|
+
timeoutId,
|
|
165
|
+
onTimeout: config.onTimeout ?? existingBlocker.onTimeout
|
|
166
|
+
};
|
|
167
|
+
set((state) => {
|
|
168
|
+
const newBlockers = new Map(state.activeBlockers);
|
|
169
|
+
newBlockers.set(id, updatedBlocker);
|
|
170
|
+
return { activeBlockers: newBlockers };
|
|
117
171
|
});
|
|
172
|
+
void get().runMiddlewares(
|
|
173
|
+
createMiddlewareContext(
|
|
174
|
+
"update",
|
|
175
|
+
id,
|
|
176
|
+
{
|
|
177
|
+
scope: updatedBlocker.scope,
|
|
178
|
+
reason: updatedBlocker.reason,
|
|
179
|
+
priority: updatedBlocker.priority,
|
|
180
|
+
timeout: updatedBlocker.timeout,
|
|
181
|
+
onTimeout: updatedBlocker.onTimeout,
|
|
182
|
+
timestamp: updatedBlocker.timestamp
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
scope: existingBlocker.scope,
|
|
186
|
+
reason: existingBlocker.reason,
|
|
187
|
+
priority: existingBlocker.priority,
|
|
188
|
+
timeout: existingBlocker.timeout,
|
|
189
|
+
onTimeout: existingBlocker.onTimeout,
|
|
190
|
+
timestamp: existingBlocker.timestamp
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
);
|
|
118
194
|
},
|
|
119
195
|
/**
|
|
120
196
|
* Remove a blocker from the store
|
|
@@ -133,13 +209,7 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
133
209
|
return { activeBlockers: newBlockers };
|
|
134
210
|
});
|
|
135
211
|
if (prevBlocker) {
|
|
136
|
-
void get().runMiddlewares(
|
|
137
|
-
action: "remove",
|
|
138
|
-
blockerId: id,
|
|
139
|
-
config: prevBlocker,
|
|
140
|
-
timestamp: Date.now(),
|
|
141
|
-
prevState: prevBlocker
|
|
142
|
-
});
|
|
212
|
+
void get().runMiddlewares(createMiddlewareContext("remove", id, prevBlocker, prevBlocker));
|
|
143
213
|
}
|
|
144
214
|
},
|
|
145
215
|
/**
|
|
@@ -186,12 +256,21 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
186
256
|
*/
|
|
187
257
|
clearAllBlockers: () => {
|
|
188
258
|
const { activeBlockers } = get();
|
|
259
|
+
const count = activeBlockers.size;
|
|
189
260
|
for (const [, blocker] of activeBlockers) {
|
|
190
261
|
if (blocker.timeoutId) {
|
|
191
262
|
clearTimeout(blocker.timeoutId);
|
|
192
263
|
}
|
|
193
264
|
}
|
|
194
265
|
set({ activeBlockers: /* @__PURE__ */ new Map() });
|
|
266
|
+
if (count > 0) {
|
|
267
|
+
void get().runMiddlewares({
|
|
268
|
+
action: "clear",
|
|
269
|
+
blockerId: "*",
|
|
270
|
+
timestamp: Date.now(),
|
|
271
|
+
count
|
|
272
|
+
});
|
|
273
|
+
}
|
|
195
274
|
},
|
|
196
275
|
/**
|
|
197
276
|
* Clear all blockers for a specific scope
|
|
@@ -201,10 +280,14 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
201
280
|
*/
|
|
202
281
|
clearBlockersForScope: (scope) => {
|
|
203
282
|
const { activeBlockers } = get();
|
|
283
|
+
let count = 0;
|
|
204
284
|
for (const [, blocker] of activeBlockers) {
|
|
205
285
|
const blockerScopes = normalizeScopeToArray(blocker.scope);
|
|
206
|
-
if (blockerScopes.includes(scope)
|
|
207
|
-
|
|
286
|
+
if (blockerScopes.includes(scope)) {
|
|
287
|
+
if (blocker.timeoutId) {
|
|
288
|
+
clearTimeout(blocker.timeoutId);
|
|
289
|
+
}
|
|
290
|
+
count++;
|
|
208
291
|
}
|
|
209
292
|
}
|
|
210
293
|
set((state) => {
|
|
@@ -217,46 +300,44 @@ var createUIBlockingActions = (set, get) => ({
|
|
|
217
300
|
}
|
|
218
301
|
return { activeBlockers: newBlockers };
|
|
219
302
|
});
|
|
303
|
+
if (count > 0) {
|
|
304
|
+
void get().runMiddlewares({
|
|
305
|
+
action: "clear_scope",
|
|
306
|
+
blockerId: "*",
|
|
307
|
+
timestamp: Date.now(),
|
|
308
|
+
scope,
|
|
309
|
+
count
|
|
310
|
+
});
|
|
311
|
+
}
|
|
220
312
|
}
|
|
221
313
|
});
|
|
222
|
-
var
|
|
314
|
+
var {
|
|
315
|
+
Provider: BaseUIBlockingProvider,
|
|
316
|
+
useContext: useUIBlockingContext,
|
|
317
|
+
useContextStore: useUIBlockingStoreFromContext,
|
|
318
|
+
useIsInsideProvider: useIsInsideUIBlockingProvider,
|
|
319
|
+
useOptionalContext
|
|
320
|
+
} = createStoreProvider(createUIBlockingActions, "UIBlocking");
|
|
223
321
|
function UIBlockingProvider({
|
|
224
322
|
children,
|
|
225
323
|
enableDevtools = process.env.NODE_ENV === "development",
|
|
226
324
|
devtoolsName = "UIBlocking",
|
|
227
325
|
middlewares = []
|
|
228
326
|
}) {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
devtools(createUIBlockingActions, {
|
|
233
|
-
name: devtoolsName,
|
|
234
|
-
enabled: enableDevtools
|
|
235
|
-
})
|
|
236
|
-
) : createStore(createUIBlockingActions);
|
|
237
|
-
middlewaresRef.current.forEach((mw, index) => {
|
|
238
|
-
baseStore.getState().registerMiddleware(`provider-middleware-${index.toString()}`, mw);
|
|
327
|
+
const handleStoreCreate = (store) => {
|
|
328
|
+
middlewares.forEach((mw, index) => {
|
|
329
|
+
store.getState().registerMiddleware(`provider-middleware-${index.toString()}`, mw);
|
|
239
330
|
});
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
return store;
|
|
252
|
-
}
|
|
253
|
-
function useIsInsideUIBlockingProvider() {
|
|
254
|
-
const store = useContext(UIBlockingContext);
|
|
255
|
-
return store !== null;
|
|
256
|
-
}
|
|
257
|
-
function useUIBlockingStoreFromContext(selector) {
|
|
258
|
-
const store = useUIBlockingContext();
|
|
259
|
-
return useStore(store, useShallow(selector));
|
|
331
|
+
};
|
|
332
|
+
return /* @__PURE__ */ jsx(
|
|
333
|
+
BaseUIBlockingProvider,
|
|
334
|
+
{
|
|
335
|
+
enableDevtools,
|
|
336
|
+
devtoolsName,
|
|
337
|
+
onStoreCreate: handleStoreCreate,
|
|
338
|
+
children
|
|
339
|
+
}
|
|
340
|
+
);
|
|
260
341
|
}
|
|
261
342
|
|
|
262
343
|
// src/store/uiBlockingStore.config.ts
|
|
@@ -269,8 +350,11 @@ var devtoolsConfig = {
|
|
|
269
350
|
var { useStore: useUIBlockingStore, useStoreApi: uiBlockingStoreApi } = createShallowStore(devtools(createUIBlockingActions, devtoolsConfig));
|
|
270
351
|
|
|
271
352
|
// src/context/useResolvedStore.ts
|
|
353
|
+
function useOptionalContextStore() {
|
|
354
|
+
return useOptionalContext();
|
|
355
|
+
}
|
|
272
356
|
function useResolvedStore() {
|
|
273
|
-
const contextStore =
|
|
357
|
+
const contextStore = useOptionalContextStore();
|
|
274
358
|
return contextStore ?? uiBlockingStoreApi;
|
|
275
359
|
}
|
|
276
360
|
function useResolvedStoreWithSelector(selector) {
|
|
@@ -284,9 +368,11 @@ var useAsyncAction = (actionId, scope, options) => {
|
|
|
284
368
|
addBlocker: state.addBlocker,
|
|
285
369
|
removeBlocker: state.removeBlocker
|
|
286
370
|
}));
|
|
371
|
+
const counterRef = useRef(0);
|
|
287
372
|
const executeWithBlocking = useCallback(
|
|
288
373
|
async (asyncFn) => {
|
|
289
|
-
|
|
374
|
+
counterRef.current += 1;
|
|
375
|
+
const blockerId = `${actionId}-${String(counterRef.current)}`;
|
|
290
376
|
try {
|
|
291
377
|
addBlocker(blockerId, {
|
|
292
378
|
scope,
|
|
@@ -621,14 +707,16 @@ var getActionEmoji = (action) => {
|
|
|
621
707
|
switch (action) {
|
|
622
708
|
case "add":
|
|
623
709
|
return "\u2795";
|
|
624
|
-
case "remove":
|
|
625
|
-
return "\u2796";
|
|
626
710
|
case "update":
|
|
627
711
|
return "\u{1F504}";
|
|
628
|
-
case "
|
|
629
|
-
return "\
|
|
712
|
+
case "remove":
|
|
713
|
+
return "\u2796";
|
|
630
714
|
case "timeout":
|
|
631
715
|
return "\u23F1\uFE0F";
|
|
716
|
+
case "clear":
|
|
717
|
+
return "\u{1F9F9}";
|
|
718
|
+
case "clear_scope":
|
|
719
|
+
return "\u{1F3AF}";
|
|
632
720
|
default:
|
|
633
721
|
return "\u2753";
|
|
634
722
|
}
|
|
@@ -649,13 +737,23 @@ var extractConfigDetails = (config) => {
|
|
|
649
737
|
};
|
|
650
738
|
var formatLogData = (context) => {
|
|
651
739
|
const configDetails = extractConfigDetails(context.config);
|
|
740
|
+
const logData = {};
|
|
741
|
+
if (context.scope !== void 0) {
|
|
742
|
+
logData.scope = context.scope;
|
|
743
|
+
}
|
|
744
|
+
if (context.count !== void 0) {
|
|
745
|
+
logData.count = context.count;
|
|
746
|
+
}
|
|
747
|
+
if (Object.keys(configDetails).length > 0) {
|
|
748
|
+
logData.config = configDetails;
|
|
749
|
+
}
|
|
652
750
|
if (context.prevState) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
751
|
+
logData.prevState = extractConfigDetails(context.prevState);
|
|
752
|
+
}
|
|
753
|
+
if (Object.keys(logData).length === 0) {
|
|
754
|
+
return configDetails;
|
|
657
755
|
}
|
|
658
|
-
return configDetails;
|
|
756
|
+
return Object.keys(logData).length === 1 && logData.config ? configDetails : logData;
|
|
659
757
|
};
|
|
660
758
|
|
|
661
759
|
// src/middleware/loggerMiddleware.ts
|