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