@montra-interactive/deepstate 0.1.0 → 0.1.1
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/dist/deepstate.d.ts +7 -1
- package/dist/deepstate.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -13
- package/package.json +1 -1
- package/src/deepstate.ts +84 -13
- package/src/index.ts +1 -1
package/dist/deepstate.d.ts
CHANGED
|
@@ -166,7 +166,13 @@ type RxNullableChildObject<T extends object> = Observable<DeepReadonly<T> | unde
|
|
|
166
166
|
};
|
|
167
167
|
type RxNodeFor<T> = IsNullableObject<T> extends true ? RxNullable<T> : [T] extends [Primitive] ? RxLeaf<T> : [T] extends [Array<infer U>] ? RxArray<U> : [T] extends [object] ? RxObject<T> : RxLeaf<T>;
|
|
168
168
|
export type RxState<T extends object> = RxObject<T>;
|
|
169
|
-
export
|
|
169
|
+
export interface StateOptions {
|
|
170
|
+
/** Enable debug logging for this store */
|
|
171
|
+
debug?: boolean;
|
|
172
|
+
/** Optional name for this store (used in debug logs) */
|
|
173
|
+
name?: string;
|
|
174
|
+
}
|
|
175
|
+
export declare function state<T extends object>(initialState: T, options?: StateOptions): RxState<T>;
|
|
170
176
|
/**
|
|
171
177
|
* Marks a value as nullable, allowing it to transition between null and object.
|
|
172
178
|
* Use this when you want to start with an object value but later set it to null.
|
package/dist/deepstate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deepstate.d.ts","sourceRoot":"","sources":["../src/deepstate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAmB,UAAU,EAAqB,YAAY,EAAE,MAAM,MAAM,CAAC;AAapF,eAAO,IAAI,iBAAiB,QAAI,CAAC;AACjC,wBAAgB,sBAAsB,SAErC;
|
|
1
|
+
{"version":3,"file":"deepstate.d.ts","sourceRoot":"","sources":["../src/deepstate.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
* - select() - Combine multiple observables
|
|
11
11
|
* - selectFromEach() - Select from each array item with precise change detection
|
|
12
12
|
*/
|
|
13
|
-
export { state, nullable, type RxState, type Draft } from "./deepstate";
|
|
13
|
+
export { state, nullable, type RxState, type Draft, type StateOptions } from "./deepstate";
|
|
14
14
|
export { select, selectFromEach } from "./helpers";
|
|
15
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -53,6 +53,29 @@ import {
|
|
|
53
53
|
filter
|
|
54
54
|
} from "rxjs/operators";
|
|
55
55
|
var distinctCallCount = 0;
|
|
56
|
+
function createDebugLog(ctx) {
|
|
57
|
+
return (path, action, oldValue, newValue) => {
|
|
58
|
+
if (!ctx.enabled)
|
|
59
|
+
return;
|
|
60
|
+
const prefix = ctx.storeName ? `[deepstate:${ctx.storeName}]` : "[deepstate]";
|
|
61
|
+
const formatValue = (v) => {
|
|
62
|
+
if (v === undefined)
|
|
63
|
+
return "undefined";
|
|
64
|
+
if (v === null)
|
|
65
|
+
return "null";
|
|
66
|
+
if (typeof v === "object") {
|
|
67
|
+
try {
|
|
68
|
+
const str = JSON.stringify(v);
|
|
69
|
+
return str.length > 50 ? str.slice(0, 50) + "..." : str;
|
|
70
|
+
} catch {
|
|
71
|
+
return "[circular]";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return String(v);
|
|
75
|
+
};
|
|
76
|
+
console.log(`${prefix} ${action} ${path}: ${formatValue(oldValue)} → ${formatValue(newValue)}`);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
56
79
|
function countedDistinctUntilChanged(compareFn) {
|
|
57
80
|
return distinctUntilChanged2((a, b) => {
|
|
58
81
|
distinctCallCount++;
|
|
@@ -311,6 +334,8 @@ function createNullableObjectNode(initialValue) {
|
|
|
311
334
|
}
|
|
312
335
|
}
|
|
313
336
|
subject$.next(value);
|
|
337
|
+
} else {
|
|
338
|
+
subject$.next(value);
|
|
314
339
|
}
|
|
315
340
|
},
|
|
316
341
|
getChild: (key) => {
|
|
@@ -541,7 +566,12 @@ function createNodeForValue(value, maybeNullable = false) {
|
|
|
541
566
|
}
|
|
542
567
|
return createObjectNode(value);
|
|
543
568
|
}
|
|
544
|
-
function wrapNullableWithProxy(node) {
|
|
569
|
+
function wrapNullableWithProxy(node, path = "", debugLog) {
|
|
570
|
+
const wrappedSet = (v) => {
|
|
571
|
+
const oldValue = node.get();
|
|
572
|
+
debugLog?.(path || "root", "set", oldValue, v);
|
|
573
|
+
node.set(v);
|
|
574
|
+
};
|
|
545
575
|
const update = (callback) => {
|
|
546
576
|
node.lock();
|
|
547
577
|
try {
|
|
@@ -550,7 +580,8 @@ function wrapNullableWithProxy(node) {
|
|
|
550
580
|
if (typeof prop === "string") {
|
|
551
581
|
const child = node.getChild(prop);
|
|
552
582
|
if (child) {
|
|
553
|
-
|
|
583
|
+
const childPath = path ? `${path}.${prop}` : prop;
|
|
584
|
+
return wrapWithProxy(child, childPath, debugLog);
|
|
554
585
|
}
|
|
555
586
|
}
|
|
556
587
|
return;
|
|
@@ -573,7 +604,7 @@ function wrapNullableWithProxy(node) {
|
|
|
573
604
|
if (prop === "get")
|
|
574
605
|
return node.get;
|
|
575
606
|
if (prop === "set")
|
|
576
|
-
return
|
|
607
|
+
return wrappedSet;
|
|
577
608
|
if (prop === "update")
|
|
578
609
|
return update;
|
|
579
610
|
if (prop === "subscribeOnce")
|
|
@@ -585,7 +616,8 @@ function wrapNullableWithProxy(node) {
|
|
|
585
616
|
}
|
|
586
617
|
if (typeof prop === "string") {
|
|
587
618
|
const child = node.getOrCreateChild(prop);
|
|
588
|
-
|
|
619
|
+
const childPath = path ? `${path}.${prop}` : prop;
|
|
620
|
+
return wrapWithProxy(child, childPath, debugLog);
|
|
589
621
|
}
|
|
590
622
|
if (prop in target) {
|
|
591
623
|
const val = target[prop];
|
|
@@ -614,15 +646,20 @@ function wrapNullableWithProxy(node) {
|
|
|
614
646
|
});
|
|
615
647
|
return proxy;
|
|
616
648
|
}
|
|
617
|
-
function wrapWithProxy(node) {
|
|
649
|
+
function wrapWithProxy(node, path = "", debugLog) {
|
|
618
650
|
if (isNullableNode(node)) {
|
|
619
|
-
return wrapNullableWithProxy(node);
|
|
651
|
+
return wrapNullableWithProxy(node, path, debugLog);
|
|
620
652
|
}
|
|
621
653
|
const value = node.get();
|
|
654
|
+
const wrappedSet = (v) => {
|
|
655
|
+
const oldValue = node.get();
|
|
656
|
+
debugLog?.(path || "root", "set", oldValue, v);
|
|
657
|
+
node.set(v);
|
|
658
|
+
};
|
|
622
659
|
if (value === null || typeof value !== "object") {
|
|
623
660
|
return Object.assign(node.$, {
|
|
624
661
|
get: node.get,
|
|
625
|
-
set:
|
|
662
|
+
set: wrappedSet,
|
|
626
663
|
subscribe: node.$.subscribe.bind(node.$),
|
|
627
664
|
pipe: node.$.pipe.bind(node.$),
|
|
628
665
|
subscribeOnce: node.subscribeOnce,
|
|
@@ -633,7 +670,7 @@ function wrapWithProxy(node) {
|
|
|
633
670
|
const arrayNode = node;
|
|
634
671
|
const wrapped = Object.assign(node.$, {
|
|
635
672
|
get: node.get,
|
|
636
|
-
set:
|
|
673
|
+
set: wrappedSet,
|
|
637
674
|
subscribe: node.$.subscribe.bind(node.$),
|
|
638
675
|
pipe: node.$.pipe.bind(node.$),
|
|
639
676
|
subscribeOnce: node.subscribeOnce,
|
|
@@ -641,7 +678,8 @@ function wrapWithProxy(node) {
|
|
|
641
678
|
const child = arrayNode.at(index);
|
|
642
679
|
if (!child)
|
|
643
680
|
return;
|
|
644
|
-
|
|
681
|
+
const childPath = path ? `${path}[${index}]` : `[${index}]`;
|
|
682
|
+
return wrapWithProxy(child, childPath, debugLog);
|
|
645
683
|
},
|
|
646
684
|
length: arrayNode.length$,
|
|
647
685
|
push: arrayNode.push,
|
|
@@ -674,7 +712,7 @@ function wrapWithProxy(node) {
|
|
|
674
712
|
if (prop === "get")
|
|
675
713
|
return node.get;
|
|
676
714
|
if (prop === "set")
|
|
677
|
-
return
|
|
715
|
+
return wrappedSet;
|
|
678
716
|
if (prop === "update")
|
|
679
717
|
return updateFn;
|
|
680
718
|
if (prop === "subscribeOnce")
|
|
@@ -687,7 +725,8 @@ function wrapWithProxy(node) {
|
|
|
687
725
|
if (objectNode.children && typeof prop === "string") {
|
|
688
726
|
const child = objectNode.children.get(prop);
|
|
689
727
|
if (child) {
|
|
690
|
-
|
|
728
|
+
const childPath = path ? `${path}.${prop}` : prop;
|
|
729
|
+
return wrapWithProxy(child, childPath, debugLog);
|
|
691
730
|
}
|
|
692
731
|
}
|
|
693
732
|
if (prop in target) {
|
|
@@ -728,9 +767,10 @@ function wrapWithProxy(node) {
|
|
|
728
767
|
}
|
|
729
768
|
return proxy;
|
|
730
769
|
}
|
|
731
|
-
function state(initialState) {
|
|
770
|
+
function state(initialState, options) {
|
|
771
|
+
const debugLog = options?.debug ? createDebugLog({ enabled: true, storeName: options.name }) : undefined;
|
|
732
772
|
const node = createObjectNode(initialState);
|
|
733
|
-
return wrapWithProxy(node);
|
|
773
|
+
return wrapWithProxy(node, "", debugLog);
|
|
734
774
|
}
|
|
735
775
|
var NULLABLE_MARKER = Symbol("nullable");
|
|
736
776
|
function nullable(value) {
|
package/package.json
CHANGED
package/src/deepstate.ts
CHANGED
|
@@ -29,6 +29,43 @@ export function resetDistinctCallCount() {
|
|
|
29
29
|
distinctCallCount = 0;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// =============================================================================
|
|
33
|
+
// Debug Context
|
|
34
|
+
// =============================================================================
|
|
35
|
+
|
|
36
|
+
interface DebugContext {
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
storeName?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function createDebugLog(ctx: DebugContext) {
|
|
42
|
+
return (path: string, action: string, oldValue: unknown, newValue: unknown) => {
|
|
43
|
+
if (!ctx.enabled) return;
|
|
44
|
+
|
|
45
|
+
const prefix = ctx.storeName
|
|
46
|
+
? `[deepstate:${ctx.storeName}]`
|
|
47
|
+
: '[deepstate]';
|
|
48
|
+
|
|
49
|
+
const formatValue = (v: unknown): string => {
|
|
50
|
+
if (v === undefined) return 'undefined';
|
|
51
|
+
if (v === null) return 'null';
|
|
52
|
+
if (typeof v === 'object') {
|
|
53
|
+
try {
|
|
54
|
+
const str = JSON.stringify(v);
|
|
55
|
+
return str.length > 50 ? str.slice(0, 50) + '...' : str;
|
|
56
|
+
} catch {
|
|
57
|
+
return '[circular]';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return String(v);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
console.log(`${prefix} ${action} ${path}: ${formatValue(oldValue)} → ${formatValue(newValue)}`);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type DebugLogFn = ReturnType<typeof createDebugLog>;
|
|
68
|
+
|
|
32
69
|
// Wrap distinctUntilChanged to count calls
|
|
33
70
|
function countedDistinctUntilChanged<T>(compareFn?: (a: T, b: T) => boolean) {
|
|
34
71
|
return distinctUntilChanged<T>((a, b) => {
|
|
@@ -682,6 +719,10 @@ function createNullableObjectNode<T>(
|
|
|
682
719
|
// This maintains reactivity for subscribers to those keys
|
|
683
720
|
}
|
|
684
721
|
subject$.next(value);
|
|
722
|
+
} else {
|
|
723
|
+
// Setting to a primitive value (string, number, boolean, etc.)
|
|
724
|
+
// This handles cases like `string | null` where null was the initial value
|
|
725
|
+
subject$.next(value);
|
|
685
726
|
}
|
|
686
727
|
},
|
|
687
728
|
|
|
@@ -1053,7 +1094,14 @@ function createNodeForValue<T>(value: T, maybeNullable: boolean = false): NodeCo
|
|
|
1053
1094
|
* - Creates/returns wrapped children when value is non-null
|
|
1054
1095
|
* - Provides update() for batched updates
|
|
1055
1096
|
*/
|
|
1056
|
-
function wrapNullableWithProxy<T>(node: NullableNodeCore<T
|
|
1097
|
+
function wrapNullableWithProxy<T>(node: NullableNodeCore<T>, path: string = '', debugLog?: DebugLogFn): RxNullable<T> {
|
|
1098
|
+
// Create a wrapped set function that logs
|
|
1099
|
+
const wrappedSet = (v: T) => {
|
|
1100
|
+
const oldValue = node.get();
|
|
1101
|
+
debugLog?.(path || 'root', 'set', oldValue, v);
|
|
1102
|
+
node.set(v);
|
|
1103
|
+
};
|
|
1104
|
+
|
|
1057
1105
|
// Create update function
|
|
1058
1106
|
const update = (callback: (draft: object) => void): T => {
|
|
1059
1107
|
node.lock();
|
|
@@ -1064,7 +1112,8 @@ function wrapNullableWithProxy<T>(node: NullableNodeCore<T>): RxNullable<T> {
|
|
|
1064
1112
|
if (typeof prop === "string") {
|
|
1065
1113
|
const child = node.getChild(prop);
|
|
1066
1114
|
if (child) {
|
|
1067
|
-
|
|
1115
|
+
const childPath = path ? `${path}.${prop}` : prop;
|
|
1116
|
+
return wrapWithProxy(child, childPath, debugLog);
|
|
1068
1117
|
}
|
|
1069
1118
|
}
|
|
1070
1119
|
return undefined;
|
|
@@ -1086,7 +1135,7 @@ function wrapNullableWithProxy<T>(node: NullableNodeCore<T>): RxNullable<T> {
|
|
|
1086
1135
|
|
|
1087
1136
|
// Node methods
|
|
1088
1137
|
if (prop === "get") return node.get;
|
|
1089
|
-
if (prop === "set") return
|
|
1138
|
+
if (prop === "set") return wrappedSet;
|
|
1090
1139
|
if (prop === "update") return update;
|
|
1091
1140
|
if (prop === "subscribeOnce") return node.subscribeOnce;
|
|
1092
1141
|
if (prop === NODE) return node;
|
|
@@ -1100,7 +1149,8 @@ function wrapNullableWithProxy<T>(node: NullableNodeCore<T>): RxNullable<T> {
|
|
|
1100
1149
|
// This means store.user.age.subscribe() works even when user is null
|
|
1101
1150
|
if (typeof prop === "string") {
|
|
1102
1151
|
const child = node.getOrCreateChild(prop);
|
|
1103
|
-
|
|
1152
|
+
const childPath = path ? `${path}.${prop}` : prop;
|
|
1153
|
+
return wrapWithProxy(child, childPath, debugLog);
|
|
1104
1154
|
}
|
|
1105
1155
|
|
|
1106
1156
|
// Fallback to observable properties
|
|
@@ -1138,19 +1188,26 @@ function wrapNullableWithProxy<T>(node: NullableNodeCore<T>): RxNullable<T> {
|
|
|
1138
1188
|
return proxy as unknown as RxNullable<T>;
|
|
1139
1189
|
}
|
|
1140
1190
|
|
|
1141
|
-
function wrapWithProxy<T>(node: NodeCore<T
|
|
1191
|
+
function wrapWithProxy<T>(node: NodeCore<T>, path: string = '', debugLog?: DebugLogFn): RxNodeFor<T> {
|
|
1142
1192
|
// Check for nullable node first (before checking value, since value might be null)
|
|
1143
1193
|
if (isNullableNode(node)) {
|
|
1144
|
-
return wrapNullableWithProxy(node) as RxNodeFor<T>;
|
|
1194
|
+
return wrapNullableWithProxy(node, path, debugLog) as RxNodeFor<T>;
|
|
1145
1195
|
}
|
|
1146
1196
|
|
|
1147
1197
|
const value = node.get();
|
|
1198
|
+
|
|
1199
|
+
// Create a wrapped set function that logs
|
|
1200
|
+
const wrappedSet = (v: T) => {
|
|
1201
|
+
const oldValue = node.get();
|
|
1202
|
+
debugLog?.(path || 'root', 'set', oldValue, v);
|
|
1203
|
+
node.set(v);
|
|
1204
|
+
};
|
|
1148
1205
|
|
|
1149
1206
|
// Primitive - just attach methods to observable
|
|
1150
1207
|
if (value === null || typeof value !== "object") {
|
|
1151
1208
|
return Object.assign(node.$, {
|
|
1152
1209
|
get: node.get,
|
|
1153
|
-
set:
|
|
1210
|
+
set: wrappedSet,
|
|
1154
1211
|
subscribe: node.$.subscribe.bind(node.$),
|
|
1155
1212
|
pipe: node.$.pipe.bind(node.$),
|
|
1156
1213
|
subscribeOnce: node.subscribeOnce,
|
|
@@ -1175,14 +1232,15 @@ function wrapWithProxy<T>(node: NodeCore<T>): RxNodeFor<T> {
|
|
|
1175
1232
|
// Create the wrapped result first so we can reference it in update
|
|
1176
1233
|
const wrapped = Object.assign(node.$, {
|
|
1177
1234
|
get: node.get,
|
|
1178
|
-
set:
|
|
1235
|
+
set: wrappedSet,
|
|
1179
1236
|
subscribe: node.$.subscribe.bind(node.$),
|
|
1180
1237
|
pipe: node.$.pipe.bind(node.$),
|
|
1181
1238
|
subscribeOnce: node.subscribeOnce,
|
|
1182
1239
|
at: (index: number) => {
|
|
1183
1240
|
const child = arrayNode.at(index);
|
|
1184
1241
|
if (!child) return undefined;
|
|
1185
|
-
|
|
1242
|
+
const childPath = path ? `${path}[${index}]` : `[${index}]`;
|
|
1243
|
+
return wrapWithProxy(child, childPath, debugLog);
|
|
1186
1244
|
},
|
|
1187
1245
|
length: arrayNode.length$,
|
|
1188
1246
|
push: arrayNode.push,
|
|
@@ -1223,7 +1281,7 @@ function wrapWithProxy<T>(node: NodeCore<T>): RxNodeFor<T> {
|
|
|
1223
1281
|
|
|
1224
1282
|
// Node methods
|
|
1225
1283
|
if (prop === "get") return node.get;
|
|
1226
|
-
if (prop === "set") return
|
|
1284
|
+
if (prop === "set") return wrappedSet;
|
|
1227
1285
|
if (prop === "update") return updateFn;
|
|
1228
1286
|
if (prop === "subscribeOnce") return node.subscribeOnce;
|
|
1229
1287
|
if (prop === NODE) return node;
|
|
@@ -1237,7 +1295,8 @@ function wrapWithProxy<T>(node: NodeCore<T>): RxNodeFor<T> {
|
|
|
1237
1295
|
if (objectNode.children && typeof prop === "string") {
|
|
1238
1296
|
const child = objectNode.children.get(prop);
|
|
1239
1297
|
if (child) {
|
|
1240
|
-
|
|
1298
|
+
const childPath = path ? `${path}.${prop}` : prop;
|
|
1299
|
+
return wrapWithProxy(child, childPath, debugLog);
|
|
1241
1300
|
}
|
|
1242
1301
|
}
|
|
1243
1302
|
|
|
@@ -1292,9 +1351,21 @@ function wrapWithProxy<T>(node: NodeCore<T>): RxNodeFor<T> {
|
|
|
1292
1351
|
// Public API
|
|
1293
1352
|
// =============================================================================
|
|
1294
1353
|
|
|
1295
|
-
export
|
|
1354
|
+
export interface StateOptions {
|
|
1355
|
+
/** Enable debug logging for this store */
|
|
1356
|
+
debug?: boolean;
|
|
1357
|
+
/** Optional name for this store (used in debug logs) */
|
|
1358
|
+
name?: string;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
export function state<T extends object>(initialState: T, options?: StateOptions): RxState<T> {
|
|
1362
|
+
// Create debug log function if debug is enabled
|
|
1363
|
+
const debugLog = options?.debug
|
|
1364
|
+
? createDebugLog({ enabled: true, storeName: options.name })
|
|
1365
|
+
: undefined;
|
|
1366
|
+
|
|
1296
1367
|
const node = createObjectNode(initialState);
|
|
1297
|
-
return wrapWithProxy(node as NodeCore<T
|
|
1368
|
+
return wrapWithProxy(node as NodeCore<T>, '', debugLog) as RxState<T>;
|
|
1298
1369
|
}
|
|
1299
1370
|
|
|
1300
1371
|
// Symbol to mark a value as nullable
|
package/src/index.ts
CHANGED
|
@@ -11,5 +11,5 @@
|
|
|
11
11
|
* - selectFromEach() - Select from each array item with precise change detection
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
export { state, nullable, type RxState, type Draft } from "./deepstate";
|
|
14
|
+
export { state, nullable, type RxState, type Draft, type StateOptions } from "./deepstate";
|
|
15
15
|
export { select, selectFromEach } from "./helpers";
|