@oxog/state 1.0.0 → 1.2.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 +261 -55
- package/dist/iife/index.global.js +11 -11
- package/dist/index.cjs +2366 -518
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1784 -64
- package/dist/index.d.ts +1784 -64
- package/dist/index.js +2321 -518
- package/dist/index.js.map +1 -1
- package/package.json +11 -4
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
4
|
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
25
|
+
|
|
5
26
|
// src/utils/deep-clone.ts
|
|
6
27
|
function deepClone(value, seen) {
|
|
7
28
|
if (value === null || typeof value !== "object") {
|
|
@@ -37,6 +58,10 @@ function deepClone(value, seen) {
|
|
|
37
58
|
}
|
|
38
59
|
return value;
|
|
39
60
|
}
|
|
61
|
+
var init_deep_clone = __esm({
|
|
62
|
+
"src/utils/deep-clone.ts"() {
|
|
63
|
+
}
|
|
64
|
+
});
|
|
40
65
|
|
|
41
66
|
// src/utils/deep-equal.ts
|
|
42
67
|
function deepEqual(a, b) {
|
|
@@ -73,6 +98,10 @@ function deepEqual(a, b) {
|
|
|
73
98
|
}
|
|
74
99
|
return true;
|
|
75
100
|
}
|
|
101
|
+
var init_deep_equal = __esm({
|
|
102
|
+
"src/utils/deep-equal.ts"() {
|
|
103
|
+
}
|
|
104
|
+
});
|
|
76
105
|
|
|
77
106
|
// src/utils/shallow-equal.ts
|
|
78
107
|
function shallowEqual(a, b) {
|
|
@@ -94,6 +123,10 @@ function shallowEqual(a, b) {
|
|
|
94
123
|
}
|
|
95
124
|
return true;
|
|
96
125
|
}
|
|
126
|
+
var init_shallow_equal = __esm({
|
|
127
|
+
"src/utils/shallow-equal.ts"() {
|
|
128
|
+
}
|
|
129
|
+
});
|
|
97
130
|
|
|
98
131
|
// src/utils/deep-merge.ts
|
|
99
132
|
function deepMerge(target, source) {
|
|
@@ -142,11 +175,20 @@ function cloneValue(value) {
|
|
|
142
175
|
}
|
|
143
176
|
return value;
|
|
144
177
|
}
|
|
178
|
+
var init_deep_merge = __esm({
|
|
179
|
+
"src/utils/deep-merge.ts"() {
|
|
180
|
+
init_deep_clone();
|
|
181
|
+
}
|
|
182
|
+
});
|
|
145
183
|
|
|
146
184
|
// src/utils/is-function.ts
|
|
147
185
|
function isFunction(value) {
|
|
148
186
|
return typeof value === "function";
|
|
149
187
|
}
|
|
188
|
+
var init_is_function = __esm({
|
|
189
|
+
"src/utils/is-function.ts"() {
|
|
190
|
+
}
|
|
191
|
+
});
|
|
150
192
|
|
|
151
193
|
// src/utils/pick.ts
|
|
152
194
|
function pick(obj, keys) {
|
|
@@ -158,6 +200,10 @@ function pick(obj, keys) {
|
|
|
158
200
|
}
|
|
159
201
|
return result;
|
|
160
202
|
}
|
|
203
|
+
var init_pick = __esm({
|
|
204
|
+
"src/utils/pick.ts"() {
|
|
205
|
+
}
|
|
206
|
+
});
|
|
161
207
|
|
|
162
208
|
// src/utils/omit.ts
|
|
163
209
|
function omit(obj, keys) {
|
|
@@ -167,227 +213,251 @@ function omit(obj, keys) {
|
|
|
167
213
|
}
|
|
168
214
|
return result;
|
|
169
215
|
}
|
|
216
|
+
var init_omit = __esm({
|
|
217
|
+
"src/utils/omit.ts"() {
|
|
218
|
+
}
|
|
219
|
+
});
|
|
170
220
|
|
|
171
221
|
// src/utils/identity.ts
|
|
172
222
|
function identity(value) {
|
|
173
223
|
return value;
|
|
174
224
|
}
|
|
225
|
+
var init_identity = __esm({
|
|
226
|
+
"src/utils/identity.ts"() {
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// src/utils/index.ts
|
|
231
|
+
var init_utils = __esm({
|
|
232
|
+
"src/utils/index.ts"() {
|
|
233
|
+
init_deep_clone();
|
|
234
|
+
init_deep_equal();
|
|
235
|
+
init_shallow_equal();
|
|
236
|
+
init_deep_merge();
|
|
237
|
+
init_is_function();
|
|
238
|
+
init_pick();
|
|
239
|
+
init_omit();
|
|
240
|
+
init_identity();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
175
243
|
|
|
176
244
|
// src/kernel.ts
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Emit an event to all subscribers.
|
|
197
|
-
*
|
|
198
|
-
* @param event - Event name
|
|
199
|
-
* @param data - Event data
|
|
200
|
-
*/
|
|
201
|
-
emit(event, data) {
|
|
202
|
-
const handlers = this.listeners.get(event);
|
|
203
|
-
if (handlers) {
|
|
204
|
-
for (const handler of handlers) {
|
|
205
|
-
try {
|
|
206
|
-
handler(data);
|
|
207
|
-
} catch (error) {
|
|
208
|
-
console.error(`Error in ${event} handler:`, error);
|
|
245
|
+
function createKernel(config) {
|
|
246
|
+
return new Kernel(config);
|
|
247
|
+
}
|
|
248
|
+
var EventBus, Kernel;
|
|
249
|
+
var init_kernel = __esm({
|
|
250
|
+
"src/kernel.ts"() {
|
|
251
|
+
EventBus = class {
|
|
252
|
+
listeners = /* @__PURE__ */ new Map();
|
|
253
|
+
/**
|
|
254
|
+
* Subscribe to an event.
|
|
255
|
+
*
|
|
256
|
+
* @param event - Event name
|
|
257
|
+
* @param handler - Event handler
|
|
258
|
+
* @returns Unsubscribe function
|
|
259
|
+
*/
|
|
260
|
+
on(event, handler) {
|
|
261
|
+
if (!this.listeners.has(event)) {
|
|
262
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
209
263
|
}
|
|
264
|
+
this.listeners.get(event).add(handler);
|
|
265
|
+
return () => {
|
|
266
|
+
this.listeners.get(event)?.delete(handler);
|
|
267
|
+
};
|
|
210
268
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Register a plugin.
|
|
229
|
-
*
|
|
230
|
-
* @param plugin - The plugin to register
|
|
231
|
-
* @param options - Plugin options
|
|
232
|
-
* @param store - The store instance
|
|
233
|
-
* @throws {Error} If plugin already registered or dependencies missing
|
|
234
|
-
*/
|
|
235
|
-
register(plugin, options, store) {
|
|
236
|
-
if (!plugin.name || !plugin.version || typeof plugin.install !== "function") {
|
|
237
|
-
throw new Error("Invalid plugin: must have name, version, and install function");
|
|
238
|
-
}
|
|
239
|
-
if (this.plugins.has(plugin.name)) {
|
|
240
|
-
throw new Error(`Plugin '${plugin.name}' is already registered`);
|
|
241
|
-
}
|
|
242
|
-
if (plugin.dependencies) {
|
|
243
|
-
for (const dep of plugin.dependencies) {
|
|
244
|
-
if (!this.plugins.has(dep)) {
|
|
245
|
-
throw new Error(
|
|
246
|
-
`Plugin '${plugin.name}' requires '${dep}' to be registered first`
|
|
247
|
-
);
|
|
269
|
+
/**
|
|
270
|
+
* Emit an event to all subscribers.
|
|
271
|
+
*
|
|
272
|
+
* @param event - Event name
|
|
273
|
+
* @param data - Event data
|
|
274
|
+
*/
|
|
275
|
+
emit(event, data) {
|
|
276
|
+
const handlers = this.listeners.get(event);
|
|
277
|
+
if (handlers) {
|
|
278
|
+
for (const handler of handlers) {
|
|
279
|
+
try {
|
|
280
|
+
handler(data);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.error(`Error in ${event} handler:`, error);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
248
285
|
}
|
|
249
286
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
options
|
|
254
|
-
};
|
|
255
|
-
try {
|
|
256
|
-
plugin.install(store, options);
|
|
257
|
-
} catch (error) {
|
|
258
|
-
throw new Error(`Failed to install plugin '${plugin.name}': ${error}`);
|
|
259
|
-
}
|
|
260
|
-
this.plugins.set(plugin.name, instance);
|
|
261
|
-
if (!this.initializing && plugin.onInit) {
|
|
262
|
-
this.runOnInit(plugin, store);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Initialize all plugins.
|
|
267
|
-
*
|
|
268
|
-
* @param store - The store instance
|
|
269
|
-
*/
|
|
270
|
-
async initializeAll(store) {
|
|
271
|
-
this.initializing = true;
|
|
272
|
-
const plugins = Array.from(this.plugins.values());
|
|
273
|
-
for (const { plugin } of plugins) {
|
|
274
|
-
if (plugin.onInit) {
|
|
275
|
-
await this.runOnInit(plugin, store);
|
|
287
|
+
/** Clear all listeners. */
|
|
288
|
+
destroy() {
|
|
289
|
+
this.listeners.clear();
|
|
276
290
|
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
} catch (error) {
|
|
287
|
-
console.error(`Error in ${plugin.name} onInit:`, error);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* Unregister a plugin.
|
|
292
|
-
*
|
|
293
|
-
* @param name - Plugin name
|
|
294
|
-
*/
|
|
295
|
-
async unregister(name) {
|
|
296
|
-
const instance = this.plugins.get(name);
|
|
297
|
-
if (!instance) {
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
if (instance.plugin.onDestroy) {
|
|
301
|
-
try {
|
|
302
|
-
await instance.plugin.onDestroy();
|
|
303
|
-
} catch (error) {
|
|
304
|
-
console.error(`Error in ${name} onDestroy:`, error);
|
|
291
|
+
};
|
|
292
|
+
Kernel = class {
|
|
293
|
+
plugins = /* @__PURE__ */ new Map();
|
|
294
|
+
eventBus = new EventBus();
|
|
295
|
+
config;
|
|
296
|
+
errorHandlers = /* @__PURE__ */ new Set();
|
|
297
|
+
initializing = false;
|
|
298
|
+
constructor(config) {
|
|
299
|
+
this.config = config || {};
|
|
305
300
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Register a plugin.
|
|
303
|
+
*
|
|
304
|
+
* @param plugin - The plugin to register
|
|
305
|
+
* @param options - Plugin options
|
|
306
|
+
* @param store - The store instance
|
|
307
|
+
* @throws {Error} If plugin already registered or dependencies missing
|
|
308
|
+
*/
|
|
309
|
+
register(plugin, options, store) {
|
|
310
|
+
if (!plugin.name || !plugin.version || typeof plugin.install !== "function") {
|
|
311
|
+
throw new Error("Invalid plugin: must have name, version, and install function");
|
|
312
|
+
}
|
|
313
|
+
if (this.plugins.has(plugin.name)) {
|
|
314
|
+
throw new Error(`Plugin '${plugin.name}' is already registered`);
|
|
315
|
+
}
|
|
316
|
+
if (plugin.dependencies) {
|
|
317
|
+
for (const dep of plugin.dependencies) {
|
|
318
|
+
if (!this.plugins.has(dep)) {
|
|
319
|
+
throw new Error(
|
|
320
|
+
`Plugin '${plugin.name}' requires '${dep}' to be registered first`
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const instance = {
|
|
326
|
+
plugin,
|
|
327
|
+
options
|
|
328
|
+
};
|
|
319
329
|
try {
|
|
320
|
-
plugin.
|
|
330
|
+
plugin.install(store, options);
|
|
321
331
|
} catch (error) {
|
|
322
|
-
|
|
332
|
+
throw new Error(`Failed to install plugin '${plugin.name}': ${error}`);
|
|
333
|
+
}
|
|
334
|
+
this.plugins.set(plugin.name, instance);
|
|
335
|
+
if (!this.initializing && plugin.onInit) {
|
|
336
|
+
this.runOnInit(plugin, store);
|
|
323
337
|
}
|
|
324
338
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
339
|
+
/**
|
|
340
|
+
* Initialize all plugins.
|
|
341
|
+
*
|
|
342
|
+
* @param store - The store instance
|
|
343
|
+
*/
|
|
344
|
+
async initializeAll(store) {
|
|
345
|
+
this.initializing = true;
|
|
346
|
+
const plugins = Array.from(this.plugins.values());
|
|
347
|
+
for (const { plugin } of plugins) {
|
|
348
|
+
if (plugin.onInit) {
|
|
349
|
+
await this.runOnInit(plugin, store);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
this.initializing = false;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Run plugin's onInit safely.
|
|
356
|
+
*/
|
|
357
|
+
async runOnInit(plugin, store) {
|
|
336
358
|
try {
|
|
337
|
-
plugin.
|
|
338
|
-
} catch (
|
|
339
|
-
console.error(`Error in ${plugin.name}
|
|
359
|
+
await plugin.onInit(store);
|
|
360
|
+
} catch (error) {
|
|
361
|
+
console.error(`Error in ${plugin.name} onInit:`, error);
|
|
340
362
|
}
|
|
341
363
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
364
|
+
/**
|
|
365
|
+
* Unregister a plugin.
|
|
366
|
+
*
|
|
367
|
+
* @param name - Plugin name
|
|
368
|
+
*/
|
|
369
|
+
async unregister(name) {
|
|
370
|
+
const instance = this.plugins.get(name);
|
|
371
|
+
if (!instance) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (instance.plugin.onDestroy) {
|
|
375
|
+
try {
|
|
376
|
+
await instance.plugin.onDestroy();
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.error(`Error in ${name} onDestroy:`, error);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
this.plugins.delete(name);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Emit a state change event.
|
|
385
|
+
*
|
|
386
|
+
* @param state - New state
|
|
387
|
+
* @param prevState - Previous state
|
|
388
|
+
*/
|
|
389
|
+
emitStateChange(state, prevState) {
|
|
390
|
+
this.eventBus.emit("stateChange", { state, prevState });
|
|
391
|
+
for (const { plugin } of this.plugins.values()) {
|
|
392
|
+
if (plugin.onStateChange) {
|
|
393
|
+
try {
|
|
394
|
+
plugin.onStateChange(state, prevState);
|
|
395
|
+
} catch (error) {
|
|
396
|
+
console.error(`Error in ${plugin.name} onStateChange:`, error);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Emit an error event.
|
|
403
|
+
*
|
|
404
|
+
* @param error - The error
|
|
405
|
+
*/
|
|
406
|
+
emitError(error) {
|
|
407
|
+
this.eventBus.emit("error", error);
|
|
408
|
+
for (const { plugin } of this.plugins.values()) {
|
|
409
|
+
if (plugin.onError) {
|
|
410
|
+
try {
|
|
411
|
+
plugin.onError(error);
|
|
412
|
+
} catch (err) {
|
|
413
|
+
console.error(`Error in ${plugin.name} onError:`, err);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
for (const handler of this.errorHandlers) {
|
|
418
|
+
try {
|
|
419
|
+
handler(error);
|
|
420
|
+
} catch (err) {
|
|
421
|
+
console.error("Error in error handler:", err);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Add an error handler.
|
|
427
|
+
*
|
|
428
|
+
* @param handler - Error handler function
|
|
429
|
+
* @returns Unsubscribe function
|
|
430
|
+
*/
|
|
431
|
+
onError(handler) {
|
|
432
|
+
this.errorHandlers.add(handler);
|
|
433
|
+
return () => {
|
|
434
|
+
this.errorHandlers.delete(handler);
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Subscribe to an event.
|
|
439
|
+
*
|
|
440
|
+
* @param event - Event name
|
|
441
|
+
* @param handler - Event handler
|
|
442
|
+
* @returns Unsubscribe function
|
|
443
|
+
*/
|
|
444
|
+
on(event, handler) {
|
|
445
|
+
return this.eventBus.on(event, handler);
|
|
446
|
+
}
|
|
447
|
+
/** Destroy the kernel and cleanup. */
|
|
448
|
+
async destroy() {
|
|
449
|
+
const pluginNames = Array.from(this.plugins.keys());
|
|
450
|
+
for (const name of pluginNames) {
|
|
451
|
+
await this.unregister(name);
|
|
452
|
+
}
|
|
453
|
+
this.eventBus.destroy();
|
|
454
|
+
this.errorHandlers.clear();
|
|
348
455
|
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Add an error handler.
|
|
353
|
-
*
|
|
354
|
-
* @param handler - Error handler function
|
|
355
|
-
* @returns Unsubscribe function
|
|
356
|
-
*/
|
|
357
|
-
onError(handler) {
|
|
358
|
-
this.errorHandlers.add(handler);
|
|
359
|
-
return () => {
|
|
360
|
-
this.errorHandlers.delete(handler);
|
|
361
456
|
};
|
|
362
457
|
}
|
|
363
|
-
|
|
364
|
-
* Subscribe to an event.
|
|
365
|
-
*
|
|
366
|
-
* @param event - Event name
|
|
367
|
-
* @param handler - Event handler
|
|
368
|
-
* @returns Unsubscribe function
|
|
369
|
-
*/
|
|
370
|
-
on(event, handler) {
|
|
371
|
-
return this.eventBus.on(event, handler);
|
|
372
|
-
}
|
|
373
|
-
/** Destroy the kernel and cleanup. */
|
|
374
|
-
async destroy() {
|
|
375
|
-
const pluginNames = Array.from(this.plugins.keys());
|
|
376
|
-
for (const name of pluginNames) {
|
|
377
|
-
await this.unregister(name);
|
|
378
|
-
}
|
|
379
|
-
this.eventBus.destroy();
|
|
380
|
-
this.errorHandlers.clear();
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
function createKernel(config) {
|
|
384
|
-
return new Kernel(config);
|
|
385
|
-
}
|
|
458
|
+
});
|
|
386
459
|
|
|
387
460
|
// src/batch-context.ts
|
|
388
|
-
var batchDepth = 0;
|
|
389
|
-
var batchStores = /* @__PURE__ */ new Set();
|
|
390
|
-
var batchManagedStores = /* @__PURE__ */ new Set();
|
|
391
461
|
function isBatching() {
|
|
392
462
|
return batchDepth > 0;
|
|
393
463
|
}
|
|
@@ -416,320 +486,94 @@ function addManagedStore(store) {
|
|
|
416
486
|
function isStoreManaged(store) {
|
|
417
487
|
return batchManagedStores.has(store);
|
|
418
488
|
}
|
|
419
|
-
var
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if ("flushNotifications" in s) {
|
|
444
|
-
s.flushNotifications();
|
|
445
|
-
}
|
|
446
|
-
if ("endBatch" in s) {
|
|
447
|
-
s.endBatch();
|
|
448
|
-
}
|
|
489
|
+
var batchDepth, batchStores, batchManagedStores, sharedBatchContext;
|
|
490
|
+
var init_batch_context = __esm({
|
|
491
|
+
"src/batch-context.ts"() {
|
|
492
|
+
batchDepth = 0;
|
|
493
|
+
batchStores = /* @__PURE__ */ new Set();
|
|
494
|
+
batchManagedStores = /* @__PURE__ */ new Set();
|
|
495
|
+
sharedBatchContext = {
|
|
496
|
+
get depth() {
|
|
497
|
+
return batchDepth;
|
|
498
|
+
},
|
|
499
|
+
get stores() {
|
|
500
|
+
return batchStores;
|
|
501
|
+
},
|
|
502
|
+
get managedStores() {
|
|
503
|
+
return batchManagedStores;
|
|
504
|
+
},
|
|
505
|
+
isBatching,
|
|
506
|
+
batch(store, fn) {
|
|
507
|
+
const wasAlreadyBatching = isStoreManaged(store);
|
|
508
|
+
incrementBatchDepth();
|
|
509
|
+
addBatchStore(store);
|
|
510
|
+
if (!wasAlreadyBatching && "beginBatch" in store) {
|
|
511
|
+
store.beginBatch();
|
|
512
|
+
addManagedStore(store);
|
|
449
513
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
514
|
+
try {
|
|
515
|
+
const result = fn();
|
|
516
|
+
decrementBatchDepth();
|
|
517
|
+
if (getBatchDepth() === 0) {
|
|
518
|
+
for (const s of getBatchManagedStores()) {
|
|
519
|
+
if ("flushNotifications" in s) {
|
|
520
|
+
s.flushNotifications();
|
|
521
|
+
}
|
|
522
|
+
if ("endBatch" in s) {
|
|
523
|
+
s.endBatch();
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
clearBatchStores();
|
|
527
|
+
}
|
|
528
|
+
return result;
|
|
529
|
+
} catch (error) {
|
|
530
|
+
decrementBatchDepth();
|
|
531
|
+
if (getBatchDepth() === 0) {
|
|
532
|
+
for (const s of getBatchManagedStores()) {
|
|
533
|
+
if ("endBatch" in s) {
|
|
534
|
+
s.endBatch();
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
clearBatchStores();
|
|
459
538
|
}
|
|
539
|
+
throw error;
|
|
460
540
|
}
|
|
461
|
-
clearBatchStores();
|
|
462
541
|
}
|
|
463
|
-
|
|
464
|
-
}
|
|
542
|
+
};
|
|
465
543
|
}
|
|
466
|
-
};
|
|
544
|
+
});
|
|
467
545
|
|
|
468
546
|
// src/types.ts
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
// src/store.ts
|
|
486
|
-
var StoreImpl = class {
|
|
487
|
-
state;
|
|
488
|
-
initialState;
|
|
489
|
-
listeners = /* @__PURE__ */ new Set();
|
|
490
|
-
selectorSubscriptions = /* @__PURE__ */ new Map();
|
|
491
|
-
kernel;
|
|
492
|
-
destroyed = false;
|
|
493
|
-
batchDepth = 0;
|
|
494
|
-
pendingNotify = false;
|
|
495
|
-
queuedState = null;
|
|
496
|
-
queuedPrevState = null;
|
|
497
|
-
actions = {};
|
|
498
|
-
constructor(initialState, kernel) {
|
|
499
|
-
this.state = deepClone(initialState);
|
|
500
|
-
this.initialState = deepClone(initialState);
|
|
501
|
-
this.kernel = kernel;
|
|
502
|
-
this.kernel.onError((error) => {
|
|
503
|
-
console.error("Store error:", error);
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
/** Get current state snapshot. */
|
|
507
|
-
getState() {
|
|
508
|
-
this.checkNotDestroyed();
|
|
509
|
-
return this.state;
|
|
510
|
-
}
|
|
511
|
-
/** Update state with partial object or function. */
|
|
512
|
-
setState(partial) {
|
|
513
|
-
this.checkNotDestroyed();
|
|
514
|
-
const prevState = this.state;
|
|
515
|
-
const update = typeof partial === "function" ? partial(this.state) : partial;
|
|
516
|
-
this.state = { ...this.state, ...update };
|
|
517
|
-
this.notifyOrQueue(this.state, prevState);
|
|
518
|
-
}
|
|
519
|
-
/** Deep merge state. */
|
|
520
|
-
merge(partial) {
|
|
521
|
-
this.checkNotDestroyed();
|
|
522
|
-
const prevState = this.state;
|
|
523
|
-
this.state = deepMerge(this.state, partial);
|
|
524
|
-
this.notifyOrQueue(this.state, prevState);
|
|
525
|
-
}
|
|
526
|
-
/** Reset to initial state. */
|
|
527
|
-
reset() {
|
|
528
|
-
this.checkNotDestroyed();
|
|
529
|
-
const prevState = this.state;
|
|
530
|
-
this.state = deepClone(this.initialState);
|
|
531
|
-
this.notifyOrQueue(this.state, prevState);
|
|
532
|
-
}
|
|
533
|
-
subscribe(arg1, arg2, arg3) {
|
|
534
|
-
this.checkNotDestroyed();
|
|
535
|
-
if (arg2 === void 0 && typeof arg1 === "function") {
|
|
536
|
-
const listener2 = arg1;
|
|
537
|
-
this.listeners.add(listener2);
|
|
538
|
-
return () => {
|
|
539
|
-
this.listeners.delete(listener2);
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
const selector2 = arg1;
|
|
543
|
-
const listener = arg2;
|
|
544
|
-
const equalityFn = arg3 || shallowEqual;
|
|
545
|
-
let subscription = this.selectorSubscriptions.get(selector2);
|
|
546
|
-
if (!subscription) {
|
|
547
|
-
const initialValue = selector2(this.state);
|
|
548
|
-
subscription = {
|
|
549
|
-
value: initialValue,
|
|
550
|
-
listeners: /* @__PURE__ */ new Set(),
|
|
551
|
-
selector: selector2,
|
|
552
|
-
equalityFn
|
|
553
|
-
};
|
|
554
|
-
this.selectorSubscriptions.set(selector2, subscription);
|
|
555
|
-
}
|
|
556
|
-
subscription.listeners.add(listener);
|
|
557
|
-
return () => {
|
|
558
|
-
const sub = this.selectorSubscriptions.get(selector2);
|
|
559
|
-
if (sub) {
|
|
560
|
-
sub.listeners.delete(listener);
|
|
561
|
-
if (sub.listeners.size === 0) {
|
|
562
|
-
this.selectorSubscriptions.delete(selector2);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
/** Register a plugin. */
|
|
568
|
-
use(plugin, options) {
|
|
569
|
-
this.checkNotDestroyed();
|
|
570
|
-
this.kernel.register(plugin, options, this);
|
|
571
|
-
return this;
|
|
572
|
-
}
|
|
573
|
-
/** Destroy store and cleanup. */
|
|
574
|
-
destroy() {
|
|
575
|
-
if (this.destroyed) {
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
this.destroyed = true;
|
|
579
|
-
this.kernel.destroy();
|
|
580
|
-
this.listeners.clear();
|
|
581
|
-
this.selectorSubscriptions.clear();
|
|
582
|
-
this.actions = {};
|
|
583
|
-
}
|
|
584
|
-
/** Add an action (for fluent builder pattern). */
|
|
585
|
-
addAction(name, fn) {
|
|
586
|
-
this.actions[name] = fn;
|
|
587
|
-
return this;
|
|
588
|
-
}
|
|
589
|
-
/** Get all actions. */
|
|
590
|
-
getActions() {
|
|
591
|
-
return this.actions;
|
|
592
|
-
}
|
|
593
|
-
/** Check if store is destroyed. */
|
|
594
|
-
checkNotDestroyed() {
|
|
595
|
-
if (this.destroyed) {
|
|
596
|
-
throw new Error("Cannot use destroyed store");
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
/** Notify listeners or queue if batching. */
|
|
600
|
-
notifyOrQueue(state, prevState) {
|
|
601
|
-
const inGlobalBatch = sharedBatchContext.isBatching();
|
|
602
|
-
if (inGlobalBatch) {
|
|
603
|
-
const managedStores = sharedBatchContext.managedStores;
|
|
604
|
-
if (!managedStores.has(this)) {
|
|
605
|
-
managedStores.add(this);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
if (this.batchDepth > 0 || inGlobalBatch) {
|
|
609
|
-
this.queuedState = state;
|
|
610
|
-
if (!this.pendingNotify) {
|
|
611
|
-
this.queuedPrevState = prevState;
|
|
612
|
-
}
|
|
613
|
-
this.pendingNotify = true;
|
|
614
|
-
} else {
|
|
615
|
-
this.notify(state, prevState);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
/** Notify all listeners. */
|
|
619
|
-
notify(state, prevState) {
|
|
620
|
-
this.kernel.emitStateChange(state, prevState);
|
|
621
|
-
for (const listener of this.listeners) {
|
|
622
|
-
try {
|
|
623
|
-
listener(state, prevState);
|
|
624
|
-
} catch (error) {
|
|
625
|
-
this.kernel.emitError(error);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
for (const subscription of this.selectorSubscriptions.values()) {
|
|
629
|
-
const newValue = subscription.selector(state);
|
|
630
|
-
if (!subscription.equalityFn(newValue, subscription.value)) {
|
|
631
|
-
const prevValue = subscription.value;
|
|
632
|
-
subscription.value = newValue;
|
|
633
|
-
for (const listener of subscription.listeners) {
|
|
634
|
-
try {
|
|
635
|
-
listener(newValue, prevValue);
|
|
636
|
-
} catch (error) {
|
|
637
|
-
this.kernel.emitError(error);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
/** Flush pending notifications (end of batch). */
|
|
644
|
-
flushNotifications() {
|
|
645
|
-
if (this.pendingNotify) {
|
|
646
|
-
this.notify(this.queuedState, this.queuedPrevState);
|
|
647
|
-
this.pendingNotify = false;
|
|
648
|
-
this.queuedState = null;
|
|
649
|
-
this.queuedPrevState = null;
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
/** Start batching. */
|
|
653
|
-
beginBatch() {
|
|
654
|
-
this.batchDepth++;
|
|
655
|
-
}
|
|
656
|
-
/** End batching. */
|
|
657
|
-
endBatch() {
|
|
658
|
-
this.batchDepth--;
|
|
659
|
-
if (this.batchDepth === 0) {
|
|
660
|
-
this.flushNotifications();
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
};
|
|
664
|
-
var StoreBuilderImpl = class {
|
|
665
|
-
store;
|
|
666
|
-
constructor(initialState, kernel) {
|
|
667
|
-
this.store = new StoreImpl(initialState, kernel);
|
|
668
|
-
}
|
|
669
|
-
getState() {
|
|
670
|
-
return this.store.getState();
|
|
671
|
-
}
|
|
672
|
-
setState(partial) {
|
|
673
|
-
this.store.setState(partial);
|
|
674
|
-
}
|
|
675
|
-
merge(partial) {
|
|
676
|
-
this.store.merge(partial);
|
|
677
|
-
}
|
|
678
|
-
reset() {
|
|
679
|
-
this.store.reset();
|
|
680
|
-
}
|
|
681
|
-
subscribe(arg1, arg2, arg3) {
|
|
682
|
-
if (arg2 === void 0) {
|
|
683
|
-
return this.store.subscribe(arg1);
|
|
684
|
-
} else {
|
|
685
|
-
return this.store.subscribe(arg1, arg2, arg3);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
use(plugin, options) {
|
|
689
|
-
this.store.use(plugin, options);
|
|
690
|
-
return this;
|
|
691
|
-
}
|
|
692
|
-
destroy() {
|
|
693
|
-
this.store.destroy();
|
|
694
|
-
}
|
|
695
|
-
action(name, fn) {
|
|
696
|
-
this.store.addAction(name, fn);
|
|
697
|
-
this[name] = (...args) => {
|
|
698
|
-
const currentState = this.getState();
|
|
699
|
-
const result = fn(currentState, ...args);
|
|
700
|
-
if (result instanceof Promise) {
|
|
701
|
-
return result.then((partial) => {
|
|
702
|
-
this.setState(partial);
|
|
703
|
-
return partial;
|
|
704
|
-
});
|
|
547
|
+
exports.StoreErrorCode = void 0; exports.StoreError = void 0;
|
|
548
|
+
var init_types = __esm({
|
|
549
|
+
"src/types.ts"() {
|
|
550
|
+
exports.StoreErrorCode = /* @__PURE__ */ ((StoreErrorCode2) => {
|
|
551
|
+
StoreErrorCode2["STORE_DESTROYED"] = "STORE_DESTROYED";
|
|
552
|
+
StoreErrorCode2["PLUGIN_EXISTS"] = "PLUGIN_EXISTS";
|
|
553
|
+
StoreErrorCode2["PLUGIN_DEPENDENCY_MISSING"] = "PLUGIN_DEPENDENCY_MISSING";
|
|
554
|
+
StoreErrorCode2["INVALID_STATE_UPDATE"] = "INVALID_STATE_UPDATE";
|
|
555
|
+
StoreErrorCode2["ACTION_ERROR"] = "ACTION_ERROR";
|
|
556
|
+
return StoreErrorCode2;
|
|
557
|
+
})(exports.StoreErrorCode || {});
|
|
558
|
+
exports.StoreError = class extends Error {
|
|
559
|
+
constructor(code, message) {
|
|
560
|
+
super(message);
|
|
561
|
+
this.code = code;
|
|
562
|
+
this.name = "StoreError";
|
|
705
563
|
}
|
|
706
|
-
this.setState(result);
|
|
707
|
-
return result;
|
|
708
564
|
};
|
|
709
|
-
return this;
|
|
710
|
-
}
|
|
711
|
-
/** Add action (internal use). */
|
|
712
|
-
addAction(name, fn) {
|
|
713
|
-
this.store.addAction(name, fn);
|
|
714
|
-
return this;
|
|
715
|
-
}
|
|
716
|
-
/** Get actions (internal use). */
|
|
717
|
-
getActions() {
|
|
718
|
-
return this.store.getActions();
|
|
719
|
-
}
|
|
720
|
-
/** Start batching. */
|
|
721
|
-
beginBatch() {
|
|
722
|
-
this.store.beginBatch();
|
|
723
565
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// src/store.ts
|
|
569
|
+
var store_exports = {};
|
|
570
|
+
__export(store_exports, {
|
|
571
|
+
StoreError: () => exports.StoreError,
|
|
572
|
+
StoreErrorCode: () => exports.StoreErrorCode,
|
|
573
|
+
StoreImpl: () => StoreImpl,
|
|
574
|
+
batch: () => batch,
|
|
575
|
+
createStore: () => createStore
|
|
576
|
+
});
|
|
733
577
|
function createStore(initialState, actions) {
|
|
734
578
|
const kernel = createKernel({ name: "store" });
|
|
735
579
|
const state = { ...initialState };
|
|
@@ -796,6 +640,270 @@ function createStore(initialState, actions) {
|
|
|
796
640
|
function batch(fn) {
|
|
797
641
|
return fn();
|
|
798
642
|
}
|
|
643
|
+
var StoreImpl, StoreBuilderImpl;
|
|
644
|
+
var init_store = __esm({
|
|
645
|
+
"src/store.ts"() {
|
|
646
|
+
init_utils();
|
|
647
|
+
init_kernel();
|
|
648
|
+
init_batch_context();
|
|
649
|
+
init_types();
|
|
650
|
+
StoreImpl = class {
|
|
651
|
+
state;
|
|
652
|
+
initialState;
|
|
653
|
+
listeners = /* @__PURE__ */ new Set();
|
|
654
|
+
selectorSubscriptions = /* @__PURE__ */ new Map();
|
|
655
|
+
kernel;
|
|
656
|
+
destroyed = false;
|
|
657
|
+
batchDepth = 0;
|
|
658
|
+
pendingNotify = false;
|
|
659
|
+
queuedState = null;
|
|
660
|
+
queuedPrevState = null;
|
|
661
|
+
actions = {};
|
|
662
|
+
constructor(initialState, kernel) {
|
|
663
|
+
this.state = deepClone(initialState);
|
|
664
|
+
this.initialState = deepClone(initialState);
|
|
665
|
+
this.kernel = kernel;
|
|
666
|
+
this.kernel.onError((error) => {
|
|
667
|
+
console.error("Store error:", error);
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
/** Get current state snapshot. */
|
|
671
|
+
getState() {
|
|
672
|
+
this.checkNotDestroyed();
|
|
673
|
+
return this.state;
|
|
674
|
+
}
|
|
675
|
+
/** Update state with partial object or function. */
|
|
676
|
+
setState(partial) {
|
|
677
|
+
this.checkNotDestroyed();
|
|
678
|
+
const prevState = this.state;
|
|
679
|
+
const update = typeof partial === "function" ? partial(this.state) : partial;
|
|
680
|
+
this.state = { ...this.state, ...update };
|
|
681
|
+
this.notifyOrQueue(this.state, prevState);
|
|
682
|
+
}
|
|
683
|
+
/** Deep merge state. */
|
|
684
|
+
merge(partial) {
|
|
685
|
+
this.checkNotDestroyed();
|
|
686
|
+
const prevState = this.state;
|
|
687
|
+
this.state = deepMerge(this.state, partial);
|
|
688
|
+
this.notifyOrQueue(this.state, prevState);
|
|
689
|
+
}
|
|
690
|
+
/** Reset to initial state. */
|
|
691
|
+
reset() {
|
|
692
|
+
this.checkNotDestroyed();
|
|
693
|
+
const prevState = this.state;
|
|
694
|
+
this.state = deepClone(this.initialState);
|
|
695
|
+
this.notifyOrQueue(this.state, prevState);
|
|
696
|
+
}
|
|
697
|
+
subscribe(arg1, arg2, arg3) {
|
|
698
|
+
this.checkNotDestroyed();
|
|
699
|
+
if (arg2 === void 0 && typeof arg1 === "function") {
|
|
700
|
+
const listener2 = arg1;
|
|
701
|
+
this.listeners.add(listener2);
|
|
702
|
+
return () => {
|
|
703
|
+
this.listeners.delete(listener2);
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
const selector2 = arg1;
|
|
707
|
+
const listener = arg2;
|
|
708
|
+
const equalityFn = arg3 || shallowEqual;
|
|
709
|
+
let subscription = this.selectorSubscriptions.get(selector2);
|
|
710
|
+
if (!subscription) {
|
|
711
|
+
const initialValue = selector2(this.state);
|
|
712
|
+
subscription = {
|
|
713
|
+
value: initialValue,
|
|
714
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
715
|
+
selector: selector2,
|
|
716
|
+
equalityFn
|
|
717
|
+
};
|
|
718
|
+
this.selectorSubscriptions.set(selector2, subscription);
|
|
719
|
+
}
|
|
720
|
+
subscription.listeners.add(listener);
|
|
721
|
+
return () => {
|
|
722
|
+
const sub = this.selectorSubscriptions.get(selector2);
|
|
723
|
+
if (sub) {
|
|
724
|
+
sub.listeners.delete(listener);
|
|
725
|
+
if (sub.listeners.size === 0) {
|
|
726
|
+
this.selectorSubscriptions.delete(selector2);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
/** Register a plugin. */
|
|
732
|
+
use(plugin, options) {
|
|
733
|
+
this.checkNotDestroyed();
|
|
734
|
+
this.kernel.register(plugin, options, this);
|
|
735
|
+
return this;
|
|
736
|
+
}
|
|
737
|
+
/** Destroy store and cleanup. */
|
|
738
|
+
destroy() {
|
|
739
|
+
if (this.destroyed) {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
this.destroyed = true;
|
|
743
|
+
this.kernel.destroy();
|
|
744
|
+
this.listeners.clear();
|
|
745
|
+
this.selectorSubscriptions.clear();
|
|
746
|
+
this.actions = {};
|
|
747
|
+
}
|
|
748
|
+
/** Add an action (for fluent builder pattern). */
|
|
749
|
+
addAction(name, fn) {
|
|
750
|
+
this.actions[name] = fn;
|
|
751
|
+
return this;
|
|
752
|
+
}
|
|
753
|
+
/** Get all actions. */
|
|
754
|
+
getActions() {
|
|
755
|
+
return this.actions;
|
|
756
|
+
}
|
|
757
|
+
/** Check if store is destroyed. */
|
|
758
|
+
checkNotDestroyed() {
|
|
759
|
+
if (this.destroyed) {
|
|
760
|
+
throw new Error("Cannot use destroyed store");
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
/** Notify listeners or queue if batching. */
|
|
764
|
+
notifyOrQueue(state, prevState) {
|
|
765
|
+
const inGlobalBatch = sharedBatchContext.isBatching();
|
|
766
|
+
if (inGlobalBatch) {
|
|
767
|
+
const managedStores = sharedBatchContext.managedStores;
|
|
768
|
+
if (!managedStores.has(this)) {
|
|
769
|
+
managedStores.add(this);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
if (this.batchDepth > 0 || inGlobalBatch) {
|
|
773
|
+
this.queuedState = state;
|
|
774
|
+
if (!this.pendingNotify) {
|
|
775
|
+
this.queuedPrevState = prevState;
|
|
776
|
+
}
|
|
777
|
+
this.pendingNotify = true;
|
|
778
|
+
} else {
|
|
779
|
+
this.notify(state, prevState);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
/** Notify all listeners. */
|
|
783
|
+
notify(state, prevState) {
|
|
784
|
+
this.kernel.emitStateChange(state, prevState);
|
|
785
|
+
for (const listener of this.listeners) {
|
|
786
|
+
try {
|
|
787
|
+
listener(state, prevState);
|
|
788
|
+
} catch (error) {
|
|
789
|
+
this.kernel.emitError(error);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
for (const subscription of this.selectorSubscriptions.values()) {
|
|
793
|
+
const newValue = subscription.selector(state);
|
|
794
|
+
if (!subscription.equalityFn(newValue, subscription.value)) {
|
|
795
|
+
const prevValue = subscription.value;
|
|
796
|
+
subscription.value = newValue;
|
|
797
|
+
for (const listener of subscription.listeners) {
|
|
798
|
+
try {
|
|
799
|
+
listener(newValue, prevValue);
|
|
800
|
+
} catch (error) {
|
|
801
|
+
this.kernel.emitError(error);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
/** Flush pending notifications (end of batch). */
|
|
808
|
+
flushNotifications() {
|
|
809
|
+
if (this.pendingNotify) {
|
|
810
|
+
this.notify(this.queuedState, this.queuedPrevState);
|
|
811
|
+
this.pendingNotify = false;
|
|
812
|
+
this.queuedState = null;
|
|
813
|
+
this.queuedPrevState = null;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
/** Start batching. */
|
|
817
|
+
beginBatch() {
|
|
818
|
+
this.batchDepth++;
|
|
819
|
+
}
|
|
820
|
+
/** End batching. */
|
|
821
|
+
endBatch() {
|
|
822
|
+
this.batchDepth--;
|
|
823
|
+
if (this.batchDepth === 0) {
|
|
824
|
+
this.flushNotifications();
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
StoreBuilderImpl = class {
|
|
829
|
+
store;
|
|
830
|
+
constructor(initialState, kernel) {
|
|
831
|
+
this.store = new StoreImpl(initialState, kernel);
|
|
832
|
+
}
|
|
833
|
+
getState() {
|
|
834
|
+
return this.store.getState();
|
|
835
|
+
}
|
|
836
|
+
setState(partial) {
|
|
837
|
+
this.store.setState(partial);
|
|
838
|
+
}
|
|
839
|
+
merge(partial) {
|
|
840
|
+
this.store.merge(partial);
|
|
841
|
+
}
|
|
842
|
+
reset() {
|
|
843
|
+
this.store.reset();
|
|
844
|
+
}
|
|
845
|
+
subscribe(arg1, arg2, arg3) {
|
|
846
|
+
if (arg2 === void 0) {
|
|
847
|
+
return this.store.subscribe(arg1);
|
|
848
|
+
} else {
|
|
849
|
+
return this.store.subscribe(arg1, arg2, arg3);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
use(plugin, options) {
|
|
853
|
+
this.store.use(plugin, options);
|
|
854
|
+
return this;
|
|
855
|
+
}
|
|
856
|
+
destroy() {
|
|
857
|
+
this.store.destroy();
|
|
858
|
+
}
|
|
859
|
+
action(name, fn) {
|
|
860
|
+
this.store.addAction(name, fn);
|
|
861
|
+
this[name] = (...args) => {
|
|
862
|
+
const currentState = this.getState();
|
|
863
|
+
const result = fn(currentState, ...args);
|
|
864
|
+
if (result instanceof Promise) {
|
|
865
|
+
return result.then((partial) => {
|
|
866
|
+
this.setState(partial);
|
|
867
|
+
return partial;
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
this.setState(result);
|
|
871
|
+
return result;
|
|
872
|
+
};
|
|
873
|
+
return this;
|
|
874
|
+
}
|
|
875
|
+
/** Add action (internal use). */
|
|
876
|
+
addAction(name, fn) {
|
|
877
|
+
this.store.addAction(name, fn);
|
|
878
|
+
return this;
|
|
879
|
+
}
|
|
880
|
+
/** Get actions (internal use). */
|
|
881
|
+
getActions() {
|
|
882
|
+
return this.store.getActions();
|
|
883
|
+
}
|
|
884
|
+
/** Start batching. */
|
|
885
|
+
beginBatch() {
|
|
886
|
+
this.store.beginBatch();
|
|
887
|
+
}
|
|
888
|
+
/** End batching. */
|
|
889
|
+
endBatch() {
|
|
890
|
+
this.store.endBatch();
|
|
891
|
+
}
|
|
892
|
+
/** Flush pending notifications. */
|
|
893
|
+
flushNotifications() {
|
|
894
|
+
this.store.flushNotifications();
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
// src/index.ts
|
|
901
|
+
init_store();
|
|
902
|
+
|
|
903
|
+
// src/react.ts
|
|
904
|
+
init_identity();
|
|
905
|
+
init_shallow_equal();
|
|
906
|
+
init_store();
|
|
799
907
|
function useStore(store, selector2 = identity, equalityFn = shallowEqual) {
|
|
800
908
|
const selectorRef = react.useRef(selector2);
|
|
801
909
|
const equalityFnRef = react.useRef(equalityFn);
|
|
@@ -857,12 +965,798 @@ function useAction(store, actionName) {
|
|
|
857
965
|
}
|
|
858
966
|
return actionRef.current;
|
|
859
967
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
if (
|
|
865
|
-
|
|
968
|
+
function useShallow(selector2) {
|
|
969
|
+
const prev = react.useRef();
|
|
970
|
+
return (state) => {
|
|
971
|
+
const next = selector2(state);
|
|
972
|
+
if (prev.current === void 0) {
|
|
973
|
+
prev.current = next;
|
|
974
|
+
return next;
|
|
975
|
+
}
|
|
976
|
+
if (shallowCompareObjects(prev.current, next)) {
|
|
977
|
+
return prev.current;
|
|
978
|
+
}
|
|
979
|
+
prev.current = next;
|
|
980
|
+
return next;
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
function shallowCompareObjects(a, b) {
|
|
984
|
+
if (a === b) return true;
|
|
985
|
+
if (typeof a !== "object" || typeof b !== "object") return false;
|
|
986
|
+
if (a === null || b === null) return false;
|
|
987
|
+
const keysA = Object.keys(a);
|
|
988
|
+
const keysB = Object.keys(b);
|
|
989
|
+
if (keysA.length !== keysB.length) return false;
|
|
990
|
+
for (const key of keysA) {
|
|
991
|
+
if (a[key] !== b[key]) {
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
return true;
|
|
996
|
+
}
|
|
997
|
+
function useStoreActions(store, ...actionNames) {
|
|
998
|
+
const storeRef = react.useRef(store);
|
|
999
|
+
const actionsRef = react.useRef({});
|
|
1000
|
+
if (storeRef.current !== store) {
|
|
1001
|
+
storeRef.current = store;
|
|
1002
|
+
actionsRef.current = {};
|
|
1003
|
+
}
|
|
1004
|
+
const result = {};
|
|
1005
|
+
for (const name of actionNames) {
|
|
1006
|
+
if (!actionsRef.current[name]) {
|
|
1007
|
+
actionsRef.current[name] = storeRef.current[name];
|
|
1008
|
+
}
|
|
1009
|
+
result[name] = actionsRef.current[name];
|
|
1010
|
+
}
|
|
1011
|
+
return result;
|
|
1012
|
+
}
|
|
1013
|
+
function useStoreSelector(store, selectors) {
|
|
1014
|
+
const selectorsRef = react.useRef(selectors);
|
|
1015
|
+
const keysRef = react.useRef(Object.keys(selectors));
|
|
1016
|
+
const prevResultRef = react.useRef(null);
|
|
1017
|
+
if (selectorsRef.current !== selectors) {
|
|
1018
|
+
selectorsRef.current = selectors;
|
|
1019
|
+
keysRef.current = Object.keys(selectors);
|
|
1020
|
+
}
|
|
1021
|
+
const combinedSelector = react.useCallback((state) => {
|
|
1022
|
+
const keys = keysRef.current;
|
|
1023
|
+
const currentSelectors = selectorsRef.current;
|
|
1024
|
+
const result = {};
|
|
1025
|
+
for (const key of keys) {
|
|
1026
|
+
const selector2 = currentSelectors[key];
|
|
1027
|
+
if (selector2) {
|
|
1028
|
+
result[key] = selector2(state);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
if (prevResultRef.current !== null) {
|
|
1032
|
+
let isEqual = true;
|
|
1033
|
+
for (const key of keys) {
|
|
1034
|
+
if (!shallowEqual(prevResultRef.current[key], result[key])) {
|
|
1035
|
+
isEqual = false;
|
|
1036
|
+
break;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
if (isEqual) {
|
|
1040
|
+
return prevResultRef.current;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
prevResultRef.current = result;
|
|
1044
|
+
return result;
|
|
1045
|
+
}, []);
|
|
1046
|
+
return useStore(store, combinedSelector);
|
|
1047
|
+
}
|
|
1048
|
+
function useTransientSubscribe(store, selector2, callback, equalityFn = shallowEqual) {
|
|
1049
|
+
const callbackRef = react.useRef(callback);
|
|
1050
|
+
callbackRef.current = callback;
|
|
1051
|
+
react.useEffect(() => {
|
|
1052
|
+
const unsubscribe = store.subscribe(
|
|
1053
|
+
selector2,
|
|
1054
|
+
(value, prevValue) => callbackRef.current(value, prevValue),
|
|
1055
|
+
equalityFn
|
|
1056
|
+
);
|
|
1057
|
+
return unsubscribe;
|
|
1058
|
+
}, [store, selector2, equalityFn]);
|
|
1059
|
+
}
|
|
1060
|
+
function useSetState(store) {
|
|
1061
|
+
const storeRef = react.useRef(store);
|
|
1062
|
+
if (storeRef.current !== store) {
|
|
1063
|
+
storeRef.current = store;
|
|
1064
|
+
}
|
|
1065
|
+
const setStateRef = react.useRef((partial) => {
|
|
1066
|
+
storeRef.current.setState(partial);
|
|
1067
|
+
});
|
|
1068
|
+
return setStateRef.current;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
// src/subscribe.ts
|
|
1072
|
+
init_utils();
|
|
1073
|
+
function createDebounced(fn, delay2) {
|
|
1074
|
+
let timeoutId = null;
|
|
1075
|
+
const debounced = ((...args) => {
|
|
1076
|
+
if (timeoutId) {
|
|
1077
|
+
clearTimeout(timeoutId);
|
|
1078
|
+
}
|
|
1079
|
+
timeoutId = setTimeout(() => {
|
|
1080
|
+
fn(...args);
|
|
1081
|
+
timeoutId = null;
|
|
1082
|
+
}, delay2);
|
|
1083
|
+
});
|
|
1084
|
+
debounced.cancel = () => {
|
|
1085
|
+
if (timeoutId) {
|
|
1086
|
+
clearTimeout(timeoutId);
|
|
1087
|
+
timeoutId = null;
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
return debounced;
|
|
1091
|
+
}
|
|
1092
|
+
function createThrottled(fn, delay2) {
|
|
1093
|
+
let lastCall = 0;
|
|
1094
|
+
let timeoutId = null;
|
|
1095
|
+
let lastArgs = null;
|
|
1096
|
+
const throttled = ((...args) => {
|
|
1097
|
+
const now = Date.now();
|
|
1098
|
+
const remaining = delay2 - (now - lastCall);
|
|
1099
|
+
lastArgs = args;
|
|
1100
|
+
if (remaining <= 0) {
|
|
1101
|
+
if (timeoutId) {
|
|
1102
|
+
clearTimeout(timeoutId);
|
|
1103
|
+
timeoutId = null;
|
|
1104
|
+
}
|
|
1105
|
+
lastCall = now;
|
|
1106
|
+
fn(...args);
|
|
1107
|
+
} else if (!timeoutId) {
|
|
1108
|
+
timeoutId = setTimeout(() => {
|
|
1109
|
+
lastCall = Date.now();
|
|
1110
|
+
timeoutId = null;
|
|
1111
|
+
if (lastArgs) {
|
|
1112
|
+
fn(...lastArgs);
|
|
1113
|
+
}
|
|
1114
|
+
}, remaining);
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
throttled.cancel = () => {
|
|
1118
|
+
if (timeoutId) {
|
|
1119
|
+
clearTimeout(timeoutId);
|
|
1120
|
+
timeoutId = null;
|
|
1121
|
+
}
|
|
1122
|
+
lastArgs = null;
|
|
1123
|
+
};
|
|
1124
|
+
return throttled;
|
|
1125
|
+
}
|
|
1126
|
+
function subscribeWithOptions(store, selector2, listener, options = {}) {
|
|
1127
|
+
const {
|
|
1128
|
+
equalityFn = shallowEqual,
|
|
1129
|
+
fireImmediately = false,
|
|
1130
|
+
debounce,
|
|
1131
|
+
throttle,
|
|
1132
|
+
when
|
|
1133
|
+
} = options;
|
|
1134
|
+
if (debounce !== void 0 && throttle !== void 0) {
|
|
1135
|
+
throw new Error("Cannot use both debounce and throttle options");
|
|
1136
|
+
}
|
|
1137
|
+
let wrappedListener = listener;
|
|
1138
|
+
let cleanup = null;
|
|
1139
|
+
if (debounce !== void 0 && debounce > 0) {
|
|
1140
|
+
const debouncedListener = createDebounced(listener, debounce);
|
|
1141
|
+
wrappedListener = debouncedListener;
|
|
1142
|
+
cleanup = () => debouncedListener.cancel();
|
|
1143
|
+
} else if (throttle !== void 0 && throttle > 0) {
|
|
1144
|
+
const throttledListener = createThrottled(listener, throttle);
|
|
1145
|
+
wrappedListener = throttledListener;
|
|
1146
|
+
cleanup = () => throttledListener.cancel();
|
|
1147
|
+
}
|
|
1148
|
+
let finalListener;
|
|
1149
|
+
let prevState = store.getState();
|
|
1150
|
+
if (when) {
|
|
1151
|
+
finalListener = (value, prevValue) => {
|
|
1152
|
+
const currentState = store.getState();
|
|
1153
|
+
if (when(currentState, prevState)) {
|
|
1154
|
+
wrappedListener(value, prevValue);
|
|
1155
|
+
}
|
|
1156
|
+
prevState = currentState;
|
|
1157
|
+
};
|
|
1158
|
+
} else {
|
|
1159
|
+
finalListener = wrappedListener;
|
|
1160
|
+
}
|
|
1161
|
+
if (fireImmediately) {
|
|
1162
|
+
const currentValue = selector2(store.getState());
|
|
1163
|
+
finalListener(currentValue, currentValue);
|
|
1164
|
+
}
|
|
1165
|
+
const unsubscribe = store.subscribe(selector2, finalListener, equalityFn);
|
|
1166
|
+
return () => {
|
|
1167
|
+
unsubscribe();
|
|
1168
|
+
if (cleanup) {
|
|
1169
|
+
cleanup();
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
function createSubscriber(defaultOptions = {}) {
|
|
1174
|
+
return (store, selector2, listener, options = {}) => {
|
|
1175
|
+
return subscribeWithOptions(store, selector2, listener, {
|
|
1176
|
+
...defaultOptions,
|
|
1177
|
+
...options
|
|
1178
|
+
});
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
function subscribeToMany(store, selectors, listener, options = {}) {
|
|
1182
|
+
const keys = Object.keys(selectors);
|
|
1183
|
+
const state = store.getState();
|
|
1184
|
+
for (const key of keys) {
|
|
1185
|
+
const selector2 = selectors[key];
|
|
1186
|
+
if (selector2) {
|
|
1187
|
+
selector2(state);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
const combinedSelector = (state2) => {
|
|
1191
|
+
const result = {};
|
|
1192
|
+
for (const key of keys) {
|
|
1193
|
+
const selector2 = selectors[key];
|
|
1194
|
+
if (selector2) {
|
|
1195
|
+
result[key] = selector2(state2);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
return result;
|
|
1199
|
+
};
|
|
1200
|
+
const combinedEqualityFn = (a, b) => {
|
|
1201
|
+
for (const key of keys) {
|
|
1202
|
+
if (!shallowEqual(a[key], b[key])) {
|
|
1203
|
+
return false;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return true;
|
|
1207
|
+
};
|
|
1208
|
+
return subscribeWithOptions(
|
|
1209
|
+
store,
|
|
1210
|
+
combinedSelector,
|
|
1211
|
+
listener,
|
|
1212
|
+
{ ...options, equalityFn: combinedEqualityFn }
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
function subscribeOnce(store, selector2, listener, options = {}) {
|
|
1216
|
+
let unsubscribe = null;
|
|
1217
|
+
const onceListener = (value, prevValue) => {
|
|
1218
|
+
listener(value, prevValue);
|
|
1219
|
+
if (unsubscribe) {
|
|
1220
|
+
unsubscribe();
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
unsubscribe = subscribeWithOptions(store, selector2, onceListener, options);
|
|
1224
|
+
return unsubscribe;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// src/computed.ts
|
|
1228
|
+
init_utils();
|
|
1229
|
+
function computed(store, selector2, options = {}) {
|
|
1230
|
+
const { equals = shallowEqual, lazy = false, cacheSize = 1 } = options;
|
|
1231
|
+
const cache = [];
|
|
1232
|
+
let currentValue;
|
|
1233
|
+
let isInitialized = false;
|
|
1234
|
+
let isDestroyed = false;
|
|
1235
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1236
|
+
let unsubscribe = null;
|
|
1237
|
+
const compute = () => {
|
|
1238
|
+
const state = store.getState();
|
|
1239
|
+
for (const entry of cache) {
|
|
1240
|
+
if (entry.input === state || shallowEqual(entry.input, state)) {
|
|
1241
|
+
return entry.output;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
const newValue = selector2(state);
|
|
1245
|
+
cache.unshift({ input: state, output: newValue });
|
|
1246
|
+
while (cache.length > cacheSize) {
|
|
1247
|
+
cache.pop();
|
|
1248
|
+
}
|
|
1249
|
+
return newValue;
|
|
1250
|
+
};
|
|
1251
|
+
const initialize = () => {
|
|
1252
|
+
if (isInitialized || isDestroyed) return;
|
|
1253
|
+
isInitialized = true;
|
|
1254
|
+
currentValue = compute();
|
|
1255
|
+
unsubscribe = store.subscribe((state, prevState) => {
|
|
1256
|
+
const prevValue = currentValue;
|
|
1257
|
+
const newValue = compute();
|
|
1258
|
+
if (!equals(newValue, prevValue)) {
|
|
1259
|
+
currentValue = newValue;
|
|
1260
|
+
for (const listener of listeners) {
|
|
1261
|
+
try {
|
|
1262
|
+
listener(newValue, prevValue);
|
|
1263
|
+
} catch (error) {
|
|
1264
|
+
console.error("Computed listener error:", error);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
});
|
|
1269
|
+
};
|
|
1270
|
+
if (!lazy) {
|
|
1271
|
+
initialize();
|
|
1272
|
+
}
|
|
1273
|
+
const get = () => {
|
|
1274
|
+
if (isDestroyed) {
|
|
1275
|
+
throw new Error("Cannot access destroyed computed value");
|
|
1276
|
+
}
|
|
1277
|
+
if (!isInitialized) {
|
|
1278
|
+
initialize();
|
|
1279
|
+
}
|
|
1280
|
+
return currentValue;
|
|
1281
|
+
};
|
|
1282
|
+
const invalidate = () => {
|
|
1283
|
+
if (isDestroyed) return;
|
|
1284
|
+
cache.length = 0;
|
|
1285
|
+
if (isInitialized) {
|
|
1286
|
+
const prevValue = currentValue;
|
|
1287
|
+
currentValue = compute();
|
|
1288
|
+
if (!equals(currentValue, prevValue)) {
|
|
1289
|
+
for (const listener of listeners) {
|
|
1290
|
+
try {
|
|
1291
|
+
listener(currentValue, prevValue);
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
console.error("Computed listener error:", error);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
const subscribe = (listener) => {
|
|
1300
|
+
if (isDestroyed) {
|
|
1301
|
+
throw new Error("Cannot subscribe to destroyed computed value");
|
|
1302
|
+
}
|
|
1303
|
+
if (!isInitialized) {
|
|
1304
|
+
initialize();
|
|
1305
|
+
}
|
|
1306
|
+
listeners.add(listener);
|
|
1307
|
+
return () => {
|
|
1308
|
+
listeners.delete(listener);
|
|
1309
|
+
};
|
|
1310
|
+
};
|
|
1311
|
+
const getHistory = () => {
|
|
1312
|
+
return cache.map((entry) => entry.output);
|
|
1313
|
+
};
|
|
1314
|
+
const destroy = () => {
|
|
1315
|
+
if (isDestroyed) return;
|
|
1316
|
+
isDestroyed = true;
|
|
1317
|
+
if (unsubscribe) {
|
|
1318
|
+
unsubscribe();
|
|
1319
|
+
unsubscribe = null;
|
|
1320
|
+
}
|
|
1321
|
+
listeners.clear();
|
|
1322
|
+
cache.length = 0;
|
|
1323
|
+
currentValue = void 0;
|
|
1324
|
+
};
|
|
1325
|
+
const computedFn = get;
|
|
1326
|
+
computedFn.get = get;
|
|
1327
|
+
computedFn.invalidate = invalidate;
|
|
1328
|
+
computedFn.subscribe = subscribe;
|
|
1329
|
+
computedFn.getHistory = getHistory;
|
|
1330
|
+
computedFn.destroy = destroy;
|
|
1331
|
+
return computedFn;
|
|
1332
|
+
}
|
|
1333
|
+
function combineComputed(computeds, combiner, options = {}) {
|
|
1334
|
+
const { equals = shallowEqual, cacheSize = 1 } = options;
|
|
1335
|
+
let currentValue;
|
|
1336
|
+
let isDestroyed = false;
|
|
1337
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1338
|
+
const unsubscribes = [];
|
|
1339
|
+
const cache = [];
|
|
1340
|
+
const compute = () => {
|
|
1341
|
+
const values = computeds.map((c) => c());
|
|
1342
|
+
return combiner(values);
|
|
1343
|
+
};
|
|
1344
|
+
currentValue = compute();
|
|
1345
|
+
cache.push(currentValue);
|
|
1346
|
+
for (const computed2 of computeds) {
|
|
1347
|
+
const unsub = computed2.subscribe(() => {
|
|
1348
|
+
if (isDestroyed) return;
|
|
1349
|
+
const prevValue = currentValue;
|
|
1350
|
+
const newValue = compute();
|
|
1351
|
+
if (!equals(newValue, prevValue)) {
|
|
1352
|
+
currentValue = newValue;
|
|
1353
|
+
cache.unshift(newValue);
|
|
1354
|
+
while (cache.length > cacheSize) {
|
|
1355
|
+
cache.pop();
|
|
1356
|
+
}
|
|
1357
|
+
for (const listener of listeners) {
|
|
1358
|
+
try {
|
|
1359
|
+
listener(newValue, prevValue);
|
|
1360
|
+
} catch (error) {
|
|
1361
|
+
console.error("Combined computed listener error:", error);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
});
|
|
1366
|
+
unsubscribes.push(unsub);
|
|
1367
|
+
}
|
|
1368
|
+
const get = () => {
|
|
1369
|
+
if (isDestroyed) {
|
|
1370
|
+
throw new Error("Cannot access destroyed computed value");
|
|
1371
|
+
}
|
|
1372
|
+
return currentValue;
|
|
1373
|
+
};
|
|
1374
|
+
const invalidate = () => {
|
|
1375
|
+
if (isDestroyed) return;
|
|
1376
|
+
for (const computed2 of computeds) {
|
|
1377
|
+
computed2.invalidate();
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
const subscribe = (listener) => {
|
|
1381
|
+
if (isDestroyed) {
|
|
1382
|
+
throw new Error("Cannot subscribe to destroyed computed value");
|
|
1383
|
+
}
|
|
1384
|
+
listeners.add(listener);
|
|
1385
|
+
return () => listeners.delete(listener);
|
|
1386
|
+
};
|
|
1387
|
+
const getHistory = () => [...cache];
|
|
1388
|
+
const destroy = () => {
|
|
1389
|
+
if (isDestroyed) return;
|
|
1390
|
+
isDestroyed = true;
|
|
1391
|
+
for (const unsub of unsubscribes) {
|
|
1392
|
+
unsub();
|
|
1393
|
+
}
|
|
1394
|
+
listeners.clear();
|
|
1395
|
+
cache.length = 0;
|
|
1396
|
+
};
|
|
1397
|
+
const combinedFn = get;
|
|
1398
|
+
combinedFn.get = get;
|
|
1399
|
+
combinedFn.invalidate = invalidate;
|
|
1400
|
+
combinedFn.subscribe = subscribe;
|
|
1401
|
+
combinedFn.getHistory = getHistory;
|
|
1402
|
+
combinedFn.destroy = destroy;
|
|
1403
|
+
return combinedFn;
|
|
1404
|
+
}
|
|
1405
|
+
function memoizeSelector(selector2, equals = shallowEqual) {
|
|
1406
|
+
let lastState;
|
|
1407
|
+
let lastResult;
|
|
1408
|
+
let isInitialized = false;
|
|
1409
|
+
return (state) => {
|
|
1410
|
+
if (!isInitialized || lastState !== state) {
|
|
1411
|
+
const newResult = selector2(state);
|
|
1412
|
+
if (!isInitialized || !equals(newResult, lastResult)) {
|
|
1413
|
+
lastResult = newResult;
|
|
1414
|
+
}
|
|
1415
|
+
lastState = state;
|
|
1416
|
+
isInitialized = true;
|
|
1417
|
+
}
|
|
1418
|
+
return lastResult;
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
// src/slices.ts
|
|
1423
|
+
init_store();
|
|
1424
|
+
function createSlice(name, creator) {
|
|
1425
|
+
return { name, creator };
|
|
1426
|
+
}
|
|
1427
|
+
function combineSlices(...slices) {
|
|
1428
|
+
const initialState = {};
|
|
1429
|
+
const actions = {};
|
|
1430
|
+
let tempStore = null;
|
|
1431
|
+
const set = (partial) => {
|
|
1432
|
+
if (tempStore) {
|
|
1433
|
+
if (typeof partial === "function") {
|
|
1434
|
+
tempStore.setState(partial);
|
|
1435
|
+
} else {
|
|
1436
|
+
tempStore.setState(partial);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
const get = () => {
|
|
1441
|
+
return tempStore ? tempStore.getState() : initialState;
|
|
1442
|
+
};
|
|
1443
|
+
const placeholderStore = {
|
|
1444
|
+
getState: () => initialState,
|
|
1445
|
+
setState: () => {
|
|
1446
|
+
},
|
|
1447
|
+
subscribe: () => () => {
|
|
1448
|
+
},
|
|
1449
|
+
merge: () => {
|
|
1450
|
+
},
|
|
1451
|
+
reset: () => {
|
|
1452
|
+
},
|
|
1453
|
+
use: () => placeholderStore,
|
|
1454
|
+
destroy: () => {
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
for (const slice of slices) {
|
|
1458
|
+
const sliceResult = slice.creator(set, get, placeholderStore);
|
|
1459
|
+
for (const [key, value] of Object.entries(sliceResult)) {
|
|
1460
|
+
if (typeof value === "function") {
|
|
1461
|
+
actions[key] = value;
|
|
1462
|
+
} else {
|
|
1463
|
+
initialState[key] = value;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
tempStore = createStore(initialState);
|
|
1468
|
+
for (const slice of slices) {
|
|
1469
|
+
const sliceResult = slice.creator(
|
|
1470
|
+
(partial) => {
|
|
1471
|
+
if (typeof partial === "function") {
|
|
1472
|
+
tempStore.setState(partial);
|
|
1473
|
+
} else {
|
|
1474
|
+
tempStore.setState(partial);
|
|
1475
|
+
}
|
|
1476
|
+
},
|
|
1477
|
+
() => tempStore.getState(),
|
|
1478
|
+
tempStore
|
|
1479
|
+
);
|
|
1480
|
+
for (const [key, value] of Object.entries(sliceResult)) {
|
|
1481
|
+
if (typeof value === "function") {
|
|
1482
|
+
actions[key] = value;
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
const store = tempStore;
|
|
1487
|
+
for (const [name, action] of Object.entries(actions)) {
|
|
1488
|
+
store[name] = action;
|
|
1489
|
+
}
|
|
1490
|
+
return store;
|
|
1491
|
+
}
|
|
1492
|
+
function createNamespacedSlice(namespace, creator) {
|
|
1493
|
+
return createSlice(namespace, (set, get, store) => {
|
|
1494
|
+
const namespacedSet = (partial) => {
|
|
1495
|
+
const current = get()[namespace] || {};
|
|
1496
|
+
const update = typeof partial === "function" ? partial(current) : partial;
|
|
1497
|
+
set({ [namespace]: { ...current, ...update } });
|
|
1498
|
+
};
|
|
1499
|
+
const namespacedGet = () => {
|
|
1500
|
+
return get()[namespace] || {};
|
|
1501
|
+
};
|
|
1502
|
+
const sliceResult = creator(namespacedSet, namespacedGet);
|
|
1503
|
+
const state = {};
|
|
1504
|
+
const actions = {};
|
|
1505
|
+
for (const [key, value] of Object.entries(sliceResult)) {
|
|
1506
|
+
if (typeof value === "function") {
|
|
1507
|
+
actions[key] = value;
|
|
1508
|
+
} else {
|
|
1509
|
+
state[key] = value;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return {
|
|
1513
|
+
[namespace]: state,
|
|
1514
|
+
...actions
|
|
1515
|
+
};
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
function extendStore(store, ...slices) {
|
|
1519
|
+
const actions = {};
|
|
1520
|
+
const set = (partial) => {
|
|
1521
|
+
if (typeof partial === "function") {
|
|
1522
|
+
store.setState(partial);
|
|
1523
|
+
} else {
|
|
1524
|
+
store.setState(partial);
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1527
|
+
const get = () => store.getState();
|
|
1528
|
+
for (const slice of slices) {
|
|
1529
|
+
const sliceResult = slice.creator(set, get, store);
|
|
1530
|
+
for (const [key, value] of Object.entries(sliceResult)) {
|
|
1531
|
+
if (typeof value === "function") {
|
|
1532
|
+
actions[key] = value;
|
|
1533
|
+
} else {
|
|
1534
|
+
store.setState({ [key]: value });
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
const extendedStore = store;
|
|
1539
|
+
for (const [name, action] of Object.entries(actions)) {
|
|
1540
|
+
extendedStore[name] = action;
|
|
1541
|
+
}
|
|
1542
|
+
return extendedStore;
|
|
1543
|
+
}
|
|
1544
|
+
function resetSlices(store, ...sliceNames) {
|
|
1545
|
+
console.warn("resetSlices: Use store.reset() to reset all state");
|
|
1546
|
+
store.reset();
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
// src/federation.ts
|
|
1550
|
+
function createFederation(stores, options = {}) {
|
|
1551
|
+
const { name = "federation", debug = false } = options;
|
|
1552
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1553
|
+
const unsubscribers = [];
|
|
1554
|
+
let isInTransaction = false;
|
|
1555
|
+
let transactionChanges = [];
|
|
1556
|
+
let previousStates = null;
|
|
1557
|
+
const getState = () => {
|
|
1558
|
+
const state = {};
|
|
1559
|
+
for (const key in stores) {
|
|
1560
|
+
const store = stores[key];
|
|
1561
|
+
if (store) {
|
|
1562
|
+
state[key] = store.getState();
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
return state;
|
|
1566
|
+
};
|
|
1567
|
+
const notify = (changedStore) => {
|
|
1568
|
+
if (isInTransaction) {
|
|
1569
|
+
if (!transactionChanges.includes(changedStore)) {
|
|
1570
|
+
transactionChanges.push(changedStore);
|
|
1571
|
+
}
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
const state = getState();
|
|
1575
|
+
const prevState = previousStates || state;
|
|
1576
|
+
for (const listener of listeners) {
|
|
1577
|
+
try {
|
|
1578
|
+
listener(state, prevState, changedStore);
|
|
1579
|
+
} catch (error) {
|
|
1580
|
+
console.error(`Federation listener error:`, error);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
previousStates = state;
|
|
1584
|
+
};
|
|
1585
|
+
for (const key in stores) {
|
|
1586
|
+
const store = stores[key];
|
|
1587
|
+
if (store) {
|
|
1588
|
+
const unsubscribe = store.subscribe(() => {
|
|
1589
|
+
notify(key);
|
|
1590
|
+
});
|
|
1591
|
+
unsubscribers.push(unsubscribe);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
previousStates = getState();
|
|
1595
|
+
const log = (message, ...args) => {
|
|
1596
|
+
if (debug) {
|
|
1597
|
+
console.log(`[${name}] ${message}`, ...args);
|
|
1598
|
+
}
|
|
1599
|
+
};
|
|
1600
|
+
return {
|
|
1601
|
+
stores,
|
|
1602
|
+
getState,
|
|
1603
|
+
subscribe(listener) {
|
|
1604
|
+
listeners.add(listener);
|
|
1605
|
+
return () => {
|
|
1606
|
+
listeners.delete(listener);
|
|
1607
|
+
};
|
|
1608
|
+
},
|
|
1609
|
+
transaction(fn) {
|
|
1610
|
+
if (isInTransaction) {
|
|
1611
|
+
fn(stores);
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
isInTransaction = true;
|
|
1615
|
+
transactionChanges = [];
|
|
1616
|
+
const snapshotStates = getState();
|
|
1617
|
+
log("Transaction started");
|
|
1618
|
+
try {
|
|
1619
|
+
const result = fn(stores);
|
|
1620
|
+
if (result instanceof Promise) {
|
|
1621
|
+
throw new Error(
|
|
1622
|
+
"Async operations in transaction() are not supported. Use transactionAsync() instead."
|
|
1623
|
+
);
|
|
1624
|
+
}
|
|
1625
|
+
isInTransaction = false;
|
|
1626
|
+
if (transactionChanges.length > 0) {
|
|
1627
|
+
log("Transaction committed, changed stores:", transactionChanges);
|
|
1628
|
+
const state = getState();
|
|
1629
|
+
for (const listener of listeners) {
|
|
1630
|
+
try {
|
|
1631
|
+
listener(state, snapshotStates, transactionChanges[0]);
|
|
1632
|
+
} catch (error) {
|
|
1633
|
+
console.error(`Federation listener error:`, error);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
previousStates = state;
|
|
1637
|
+
}
|
|
1638
|
+
} catch (error) {
|
|
1639
|
+
isInTransaction = false;
|
|
1640
|
+
log("Transaction failed, rolling back");
|
|
1641
|
+
for (const key in stores) {
|
|
1642
|
+
const store = stores[key];
|
|
1643
|
+
if (store) {
|
|
1644
|
+
store.setState(snapshotStates[key]);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
throw error;
|
|
1648
|
+
}
|
|
1649
|
+
},
|
|
1650
|
+
async transactionAsync(fn) {
|
|
1651
|
+
if (isInTransaction) {
|
|
1652
|
+
await fn(stores);
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
isInTransaction = true;
|
|
1656
|
+
transactionChanges = [];
|
|
1657
|
+
const snapshotStates = getState();
|
|
1658
|
+
log("Async transaction started");
|
|
1659
|
+
try {
|
|
1660
|
+
await fn(stores);
|
|
1661
|
+
isInTransaction = false;
|
|
1662
|
+
if (transactionChanges.length > 0) {
|
|
1663
|
+
log("Async transaction committed, changed stores:", transactionChanges);
|
|
1664
|
+
const state = getState();
|
|
1665
|
+
for (const listener of listeners) {
|
|
1666
|
+
try {
|
|
1667
|
+
listener(state, snapshotStates, transactionChanges[0]);
|
|
1668
|
+
} catch (error) {
|
|
1669
|
+
console.error(`Federation listener error:`, error);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
previousStates = state;
|
|
1673
|
+
}
|
|
1674
|
+
} catch (error) {
|
|
1675
|
+
isInTransaction = false;
|
|
1676
|
+
log("Async transaction failed, rolling back");
|
|
1677
|
+
for (const key in stores) {
|
|
1678
|
+
const store = stores[key];
|
|
1679
|
+
if (store) {
|
|
1680
|
+
store.setState(snapshotStates[key]);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
throw error;
|
|
1684
|
+
}
|
|
1685
|
+
},
|
|
1686
|
+
getStore(storeName) {
|
|
1687
|
+
return stores[storeName];
|
|
1688
|
+
},
|
|
1689
|
+
destroy() {
|
|
1690
|
+
log("Destroying federation");
|
|
1691
|
+
for (const unsubscribe of unsubscribers) {
|
|
1692
|
+
unsubscribe();
|
|
1693
|
+
}
|
|
1694
|
+
listeners.clear();
|
|
1695
|
+
}
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
function createFederatedSelector(federation, selector2) {
|
|
1699
|
+
return () => selector2(federation.getState());
|
|
1700
|
+
}
|
|
1701
|
+
function createFederatedComputed(federation, selector2, equalityFn = Object.is) {
|
|
1702
|
+
let cachedValue = selector2(federation.getState());
|
|
1703
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1704
|
+
federation.subscribe((state) => {
|
|
1705
|
+
const newValue = selector2(state);
|
|
1706
|
+
if (!equalityFn(newValue, cachedValue)) {
|
|
1707
|
+
const prevValue = cachedValue;
|
|
1708
|
+
cachedValue = newValue;
|
|
1709
|
+
for (const listener of listeners) {
|
|
1710
|
+
listener(newValue, prevValue);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
});
|
|
1714
|
+
return {
|
|
1715
|
+
get() {
|
|
1716
|
+
return cachedValue;
|
|
1717
|
+
},
|
|
1718
|
+
subscribe(listener) {
|
|
1719
|
+
listeners.add(listener);
|
|
1720
|
+
return () => {
|
|
1721
|
+
listeners.delete(listener);
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
};
|
|
1725
|
+
}
|
|
1726
|
+
function waitForFederated(federation, predicate, timeout) {
|
|
1727
|
+
return new Promise((resolve, reject) => {
|
|
1728
|
+
const state = federation.getState();
|
|
1729
|
+
if (predicate(state)) {
|
|
1730
|
+
resolve(state);
|
|
1731
|
+
return;
|
|
1732
|
+
}
|
|
1733
|
+
let timeoutId;
|
|
1734
|
+
let unsubscribe;
|
|
1735
|
+
const cleanup = () => {
|
|
1736
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1737
|
+
if (unsubscribe) unsubscribe();
|
|
1738
|
+
};
|
|
1739
|
+
if (timeout) {
|
|
1740
|
+
timeoutId = setTimeout(() => {
|
|
1741
|
+
cleanup();
|
|
1742
|
+
reject(new Error(`waitForFederated timed out after ${timeout}ms`));
|
|
1743
|
+
}, timeout);
|
|
1744
|
+
}
|
|
1745
|
+
unsubscribe = federation.subscribe((state2) => {
|
|
1746
|
+
if (predicate(state2)) {
|
|
1747
|
+
cleanup();
|
|
1748
|
+
resolve(state2);
|
|
1749
|
+
}
|
|
1750
|
+
});
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
// src/plugins/persist.ts
|
|
1755
|
+
init_utils();
|
|
1756
|
+
var defaultStorage = {
|
|
1757
|
+
getItem: (key) => {
|
|
1758
|
+
if (typeof window === "undefined") return null;
|
|
1759
|
+
try {
|
|
866
1760
|
return window.localStorage.getItem(key);
|
|
867
1761
|
} catch {
|
|
868
1762
|
return null;
|
|
@@ -903,36 +1797,133 @@ var sessionStorage = {
|
|
|
903
1797
|
}
|
|
904
1798
|
}
|
|
905
1799
|
};
|
|
1800
|
+
function createDebounced2(fn, delay2) {
|
|
1801
|
+
let timeoutId = null;
|
|
1802
|
+
const debounced = ((...args) => {
|
|
1803
|
+
if (timeoutId) {
|
|
1804
|
+
clearTimeout(timeoutId);
|
|
1805
|
+
}
|
|
1806
|
+
timeoutId = setTimeout(() => {
|
|
1807
|
+
fn(...args);
|
|
1808
|
+
timeoutId = null;
|
|
1809
|
+
}, delay2);
|
|
1810
|
+
});
|
|
1811
|
+
debounced.cancel = () => {
|
|
1812
|
+
if (timeoutId) {
|
|
1813
|
+
clearTimeout(timeoutId);
|
|
1814
|
+
timeoutId = null;
|
|
1815
|
+
}
|
|
1816
|
+
};
|
|
1817
|
+
return debounced;
|
|
1818
|
+
}
|
|
906
1819
|
function persist(options) {
|
|
907
|
-
const {
|
|
1820
|
+
const {
|
|
1821
|
+
key,
|
|
1822
|
+
storage = defaultStorage,
|
|
1823
|
+
whitelist,
|
|
1824
|
+
blacklist,
|
|
1825
|
+
partialize,
|
|
1826
|
+
merge: customMerge,
|
|
1827
|
+
version = 0,
|
|
1828
|
+
migrate,
|
|
1829
|
+
serialize = JSON.stringify,
|
|
1830
|
+
deserialize = JSON.parse,
|
|
1831
|
+
encrypt,
|
|
1832
|
+
decrypt,
|
|
1833
|
+
writeDebounce,
|
|
1834
|
+
onRehydrateStorage,
|
|
1835
|
+
onHydrationComplete,
|
|
1836
|
+
onPersistError
|
|
1837
|
+
} = options;
|
|
1838
|
+
let cancelDebounce = null;
|
|
908
1839
|
return {
|
|
909
1840
|
name: "persist",
|
|
910
1841
|
version: "1.0.0",
|
|
911
1842
|
install(store) {
|
|
912
1843
|
try {
|
|
913
|
-
|
|
1844
|
+
let saved = storage.getItem(key);
|
|
1845
|
+
if (saved && decrypt) {
|
|
1846
|
+
try {
|
|
1847
|
+
saved = decrypt(saved);
|
|
1848
|
+
} catch {
|
|
1849
|
+
saved = null;
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
914
1852
|
if (saved) {
|
|
915
|
-
|
|
916
|
-
|
|
1853
|
+
let parsed;
|
|
1854
|
+
let persistedVersion = 0;
|
|
1855
|
+
try {
|
|
1856
|
+
const data = deserialize(saved);
|
|
1857
|
+
if (data && typeof data === "object" && "state" in data && "version" in data) {
|
|
1858
|
+
parsed = data.state;
|
|
1859
|
+
persistedVersion = data.version;
|
|
1860
|
+
} else {
|
|
1861
|
+
parsed = data;
|
|
1862
|
+
}
|
|
1863
|
+
} catch {
|
|
1864
|
+
parsed = deserialize(saved);
|
|
1865
|
+
}
|
|
1866
|
+
if (onRehydrateStorage) {
|
|
1867
|
+
onRehydrateStorage(parsed);
|
|
1868
|
+
}
|
|
1869
|
+
if (migrate && persistedVersion < version) {
|
|
1870
|
+
parsed = migrate(parsed, persistedVersion);
|
|
1871
|
+
}
|
|
1872
|
+
if (customMerge) {
|
|
1873
|
+
const currentState = store.getState();
|
|
1874
|
+
const mergedState = customMerge(parsed, currentState);
|
|
1875
|
+
store.setState(mergedState);
|
|
1876
|
+
} else {
|
|
1877
|
+
store.merge(parsed);
|
|
1878
|
+
}
|
|
1879
|
+
if (onHydrationComplete) {
|
|
1880
|
+
onHydrationComplete(store.getState());
|
|
1881
|
+
}
|
|
917
1882
|
}
|
|
918
1883
|
} catch (error) {
|
|
919
|
-
|
|
1884
|
+
if (onPersistError) {
|
|
1885
|
+
onPersistError(error);
|
|
1886
|
+
} else {
|
|
1887
|
+
console.error(`Failed to hydrate state from '${key}':`, error);
|
|
1888
|
+
}
|
|
920
1889
|
}
|
|
921
|
-
|
|
1890
|
+
const persistState = (state) => {
|
|
922
1891
|
try {
|
|
923
1892
|
let toSave = state;
|
|
924
|
-
if (
|
|
1893
|
+
if (partialize) {
|
|
1894
|
+
toSave = partialize(state);
|
|
1895
|
+
} else if (whitelist) {
|
|
925
1896
|
toSave = pick(state, whitelist);
|
|
926
1897
|
} else if (blacklist) {
|
|
927
1898
|
toSave = omit(state, blacklist);
|
|
928
1899
|
}
|
|
929
|
-
|
|
1900
|
+
const dataToStore = {
|
|
1901
|
+
state: toSave,
|
|
1902
|
+
version
|
|
1903
|
+
};
|
|
1904
|
+
let serialized = serialize(dataToStore);
|
|
1905
|
+
if (encrypt) {
|
|
1906
|
+
serialized = encrypt(serialized);
|
|
1907
|
+
}
|
|
1908
|
+
storage.setItem(key, serialized);
|
|
930
1909
|
} catch (error) {
|
|
931
|
-
|
|
1910
|
+
if (onPersistError) {
|
|
1911
|
+
onPersistError(error);
|
|
1912
|
+
} else {
|
|
1913
|
+
console.error(`Failed to persist state to '${key}':`, error);
|
|
1914
|
+
}
|
|
932
1915
|
}
|
|
933
|
-
}
|
|
1916
|
+
};
|
|
1917
|
+
const debouncedPersist = writeDebounce && writeDebounce > 0 ? createDebounced2(persistState, writeDebounce) : persistState;
|
|
1918
|
+
if (writeDebounce && writeDebounce > 0) {
|
|
1919
|
+
cancelDebounce = () => debouncedPersist.cancel?.();
|
|
1920
|
+
}
|
|
1921
|
+
store.subscribe(debouncedPersist);
|
|
934
1922
|
},
|
|
935
1923
|
onDestroy() {
|
|
1924
|
+
if (cancelDebounce) {
|
|
1925
|
+
cancelDebounce();
|
|
1926
|
+
}
|
|
936
1927
|
}
|
|
937
1928
|
};
|
|
938
1929
|
}
|
|
@@ -1262,31 +2253,888 @@ function selector(options) {
|
|
|
1262
2253
|
};
|
|
1263
2254
|
}
|
|
1264
2255
|
|
|
1265
|
-
|
|
1266
|
-
|
|
2256
|
+
// src/plugins/logger.ts
|
|
2257
|
+
var LOG_LEVELS = {
|
|
2258
|
+
debug: 0,
|
|
2259
|
+
info: 1,
|
|
2260
|
+
warn: 2,
|
|
2261
|
+
error: 3
|
|
2262
|
+
};
|
|
2263
|
+
var COLORS = {
|
|
2264
|
+
title: "#9E9E9E",
|
|
2265
|
+
prevState: "#9E9E9E",
|
|
2266
|
+
action: "#03A9F4",
|
|
2267
|
+
nextState: "#4CAF50",
|
|
2268
|
+
diff: {
|
|
2269
|
+
changed: "#2196F3"
|
|
2270
|
+
}
|
|
2271
|
+
};
|
|
2272
|
+
function formatTime(date) {
|
|
2273
|
+
const hours = date.getHours().toString().padStart(2, "0");
|
|
2274
|
+
const minutes = date.getMinutes().toString().padStart(2, "0");
|
|
2275
|
+
const seconds = date.getSeconds().toString().padStart(2, "0");
|
|
2276
|
+
const ms = date.getMilliseconds().toString().padStart(3, "0");
|
|
2277
|
+
return `${hours}:${minutes}:${seconds}.${ms}`;
|
|
2278
|
+
}
|
|
2279
|
+
function calculateDiff(prevState, nextState) {
|
|
2280
|
+
const diff = {};
|
|
2281
|
+
if (typeof prevState !== "object" || typeof nextState !== "object") {
|
|
2282
|
+
if (prevState !== nextState) {
|
|
2283
|
+
diff["value"] = { prev: prevState, next: nextState };
|
|
2284
|
+
}
|
|
2285
|
+
return diff;
|
|
2286
|
+
}
|
|
2287
|
+
if (prevState === null || nextState === null) {
|
|
2288
|
+
if (prevState !== nextState) {
|
|
2289
|
+
diff["value"] = { prev: prevState, next: nextState };
|
|
2290
|
+
}
|
|
2291
|
+
return diff;
|
|
2292
|
+
}
|
|
2293
|
+
const allKeys = /* @__PURE__ */ new Set([
|
|
2294
|
+
...Object.keys(prevState),
|
|
2295
|
+
...Object.keys(nextState)
|
|
2296
|
+
]);
|
|
2297
|
+
for (const key of allKeys) {
|
|
2298
|
+
const prev = prevState[key];
|
|
2299
|
+
const next = nextState[key];
|
|
2300
|
+
if (prev !== next) {
|
|
2301
|
+
diff[key] = { prev, next };
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
return diff;
|
|
2305
|
+
}
|
|
2306
|
+
function isBrowser() {
|
|
2307
|
+
return typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
2308
|
+
}
|
|
2309
|
+
function logger(options = {}) {
|
|
2310
|
+
const {
|
|
2311
|
+
level = "debug",
|
|
2312
|
+
logger: customLogger = console,
|
|
2313
|
+
collapsed = true,
|
|
2314
|
+
diff = true,
|
|
2315
|
+
colors = isBrowser(),
|
|
2316
|
+
timestamp = true,
|
|
2317
|
+
filter,
|
|
2318
|
+
stateTransformer,
|
|
2319
|
+
actionName = "STATE_CHANGE",
|
|
2320
|
+
name = "Store",
|
|
2321
|
+
enabled = true
|
|
2322
|
+
} = options;
|
|
2323
|
+
const minLevel = LOG_LEVELS[level];
|
|
2324
|
+
return {
|
|
2325
|
+
name: "logger",
|
|
2326
|
+
version: "1.0.0",
|
|
2327
|
+
install(store) {
|
|
2328
|
+
if (!enabled) return;
|
|
2329
|
+
store.subscribe((state, prevState) => {
|
|
2330
|
+
if (filter && !filter(state, prevState)) {
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
if (LOG_LEVELS.info < minLevel) {
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
const now = /* @__PURE__ */ new Date();
|
|
2337
|
+
const action = typeof actionName === "function" ? actionName(state, prevState) : actionName;
|
|
2338
|
+
const displayPrevState = stateTransformer ? stateTransformer(prevState) : prevState;
|
|
2339
|
+
const displayNextState = stateTransformer ? stateTransformer(state) : state;
|
|
2340
|
+
const timeStr = timestamp ? ` @ ${formatTime(now)}` : "";
|
|
2341
|
+
const title = `${name} ${action}${timeStr}`;
|
|
2342
|
+
if (colors && isBrowser()) {
|
|
2343
|
+
logWithColors(
|
|
2344
|
+
customLogger,
|
|
2345
|
+
title,
|
|
2346
|
+
displayPrevState,
|
|
2347
|
+
displayNextState,
|
|
2348
|
+
collapsed,
|
|
2349
|
+
diff ? calculateDiff(prevState, state) : void 0
|
|
2350
|
+
);
|
|
2351
|
+
} else {
|
|
2352
|
+
logPlain(
|
|
2353
|
+
customLogger,
|
|
2354
|
+
title,
|
|
2355
|
+
displayPrevState,
|
|
2356
|
+
displayNextState,
|
|
2357
|
+
collapsed,
|
|
2358
|
+
diff ? calculateDiff(prevState, state) : void 0
|
|
2359
|
+
);
|
|
2360
|
+
}
|
|
2361
|
+
});
|
|
2362
|
+
}
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
function logWithColors(log, title, prevState, nextState, collapsed, diff) {
|
|
2366
|
+
const titleStyle = `color: ${COLORS.title}; font-weight: lighter;`;
|
|
2367
|
+
const groupMethod = collapsed ? "groupCollapsed" : "group";
|
|
2368
|
+
log[groupMethod](`%c${title}`, titleStyle);
|
|
2369
|
+
log.log("%cprev state", `color: ${COLORS.prevState}; font-weight: bold;`, prevState);
|
|
2370
|
+
log.log("%cnext state", `color: ${COLORS.nextState}; font-weight: bold;`, nextState);
|
|
2371
|
+
if (diff && Object.keys(diff).length > 0) {
|
|
2372
|
+
log.log("%cdiff", `color: ${COLORS.action}; font-weight: bold;`);
|
|
2373
|
+
for (const [key, { prev, next }] of Object.entries(diff)) {
|
|
2374
|
+
log.log(
|
|
2375
|
+
` %c${key}:`,
|
|
2376
|
+
`color: ${COLORS.diff.changed};`,
|
|
2377
|
+
prev,
|
|
2378
|
+
"\u2192",
|
|
2379
|
+
next
|
|
2380
|
+
);
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
log.groupEnd();
|
|
2384
|
+
}
|
|
2385
|
+
function logPlain(log, title, prevState, nextState, collapsed, diff) {
|
|
2386
|
+
const hasGroupCollapsed = typeof log.groupCollapsed === "function";
|
|
2387
|
+
const groupMethod = collapsed && hasGroupCollapsed ? "groupCollapsed" : "group";
|
|
2388
|
+
const hasGroup = typeof log[groupMethod] === "function";
|
|
2389
|
+
if (hasGroup) {
|
|
2390
|
+
log[groupMethod](title);
|
|
2391
|
+
} else {
|
|
2392
|
+
log.log(`=== ${title} ===`);
|
|
2393
|
+
}
|
|
2394
|
+
log.log("prev state:", prevState);
|
|
2395
|
+
log.log("next state:", nextState);
|
|
2396
|
+
if (diff && Object.keys(diff).length > 0) {
|
|
2397
|
+
log.log("diff:");
|
|
2398
|
+
for (const [key, { prev, next }] of Object.entries(diff)) {
|
|
2399
|
+
log.log(` ${key}:`, prev, "\u2192", next);
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
if (hasGroup) {
|
|
2403
|
+
log.groupEnd();
|
|
2404
|
+
} else {
|
|
2405
|
+
log.log("===");
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
function createLogger(defaultOptions = {}) {
|
|
2409
|
+
return (options = {}) => logger({ ...defaultOptions, ...options });
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
// src/plugins/effects.ts
|
|
2413
|
+
init_utils();
|
|
2414
|
+
function createDebounced3(fn, delay2) {
|
|
2415
|
+
let timeoutId = null;
|
|
2416
|
+
const debounced = ((...args) => {
|
|
2417
|
+
if (timeoutId) {
|
|
2418
|
+
clearTimeout(timeoutId);
|
|
2419
|
+
}
|
|
2420
|
+
timeoutId = setTimeout(() => {
|
|
2421
|
+
fn(...args);
|
|
2422
|
+
timeoutId = null;
|
|
2423
|
+
}, delay2);
|
|
2424
|
+
});
|
|
2425
|
+
debounced.cancel = () => {
|
|
2426
|
+
if (timeoutId) {
|
|
2427
|
+
clearTimeout(timeoutId);
|
|
2428
|
+
timeoutId = null;
|
|
2429
|
+
}
|
|
2430
|
+
};
|
|
2431
|
+
return debounced;
|
|
2432
|
+
}
|
|
2433
|
+
function effects(options) {
|
|
2434
|
+
const { effects: effectDefs, onError } = options;
|
|
2435
|
+
return {
|
|
2436
|
+
name: "effects",
|
|
2437
|
+
version: "1.0.0",
|
|
2438
|
+
install(store) {
|
|
2439
|
+
const cleanups = [];
|
|
2440
|
+
const effectCleanups = /* @__PURE__ */ new Map();
|
|
2441
|
+
for (let i = 0; i < effectDefs.length; i++) {
|
|
2442
|
+
const def = effectDefs[i];
|
|
2443
|
+
if (!def) continue;
|
|
2444
|
+
const {
|
|
2445
|
+
selector: selector2,
|
|
2446
|
+
effect,
|
|
2447
|
+
equalityFn = shallowEqual,
|
|
2448
|
+
fireImmediately = false,
|
|
2449
|
+
debounce,
|
|
2450
|
+
name
|
|
2451
|
+
} = def;
|
|
2452
|
+
let isActive = true;
|
|
2453
|
+
let prevValue = selector2(store.getState());
|
|
2454
|
+
const createUtils = () => ({
|
|
2455
|
+
getState: () => store.getState(),
|
|
2456
|
+
setState: (partial) => store.setState(partial),
|
|
2457
|
+
isActive: () => isActive,
|
|
2458
|
+
cancel: () => {
|
|
2459
|
+
isActive = false;
|
|
2460
|
+
},
|
|
2461
|
+
onlyIfActive: (fn) => {
|
|
2462
|
+
if (isActive) {
|
|
2463
|
+
return fn();
|
|
2464
|
+
}
|
|
2465
|
+
return void 0;
|
|
2466
|
+
}
|
|
2467
|
+
});
|
|
2468
|
+
const runEffect = async (value, prev) => {
|
|
2469
|
+
const prevCleanup = effectCleanups.get(i);
|
|
2470
|
+
if (prevCleanup) {
|
|
2471
|
+
try {
|
|
2472
|
+
prevCleanup();
|
|
2473
|
+
} catch (e) {
|
|
2474
|
+
}
|
|
2475
|
+
effectCleanups.delete(i);
|
|
2476
|
+
}
|
|
2477
|
+
isActive = true;
|
|
2478
|
+
try {
|
|
2479
|
+
const result = effect(value, prev, createUtils());
|
|
2480
|
+
if (result instanceof Promise) {
|
|
2481
|
+
const cleanup = await result;
|
|
2482
|
+
if (cleanup && isActive) {
|
|
2483
|
+
effectCleanups.set(i, cleanup);
|
|
2484
|
+
}
|
|
2485
|
+
} else if (result) {
|
|
2486
|
+
effectCleanups.set(i, result);
|
|
2487
|
+
}
|
|
2488
|
+
} catch (error) {
|
|
2489
|
+
if (onError) {
|
|
2490
|
+
onError(error, name);
|
|
2491
|
+
} else {
|
|
2492
|
+
console.error(`Effect error${name ? ` in '${name}'` : ""}:`, error);
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
};
|
|
2496
|
+
const effectFn = debounce && debounce > 0 ? createDebounced3(runEffect, debounce) : runEffect;
|
|
2497
|
+
if (fireImmediately) {
|
|
2498
|
+
effectFn(prevValue, prevValue);
|
|
2499
|
+
}
|
|
2500
|
+
const unsubscribe = store.subscribe(
|
|
2501
|
+
selector2,
|
|
2502
|
+
(value, prev) => {
|
|
2503
|
+
if (!equalityFn(value, prev)) {
|
|
2504
|
+
prevValue = value;
|
|
2505
|
+
effectFn(value, prev);
|
|
2506
|
+
}
|
|
2507
|
+
},
|
|
2508
|
+
equalityFn
|
|
2509
|
+
);
|
|
2510
|
+
cleanups.push(() => {
|
|
2511
|
+
unsubscribe();
|
|
2512
|
+
isActive = false;
|
|
2513
|
+
if ("cancel" in effectFn) {
|
|
2514
|
+
effectFn.cancel();
|
|
2515
|
+
}
|
|
2516
|
+
const cleanup = effectCleanups.get(i);
|
|
2517
|
+
if (cleanup) {
|
|
2518
|
+
try {
|
|
2519
|
+
cleanup();
|
|
2520
|
+
} catch {
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
store.__effectsCleanup = () => {
|
|
2526
|
+
for (const cleanup of cleanups) {
|
|
2527
|
+
cleanup();
|
|
2528
|
+
}
|
|
2529
|
+
effectCleanups.clear();
|
|
2530
|
+
};
|
|
2531
|
+
}
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
function createEffect(name, selector2, effect, options = {}) {
|
|
2535
|
+
return {
|
|
2536
|
+
name,
|
|
2537
|
+
selector: selector2,
|
|
2538
|
+
effect,
|
|
2539
|
+
...options
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
function createSimpleEffect(effect, options = {}) {
|
|
2543
|
+
return {
|
|
2544
|
+
selector: (s) => s,
|
|
2545
|
+
effect,
|
|
2546
|
+
equalityFn: () => false,
|
|
2547
|
+
// Always run
|
|
2548
|
+
...options
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
function combineEffects(...effectDefs) {
|
|
2552
|
+
return {
|
|
2553
|
+
effects: effectDefs
|
|
2554
|
+
};
|
|
2555
|
+
}
|
|
2556
|
+
function createDebouncedEffect(delay2, selector2, effect, options = {}) {
|
|
2557
|
+
return {
|
|
2558
|
+
selector: selector2,
|
|
2559
|
+
effect,
|
|
2560
|
+
debounce: delay2,
|
|
2561
|
+
...options
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
// src/plugins/validate.ts
|
|
2566
|
+
function isZodLike(schema) {
|
|
2567
|
+
return typeof schema === "object" && schema !== null && "safeParse" in schema && typeof schema.safeParse === "function";
|
|
2568
|
+
}
|
|
2569
|
+
function isYupLike(schema) {
|
|
2570
|
+
return typeof schema === "object" && schema !== null && "validateSync" in schema && typeof schema.validateSync === "function";
|
|
2571
|
+
}
|
|
2572
|
+
function extractPaths(state, paths) {
|
|
2573
|
+
const result = {};
|
|
2574
|
+
for (const path of paths) {
|
|
2575
|
+
if (path in state) {
|
|
2576
|
+
result[path] = state[path];
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
return result;
|
|
2580
|
+
}
|
|
2581
|
+
function validateState(state, schema, paths) {
|
|
2582
|
+
const stateToValidate = paths ? extractPaths(state, paths) : state;
|
|
2583
|
+
if (typeof schema === "function") {
|
|
2584
|
+
const result = schema(stateToValidate);
|
|
2585
|
+
if (typeof result === "boolean") {
|
|
2586
|
+
return {
|
|
2587
|
+
valid: result,
|
|
2588
|
+
errors: result ? [] : [{ path: [], message: "Validation failed" }]
|
|
2589
|
+
};
|
|
2590
|
+
}
|
|
2591
|
+
if ("valid" in result && "errors" in result) {
|
|
2592
|
+
return result;
|
|
2593
|
+
}
|
|
2594
|
+
if (Array.isArray(result)) {
|
|
2595
|
+
return {
|
|
2596
|
+
valid: result.length === 0,
|
|
2597
|
+
errors: result
|
|
2598
|
+
};
|
|
2599
|
+
}
|
|
2600
|
+
return { valid: true, errors: [] };
|
|
2601
|
+
}
|
|
2602
|
+
if (isZodLike(schema)) {
|
|
2603
|
+
const result = schema.safeParse(stateToValidate);
|
|
2604
|
+
if (result.success) {
|
|
2605
|
+
return { valid: true, errors: [] };
|
|
2606
|
+
}
|
|
2607
|
+
const errors = (result.error?.issues || []).map((issue) => ({
|
|
2608
|
+
path: issue.path.map(String),
|
|
2609
|
+
message: issue.message,
|
|
2610
|
+
rule: issue.code
|
|
2611
|
+
}));
|
|
2612
|
+
return { valid: false, errors };
|
|
2613
|
+
}
|
|
2614
|
+
if (isYupLike(schema)) {
|
|
2615
|
+
try {
|
|
2616
|
+
schema.validateSync(stateToValidate, { abortEarly: false });
|
|
2617
|
+
return { valid: true, errors: [] };
|
|
2618
|
+
} catch (error) {
|
|
2619
|
+
const errors = [];
|
|
2620
|
+
if (error.inner && Array.isArray(error.inner)) {
|
|
2621
|
+
for (const err of error.inner) {
|
|
2622
|
+
errors.push({
|
|
2623
|
+
path: err.path ? err.path.split(".") : [],
|
|
2624
|
+
message: err.message,
|
|
2625
|
+
rule: err.type,
|
|
2626
|
+
value: err.value
|
|
2627
|
+
});
|
|
2628
|
+
}
|
|
2629
|
+
} else {
|
|
2630
|
+
errors.push({
|
|
2631
|
+
path: error.path ? error.path.split(".") : [],
|
|
2632
|
+
message: error.message || "Validation failed",
|
|
2633
|
+
rule: error.type
|
|
2634
|
+
});
|
|
2635
|
+
}
|
|
2636
|
+
return { valid: false, errors };
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
return { valid: true, errors: [] };
|
|
2640
|
+
}
|
|
2641
|
+
function validate(options) {
|
|
2642
|
+
const {
|
|
2643
|
+
schema,
|
|
2644
|
+
on = "change",
|
|
2645
|
+
onError,
|
|
2646
|
+
throwOnError = false,
|
|
2647
|
+
paths,
|
|
2648
|
+
name = "validate"
|
|
2649
|
+
} = options;
|
|
2650
|
+
return {
|
|
2651
|
+
name,
|
|
2652
|
+
version: "1.0.0",
|
|
2653
|
+
install(store) {
|
|
2654
|
+
let currentErrors = [];
|
|
2655
|
+
const doValidate = () => {
|
|
2656
|
+
const state = store.getState();
|
|
2657
|
+
const result = validateState(state, schema, paths);
|
|
2658
|
+
currentErrors = result.errors;
|
|
2659
|
+
if (!result.valid) {
|
|
2660
|
+
if (onError) {
|
|
2661
|
+
onError(result.errors);
|
|
2662
|
+
}
|
|
2663
|
+
if (throwOnError) {
|
|
2664
|
+
const errorMessages = result.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
|
|
2665
|
+
throw new Error(`Validation failed: ${errorMessages}`);
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
return result;
|
|
2669
|
+
};
|
|
2670
|
+
if (on === "change") {
|
|
2671
|
+
store.subscribe(() => {
|
|
2672
|
+
doValidate();
|
|
2673
|
+
});
|
|
2674
|
+
}
|
|
2675
|
+
const validationAPI = {
|
|
2676
|
+
validate: doValidate,
|
|
2677
|
+
isValid: () => currentErrors.length === 0,
|
|
2678
|
+
getErrors: () => [...currentErrors],
|
|
2679
|
+
clearErrors: () => {
|
|
2680
|
+
currentErrors = [];
|
|
2681
|
+
}
|
|
2682
|
+
};
|
|
2683
|
+
Object.assign(store, validationAPI);
|
|
2684
|
+
}
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2687
|
+
function createValidator(predicate, message) {
|
|
2688
|
+
return (state) => {
|
|
2689
|
+
const valid = predicate(state);
|
|
2690
|
+
return {
|
|
2691
|
+
valid,
|
|
2692
|
+
errors: valid ? [] : [{ path: [], message }]
|
|
2693
|
+
};
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
function combineValidators(...validators) {
|
|
2697
|
+
return (state) => {
|
|
2698
|
+
const allErrors = [];
|
|
2699
|
+
for (const validator of validators) {
|
|
2700
|
+
const result = validator(state);
|
|
2701
|
+
if (typeof result === "boolean") {
|
|
2702
|
+
if (!result) {
|
|
2703
|
+
allErrors.push({ path: [], message: "Validation failed" });
|
|
2704
|
+
}
|
|
2705
|
+
} else if ("valid" in result && "errors" in result) {
|
|
2706
|
+
allErrors.push(...result.errors);
|
|
2707
|
+
} else if (Array.isArray(result)) {
|
|
2708
|
+
allErrors.push(...result);
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
return {
|
|
2712
|
+
valid: allErrors.length === 0,
|
|
2713
|
+
errors: allErrors
|
|
2714
|
+
};
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
function createFieldValidator(field, predicate, message) {
|
|
2718
|
+
return (state) => {
|
|
2719
|
+
const valid = predicate(state[field], state);
|
|
2720
|
+
return {
|
|
2721
|
+
valid,
|
|
2722
|
+
errors: valid ? [] : [{ path: [String(field)], message, value: state[field] }]
|
|
2723
|
+
};
|
|
2724
|
+
};
|
|
2725
|
+
}
|
|
2726
|
+
function createAsyncValidator(validator) {
|
|
2727
|
+
return async (state) => {
|
|
2728
|
+
const errors = await validator(state);
|
|
2729
|
+
return {
|
|
2730
|
+
valid: errors.length === 0,
|
|
2731
|
+
errors
|
|
2732
|
+
};
|
|
2733
|
+
};
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2736
|
+
// src/index.ts
|
|
2737
|
+
init_utils();
|
|
2738
|
+
|
|
2739
|
+
// src/testing.ts
|
|
2740
|
+
init_store();
|
|
2741
|
+
init_utils();
|
|
2742
|
+
function createMockStorage() {
|
|
2743
|
+
const storage = /* @__PURE__ */ new Map();
|
|
2744
|
+
return {
|
|
2745
|
+
getItem(key) {
|
|
2746
|
+
return storage.get(key) ?? null;
|
|
2747
|
+
},
|
|
2748
|
+
setItem(key, value) {
|
|
2749
|
+
storage.set(key, value);
|
|
2750
|
+
},
|
|
2751
|
+
removeItem(key) {
|
|
2752
|
+
storage.delete(key);
|
|
2753
|
+
},
|
|
2754
|
+
clear() {
|
|
2755
|
+
storage.clear();
|
|
2756
|
+
},
|
|
2757
|
+
getAll() {
|
|
2758
|
+
const result = {};
|
|
2759
|
+
for (const [key, value] of storage) {
|
|
2760
|
+
result[key] = value;
|
|
2761
|
+
}
|
|
2762
|
+
return result;
|
|
2763
|
+
}
|
|
2764
|
+
};
|
|
2765
|
+
}
|
|
2766
|
+
function spyOnStore(store) {
|
|
2767
|
+
const changes = [];
|
|
2768
|
+
const waiters = [];
|
|
2769
|
+
const unsubscribe = store.subscribe((state, prevState) => {
|
|
2770
|
+
changes.push({
|
|
2771
|
+
state,
|
|
2772
|
+
prevState,
|
|
2773
|
+
timestamp: Date.now()
|
|
2774
|
+
});
|
|
2775
|
+
for (let i = waiters.length - 1; i >= 0; i--) {
|
|
2776
|
+
const waiter = waiters[i];
|
|
2777
|
+
if (waiter && waiter.predicate(state)) {
|
|
2778
|
+
waiter.resolve(state);
|
|
2779
|
+
waiters.splice(i, 1);
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
});
|
|
2783
|
+
return {
|
|
2784
|
+
changes,
|
|
2785
|
+
lastChange: () => changes[changes.length - 1],
|
|
2786
|
+
lastState: () => changes[changes.length - 1]?.state,
|
|
2787
|
+
reset: () => {
|
|
2788
|
+
changes.length = 0;
|
|
2789
|
+
},
|
|
2790
|
+
unsubscribe,
|
|
2791
|
+
waitFor: (predicate, timeout = 5e3) => {
|
|
2792
|
+
return new Promise((resolve, reject) => {
|
|
2793
|
+
const current = store.getState();
|
|
2794
|
+
if (predicate(current)) {
|
|
2795
|
+
resolve(current);
|
|
2796
|
+
return;
|
|
2797
|
+
}
|
|
2798
|
+
const waiter = { resolve, predicate };
|
|
2799
|
+
waiters.push(waiter);
|
|
2800
|
+
const timeoutId = setTimeout(() => {
|
|
2801
|
+
const index = waiters.indexOf(waiter);
|
|
2802
|
+
if (index >= 0) {
|
|
2803
|
+
waiters.splice(index, 1);
|
|
2804
|
+
reject(new Error(`waitFor timeout after ${timeout}ms`));
|
|
2805
|
+
}
|
|
2806
|
+
}, timeout);
|
|
2807
|
+
const originalResolve = waiter.resolve;
|
|
2808
|
+
waiter.resolve = (state) => {
|
|
2809
|
+
clearTimeout(timeoutId);
|
|
2810
|
+
originalResolve(state);
|
|
2811
|
+
};
|
|
2812
|
+
});
|
|
2813
|
+
},
|
|
2814
|
+
waitForChanges: (count, timeout = 5e3) => {
|
|
2815
|
+
return new Promise((resolve, reject) => {
|
|
2816
|
+
const startCount = changes.length;
|
|
2817
|
+
const check = () => {
|
|
2818
|
+
if (changes.length >= startCount + count) {
|
|
2819
|
+
return changes.slice(startCount, startCount + count);
|
|
2820
|
+
}
|
|
2821
|
+
return null;
|
|
2822
|
+
};
|
|
2823
|
+
const immediate = check();
|
|
2824
|
+
if (immediate) {
|
|
2825
|
+
resolve(immediate);
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
const intervalId = setInterval(() => {
|
|
2829
|
+
const result = check();
|
|
2830
|
+
if (result) {
|
|
2831
|
+
clearInterval(intervalId);
|
|
2832
|
+
clearTimeout(timeoutId);
|
|
2833
|
+
resolve(result);
|
|
2834
|
+
}
|
|
2835
|
+
}, 10);
|
|
2836
|
+
const timeoutId = setTimeout(() => {
|
|
2837
|
+
clearInterval(intervalId);
|
|
2838
|
+
reject(new Error(`waitForChanges timeout after ${timeout}ms`));
|
|
2839
|
+
}, timeout);
|
|
2840
|
+
});
|
|
2841
|
+
}
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2844
|
+
function assertState(store) {
|
|
2845
|
+
return {
|
|
2846
|
+
toEqual: (expected) => {
|
|
2847
|
+
const state = store.getState();
|
|
2848
|
+
for (const [key, value] of Object.entries(expected)) {
|
|
2849
|
+
if (state[key] !== value) {
|
|
2850
|
+
throw new Error(
|
|
2851
|
+
`Expected state.${key} to be ${JSON.stringify(value)}, but got ${JSON.stringify(state[key])}`
|
|
2852
|
+
);
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
},
|
|
2856
|
+
toHaveSelected: (selector2, expected) => {
|
|
2857
|
+
const actual = selector2(store.getState());
|
|
2858
|
+
if (!shallowEqual(actual, expected)) {
|
|
2859
|
+
throw new Error(
|
|
2860
|
+
`Expected selector to return ${JSON.stringify(expected)}, but got ${JSON.stringify(actual)}`
|
|
2861
|
+
);
|
|
2862
|
+
}
|
|
2863
|
+
},
|
|
2864
|
+
toMatch: (predicate) => {
|
|
2865
|
+
const state = store.getState();
|
|
2866
|
+
if (!predicate(state)) {
|
|
2867
|
+
throw new Error(
|
|
2868
|
+
`State did not match predicate: ${JSON.stringify(state)}`
|
|
2869
|
+
);
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
function createTestStore(options) {
|
|
2875
|
+
const { initialState, spy: enableSpy = true } = options;
|
|
2876
|
+
const store = createStore(initialState);
|
|
2877
|
+
const spy = enableSpy ? spyOnStore(store) : void 0;
|
|
2878
|
+
const assert = assertState(store);
|
|
2879
|
+
return {
|
|
2880
|
+
store,
|
|
2881
|
+
spy,
|
|
2882
|
+
assert,
|
|
2883
|
+
reset: () => {
|
|
2884
|
+
store.reset();
|
|
2885
|
+
spy?.reset();
|
|
2886
|
+
},
|
|
2887
|
+
destroy: () => {
|
|
2888
|
+
spy?.unsubscribe();
|
|
2889
|
+
store.destroy();
|
|
2890
|
+
}
|
|
2891
|
+
};
|
|
2892
|
+
}
|
|
2893
|
+
function delay(ms) {
|
|
2894
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2895
|
+
}
|
|
2896
|
+
async function flushMicrotasks(fn) {
|
|
2897
|
+
if (fn) {
|
|
2898
|
+
fn();
|
|
2899
|
+
}
|
|
2900
|
+
await Promise.resolve();
|
|
2901
|
+
}
|
|
2902
|
+
function mockAction(fn) {
|
|
2903
|
+
const calls = [];
|
|
2904
|
+
const mocked = ((...args) => {
|
|
2905
|
+
calls.push(args);
|
|
2906
|
+
return fn(...args);
|
|
2907
|
+
});
|
|
2908
|
+
Object.defineProperty(mocked, "callCount", {
|
|
2909
|
+
get: () => calls.length
|
|
2910
|
+
});
|
|
2911
|
+
mocked.calls = calls;
|
|
2912
|
+
mocked.reset = () => {
|
|
2913
|
+
calls.length = 0;
|
|
2914
|
+
};
|
|
2915
|
+
return mocked;
|
|
2916
|
+
}
|
|
2917
|
+
function snapshot(store) {
|
|
2918
|
+
return JSON.parse(JSON.stringify(store.getState()));
|
|
2919
|
+
}
|
|
2920
|
+
function stateDiff(before, after) {
|
|
2921
|
+
const diff = {};
|
|
2922
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
2923
|
+
for (const key of allKeys) {
|
|
2924
|
+
if (!shallowEqual(before[key], after[key])) {
|
|
2925
|
+
diff[key] = { before: before[key], after: after[key] };
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
return diff;
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2931
|
+
// src/compat/middleware.ts
|
|
2932
|
+
function toMiddlewareApi(store) {
|
|
2933
|
+
return {
|
|
2934
|
+
setState: (partial, replace) => {
|
|
2935
|
+
if (typeof partial === "function") {
|
|
2936
|
+
const currentState = store.getState();
|
|
2937
|
+
const newPartial = partial(currentState);
|
|
2938
|
+
if (replace) {
|
|
2939
|
+
store.setState(newPartial);
|
|
2940
|
+
} else {
|
|
2941
|
+
store.merge(newPartial);
|
|
2942
|
+
}
|
|
2943
|
+
} else {
|
|
2944
|
+
if (replace) {
|
|
2945
|
+
store.setState(partial);
|
|
2946
|
+
} else {
|
|
2947
|
+
store.merge(partial);
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
},
|
|
2951
|
+
getState: () => store.getState(),
|
|
2952
|
+
subscribe: (listener) => store.subscribe(listener),
|
|
2953
|
+
destroy: () => store.destroy()
|
|
2954
|
+
};
|
|
2955
|
+
}
|
|
2956
|
+
function middlewareCompat(middleware, options) {
|
|
2957
|
+
return {
|
|
2958
|
+
name: "middleware-compat",
|
|
2959
|
+
version: "1.0.0",
|
|
2960
|
+
install(store) {
|
|
2961
|
+
const api = toMiddlewareApi(store);
|
|
2962
|
+
const stateCreator = (set, get) => {
|
|
2963
|
+
return get();
|
|
2964
|
+
};
|
|
2965
|
+
const enhancedCreator = middleware(stateCreator);
|
|
2966
|
+
const enhancedState = enhancedCreator(api.setState, api.getState, api);
|
|
2967
|
+
if (enhancedState && typeof enhancedState === "object") {
|
|
2968
|
+
const currentState = store.getState();
|
|
2969
|
+
const stateOnly = {};
|
|
2970
|
+
for (const key in enhancedState) {
|
|
2971
|
+
const value = enhancedState[key];
|
|
2972
|
+
if (typeof value !== "function") {
|
|
2973
|
+
stateOnly[key] = value;
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
if (Object.keys(stateOnly).length > 0) {
|
|
2977
|
+
const hasChanges = Object.keys(stateOnly).some(
|
|
2978
|
+
(key) => currentState[key] !== stateOnly[key]
|
|
2979
|
+
);
|
|
2980
|
+
if (hasChanges) {
|
|
2981
|
+
store.merge(stateOnly);
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
};
|
|
2987
|
+
}
|
|
2988
|
+
function createWithMiddleware(creator) {
|
|
2989
|
+
const { createStore: createStore2 } = (init_store(), __toCommonJS(store_exports));
|
|
2990
|
+
let currentState = null;
|
|
2991
|
+
let storeRef = null;
|
|
2992
|
+
const set = (partial, replace) => {
|
|
2993
|
+
if (!storeRef) {
|
|
2994
|
+
throw new Error("Store not initialized");
|
|
2995
|
+
}
|
|
2996
|
+
if (typeof partial === "function") {
|
|
2997
|
+
const newPartial = partial(storeRef.getState());
|
|
2998
|
+
if (replace) {
|
|
2999
|
+
storeRef.setState(newPartial);
|
|
3000
|
+
} else {
|
|
3001
|
+
storeRef.merge(newPartial);
|
|
3002
|
+
}
|
|
3003
|
+
} else {
|
|
3004
|
+
if (replace) {
|
|
3005
|
+
storeRef.setState(partial);
|
|
3006
|
+
} else {
|
|
3007
|
+
storeRef.merge(partial);
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
};
|
|
3011
|
+
const get = () => {
|
|
3012
|
+
if (storeRef) {
|
|
3013
|
+
return storeRef.getState();
|
|
3014
|
+
}
|
|
3015
|
+
if (currentState) {
|
|
3016
|
+
return currentState;
|
|
3017
|
+
}
|
|
3018
|
+
throw new Error("Store not initialized");
|
|
3019
|
+
};
|
|
3020
|
+
const placeholderApi = {
|
|
3021
|
+
setState: set,
|
|
3022
|
+
getState: get,
|
|
3023
|
+
subscribe: () => () => {
|
|
3024
|
+
}
|
|
3025
|
+
};
|
|
3026
|
+
currentState = creator(set, get, placeholderApi);
|
|
3027
|
+
storeRef = createStore2(currentState);
|
|
3028
|
+
const api = {
|
|
3029
|
+
setState: (partial, replace) => {
|
|
3030
|
+
if (typeof partial === "function") {
|
|
3031
|
+
const newPartial = partial(storeRef.getState());
|
|
3032
|
+
if (replace) {
|
|
3033
|
+
storeRef.setState(newPartial);
|
|
3034
|
+
} else {
|
|
3035
|
+
storeRef.merge(newPartial);
|
|
3036
|
+
}
|
|
3037
|
+
} else {
|
|
3038
|
+
if (replace) {
|
|
3039
|
+
storeRef.setState(partial);
|
|
3040
|
+
} else {
|
|
3041
|
+
storeRef.merge(partial);
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
},
|
|
3045
|
+
getState: () => storeRef.getState(),
|
|
3046
|
+
subscribe: (listener) => storeRef.subscribe(listener),
|
|
3047
|
+
destroy: () => storeRef.destroy(),
|
|
3048
|
+
use: (plugin) => {
|
|
3049
|
+
storeRef.use(plugin);
|
|
3050
|
+
return api;
|
|
3051
|
+
}
|
|
3052
|
+
};
|
|
3053
|
+
return api;
|
|
3054
|
+
}
|
|
3055
|
+
function createSimpleMiddleware(name, enhancer) {
|
|
3056
|
+
const middleware = enhancer;
|
|
3057
|
+
Object.defineProperty(middleware, "name", { value: name });
|
|
3058
|
+
return middleware;
|
|
3059
|
+
}
|
|
3060
|
+
function compose(...middlewares) {
|
|
3061
|
+
return (creator) => {
|
|
3062
|
+
return middlewares.reduceRight((acc, middleware) => {
|
|
3063
|
+
return middleware(acc);
|
|
3064
|
+
}, creator);
|
|
3065
|
+
};
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
exports.assertState = assertState;
|
|
1267
3069
|
exports.batch = batch;
|
|
3070
|
+
exports.combineComputed = combineComputed;
|
|
3071
|
+
exports.combineEffects = combineEffects;
|
|
3072
|
+
exports.combineSlices = combineSlices;
|
|
3073
|
+
exports.combineValidators = combineValidators;
|
|
3074
|
+
exports.compose = compose;
|
|
3075
|
+
exports.computed = computed;
|
|
3076
|
+
exports.createAsyncValidator = createAsyncValidator;
|
|
3077
|
+
exports.createDebouncedEffect = createDebouncedEffect;
|
|
3078
|
+
exports.createEffect = createEffect;
|
|
3079
|
+
exports.createFederatedComputed = createFederatedComputed;
|
|
3080
|
+
exports.createFederatedSelector = createFederatedSelector;
|
|
3081
|
+
exports.createFederation = createFederation;
|
|
3082
|
+
exports.createFieldValidator = createFieldValidator;
|
|
3083
|
+
exports.createLogger = createLogger;
|
|
3084
|
+
exports.createMockStorage = createMockStorage;
|
|
3085
|
+
exports.createNamespacedSlice = createNamespacedSlice;
|
|
3086
|
+
exports.createSimpleEffect = createSimpleEffect;
|
|
3087
|
+
exports.createSimpleMiddleware = createSimpleMiddleware;
|
|
3088
|
+
exports.createSlice = createSlice;
|
|
1268
3089
|
exports.createStorage = createStorage;
|
|
1269
3090
|
exports.createStore = createStore;
|
|
3091
|
+
exports.createSubscriber = createSubscriber;
|
|
3092
|
+
exports.createTestStore = createTestStore;
|
|
3093
|
+
exports.createValidator = createValidator;
|
|
3094
|
+
exports.createWithMiddleware = createWithMiddleware;
|
|
1270
3095
|
exports.deepClone = deepClone;
|
|
1271
3096
|
exports.deepEqual = deepEqual;
|
|
1272
3097
|
exports.deepMerge = deepMerge;
|
|
3098
|
+
exports.delay = delay;
|
|
1273
3099
|
exports.devtools = devtools;
|
|
3100
|
+
exports.effects = effects;
|
|
3101
|
+
exports.extendStore = extendStore;
|
|
3102
|
+
exports.flushMicrotasks = flushMicrotasks;
|
|
1274
3103
|
exports.hasHistory = hasHistory;
|
|
1275
3104
|
exports.history = history;
|
|
1276
3105
|
exports.identity = identity;
|
|
1277
3106
|
exports.immer = immer;
|
|
1278
3107
|
exports.isFunction = isFunction;
|
|
3108
|
+
exports.logger = logger;
|
|
3109
|
+
exports.memoizeSelector = memoizeSelector;
|
|
3110
|
+
exports.middlewareCompat = middlewareCompat;
|
|
3111
|
+
exports.mockAction = mockAction;
|
|
1279
3112
|
exports.omit = omit;
|
|
1280
3113
|
exports.persist = persist;
|
|
1281
3114
|
exports.pick = pick;
|
|
1282
3115
|
exports.produce = produce;
|
|
3116
|
+
exports.resetSlices = resetSlices;
|
|
1283
3117
|
exports.selector = selector;
|
|
1284
3118
|
exports.sessionStorage = sessionStorage;
|
|
1285
3119
|
exports.shallowEqual = shallowEqual;
|
|
3120
|
+
exports.snapshot = snapshot;
|
|
3121
|
+
exports.spyOnStore = spyOnStore;
|
|
3122
|
+
exports.stateDiff = stateDiff;
|
|
3123
|
+
exports.subscribeOnce = subscribeOnce;
|
|
3124
|
+
exports.subscribeToMany = subscribeToMany;
|
|
3125
|
+
exports.subscribeWithOptions = subscribeWithOptions;
|
|
1286
3126
|
exports.sync = sync;
|
|
3127
|
+
exports.toMiddlewareApi = toMiddlewareApi;
|
|
1287
3128
|
exports.triggerSync = triggerSync;
|
|
1288
3129
|
exports.useAction = useAction;
|
|
1289
3130
|
exports.useCreateStore = useCreateStore;
|
|
3131
|
+
exports.useSetState = useSetState;
|
|
3132
|
+
exports.useShallow = useShallow;
|
|
1290
3133
|
exports.useStore = useStore;
|
|
3134
|
+
exports.useStoreActions = useStoreActions;
|
|
3135
|
+
exports.useStoreSelector = useStoreSelector;
|
|
3136
|
+
exports.useTransientSubscribe = useTransientSubscribe;
|
|
3137
|
+
exports.validate = validate;
|
|
3138
|
+
exports.waitForFederated = waitForFederated;
|
|
1291
3139
|
//# sourceMappingURL=index.cjs.map
|
|
1292
3140
|
//# sourceMappingURL=index.cjs.map
|