@ngstato/core 0.2.0 → 0.3.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 +38 -0
- package/dist/index.d.mts +151 -1
- package/dist/index.d.ts +151 -1
- package/dist/index.js +1033 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1012 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
// src/action-bus.ts
|
|
2
|
+
var listenersByAction = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
function emitActionEvent(event) {
|
|
4
|
+
const set = listenersByAction.get(event.action);
|
|
5
|
+
if (!set?.size) return;
|
|
6
|
+
for (const listener of set) {
|
|
7
|
+
try {
|
|
8
|
+
listener(event);
|
|
9
|
+
} catch {
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function subscribeToAction(action, listener) {
|
|
14
|
+
let set = listenersByAction.get(action);
|
|
15
|
+
if (!set) {
|
|
16
|
+
set = /* @__PURE__ */ new Set();
|
|
17
|
+
listenersByAction.set(action, set);
|
|
18
|
+
}
|
|
19
|
+
set.add(listener);
|
|
20
|
+
return () => {
|
|
21
|
+
set?.delete(listener);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
1
25
|
// src/store.ts
|
|
2
26
|
var StatoStore = class {
|
|
3
27
|
// Le state interne — jamais accessible directement
|
|
@@ -14,6 +38,8 @@ var StatoStore = class {
|
|
|
14
38
|
// Les hooks lifecycle
|
|
15
39
|
_hooks;
|
|
16
40
|
_publicStore = null;
|
|
41
|
+
_publicActions = {};
|
|
42
|
+
_initialized = false;
|
|
17
43
|
_effects = [];
|
|
18
44
|
_createMemoizedSelector(fn) {
|
|
19
45
|
let initialized = false;
|
|
@@ -156,6 +182,7 @@ var StatoStore = class {
|
|
|
156
182
|
if (!action) {
|
|
157
183
|
throw new Error(`[Stato] Action "${actionName}" introuvable`);
|
|
158
184
|
}
|
|
185
|
+
const publicAction = this._publicActions[actionName];
|
|
159
186
|
this._hooks.onAction?.(actionName, args);
|
|
160
187
|
const start = Date.now();
|
|
161
188
|
const prevState = { ...this._state };
|
|
@@ -168,10 +195,32 @@ var StatoStore = class {
|
|
|
168
195
|
});
|
|
169
196
|
try {
|
|
170
197
|
await action(stateProxy, ...args);
|
|
171
|
-
|
|
198
|
+
const duration = Date.now() - start;
|
|
199
|
+
this._hooks.onActionDone?.(actionName, duration);
|
|
172
200
|
this._hooks.onStateChange?.(prevState, { ...this._state });
|
|
201
|
+
if (publicAction) {
|
|
202
|
+
emitActionEvent({
|
|
203
|
+
action: publicAction,
|
|
204
|
+
name: actionName,
|
|
205
|
+
args,
|
|
206
|
+
store: this._publicStore,
|
|
207
|
+
status: "success",
|
|
208
|
+
duration
|
|
209
|
+
});
|
|
210
|
+
}
|
|
173
211
|
} catch (error) {
|
|
174
212
|
this._hooks.onError?.(error, actionName);
|
|
213
|
+
if (publicAction) {
|
|
214
|
+
emitActionEvent({
|
|
215
|
+
action: publicAction,
|
|
216
|
+
name: actionName,
|
|
217
|
+
args,
|
|
218
|
+
store: this._publicStore,
|
|
219
|
+
status: "error",
|
|
220
|
+
duration: Date.now() - start,
|
|
221
|
+
error
|
|
222
|
+
});
|
|
223
|
+
}
|
|
175
224
|
throw error;
|
|
176
225
|
}
|
|
177
226
|
}
|
|
@@ -190,6 +239,9 @@ var StatoStore = class {
|
|
|
190
239
|
registerCleanup(fn) {
|
|
191
240
|
this._cleanups.push(fn);
|
|
192
241
|
}
|
|
242
|
+
registerPublicAction(name, fn) {
|
|
243
|
+
this._publicActions[name] = fn;
|
|
244
|
+
}
|
|
193
245
|
hydrate(partial) {
|
|
194
246
|
this._setState(partial);
|
|
195
247
|
}
|
|
@@ -200,7 +252,10 @@ var StatoStore = class {
|
|
|
200
252
|
// ── Lifecycle — appelé par l'adaptateur Angular ────
|
|
201
253
|
init(publicStore) {
|
|
202
254
|
this._publicStore = publicStore;
|
|
203
|
-
this.
|
|
255
|
+
if (!this._initialized) {
|
|
256
|
+
this._initialized = true;
|
|
257
|
+
this._hooks.onInit?.(publicStore);
|
|
258
|
+
}
|
|
204
259
|
this._runEffects(true);
|
|
205
260
|
}
|
|
206
261
|
destroy(publicStore) {
|
|
@@ -214,6 +269,7 @@ var StatoStore = class {
|
|
|
214
269
|
}
|
|
215
270
|
this._cleanups = [];
|
|
216
271
|
this._subscribers.clear();
|
|
272
|
+
this._initialized = false;
|
|
217
273
|
}
|
|
218
274
|
};
|
|
219
275
|
function createStore(config) {
|
|
@@ -239,7 +295,9 @@ function createStore(config) {
|
|
|
239
295
|
const { actions, computed, selectors } = config;
|
|
240
296
|
if (actions) {
|
|
241
297
|
for (const name of Object.keys(actions)) {
|
|
242
|
-
|
|
298
|
+
const fn = (...args) => store.dispatch(name, ...args);
|
|
299
|
+
publicStore[name] = fn;
|
|
300
|
+
store.registerPublicAction(name, fn);
|
|
243
301
|
}
|
|
244
302
|
}
|
|
245
303
|
if (computed) {
|
|
@@ -260,9 +318,23 @@ function createStore(config) {
|
|
|
260
318
|
});
|
|
261
319
|
}
|
|
262
320
|
}
|
|
263
|
-
store.
|
|
321
|
+
store.init(publicStore);
|
|
264
322
|
return publicStore;
|
|
265
323
|
}
|
|
324
|
+
function on(sourceAction, handler) {
|
|
325
|
+
return subscribeToAction(sourceAction, (event) => {
|
|
326
|
+
try {
|
|
327
|
+
void handler(event.store, {
|
|
328
|
+
name: event.name,
|
|
329
|
+
args: event.args,
|
|
330
|
+
status: event.status,
|
|
331
|
+
duration: event.duration,
|
|
332
|
+
error: event.error
|
|
333
|
+
});
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
266
338
|
|
|
267
339
|
// src/types.ts
|
|
268
340
|
var StatoHttpError = class extends Error {
|
|
@@ -493,6 +565,941 @@ function optimistic(immediate, confirm) {
|
|
|
493
565
|
};
|
|
494
566
|
}
|
|
495
567
|
|
|
568
|
+
// src/helpers/exclusive.ts
|
|
569
|
+
function exclusive(fn) {
|
|
570
|
+
let running = false;
|
|
571
|
+
let current = null;
|
|
572
|
+
return (state, ...args) => {
|
|
573
|
+
if (running && current) return current;
|
|
574
|
+
running = true;
|
|
575
|
+
current = (async () => {
|
|
576
|
+
try {
|
|
577
|
+
await fn(state, ...args);
|
|
578
|
+
} finally {
|
|
579
|
+
running = false;
|
|
580
|
+
current = null;
|
|
581
|
+
}
|
|
582
|
+
})();
|
|
583
|
+
return current;
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// src/helpers/queued.ts
|
|
588
|
+
function queued(fn) {
|
|
589
|
+
const queue = [];
|
|
590
|
+
let processing = false;
|
|
591
|
+
const processNext = () => {
|
|
592
|
+
if (processing) return;
|
|
593
|
+
processing = true;
|
|
594
|
+
const run = async () => {
|
|
595
|
+
while (queue.length) {
|
|
596
|
+
const item = queue.shift();
|
|
597
|
+
if (!item) break;
|
|
598
|
+
try {
|
|
599
|
+
await fn(item.state, ...item.args);
|
|
600
|
+
item.resolve();
|
|
601
|
+
} catch (err) {
|
|
602
|
+
item.reject(err);
|
|
603
|
+
while (queue.length) {
|
|
604
|
+
const rest = queue.shift();
|
|
605
|
+
rest?.reject(err);
|
|
606
|
+
}
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
void run().finally(() => {
|
|
612
|
+
processing = false;
|
|
613
|
+
});
|
|
614
|
+
};
|
|
615
|
+
return (state, ...args) => {
|
|
616
|
+
return new Promise((resolve, reject) => {
|
|
617
|
+
queue.push({ state, args, resolve, reject });
|
|
618
|
+
processNext();
|
|
619
|
+
});
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/helpers/distinct-until-changed.ts
|
|
624
|
+
function distinctUntilChanged(fn, keySelector, comparator = Object.is) {
|
|
625
|
+
let initialized = false;
|
|
626
|
+
let prevKey;
|
|
627
|
+
return async (state, ...args) => {
|
|
628
|
+
const nextKey = keySelector(...args);
|
|
629
|
+
if (initialized && comparator(prevKey, nextKey)) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
initialized = true;
|
|
633
|
+
prevKey = nextKey;
|
|
634
|
+
await fn(state, ...args);
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/helpers/fork-join.ts
|
|
639
|
+
async function forkJoin(tasks, options) {
|
|
640
|
+
const controller = new AbortController();
|
|
641
|
+
const signal = options?.signal;
|
|
642
|
+
if (signal) {
|
|
643
|
+
if (signal.aborted) controller.abort();
|
|
644
|
+
else signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
645
|
+
}
|
|
646
|
+
const entries = Object.entries(tasks);
|
|
647
|
+
const results = await Promise.all(entries.map(async ([key, task]) => {
|
|
648
|
+
const value = await task({ signal: controller.signal });
|
|
649
|
+
return [key, value];
|
|
650
|
+
}));
|
|
651
|
+
return Object.fromEntries(results);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// src/helpers/race.ts
|
|
655
|
+
async function race(tasks, options) {
|
|
656
|
+
const controller = new AbortController();
|
|
657
|
+
const outer = options?.signal;
|
|
658
|
+
if (outer) {
|
|
659
|
+
if (outer.aborted) controller.abort();
|
|
660
|
+
else outer.addEventListener("abort", () => controller.abort(), { once: true });
|
|
661
|
+
}
|
|
662
|
+
const wrapped = tasks.map((task) => (async () => task({ signal: controller.signal }))());
|
|
663
|
+
try {
|
|
664
|
+
return await Promise.race(wrapped);
|
|
665
|
+
} finally {
|
|
666
|
+
controller.abort();
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// src/helpers/combine-latest.ts
|
|
671
|
+
function combineLatest() {
|
|
672
|
+
return (...deps) => {
|
|
673
|
+
return (state) => deps.map((fn) => fn(state));
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// src/helpers/combine-latest-stream.ts
|
|
678
|
+
function combineLatestStream(...sources) {
|
|
679
|
+
return {
|
|
680
|
+
subscribe(observer) {
|
|
681
|
+
const n = sources.length;
|
|
682
|
+
if (!n) {
|
|
683
|
+
observer.complete?.();
|
|
684
|
+
return { unsubscribe() {
|
|
685
|
+
} };
|
|
686
|
+
}
|
|
687
|
+
const hasValue = new Array(n).fill(false);
|
|
688
|
+
const values = new Array(n);
|
|
689
|
+
let completed = 0;
|
|
690
|
+
let closed = false;
|
|
691
|
+
const subs = [];
|
|
692
|
+
const tryEmit = () => {
|
|
693
|
+
if (closed) return;
|
|
694
|
+
if (hasValue.every(Boolean)) {
|
|
695
|
+
observer.next?.(values.slice());
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
const closeAll = () => {
|
|
699
|
+
if (closed) return;
|
|
700
|
+
closed = true;
|
|
701
|
+
for (const s of subs) {
|
|
702
|
+
try {
|
|
703
|
+
s.unsubscribe();
|
|
704
|
+
} catch {
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
sources.forEach((src, index) => {
|
|
709
|
+
const sub = src.subscribe({
|
|
710
|
+
next: (v) => {
|
|
711
|
+
if (closed) return;
|
|
712
|
+
values[index] = v;
|
|
713
|
+
hasValue[index] = true;
|
|
714
|
+
tryEmit();
|
|
715
|
+
},
|
|
716
|
+
error: (err) => {
|
|
717
|
+
if (closed) return;
|
|
718
|
+
observer.error?.(err);
|
|
719
|
+
closeAll();
|
|
720
|
+
},
|
|
721
|
+
complete: () => {
|
|
722
|
+
if (closed) return;
|
|
723
|
+
completed++;
|
|
724
|
+
if (completed >= n) {
|
|
725
|
+
observer.complete?.();
|
|
726
|
+
closeAll();
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
subs.push(sub);
|
|
731
|
+
});
|
|
732
|
+
return {
|
|
733
|
+
unsubscribe() {
|
|
734
|
+
closeAll();
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// src/helpers/entity-adapter.ts
|
|
742
|
+
function defaultSelectId(entity) {
|
|
743
|
+
return entity.id;
|
|
744
|
+
}
|
|
745
|
+
function idKey(id) {
|
|
746
|
+
return String(id);
|
|
747
|
+
}
|
|
748
|
+
function ensureSort(state, sortComparer) {
|
|
749
|
+
if (!sortComparer) return;
|
|
750
|
+
state.ids.sort((a, b) => {
|
|
751
|
+
const ea = state.entities[idKey(a)];
|
|
752
|
+
const eb = state.entities[idKey(b)];
|
|
753
|
+
if (!ea || !eb) return 0;
|
|
754
|
+
return sortComparer(ea, eb);
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
function createEntityAdapter(options = {}) {
|
|
758
|
+
const selectId = options.selectId ?? defaultSelectId;
|
|
759
|
+
const sortComparer = options.sortComparer;
|
|
760
|
+
const getInitialState = (extra) => {
|
|
761
|
+
return {
|
|
762
|
+
ids: [],
|
|
763
|
+
entities: {},
|
|
764
|
+
...extra ?? {}
|
|
765
|
+
};
|
|
766
|
+
};
|
|
767
|
+
const addOne = (entity, state) => {
|
|
768
|
+
const id = selectId(entity);
|
|
769
|
+
const key = idKey(id);
|
|
770
|
+
if (state.entities[key]) return;
|
|
771
|
+
state.ids.push(id);
|
|
772
|
+
state.entities[key] = entity;
|
|
773
|
+
ensureSort(state, sortComparer);
|
|
774
|
+
};
|
|
775
|
+
const addMany = (entities, state) => {
|
|
776
|
+
for (const entity of entities) addOne(entity, state);
|
|
777
|
+
};
|
|
778
|
+
const setAll = (entities, state) => {
|
|
779
|
+
state.ids = [];
|
|
780
|
+
state.entities = {};
|
|
781
|
+
for (const entity of entities) {
|
|
782
|
+
const id = selectId(entity);
|
|
783
|
+
state.ids.push(id);
|
|
784
|
+
state.entities[idKey(id)] = entity;
|
|
785
|
+
}
|
|
786
|
+
ensureSort(state, sortComparer);
|
|
787
|
+
};
|
|
788
|
+
const upsertOne = (entity, state) => {
|
|
789
|
+
const id = selectId(entity);
|
|
790
|
+
const key = idKey(id);
|
|
791
|
+
const exists = !!state.entities[key];
|
|
792
|
+
state.entities[key] = entity;
|
|
793
|
+
if (!exists) state.ids.push(id);
|
|
794
|
+
ensureSort(state, sortComparer);
|
|
795
|
+
};
|
|
796
|
+
const upsertMany = (entities, state) => {
|
|
797
|
+
for (const entity of entities) upsertOne(entity, state);
|
|
798
|
+
};
|
|
799
|
+
const updateOne = (update, state) => {
|
|
800
|
+
const key = idKey(update.id);
|
|
801
|
+
const current = state.entities[key];
|
|
802
|
+
if (!current) return;
|
|
803
|
+
state.entities[key] = { ...current, ...update.changes };
|
|
804
|
+
ensureSort(state, sortComparer);
|
|
805
|
+
};
|
|
806
|
+
const removeOne = (id, state) => {
|
|
807
|
+
const key = idKey(id);
|
|
808
|
+
if (!state.entities[key]) return;
|
|
809
|
+
delete state.entities[key];
|
|
810
|
+
state.ids = state.ids.filter((x) => !Object.is(x, id));
|
|
811
|
+
};
|
|
812
|
+
const removeMany = (ids, state) => {
|
|
813
|
+
const removeSet = new Set(ids.map(idKey));
|
|
814
|
+
for (const key of Object.keys(state.entities)) {
|
|
815
|
+
if (removeSet.has(key)) delete state.entities[key];
|
|
816
|
+
}
|
|
817
|
+
state.ids = state.ids.filter((id) => !removeSet.has(idKey(id)));
|
|
818
|
+
};
|
|
819
|
+
const removeAll = (state) => {
|
|
820
|
+
state.ids = [];
|
|
821
|
+
state.entities = {};
|
|
822
|
+
};
|
|
823
|
+
const getSelectors = (selectState) => {
|
|
824
|
+
const pick = (state) => selectState ? selectState(state) : state;
|
|
825
|
+
return {
|
|
826
|
+
selectIds: (state) => pick(state).ids,
|
|
827
|
+
selectEntities: (state) => pick(state).entities,
|
|
828
|
+
selectAll: (state) => {
|
|
829
|
+
const s = pick(state);
|
|
830
|
+
return s.ids.map((id) => s.entities[idKey(id)]).filter(Boolean);
|
|
831
|
+
},
|
|
832
|
+
selectTotal: (state) => pick(state).ids.length,
|
|
833
|
+
selectById: (state, id) => pick(state).entities[idKey(id)]
|
|
834
|
+
};
|
|
835
|
+
};
|
|
836
|
+
return {
|
|
837
|
+
selectId,
|
|
838
|
+
sortComparer,
|
|
839
|
+
getInitialState,
|
|
840
|
+
addOne,
|
|
841
|
+
addMany,
|
|
842
|
+
setAll,
|
|
843
|
+
upsertOne,
|
|
844
|
+
upsertMany,
|
|
845
|
+
updateOne,
|
|
846
|
+
removeOne,
|
|
847
|
+
removeMany,
|
|
848
|
+
removeAll,
|
|
849
|
+
getSelectors
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/helpers/with-entities.ts
|
|
854
|
+
function cloneEntityState(state) {
|
|
855
|
+
return {
|
|
856
|
+
ids: [...state.ids],
|
|
857
|
+
entities: { ...state.entities }
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
function withEntities(config, options) {
|
|
861
|
+
const { key, adapter, initial } = options;
|
|
862
|
+
const initialSlice = adapter.getInitialState();
|
|
863
|
+
if (initial?.length) {
|
|
864
|
+
adapter.setAll(initial, initialSlice);
|
|
865
|
+
}
|
|
866
|
+
const baseSelectors = config.selectors ?? {};
|
|
867
|
+
const baseActions = config.actions ?? {};
|
|
868
|
+
const scopedSelectors = adapter.getSelectors((s) => s[key]);
|
|
869
|
+
const selectorNames = {
|
|
870
|
+
ids: options.selectors?.ids ?? `${key}Ids`,
|
|
871
|
+
entities: options.selectors?.entities ?? `${key}Entities`,
|
|
872
|
+
all: options.selectors?.all ?? `${key}All`,
|
|
873
|
+
total: options.selectors?.total ?? `${key}Total`,
|
|
874
|
+
byId: options.selectors?.byId ?? `${key}ById`
|
|
875
|
+
};
|
|
876
|
+
const actionNames = {
|
|
877
|
+
addOne: options.actions?.addOne ?? `${key}AddOne`,
|
|
878
|
+
addMany: options.actions?.addMany ?? `${key}AddMany`,
|
|
879
|
+
setAll: options.actions?.setAll ?? `${key}SetAll`,
|
|
880
|
+
upsertOne: options.actions?.upsertOne ?? `${key}UpsertOne`,
|
|
881
|
+
upsertMany: options.actions?.upsertMany ?? `${key}UpsertMany`,
|
|
882
|
+
updateOne: options.actions?.updateOne ?? `${key}UpdateOne`,
|
|
883
|
+
removeOne: options.actions?.removeOne ?? `${key}RemoveOne`,
|
|
884
|
+
removeMany: options.actions?.removeMany ?? `${key}RemoveMany`,
|
|
885
|
+
removeAll: options.actions?.removeAll ?? `${key}RemoveAll`
|
|
886
|
+
};
|
|
887
|
+
const nextSelectors = {
|
|
888
|
+
...baseSelectors,
|
|
889
|
+
[selectorNames.ids]: (state) => scopedSelectors.selectIds(state),
|
|
890
|
+
[selectorNames.entities]: (state) => scopedSelectors.selectEntities(state),
|
|
891
|
+
[selectorNames.all]: (state) => scopedSelectors.selectAll(state),
|
|
892
|
+
[selectorNames.total]: (state) => scopedSelectors.selectTotal(state),
|
|
893
|
+
[selectorNames.byId]: (state) => (id) => scopedSelectors.selectById(state, id)
|
|
894
|
+
};
|
|
895
|
+
const nextActions = {
|
|
896
|
+
...baseActions,
|
|
897
|
+
[actionNames.addOne]: (state, entity) => {
|
|
898
|
+
const prev = state[key];
|
|
899
|
+
const next = cloneEntityState(prev);
|
|
900
|
+
adapter.addOne(entity, next);
|
|
901
|
+
state[key] = next;
|
|
902
|
+
},
|
|
903
|
+
[actionNames.addMany]: (state, entities) => {
|
|
904
|
+
const prev = state[key];
|
|
905
|
+
const next = cloneEntityState(prev);
|
|
906
|
+
adapter.addMany(entities, next);
|
|
907
|
+
state[key] = next;
|
|
908
|
+
},
|
|
909
|
+
[actionNames.setAll]: (state, entities) => {
|
|
910
|
+
const next = adapter.getInitialState();
|
|
911
|
+
adapter.setAll(entities, next);
|
|
912
|
+
state[key] = next;
|
|
913
|
+
},
|
|
914
|
+
[actionNames.upsertOne]: (state, entity) => {
|
|
915
|
+
const prev = state[key];
|
|
916
|
+
const next = cloneEntityState(prev);
|
|
917
|
+
adapter.upsertOne(entity, next);
|
|
918
|
+
state[key] = next;
|
|
919
|
+
},
|
|
920
|
+
[actionNames.upsertMany]: (state, entities) => {
|
|
921
|
+
const prev = state[key];
|
|
922
|
+
const next = cloneEntityState(prev);
|
|
923
|
+
adapter.upsertMany(entities, next);
|
|
924
|
+
state[key] = next;
|
|
925
|
+
},
|
|
926
|
+
[actionNames.updateOne]: (state, update) => {
|
|
927
|
+
const prev = state[key];
|
|
928
|
+
const next = cloneEntityState(prev);
|
|
929
|
+
adapter.updateOne(update, next);
|
|
930
|
+
state[key] = next;
|
|
931
|
+
},
|
|
932
|
+
[actionNames.removeOne]: (state, id) => {
|
|
933
|
+
const prev = state[key];
|
|
934
|
+
const next = cloneEntityState(prev);
|
|
935
|
+
adapter.removeOne(id, next);
|
|
936
|
+
state[key] = next;
|
|
937
|
+
},
|
|
938
|
+
[actionNames.removeMany]: (state, ids) => {
|
|
939
|
+
const prev = state[key];
|
|
940
|
+
const next = cloneEntityState(prev);
|
|
941
|
+
adapter.removeMany(ids, next);
|
|
942
|
+
state[key] = next;
|
|
943
|
+
},
|
|
944
|
+
[actionNames.removeAll]: (state) => {
|
|
945
|
+
state[key] = adapter.getInitialState();
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
return {
|
|
949
|
+
...config,
|
|
950
|
+
[key]: config[key] ?? initialSlice,
|
|
951
|
+
actions: nextActions,
|
|
952
|
+
selectors: nextSelectors
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// src/helpers/stream-operators.ts
|
|
957
|
+
function pipeStream(source, ...ops) {
|
|
958
|
+
return ops.reduce((acc, op) => op(acc), source);
|
|
959
|
+
}
|
|
960
|
+
function isObservable(value) {
|
|
961
|
+
return !!value && typeof value === "object" && typeof value.subscribe === "function";
|
|
962
|
+
}
|
|
963
|
+
function toObservable(value) {
|
|
964
|
+
if (isObservable(value)) return value;
|
|
965
|
+
return {
|
|
966
|
+
subscribe(observer) {
|
|
967
|
+
let closed = false;
|
|
968
|
+
Promise.resolve(value).then((resolved) => {
|
|
969
|
+
if (closed) return;
|
|
970
|
+
observer.next?.(resolved);
|
|
971
|
+
observer.complete?.();
|
|
972
|
+
}).catch((error) => {
|
|
973
|
+
if (closed) return;
|
|
974
|
+
observer.error?.(error);
|
|
975
|
+
});
|
|
976
|
+
return { unsubscribe: () => {
|
|
977
|
+
closed = true;
|
|
978
|
+
} };
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
function mapStream(mapFn) {
|
|
983
|
+
return (source) => ({
|
|
984
|
+
subscribe(observer) {
|
|
985
|
+
return source.subscribe({
|
|
986
|
+
next: (value) => observer.next?.(mapFn(value)),
|
|
987
|
+
error: (error) => observer.error?.(error),
|
|
988
|
+
complete: () => observer.complete?.()
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
function filterStream(predicate) {
|
|
994
|
+
return (source) => ({
|
|
995
|
+
subscribe(observer) {
|
|
996
|
+
return source.subscribe({
|
|
997
|
+
next: (value) => {
|
|
998
|
+
if (predicate(value)) observer.next?.(value);
|
|
999
|
+
},
|
|
1000
|
+
error: (error) => observer.error?.(error),
|
|
1001
|
+
complete: () => observer.complete?.()
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
function closeSubs(subs) {
|
|
1007
|
+
for (const sub of subs) {
|
|
1008
|
+
try {
|
|
1009
|
+
sub.unsubscribe();
|
|
1010
|
+
} catch {
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
function switchMapStream(mapper) {
|
|
1015
|
+
return (source) => ({
|
|
1016
|
+
subscribe(observer) {
|
|
1017
|
+
let closed = false;
|
|
1018
|
+
let sourceDone = false;
|
|
1019
|
+
let innerSub = null;
|
|
1020
|
+
let innerActive = false;
|
|
1021
|
+
let controller = null;
|
|
1022
|
+
const maybeComplete = () => {
|
|
1023
|
+
if (!closed && sourceDone && !innerActive) {
|
|
1024
|
+
closed = true;
|
|
1025
|
+
observer.complete?.();
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
const sourceSub = source.subscribe({
|
|
1029
|
+
next: (value) => {
|
|
1030
|
+
if (closed) return;
|
|
1031
|
+
controller?.abort();
|
|
1032
|
+
innerSub?.unsubscribe();
|
|
1033
|
+
controller = new AbortController();
|
|
1034
|
+
innerActive = true;
|
|
1035
|
+
innerSub = toObservable(mapper(value, { signal: controller.signal })).subscribe({
|
|
1036
|
+
next: (v) => {
|
|
1037
|
+
if (!closed) observer.next?.(v);
|
|
1038
|
+
},
|
|
1039
|
+
error: (error) => {
|
|
1040
|
+
if (closed) return;
|
|
1041
|
+
closed = true;
|
|
1042
|
+
observer.error?.(error);
|
|
1043
|
+
sourceSub.unsubscribe();
|
|
1044
|
+
innerSub?.unsubscribe();
|
|
1045
|
+
},
|
|
1046
|
+
complete: () => {
|
|
1047
|
+
innerActive = false;
|
|
1048
|
+
maybeComplete();
|
|
1049
|
+
}
|
|
1050
|
+
});
|
|
1051
|
+
},
|
|
1052
|
+
error: (error) => {
|
|
1053
|
+
if (closed) return;
|
|
1054
|
+
closed = true;
|
|
1055
|
+
observer.error?.(error);
|
|
1056
|
+
controller?.abort();
|
|
1057
|
+
innerSub?.unsubscribe();
|
|
1058
|
+
},
|
|
1059
|
+
complete: () => {
|
|
1060
|
+
sourceDone = true;
|
|
1061
|
+
maybeComplete();
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
return {
|
|
1065
|
+
unsubscribe() {
|
|
1066
|
+
if (closed) return;
|
|
1067
|
+
closed = true;
|
|
1068
|
+
controller?.abort();
|
|
1069
|
+
sourceSub.unsubscribe();
|
|
1070
|
+
innerSub?.unsubscribe();
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
function concatMapStream(mapper) {
|
|
1077
|
+
return (source) => ({
|
|
1078
|
+
subscribe(observer) {
|
|
1079
|
+
let closed = false;
|
|
1080
|
+
let sourceDone = false;
|
|
1081
|
+
const queue = [];
|
|
1082
|
+
let running = false;
|
|
1083
|
+
let currentSub = null;
|
|
1084
|
+
let currentController = null;
|
|
1085
|
+
const maybeComplete = () => {
|
|
1086
|
+
if (!closed && sourceDone && !running && queue.length === 0) {
|
|
1087
|
+
closed = true;
|
|
1088
|
+
observer.complete?.();
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
const runNext = () => {
|
|
1092
|
+
if (closed || running || queue.length === 0) {
|
|
1093
|
+
maybeComplete();
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
running = true;
|
|
1097
|
+
const value = queue.shift();
|
|
1098
|
+
currentController = new AbortController();
|
|
1099
|
+
currentSub = toObservable(mapper(value, { signal: currentController.signal })).subscribe({
|
|
1100
|
+
next: (v) => {
|
|
1101
|
+
if (!closed) observer.next?.(v);
|
|
1102
|
+
},
|
|
1103
|
+
error: (error) => {
|
|
1104
|
+
if (closed) return;
|
|
1105
|
+
closed = true;
|
|
1106
|
+
observer.error?.(error);
|
|
1107
|
+
sourceSub.unsubscribe();
|
|
1108
|
+
currentController?.abort();
|
|
1109
|
+
currentSub?.unsubscribe();
|
|
1110
|
+
queue.length = 0;
|
|
1111
|
+
},
|
|
1112
|
+
complete: () => {
|
|
1113
|
+
running = false;
|
|
1114
|
+
runNext();
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
};
|
|
1118
|
+
const sourceSub = source.subscribe({
|
|
1119
|
+
next: (value) => {
|
|
1120
|
+
if (closed) return;
|
|
1121
|
+
queue.push(value);
|
|
1122
|
+
runNext();
|
|
1123
|
+
},
|
|
1124
|
+
error: (error) => {
|
|
1125
|
+
if (closed) return;
|
|
1126
|
+
closed = true;
|
|
1127
|
+
observer.error?.(error);
|
|
1128
|
+
currentController?.abort();
|
|
1129
|
+
currentSub?.unsubscribe();
|
|
1130
|
+
queue.length = 0;
|
|
1131
|
+
},
|
|
1132
|
+
complete: () => {
|
|
1133
|
+
sourceDone = true;
|
|
1134
|
+
maybeComplete();
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
return {
|
|
1138
|
+
unsubscribe() {
|
|
1139
|
+
if (closed) return;
|
|
1140
|
+
closed = true;
|
|
1141
|
+
sourceSub.unsubscribe();
|
|
1142
|
+
currentController?.abort();
|
|
1143
|
+
currentSub?.unsubscribe();
|
|
1144
|
+
queue.length = 0;
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
function exhaustMapStream(mapper) {
|
|
1151
|
+
return (source) => ({
|
|
1152
|
+
subscribe(observer) {
|
|
1153
|
+
let closed = false;
|
|
1154
|
+
let sourceDone = false;
|
|
1155
|
+
let running = false;
|
|
1156
|
+
let currentSub = null;
|
|
1157
|
+
let controller = null;
|
|
1158
|
+
const maybeComplete = () => {
|
|
1159
|
+
if (!closed && sourceDone && !running) {
|
|
1160
|
+
closed = true;
|
|
1161
|
+
observer.complete?.();
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
const sourceSub = source.subscribe({
|
|
1165
|
+
next: (value) => {
|
|
1166
|
+
if (closed || running) return;
|
|
1167
|
+
running = true;
|
|
1168
|
+
controller = new AbortController();
|
|
1169
|
+
currentSub = toObservable(mapper(value, { signal: controller.signal })).subscribe({
|
|
1170
|
+
next: (v) => {
|
|
1171
|
+
if (!closed) observer.next?.(v);
|
|
1172
|
+
},
|
|
1173
|
+
error: (error) => {
|
|
1174
|
+
if (closed) return;
|
|
1175
|
+
closed = true;
|
|
1176
|
+
observer.error?.(error);
|
|
1177
|
+
sourceSub.unsubscribe();
|
|
1178
|
+
controller?.abort();
|
|
1179
|
+
currentSub?.unsubscribe();
|
|
1180
|
+
},
|
|
1181
|
+
complete: () => {
|
|
1182
|
+
running = false;
|
|
1183
|
+
maybeComplete();
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
},
|
|
1187
|
+
error: (error) => {
|
|
1188
|
+
if (closed) return;
|
|
1189
|
+
closed = true;
|
|
1190
|
+
observer.error?.(error);
|
|
1191
|
+
controller?.abort();
|
|
1192
|
+
currentSub?.unsubscribe();
|
|
1193
|
+
},
|
|
1194
|
+
complete: () => {
|
|
1195
|
+
sourceDone = true;
|
|
1196
|
+
maybeComplete();
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
return {
|
|
1200
|
+
unsubscribe() {
|
|
1201
|
+
if (closed) return;
|
|
1202
|
+
closed = true;
|
|
1203
|
+
sourceSub.unsubscribe();
|
|
1204
|
+
controller?.abort();
|
|
1205
|
+
currentSub?.unsubscribe();
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
function mergeMapStream(mapper, options) {
|
|
1212
|
+
const concurrency = Math.max(1, options?.concurrency ?? Number.POSITIVE_INFINITY);
|
|
1213
|
+
return (source) => ({
|
|
1214
|
+
subscribe(observer) {
|
|
1215
|
+
let closed = false;
|
|
1216
|
+
let sourceDone = false;
|
|
1217
|
+
const queue = [];
|
|
1218
|
+
const active = /* @__PURE__ */ new Set();
|
|
1219
|
+
const controllers = /* @__PURE__ */ new Set();
|
|
1220
|
+
const maybeComplete = () => {
|
|
1221
|
+
if (!closed && sourceDone && active.size === 0 && queue.length === 0) {
|
|
1222
|
+
closed = true;
|
|
1223
|
+
observer.complete?.();
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
const spawn = (value) => {
|
|
1227
|
+
const controller = new AbortController();
|
|
1228
|
+
controllers.add(controller);
|
|
1229
|
+
const sub = toObservable(mapper(value, { signal: controller.signal })).subscribe({
|
|
1230
|
+
next: (v) => {
|
|
1231
|
+
if (!closed) observer.next?.(v);
|
|
1232
|
+
},
|
|
1233
|
+
error: (error) => {
|
|
1234
|
+
if (closed) return;
|
|
1235
|
+
closed = true;
|
|
1236
|
+
observer.error?.(error);
|
|
1237
|
+
sourceSub.unsubscribe();
|
|
1238
|
+
closeSubs(Array.from(active));
|
|
1239
|
+
active.clear();
|
|
1240
|
+
for (const c of controllers) c.abort();
|
|
1241
|
+
controllers.clear();
|
|
1242
|
+
queue.length = 0;
|
|
1243
|
+
},
|
|
1244
|
+
complete: () => {
|
|
1245
|
+
active.delete(sub);
|
|
1246
|
+
controllers.delete(controller);
|
|
1247
|
+
drain();
|
|
1248
|
+
maybeComplete();
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
active.add(sub);
|
|
1252
|
+
};
|
|
1253
|
+
const drain = () => {
|
|
1254
|
+
while (!closed && active.size < concurrency && queue.length > 0) {
|
|
1255
|
+
spawn(queue.shift());
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
const sourceSub = source.subscribe({
|
|
1259
|
+
next: (value) => {
|
|
1260
|
+
if (closed) return;
|
|
1261
|
+
queue.push(value);
|
|
1262
|
+
drain();
|
|
1263
|
+
},
|
|
1264
|
+
error: (error) => {
|
|
1265
|
+
if (closed) return;
|
|
1266
|
+
closed = true;
|
|
1267
|
+
observer.error?.(error);
|
|
1268
|
+
closeSubs(Array.from(active));
|
|
1269
|
+
active.clear();
|
|
1270
|
+
for (const c of controllers) c.abort();
|
|
1271
|
+
controllers.clear();
|
|
1272
|
+
queue.length = 0;
|
|
1273
|
+
},
|
|
1274
|
+
complete: () => {
|
|
1275
|
+
sourceDone = true;
|
|
1276
|
+
maybeComplete();
|
|
1277
|
+
}
|
|
1278
|
+
});
|
|
1279
|
+
return {
|
|
1280
|
+
unsubscribe() {
|
|
1281
|
+
if (closed) return;
|
|
1282
|
+
closed = true;
|
|
1283
|
+
sourceSub.unsubscribe();
|
|
1284
|
+
closeSubs(Array.from(active));
|
|
1285
|
+
active.clear();
|
|
1286
|
+
for (const c of controllers) c.abort();
|
|
1287
|
+
controllers.clear();
|
|
1288
|
+
queue.length = 0;
|
|
1289
|
+
}
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
function distinctUntilChangedStream(keySelector, comparator = Object.is) {
|
|
1295
|
+
return (source) => ({
|
|
1296
|
+
subscribe(observer) {
|
|
1297
|
+
let initialized = false;
|
|
1298
|
+
let prevKey;
|
|
1299
|
+
return source.subscribe({
|
|
1300
|
+
next: (value) => {
|
|
1301
|
+
const nextKey = keySelector ? keySelector(value) : value;
|
|
1302
|
+
if (initialized && comparator(prevKey, nextKey)) return;
|
|
1303
|
+
initialized = true;
|
|
1304
|
+
prevKey = nextKey;
|
|
1305
|
+
observer.next?.(value);
|
|
1306
|
+
},
|
|
1307
|
+
error: (error) => observer.error?.(error),
|
|
1308
|
+
complete: () => observer.complete?.()
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
function debounceStream(ms) {
|
|
1314
|
+
return (source) => ({
|
|
1315
|
+
subscribe(observer) {
|
|
1316
|
+
let timer = null;
|
|
1317
|
+
let sourceDone = false;
|
|
1318
|
+
let lastValue;
|
|
1319
|
+
let hasValue = false;
|
|
1320
|
+
let closed = false;
|
|
1321
|
+
const flush = () => {
|
|
1322
|
+
if (!hasValue || closed) return;
|
|
1323
|
+
observer.next?.(lastValue);
|
|
1324
|
+
hasValue = false;
|
|
1325
|
+
lastValue = void 0;
|
|
1326
|
+
};
|
|
1327
|
+
const maybeComplete = () => {
|
|
1328
|
+
if (sourceDone && !timer && !closed) {
|
|
1329
|
+
closed = true;
|
|
1330
|
+
observer.complete?.();
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
const sub = source.subscribe({
|
|
1334
|
+
next: (value) => {
|
|
1335
|
+
if (closed) return;
|
|
1336
|
+
lastValue = value;
|
|
1337
|
+
hasValue = true;
|
|
1338
|
+
if (timer) clearTimeout(timer);
|
|
1339
|
+
timer = setTimeout(() => {
|
|
1340
|
+
timer = null;
|
|
1341
|
+
flush();
|
|
1342
|
+
maybeComplete();
|
|
1343
|
+
}, ms);
|
|
1344
|
+
},
|
|
1345
|
+
error: (error) => {
|
|
1346
|
+
if (closed) return;
|
|
1347
|
+
closed = true;
|
|
1348
|
+
if (timer) clearTimeout(timer);
|
|
1349
|
+
observer.error?.(error);
|
|
1350
|
+
},
|
|
1351
|
+
complete: () => {
|
|
1352
|
+
sourceDone = true;
|
|
1353
|
+
if (!timer) {
|
|
1354
|
+
maybeComplete();
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
return {
|
|
1359
|
+
unsubscribe() {
|
|
1360
|
+
if (closed) return;
|
|
1361
|
+
closed = true;
|
|
1362
|
+
if (timer) clearTimeout(timer);
|
|
1363
|
+
sub.unsubscribe();
|
|
1364
|
+
}
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
function throttleStream(ms) {
|
|
1370
|
+
return (source) => ({
|
|
1371
|
+
subscribe(observer) {
|
|
1372
|
+
let throttled2 = false;
|
|
1373
|
+
let timer = null;
|
|
1374
|
+
let closed = false;
|
|
1375
|
+
const sub = source.subscribe({
|
|
1376
|
+
next: (value) => {
|
|
1377
|
+
if (closed || throttled2) return;
|
|
1378
|
+
observer.next?.(value);
|
|
1379
|
+
throttled2 = true;
|
|
1380
|
+
timer = setTimeout(() => {
|
|
1381
|
+
throttled2 = false;
|
|
1382
|
+
timer = null;
|
|
1383
|
+
}, ms);
|
|
1384
|
+
},
|
|
1385
|
+
error: (error) => {
|
|
1386
|
+
if (closed) return;
|
|
1387
|
+
closed = true;
|
|
1388
|
+
if (timer) clearTimeout(timer);
|
|
1389
|
+
observer.error?.(error);
|
|
1390
|
+
},
|
|
1391
|
+
complete: () => {
|
|
1392
|
+
if (closed) return;
|
|
1393
|
+
closed = true;
|
|
1394
|
+
if (timer) clearTimeout(timer);
|
|
1395
|
+
observer.complete?.();
|
|
1396
|
+
}
|
|
1397
|
+
});
|
|
1398
|
+
return {
|
|
1399
|
+
unsubscribe() {
|
|
1400
|
+
if (closed) return;
|
|
1401
|
+
closed = true;
|
|
1402
|
+
if (timer) clearTimeout(timer);
|
|
1403
|
+
sub.unsubscribe();
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
function catchErrorStream(handler) {
|
|
1410
|
+
return (source) => ({
|
|
1411
|
+
subscribe(observer) {
|
|
1412
|
+
let closed = false;
|
|
1413
|
+
let fallbackSub = null;
|
|
1414
|
+
const sourceSub = source.subscribe({
|
|
1415
|
+
next: (value) => {
|
|
1416
|
+
if (!closed) observer.next?.(value);
|
|
1417
|
+
},
|
|
1418
|
+
error: (error) => {
|
|
1419
|
+
if (closed) return;
|
|
1420
|
+
fallbackSub = toObservable(handler(error)).subscribe({
|
|
1421
|
+
next: (value) => {
|
|
1422
|
+
if (!closed) observer.next?.(value);
|
|
1423
|
+
},
|
|
1424
|
+
error: (innerErr) => {
|
|
1425
|
+
if (closed) return;
|
|
1426
|
+
closed = true;
|
|
1427
|
+
observer.error?.(innerErr);
|
|
1428
|
+
},
|
|
1429
|
+
complete: () => {
|
|
1430
|
+
if (closed) return;
|
|
1431
|
+
closed = true;
|
|
1432
|
+
observer.complete?.();
|
|
1433
|
+
}
|
|
1434
|
+
});
|
|
1435
|
+
},
|
|
1436
|
+
complete: () => {
|
|
1437
|
+
if (closed) return;
|
|
1438
|
+
closed = true;
|
|
1439
|
+
observer.complete?.();
|
|
1440
|
+
}
|
|
1441
|
+
});
|
|
1442
|
+
return {
|
|
1443
|
+
unsubscribe() {
|
|
1444
|
+
if (closed) return;
|
|
1445
|
+
closed = true;
|
|
1446
|
+
sourceSub.unsubscribe();
|
|
1447
|
+
fallbackSub?.unsubscribe();
|
|
1448
|
+
}
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
function retryStream(options = {}) {
|
|
1454
|
+
const attempts = Math.max(1, options.attempts ?? 3);
|
|
1455
|
+
const delay = Math.max(0, options.delay ?? 0);
|
|
1456
|
+
const backoff = options.backoff ?? "fixed";
|
|
1457
|
+
return (source) => ({
|
|
1458
|
+
subscribe(observer) {
|
|
1459
|
+
let closed = false;
|
|
1460
|
+
let attempt = 0;
|
|
1461
|
+
let activeSub = null;
|
|
1462
|
+
let retryTimer = null;
|
|
1463
|
+
const subscribeOnce = () => {
|
|
1464
|
+
if (closed) return;
|
|
1465
|
+
attempt++;
|
|
1466
|
+
activeSub = source.subscribe({
|
|
1467
|
+
next: (value) => {
|
|
1468
|
+
if (!closed) observer.next?.(value);
|
|
1469
|
+
},
|
|
1470
|
+
complete: () => {
|
|
1471
|
+
if (closed) return;
|
|
1472
|
+
closed = true;
|
|
1473
|
+
observer.complete?.();
|
|
1474
|
+
},
|
|
1475
|
+
error: (error) => {
|
|
1476
|
+
if (closed) return;
|
|
1477
|
+
if (attempt >= attempts) {
|
|
1478
|
+
closed = true;
|
|
1479
|
+
observer.error?.(error);
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
const wait = backoff === "exponential" ? delay * Math.pow(2, attempt - 1) : delay;
|
|
1483
|
+
retryTimer = setTimeout(() => {
|
|
1484
|
+
retryTimer = null;
|
|
1485
|
+
subscribeOnce();
|
|
1486
|
+
}, wait);
|
|
1487
|
+
}
|
|
1488
|
+
});
|
|
1489
|
+
};
|
|
1490
|
+
subscribeOnce();
|
|
1491
|
+
return {
|
|
1492
|
+
unsubscribe() {
|
|
1493
|
+
if (closed) return;
|
|
1494
|
+
closed = true;
|
|
1495
|
+
if (retryTimer) clearTimeout(retryTimer);
|
|
1496
|
+
activeSub?.unsubscribe();
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
|
|
496
1503
|
// src/helpers/with-persist.ts
|
|
497
1504
|
function resolveStorage(custom) {
|
|
498
1505
|
if (custom) return custom;
|
|
@@ -658,6 +1665,6 @@ function connectDevTools(store, storeName) {
|
|
|
658
1665
|
};
|
|
659
1666
|
}
|
|
660
1667
|
|
|
661
|
-
export { StatoHttp, StatoHttpError, abortable, configureHttp, connectDevTools, createDevTools, createHttp, createStore, debounced, devTools, fromStream, http, optimistic, retryable, throttled, withPersist };
|
|
1668
|
+
export { StatoHttp, StatoHttpError, abortable, catchErrorStream, combineLatest, combineLatestStream, concatMapStream, configureHttp, connectDevTools, createDevTools, createEntityAdapter, createHttp, createStore, debounceStream, debounced, devTools, distinctUntilChanged, distinctUntilChangedStream, exclusive, exhaustMapStream, filterStream, forkJoin, fromStream, http, mapStream, mergeMapStream, on, optimistic, pipeStream, queued, race, retryStream, retryable, switchMapStream, throttleStream, throttled, withEntities, withPersist };
|
|
662
1669
|
//# sourceMappingURL=index.mjs.map
|
|
663
1670
|
//# sourceMappingURL=index.mjs.map
|