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