@montra-interactive/deepstate 0.1.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.
@@ -0,0 +1,189 @@
1
+ /**
2
+ * deepstate v2 - Nested BehaviorSubjects Architecture
3
+ *
4
+ * Each property has its own observable that emits standalone.
5
+ * Parent notifications flow upward automatically via RxJS subscriptions.
6
+ * Siblings are never notified.
7
+ *
8
+ * Architecture:
9
+ * - Leaves (primitives): BehaviorSubject is source of truth
10
+ * - Objects: combineLatest(children) derives the observable, children are source of truth
11
+ * - Arrays: BehaviorSubject<T[]> is source of truth, children are projections
12
+ */
13
+ import { Observable, Subscription } from "rxjs";
14
+ export declare let distinctCallCount: number;
15
+ export declare function resetDistinctCallCount(): void;
16
+ type Primitive = string | number | boolean | null | undefined | symbol | bigint;
17
+ type NonNullablePart<T> = T extends null | undefined ? never : T;
18
+ type HasNull<T> = null extends T ? true : false;
19
+ type HasUndefined<T> = undefined extends T ? true : false;
20
+ type IsNullish<T> = HasNull<T> extends true ? true : HasUndefined<T>;
21
+ type NonNullPartIsObject<T> = NonNullablePart<T> extends object ? NonNullablePart<T> extends Array<unknown> ? false : true : false;
22
+ type IsNullableObject<T> = IsNullish<T> extends true ? NonNullPartIsObject<T> : false;
23
+ /**
24
+ * Deep readonly type - makes all nested properties readonly.
25
+ * Used for return types of get() and subscribe() to prevent accidental mutations.
26
+ */
27
+ export type DeepReadonly<T> = [T] extends [Primitive] ? T : [T] extends [Array<infer U>] ? ReadonlyArray<DeepReadonly<U>> : [T] extends [object] ? {
28
+ readonly [K in keyof T]: DeepReadonly<T[K]>;
29
+ } : T;
30
+ /**
31
+ * A mutable draft of state T for use in update callbacks.
32
+ */
33
+ export type Draft<T> = T;
34
+ interface NodeCore<T> {
35
+ readonly $: Observable<T>;
36
+ get(): T;
37
+ set(value: T): void;
38
+ subscribeOnce?(callback: (value: T) => void): Subscription;
39
+ }
40
+ declare const NODE: unique symbol;
41
+ type RxLeaf<T> = Observable<DeepReadonly<T>> & {
42
+ /** Get current value synchronously */
43
+ get(): DeepReadonly<T>;
44
+ /** Set value */
45
+ set(value: T): void;
46
+ /** Subscribe to a single emission, then automatically unsubscribe */
47
+ subscribeOnce(callback: (value: DeepReadonly<T>) => void): Subscription;
48
+ [NODE]: NodeCore<T>;
49
+ };
50
+ type RxObject<T extends object> = {
51
+ [K in keyof T]: RxNodeFor<T[K]>;
52
+ } & Observable<DeepReadonly<T>> & {
53
+ /** Get current value synchronously */
54
+ get(): DeepReadonly<T>;
55
+ /** Set value */
56
+ set(value: T): void;
57
+ /**
58
+ * Update multiple properties in a single emission.
59
+ * The callback receives the reactive state object - use .set() on properties to update them.
60
+ * All changes are batched into a single emission.
61
+ * @example
62
+ * store.user.update(draft => {
63
+ * draft.name.set("Bob");
64
+ * draft.age.set(31);
65
+ * });
66
+ */
67
+ update(callback: (draft: RxObject<T>) => void): DeepReadonly<T>;
68
+ /** Subscribe to a single emission, then automatically unsubscribe */
69
+ subscribeOnce(callback: (value: DeepReadonly<T>) => void): Subscription;
70
+ [NODE]: NodeCore<T>;
71
+ };
72
+ type RxArray<T> = Observable<DeepReadonly<T[]>> & {
73
+ /** Get current value synchronously */
74
+ get(): DeepReadonly<T[]>;
75
+ /** Set value */
76
+ set(value: T[]): void;
77
+ /**
78
+ * Update array in a single emission.
79
+ * The callback receives the reactive array - use .at(), .push(), .pop() etc to update.
80
+ * All changes are batched into a single emission.
81
+ * @example
82
+ * store.items.update(draft => {
83
+ * draft.at(0)?.name.set("Updated");
84
+ * draft.push({ id: 2, name: "New" });
85
+ * });
86
+ */
87
+ update(callback: (draft: RxArray<T>) => void): DeepReadonly<T[]>;
88
+ /** Subscribe to a single emission, then automatically unsubscribe */
89
+ subscribeOnce(callback: (value: DeepReadonly<T[]>) => void): Subscription;
90
+ /** Get reactive node for array element at index */
91
+ at(index: number): RxNodeFor<T> | undefined;
92
+ /** Get current length (also observable) */
93
+ length: Observable<number> & {
94
+ get(): number;
95
+ };
96
+ /** Push items and return new length */
97
+ push(...items: T[]): number;
98
+ /** Pop last item */
99
+ pop(): DeepReadonly<T> | undefined;
100
+ /** Map over current values (non-reactive, use .subscribe for reactive) */
101
+ map<U>(fn: (item: DeepReadonly<T>, index: number) => U): U[];
102
+ /** Filter current values */
103
+ filter(fn: (item: DeepReadonly<T>, index: number) => boolean): DeepReadonly<T>[];
104
+ [NODE]: NodeCore<T[]>;
105
+ };
106
+ /**
107
+ * RxNullable - For properties typed as `{ ... } | null` or `{ ... } | undefined`
108
+ *
109
+ * The node is always present at runtime, enabling deep subscription:
110
+ * - You can subscribe to `store.user.name` even when `user` is null
111
+ * - The subscription will emit `undefined` while user is null
112
+ * - Once user is set to an object, the subscription will emit the actual name value
113
+ *
114
+ * @example
115
+ * const store = state<{ user: { name: string } | null }>({ user: null });
116
+ *
117
+ * // Deep subscription works even when user is null
118
+ * store.user.name.subscribe(name => {
119
+ * console.log(name); // undefined when user is null, actual value when set
120
+ * });
121
+ *
122
+ * store.user.get(); // null
123
+ * store.user.set({ name: "Alice" }); // Now name subscription emits "Alice"
124
+ * store.user.name.get(); // "Alice"
125
+ * store.user.name.set("Bob"); // Works!
126
+ */
127
+ type RxNullable<T, TNonNull extends object = NonNullablePart<T> & object> = Observable<DeepReadonly<T>> & {
128
+ /** Get current value (may be null/undefined) */
129
+ get(): DeepReadonly<T>;
130
+ /** Set value (can be null/undefined or the full object) */
131
+ set(value: T): void;
132
+ /** Subscribe to a single emission, then automatically unsubscribe */
133
+ subscribeOnce(callback: (value: DeepReadonly<T>) => void): Subscription;
134
+ /**
135
+ * Update multiple properties in a single emission.
136
+ * @example
137
+ * store.user.update(user => {
138
+ * user.name.set("Bob");
139
+ * user.age.set(31);
140
+ * });
141
+ */
142
+ update(callback: (draft: RxObject<TNonNull>) => void): DeepReadonly<T>;
143
+ [NODE]: NodeCore<T>;
144
+ } & {
145
+ [K in keyof TNonNull]: RxNullableChild<TNonNull[K]>;
146
+ };
147
+ /**
148
+ * Type for children of a nullable object.
149
+ * Children are wrapped to handle the case where parent is null:
150
+ * - When parent is null: get() returns undefined, subscribe emits undefined
151
+ * - When parent has value: behaves like normal RxNodeFor
152
+ */
153
+ type RxNullableChild<T> = IsNullableObject<T> extends true ? RxNullable<T> : [T] extends [Primitive] ? RxLeaf<T | undefined> : [T] extends [Array<infer U>] ? RxArray<U> | RxLeaf<undefined> : [T] extends [object] ? RxNullableChildObject<T> : RxLeaf<T | undefined>;
154
+ /**
155
+ * Type for object children under a nullable parent.
156
+ * The object itself might be undefined (if parent is null), but if present
157
+ * it has all the normal object methods and children.
158
+ */
159
+ type RxNullableChildObject<T extends object> = Observable<DeepReadonly<T> | undefined> & {
160
+ get(): DeepReadonly<T> | undefined;
161
+ set(value: T): void;
162
+ subscribeOnce(callback: (value: DeepReadonly<T> | undefined) => void): Subscription;
163
+ [NODE]: NodeCore<T | undefined>;
164
+ } & {
165
+ [K in keyof T]: RxNullableChild<T[K]>;
166
+ };
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
+ export type RxState<T extends object> = RxObject<T>;
169
+ export declare function state<T extends object>(initialState: T): RxState<T>;
170
+ /**
171
+ * Marks a value as nullable, allowing it to transition between null and object.
172
+ * Use this when you want to start with an object value but later set it to null.
173
+ *
174
+ * @example
175
+ * const store = state({
176
+ * // Can start with object and later be set to null
177
+ * user: nullable({ name: "Alice", age: 30 }),
178
+ * // Can start with null and later be set to object
179
+ * profile: nullable<{ bio: string }>(null),
180
+ * });
181
+ *
182
+ * // Use ?. on the nullable property, then access children directly
183
+ * store.user?.set(null); // Works!
184
+ * store.user?.set({ name: "Bob", age: 25 }); // Works!
185
+ * store.user?.name.set("Charlie"); // After ?. on user, children are directly accessible
186
+ */
187
+ export declare function nullable<T extends object>(value: T | null): T | null;
188
+ export {};
189
+ //# sourceMappingURL=deepstate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepstate.d.ts","sourceRoot":"","sources":["../src/deepstate.ts"],"names":[],"mappings":"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"}