@montra-interactive/deepstate 0.3.4 → 0.4.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 +15 -1
- package/dist/deepstate.d.ts +16 -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 +54 -12
- package/package.json +1 -1
- package/src/deepstate.ts +102 -10
- package/src/index.ts +1 -1
package/README.md
CHANGED
|
@@ -191,7 +191,7 @@ const store = state({
|
|
|
191
191
|
| `"deep"` | JSON comparison: `JSON.stringify(a) === JSON.stringify(b)` |
|
|
192
192
|
| `(a, b) => boolean` | Custom comparator function |
|
|
193
193
|
|
|
194
|
-
### `nullable(value)` - Nullable Objects
|
|
194
|
+
### `nullable(value, options?)` - Nullable Objects
|
|
195
195
|
|
|
196
196
|
For properties that can be `null` or an object:
|
|
197
197
|
|
|
@@ -204,6 +204,9 @@ const store = state({
|
|
|
204
204
|
|
|
205
205
|
// Start as object, can become null
|
|
206
206
|
profile: nullable({ bio: "Hello", avatar: "url" }),
|
|
207
|
+
|
|
208
|
+
// With distinct option to control emission deduplication
|
|
209
|
+
settings: nullable({ theme: "dark" }, { distinct: "shallow" }),
|
|
207
210
|
});
|
|
208
211
|
|
|
209
212
|
// Deep subscription works even when null!
|
|
@@ -217,6 +220,17 @@ store.user.name.set("Bob"); // Update nested
|
|
|
217
220
|
store.user.set(null); // Back to null
|
|
218
221
|
```
|
|
219
222
|
|
|
223
|
+
**Distinct Options:**
|
|
224
|
+
|
|
225
|
+
| Value | Description |
|
|
226
|
+
|-------|-------------|
|
|
227
|
+
| `false` | No deduplication (always emits on set) |
|
|
228
|
+
| `"shallow"` | Shallow key-by-key `===` comparison |
|
|
229
|
+
| `"deep"` | JSON comparison: `JSON.stringify(a) === JSON.stringify(b)` (same as default) |
|
|
230
|
+
| `(a, b) => boolean` | Custom comparator; `a`/`b` may be `null` |
|
|
231
|
+
|
|
232
|
+
Without options, nullable nodes use `JSON.stringify` deduplication by default.
|
|
233
|
+
|
|
220
234
|
### `select(...observables)` - Combine Observables
|
|
221
235
|
|
|
222
236
|
```ts
|
package/dist/deepstate.d.ts
CHANGED
|
@@ -170,6 +170,19 @@ export interface StateOptions {
|
|
|
170
170
|
name?: string;
|
|
171
171
|
}
|
|
172
172
|
export declare function state<T extends object>(initialState: T, options?: StateOptions): RxState<T>;
|
|
173
|
+
/** Comparison mode for nullable object distinct checking */
|
|
174
|
+
export type NullableDistinct<T> = false | 'shallow' | 'deep' | ((a: T | null, b: T | null) => boolean);
|
|
175
|
+
/** Options for the nullable() helper */
|
|
176
|
+
export interface NullableOptions<T> {
|
|
177
|
+
/**
|
|
178
|
+
* How to compare nullable object values to prevent duplicate emissions.
|
|
179
|
+
* - false: No deduplication (always emits on set)
|
|
180
|
+
* - 'shallow': Shallow key-by-key === comparison
|
|
181
|
+
* - 'deep': JSON.stringify comparison (default behavior without options)
|
|
182
|
+
* - function: Custom comparator (a, b) => boolean where a/b may be null
|
|
183
|
+
*/
|
|
184
|
+
distinct?: NullableDistinct<T>;
|
|
185
|
+
}
|
|
173
186
|
/**
|
|
174
187
|
* Marks a value as nullable, allowing it to transition between null and object.
|
|
175
188
|
* Use this when you want to start with an object value but later set it to null.
|
|
@@ -180,6 +193,8 @@ export declare function state<T extends object>(initialState: T, options?: State
|
|
|
180
193
|
* user: nullable({ name: "Alice", age: 30 }),
|
|
181
194
|
* // Can start with null and later be set to object
|
|
182
195
|
* profile: nullable<{ bio: string }>(null),
|
|
196
|
+
* // With distinct option to control emission deduplication
|
|
197
|
+
* settings: nullable({ theme: 'dark', lang: 'en' }, { distinct: 'shallow' }),
|
|
183
198
|
* });
|
|
184
199
|
*
|
|
185
200
|
* // Use ?. on the nullable property, then access children directly
|
|
@@ -187,7 +202,7 @@ export declare function state<T extends object>(initialState: T, options?: State
|
|
|
187
202
|
* store.user?.set({ name: "Bob", age: 25 }); // Works!
|
|
188
203
|
* store.user?.name.set("Charlie"); // After ?. on user, children are directly accessible
|
|
189
204
|
*/
|
|
190
|
-
export declare function nullable<T extends object>(value: T | null): T | null;
|
|
205
|
+
export declare function nullable<T extends object>(value: T | null, options?: NullableOptions<T>): T | null;
|
|
191
206
|
/** Comparison mode for array distinct checking */
|
|
192
207
|
export type ArrayDistinct<T> = false | 'shallow' | 'deep' | ((a: T[], b: T[]) => boolean);
|
|
193
208
|
/** Options for the array() helper */
|
package/dist/deepstate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deepstate.d.ts","sourceRoot":"","sources":["../src/deepstate.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"deepstate.d.ts","sourceRoot":"","sources":["../src/deepstate.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
* - select() - Combine multiple observables
|
|
12
12
|
* - selectFromEach() - Select from each array item with precise change detection
|
|
13
13
|
*/
|
|
14
|
-
export { state, nullable, array, type RxState, type Draft, type StateOptions, type ArrayOptions, type ArrayDistinct } from "./deepstate";
|
|
14
|
+
export { state, nullable, array, type RxState, type Draft, type StateOptions, type ArrayOptions, type ArrayDistinct, type NullableOptions, type NullableDistinct } from "./deepstate";
|
|
15
15
|
export { select, selectFromEach } from "./helpers";
|
|
16
16
|
//# 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;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACtL,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -238,7 +238,7 @@ function createArrayNode(value, comparator) {
|
|
|
238
238
|
};
|
|
239
239
|
}
|
|
240
240
|
var NULLABLE_NODE = Symbol("nullableNode");
|
|
241
|
-
function createNullableObjectNode(initialValue) {
|
|
241
|
+
function createNullableObjectNode(initialValue, comparator) {
|
|
242
242
|
const subject$ = new BehaviorSubject(initialValue);
|
|
243
243
|
let children = null;
|
|
244
244
|
const pendingChildren = new Map;
|
|
@@ -264,7 +264,7 @@ function createNullableObjectNode(initialValue) {
|
|
|
264
264
|
}
|
|
265
265
|
return result;
|
|
266
266
|
};
|
|
267
|
-
const $ = combineLatest2([subject$, lock$]).pipe(filter(([_, unlocked]) => unlocked), map2(([value, _]) => {
|
|
267
|
+
const baseLocked$ = combineLatest2([subject$, lock$]).pipe(filter(([_, unlocked]) => unlocked), map2(([value, _]) => {
|
|
268
268
|
if (value === null || value === undefined || !children) {
|
|
269
269
|
return value;
|
|
270
270
|
}
|
|
@@ -273,13 +273,8 @@ function createNullableObjectNode(initialValue) {
|
|
|
273
273
|
result[key] = child.get();
|
|
274
274
|
}
|
|
275
275
|
return result;
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
return a === b;
|
|
279
|
-
if (b === null || b === undefined)
|
|
280
|
-
return false;
|
|
281
|
-
return JSON.stringify(a) === JSON.stringify(b);
|
|
282
|
-
}), shareReplay(1));
|
|
276
|
+
}));
|
|
277
|
+
const $ = (comparator === undefined ? baseLocked$.pipe(distinctUntilChanged2(nullableDeepComparator)) : comparator ? baseLocked$.pipe(distinctUntilChanged2(comparator)) : baseLocked$).pipe(shareReplay(1));
|
|
283
278
|
$.subscribe();
|
|
284
279
|
const nodeState = { children };
|
|
285
280
|
const updateChildrenRef = () => {
|
|
@@ -569,8 +564,11 @@ function findCircularReference(obj, currentPath = "root", seen = new Map) {
|
|
|
569
564
|
}
|
|
570
565
|
function createNodeForValue(value, maybeNullable = false) {
|
|
571
566
|
if (isNullableMarked(value)) {
|
|
567
|
+
const options = getNullableMarkerOptions(value);
|
|
568
|
+
const comparator = getNullableComparator(options);
|
|
572
569
|
delete value[NULLABLE_MARKER];
|
|
573
|
-
|
|
570
|
+
const initialValue = Object.keys(value).length === 0 ? null : value;
|
|
571
|
+
return createNullableObjectNode(initialValue, comparator);
|
|
574
572
|
}
|
|
575
573
|
if (value === null || value === undefined) {
|
|
576
574
|
if (maybeNullable) {
|
|
@@ -803,15 +801,59 @@ function state(initialState, options) {
|
|
|
803
801
|
return wrapWithProxy(node, "", debugLog);
|
|
804
802
|
}
|
|
805
803
|
var NULLABLE_MARKER = Symbol("nullable");
|
|
806
|
-
function
|
|
804
|
+
function nullableDeepComparator(a, b) {
|
|
805
|
+
if (a === null || a === undefined)
|
|
806
|
+
return a === b;
|
|
807
|
+
if (b === null || b === undefined)
|
|
808
|
+
return false;
|
|
809
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
810
|
+
}
|
|
811
|
+
function getNullableComparator(options) {
|
|
812
|
+
if (options.distinct === undefined)
|
|
813
|
+
return;
|
|
814
|
+
if (options.distinct === false)
|
|
815
|
+
return false;
|
|
816
|
+
if (options.distinct === "shallow") {
|
|
817
|
+
return (a, b) => {
|
|
818
|
+
if (a === null || a === undefined)
|
|
819
|
+
return a === b;
|
|
820
|
+
if (b === null || b === undefined)
|
|
821
|
+
return false;
|
|
822
|
+
if (typeof a !== "object" || typeof b !== "object")
|
|
823
|
+
return a === b;
|
|
824
|
+
const aKeys = Object.keys(a);
|
|
825
|
+
const bKeys = Object.keys(b);
|
|
826
|
+
if (aKeys.length !== bKeys.length)
|
|
827
|
+
return false;
|
|
828
|
+
return aKeys.every((key) => a[key] === b[key]);
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
if (options.distinct === "deep") {
|
|
832
|
+
return nullableDeepComparator;
|
|
833
|
+
}
|
|
834
|
+
const customFn = options.distinct;
|
|
835
|
+
return (a, b) => customFn(a, b);
|
|
836
|
+
}
|
|
837
|
+
function nullable(value, options = {}) {
|
|
807
838
|
if (value === null) {
|
|
839
|
+
if (Object.keys(options).length > 0) {
|
|
840
|
+
const marker = Object.create(null);
|
|
841
|
+
marker[NULLABLE_MARKER] = options;
|
|
842
|
+
return marker;
|
|
843
|
+
}
|
|
808
844
|
return null;
|
|
809
845
|
}
|
|
810
|
-
return Object.assign(value, { [NULLABLE_MARKER]:
|
|
846
|
+
return Object.assign(value, { [NULLABLE_MARKER]: options });
|
|
811
847
|
}
|
|
812
848
|
function isNullableMarked(value) {
|
|
813
849
|
return value !== null && typeof value === "object" && NULLABLE_MARKER in value;
|
|
814
850
|
}
|
|
851
|
+
function getNullableMarkerOptions(value) {
|
|
852
|
+
if (value !== null && typeof value === "object" && NULLABLE_MARKER in value) {
|
|
853
|
+
return value[NULLABLE_MARKER];
|
|
854
|
+
}
|
|
855
|
+
return {};
|
|
856
|
+
}
|
|
815
857
|
var ARRAY_MARKER = Symbol("array");
|
|
816
858
|
function array(value, options = {}) {
|
|
817
859
|
const marked = [...value];
|
package/package.json
CHANGED
package/src/deepstate.ts
CHANGED
|
@@ -567,7 +567,8 @@ interface NullableNodeCore<T> extends NodeCore<T> {
|
|
|
567
567
|
* When value is set to object: children are created lazily from the object's keys
|
|
568
568
|
*/
|
|
569
569
|
function createNullableObjectNode<T>(
|
|
570
|
-
initialValue: T
|
|
570
|
+
initialValue: T,
|
|
571
|
+
comparator?: false | ((a: T, b: T) => boolean),
|
|
571
572
|
): NullableNodeCore<T> {
|
|
572
573
|
// Subject holds the raw value (null or object)
|
|
573
574
|
const subject$ = new BehaviorSubject<T>(initialValue);
|
|
@@ -612,7 +613,7 @@ function createNullableObjectNode<T>(
|
|
|
612
613
|
};
|
|
613
614
|
|
|
614
615
|
// Observable that emits the current value, respecting lock
|
|
615
|
-
const $ = combineLatest([subject$, lock$]).pipe(
|
|
616
|
+
const baseLocked$ = combineLatest([subject$, lock$]).pipe(
|
|
616
617
|
filter(([_, unlocked]) => unlocked),
|
|
617
618
|
map(([value, _]) => {
|
|
618
619
|
if (value === null || value === undefined || !children) {
|
|
@@ -625,11 +626,15 @@ function createNullableObjectNode<T>(
|
|
|
625
626
|
}
|
|
626
627
|
return result as T;
|
|
627
628
|
}),
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
// Apply distinct comparison: use provided comparator, fall back to default, or skip entirely
|
|
632
|
+
const $ = (comparator === undefined
|
|
633
|
+
? baseLocked$.pipe(distinctUntilChanged(nullableDeepComparator))
|
|
634
|
+
: comparator
|
|
635
|
+
? baseLocked$.pipe(distinctUntilChanged(comparator))
|
|
636
|
+
: baseLocked$
|
|
637
|
+
).pipe(
|
|
633
638
|
shareReplay(1)
|
|
634
639
|
);
|
|
635
640
|
$.subscribe(); // Keep hot
|
|
@@ -1082,9 +1087,16 @@ function findCircularReference(
|
|
|
1082
1087
|
function createNodeForValue<T>(value: T, maybeNullable: boolean = false): NodeCore<T> {
|
|
1083
1088
|
// Check for nullable marker (from nullable() helper)
|
|
1084
1089
|
if (isNullableMarked(value)) {
|
|
1090
|
+
const options = getNullableMarkerOptions(value);
|
|
1091
|
+
const comparator = getNullableComparator(options);
|
|
1085
1092
|
// Remove the marker before creating the node
|
|
1086
1093
|
delete (value as Record<symbol, unknown>)[NULLABLE_MARKER];
|
|
1087
|
-
|
|
1094
|
+
// If the marked value was a sentinel for nullable(null, options), it has no
|
|
1095
|
+
// own enumerable keys after stripping the marker — pass null as initial value.
|
|
1096
|
+
const initialValue = Object.keys(value as object).length === 0
|
|
1097
|
+
? null as unknown as T
|
|
1098
|
+
: value;
|
|
1099
|
+
return createNullableObjectNode(initialValue, comparator) as NodeCore<T>;
|
|
1088
1100
|
}
|
|
1089
1101
|
|
|
1090
1102
|
if (value === null || value === undefined) {
|
|
@@ -1409,6 +1421,64 @@ export function state<T extends object>(initialState: T, options?: StateOptions)
|
|
|
1409
1421
|
// Symbol to mark a value as nullable
|
|
1410
1422
|
const NULLABLE_MARKER = Symbol("nullable");
|
|
1411
1423
|
|
|
1424
|
+
/** Comparison mode for nullable object distinct checking */
|
|
1425
|
+
export type NullableDistinct<T> =
|
|
1426
|
+
| false // No deduplication (always emits on set)
|
|
1427
|
+
| 'shallow' // Shallow key-by-key === comparison
|
|
1428
|
+
| 'deep' // JSON.stringify comparison (same as default behavior without options)
|
|
1429
|
+
| ((a: T | null, b: T | null) => boolean); // Custom comparator
|
|
1430
|
+
|
|
1431
|
+
/** Options for the nullable() helper */
|
|
1432
|
+
export interface NullableOptions<T> {
|
|
1433
|
+
/**
|
|
1434
|
+
* How to compare nullable object values to prevent duplicate emissions.
|
|
1435
|
+
* - false: No deduplication (always emits on set)
|
|
1436
|
+
* - 'shallow': Shallow key-by-key === comparison
|
|
1437
|
+
* - 'deep': JSON.stringify comparison (default behavior without options)
|
|
1438
|
+
* - function: Custom comparator (a, b) => boolean where a/b may be null
|
|
1439
|
+
*/
|
|
1440
|
+
distinct?: NullableDistinct<T>;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
// Null-safe JSON.stringify comparator, shared between default behavior and distinct: 'deep'
|
|
1444
|
+
function nullableDeepComparator<T>(a: T, b: T): boolean {
|
|
1445
|
+
if (a === null || a === undefined) return a === b;
|
|
1446
|
+
if (b === null || b === undefined) return false;
|
|
1447
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
// Get the distinct comparator function from nullable options
|
|
1451
|
+
// Returns undefined to use default (JSON.stringify), false to disable, or a function for custom comparison
|
|
1452
|
+
function getNullableComparator<T>(options: NullableOptions<T>): false | ((a: T, b: T) => boolean) | undefined {
|
|
1453
|
+
if (options.distinct === undefined) return undefined;
|
|
1454
|
+
if (options.distinct === false) return false;
|
|
1455
|
+
|
|
1456
|
+
if (options.distinct === 'shallow') {
|
|
1457
|
+
return (a, b) => {
|
|
1458
|
+
if (a === null || a === undefined) return a === b;
|
|
1459
|
+
if (b === null || b === undefined) return false;
|
|
1460
|
+
if (typeof a !== 'object' || typeof b !== 'object') return a === b;
|
|
1461
|
+
const aKeys = Object.keys(a);
|
|
1462
|
+
const bKeys = Object.keys(b);
|
|
1463
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
1464
|
+
return aKeys.every(key =>
|
|
1465
|
+
(a as Record<string, unknown>)[key] === (b as Record<string, unknown>)[key]
|
|
1466
|
+
);
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
if (options.distinct === 'deep') {
|
|
1471
|
+
return nullableDeepComparator;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
// Custom function — wrap to match the (a: T, b: T) => boolean signature
|
|
1475
|
+
const customFn = options.distinct;
|
|
1476
|
+
return (a, b) => customFn(a as T | null, b as T | null);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
interface MarkedNullable<T> {
|
|
1480
|
+
[NULLABLE_MARKER]: NullableOptions<T>;
|
|
1481
|
+
}
|
|
1412
1482
|
|
|
1413
1483
|
/**
|
|
1414
1484
|
* Marks a value as nullable, allowing it to transition between null and object.
|
|
@@ -1420,6 +1490,8 @@ const NULLABLE_MARKER = Symbol("nullable");
|
|
|
1420
1490
|
* user: nullable({ name: "Alice", age: 30 }),
|
|
1421
1491
|
* // Can start with null and later be set to object
|
|
1422
1492
|
* profile: nullable<{ bio: string }>(null),
|
|
1493
|
+
* // With distinct option to control emission deduplication
|
|
1494
|
+
* settings: nullable({ theme: 'dark', lang: 'en' }, { distinct: 'shallow' }),
|
|
1423
1495
|
* });
|
|
1424
1496
|
*
|
|
1425
1497
|
* // Use ?. on the nullable property, then access children directly
|
|
@@ -1427,12 +1499,24 @@ const NULLABLE_MARKER = Symbol("nullable");
|
|
|
1427
1499
|
* store.user?.set({ name: "Bob", age: 25 }); // Works!
|
|
1428
1500
|
* store.user?.name.set("Charlie"); // After ?. on user, children are directly accessible
|
|
1429
1501
|
*/
|
|
1430
|
-
export function nullable<T extends object>(value: T | null): T | null {
|
|
1502
|
+
export function nullable<T extends object>(value: T | null, options: NullableOptions<T> = {}): T | null {
|
|
1431
1503
|
if (value === null) {
|
|
1504
|
+
// For null values, we can't attach the marker to the value itself.
|
|
1505
|
+
// We store options on a sentinel object that createNodeForValue will check.
|
|
1506
|
+
if (Object.keys(options).length > 0) {
|
|
1507
|
+
// Store options in a module-level map keyed by a unique object
|
|
1508
|
+
// The caller will pass this null through state(), and createNodeForValue
|
|
1509
|
+
// will receive maybeNullable=true for null children of nullable parents.
|
|
1510
|
+
// For top-level nullable(null, options), we need a different approach:
|
|
1511
|
+
// return a special marker object that looks null-ish but carries options.
|
|
1512
|
+
const marker = Object.create(null) as MarkedNullable<T>;
|
|
1513
|
+
marker[NULLABLE_MARKER] = options;
|
|
1514
|
+
return marker as unknown as T | null;
|
|
1515
|
+
}
|
|
1432
1516
|
return null;
|
|
1433
1517
|
}
|
|
1434
1518
|
// Mark the object so createNodeForValue knows to use NullableNodeCore
|
|
1435
|
-
return Object.assign(value, { [NULLABLE_MARKER]:
|
|
1519
|
+
return Object.assign(value, { [NULLABLE_MARKER]: options }) as T | null;
|
|
1436
1520
|
}
|
|
1437
1521
|
|
|
1438
1522
|
// Check if a value was marked as nullable
|
|
@@ -1440,6 +1524,14 @@ function isNullableMarked<T>(value: T): boolean {
|
|
|
1440
1524
|
return value !== null && typeof value === "object" && NULLABLE_MARKER in value;
|
|
1441
1525
|
}
|
|
1442
1526
|
|
|
1527
|
+
// Extract options from a nullable-marked value
|
|
1528
|
+
function getNullableMarkerOptions<T>(value: T): NullableOptions<T> {
|
|
1529
|
+
if (value !== null && typeof value === "object" && NULLABLE_MARKER in value) {
|
|
1530
|
+
return (value as unknown as MarkedNullable<T>)[NULLABLE_MARKER];
|
|
1531
|
+
}
|
|
1532
|
+
return {};
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1443
1535
|
// Symbol to mark an array with distinct options
|
|
1444
1536
|
const ARRAY_MARKER = Symbol("array");
|
|
1445
1537
|
|
package/src/index.ts
CHANGED
|
@@ -12,5 +12,5 @@
|
|
|
12
12
|
* - selectFromEach() - Select from each array item with precise change detection
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
export { state, nullable, array, type RxState, type Draft, type StateOptions, type ArrayOptions, type ArrayDistinct } from "./deepstate";
|
|
15
|
+
export { state, nullable, array, type RxState, type Draft, type StateOptions, type ArrayOptions, type ArrayDistinct, type NullableOptions, type NullableDistinct } from "./deepstate";
|
|
16
16
|
export { select, selectFromEach } from "./helpers";
|