@thalesfp/snapstate 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,109 @@
1
+ type AsyncStatusValue = "idle" | "loading" | "ready" | "error";
2
+ interface AsyncStatus {
3
+ readonly value: AsyncStatusValue;
4
+ readonly isIdle: boolean;
5
+ readonly isLoading: boolean;
6
+ readonly isReady: boolean;
7
+ readonly isError: boolean;
8
+ }
9
+ declare function asyncStatus(value: AsyncStatusValue): AsyncStatus;
10
+ /** Async operation status and error. Tracks the lifecycle of an `api.fetch`/`api.get`/`api.post` call. */
11
+ interface OperationState {
12
+ status: AsyncStatus;
13
+ error: string | null;
14
+ }
15
+ type Listener = () => void;
16
+ type Unsubscribe = () => void;
17
+ type DotPaths<T, Prefix extends string = ""> = T extends object ? {
18
+ [K in keyof T & string]: `${Prefix}${K}` | DotPaths<T[K], `${Prefix}${K}.`>;
19
+ }[keyof T & string] : never;
20
+ type GetByPath<T, P extends string> = P extends "" ? T : P extends `${infer K}.${infer Rest}` ? K extends keyof T ? GetByPath<T[K], Rest> : never : P extends keyof T ? T[P] : never;
21
+ type ArrayPaths<T> = {
22
+ [K in keyof T & string]: T[K] extends any[] ? K : never;
23
+ }[keyof T & string];
24
+ type ObjectArrayPaths<T> = {
25
+ [K in keyof T & string]: T[K] extends (infer V)[] ? V extends Date | RegExp | Map<any, any> | Set<any> | Function | any[] ? never : V extends Record<string, any> ? K : never : never;
26
+ }[keyof T & string];
27
+ type ElementOf<A> = A extends (infer V)[] ? V : never;
28
+ type Updater<V> = V | ((prev: V) => V);
29
+ interface StoreOptions {
30
+ /** Auto-batch synchronous sets via microtask (default: true) */
31
+ autoBatch?: boolean;
32
+ }
33
+ /** Handle to a computed (derived) value. Call `get()` to read, `destroy()` to stop tracking. */
34
+ interface ComputedRef<V> {
35
+ get(): V;
36
+ destroy(): void;
37
+ }
38
+ /** Options for an HTTP request (method, body, headers). */
39
+ interface HttpRequestInit {
40
+ method?: string;
41
+ body?: unknown;
42
+ headers?: Record<string, string>;
43
+ }
44
+ /** Interface for the HTTP layer used by `api.get` and `api.post/put/patch/delete`. */
45
+ interface HttpClient {
46
+ request<R = unknown>(url: string, init?: HttpRequestInit): Promise<R>;
47
+ }
48
+ /** Options for HTTP verb methods (`api.post`, `api.put`, etc.). */
49
+ interface ApiRequestOptions<R = unknown> {
50
+ body?: unknown;
51
+ headers?: Record<string, string>;
52
+ onSuccess?: (data: R) => void;
53
+ onError?: (error: Error) => void;
54
+ }
55
+ interface Subscribable<T extends object> {
56
+ subscribe(callback: Listener): Unsubscribe;
57
+ getSnapshot(): T;
58
+ }
59
+ interface StateAccessor<T extends object> {
60
+ get(): T;
61
+ get<P extends DotPaths<T>>(path: P): GetByPath<T, P>;
62
+ set<P extends DotPaths<T>>(path: P, value: Updater<GetByPath<T, P>>): void;
63
+ batch(fn: () => void): void;
64
+ computed<V>(deps: (keyof T & string)[], fn: (state: T) => V): ComputedRef<V>;
65
+ append<P extends ArrayPaths<T>>(path: P, ...items: ElementOf<T[P]>[]): void;
66
+ prepend<P extends ArrayPaths<T>>(path: P, ...items: ElementOf<T[P]>[]): void;
67
+ insertAt<P extends ArrayPaths<T>>(path: P, index: number, ...items: ElementOf<T[P]>[]): void;
68
+ patch<P extends ObjectArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean, updates: Partial<ElementOf<T[P]>>): void;
69
+ remove<P extends ArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean): void;
70
+ removeAt<P extends ArrayPaths<T>>(path: P, index: number): void;
71
+ at<P extends ArrayPaths<T>>(path: P, index: number): ElementOf<T[P]> | undefined;
72
+ filter<P extends ArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean): ElementOf<T[P]>[];
73
+ find<P extends ArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean): ElementOf<T[P]> | undefined;
74
+ findIndexOf<P extends ArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean): number;
75
+ count<P extends ArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean): number;
76
+ }
77
+ interface ApiAccessor<K extends string> {
78
+ fetch(key: K, fn: () => Promise<void>): Promise<void>;
79
+ get<R = unknown>(key: K, url: string, onSuccess?: (data: R) => void): Promise<void>;
80
+ post<R = unknown>(key: K, url: string, options?: ApiRequestOptions<R>): Promise<void>;
81
+ put<R = unknown>(key: K, url: string, options?: ApiRequestOptions<R>): Promise<void>;
82
+ patch<R = unknown>(key: K, url: string, options?: ApiRequestOptions<R>): Promise<void>;
83
+ delete<R = unknown>(key: K, url: string, options?: ApiRequestOptions<R>): Promise<void>;
84
+ }
85
+
86
+ /** Replace the global HTTP client used by `api.get` and `api.post/put/patch/delete`. */
87
+ declare function setHttpClient(client: HttpClient): void;
88
+ /** Set default headers merged into every HTTP request. Per-request headers override defaults. */
89
+ declare function setDefaultHeaders(headers: Record<string, string>): void;
90
+ declare class SnapStore<T extends object, K extends string = string> {
91
+ private _store;
92
+ private _operations;
93
+ private _generations;
94
+ protected readonly state: StateAccessor<T>;
95
+ protected readonly api: ApiAccessor<K>;
96
+ constructor(initialState: T, options?: StoreOptions);
97
+ /** Subscribe to all state changes. Returns an unsubscribe function. */
98
+ subscribe(callback: Listener): Unsubscribe;
99
+ /** Subscribe to changes at a specific dot-separated path. */
100
+ subscribe(path: string, callback: Listener): Unsubscribe;
101
+ /** Return a snapshot of the current state. Compatible with React's `useSyncExternalStore`. */
102
+ getSnapshot: () => T;
103
+ /** Get the async status of an operation by key. Returns `idle` if never started. */
104
+ getStatus(key: K): OperationState;
105
+ /** Tear down subscriptions and cleanup. */
106
+ destroy(): void;
107
+ }
108
+
109
+ export { type AsyncStatus, type AsyncStatusValue, type ComputedRef, type HttpClient, type Listener, SnapStore, type StoreOptions, type Subscribable, type Unsubscribe, asyncStatus, setDefaultHeaders, setHttpClient };
@@ -0,0 +1,109 @@
1
+ type AsyncStatusValue = "idle" | "loading" | "ready" | "error";
2
+ interface AsyncStatus {
3
+ readonly value: AsyncStatusValue;
4
+ readonly isIdle: boolean;
5
+ readonly isLoading: boolean;
6
+ readonly isReady: boolean;
7
+ readonly isError: boolean;
8
+ }
9
+ declare function asyncStatus(value: AsyncStatusValue): AsyncStatus;
10
+ /** Async operation status and error. Tracks the lifecycle of an `api.fetch`/`api.get`/`api.post` call. */
11
+ interface OperationState {
12
+ status: AsyncStatus;
13
+ error: string | null;
14
+ }
15
+ type Listener = () => void;
16
+ type Unsubscribe = () => void;
17
+ type DotPaths<T, Prefix extends string = ""> = T extends object ? {
18
+ [K in keyof T & string]: `${Prefix}${K}` | DotPaths<T[K], `${Prefix}${K}.`>;
19
+ }[keyof T & string] : never;
20
+ type GetByPath<T, P extends string> = P extends "" ? T : P extends `${infer K}.${infer Rest}` ? K extends keyof T ? GetByPath<T[K], Rest> : never : P extends keyof T ? T[P] : never;
21
+ type ArrayPaths<T> = {
22
+ [K in keyof T & string]: T[K] extends any[] ? K : never;
23
+ }[keyof T & string];
24
+ type ObjectArrayPaths<T> = {
25
+ [K in keyof T & string]: T[K] extends (infer V)[] ? V extends Date | RegExp | Map<any, any> | Set<any> | Function | any[] ? never : V extends Record<string, any> ? K : never : never;
26
+ }[keyof T & string];
27
+ type ElementOf<A> = A extends (infer V)[] ? V : never;
28
+ type Updater<V> = V | ((prev: V) => V);
29
+ interface StoreOptions {
30
+ /** Auto-batch synchronous sets via microtask (default: true) */
31
+ autoBatch?: boolean;
32
+ }
33
+ /** Handle to a computed (derived) value. Call `get()` to read, `destroy()` to stop tracking. */
34
+ interface ComputedRef<V> {
35
+ get(): V;
36
+ destroy(): void;
37
+ }
38
+ /** Options for an HTTP request (method, body, headers). */
39
+ interface HttpRequestInit {
40
+ method?: string;
41
+ body?: unknown;
42
+ headers?: Record<string, string>;
43
+ }
44
+ /** Interface for the HTTP layer used by `api.get` and `api.post/put/patch/delete`. */
45
+ interface HttpClient {
46
+ request<R = unknown>(url: string, init?: HttpRequestInit): Promise<R>;
47
+ }
48
+ /** Options for HTTP verb methods (`api.post`, `api.put`, etc.). */
49
+ interface ApiRequestOptions<R = unknown> {
50
+ body?: unknown;
51
+ headers?: Record<string, string>;
52
+ onSuccess?: (data: R) => void;
53
+ onError?: (error: Error) => void;
54
+ }
55
+ interface Subscribable<T extends object> {
56
+ subscribe(callback: Listener): Unsubscribe;
57
+ getSnapshot(): T;
58
+ }
59
+ interface StateAccessor<T extends object> {
60
+ get(): T;
61
+ get<P extends DotPaths<T>>(path: P): GetByPath<T, P>;
62
+ set<P extends DotPaths<T>>(path: P, value: Updater<GetByPath<T, P>>): void;
63
+ batch(fn: () => void): void;
64
+ computed<V>(deps: (keyof T & string)[], fn: (state: T) => V): ComputedRef<V>;
65
+ append<P extends ArrayPaths<T>>(path: P, ...items: ElementOf<T[P]>[]): void;
66
+ prepend<P extends ArrayPaths<T>>(path: P, ...items: ElementOf<T[P]>[]): void;
67
+ insertAt<P extends ArrayPaths<T>>(path: P, index: number, ...items: ElementOf<T[P]>[]): void;
68
+ patch<P extends ObjectArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean, updates: Partial<ElementOf<T[P]>>): void;
69
+ remove<P extends ArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean): void;
70
+ removeAt<P extends ArrayPaths<T>>(path: P, index: number): void;
71
+ at<P extends ArrayPaths<T>>(path: P, index: number): ElementOf<T[P]> | undefined;
72
+ filter<P extends ArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean): ElementOf<T[P]>[];
73
+ find<P extends ArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean): ElementOf<T[P]> | undefined;
74
+ findIndexOf<P extends ArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean): number;
75
+ count<P extends ArrayPaths<T>>(path: P, predicate: (item: ElementOf<T[P]>) => boolean): number;
76
+ }
77
+ interface ApiAccessor<K extends string> {
78
+ fetch(key: K, fn: () => Promise<void>): Promise<void>;
79
+ get<R = unknown>(key: K, url: string, onSuccess?: (data: R) => void): Promise<void>;
80
+ post<R = unknown>(key: K, url: string, options?: ApiRequestOptions<R>): Promise<void>;
81
+ put<R = unknown>(key: K, url: string, options?: ApiRequestOptions<R>): Promise<void>;
82
+ patch<R = unknown>(key: K, url: string, options?: ApiRequestOptions<R>): Promise<void>;
83
+ delete<R = unknown>(key: K, url: string, options?: ApiRequestOptions<R>): Promise<void>;
84
+ }
85
+
86
+ /** Replace the global HTTP client used by `api.get` and `api.post/put/patch/delete`. */
87
+ declare function setHttpClient(client: HttpClient): void;
88
+ /** Set default headers merged into every HTTP request. Per-request headers override defaults. */
89
+ declare function setDefaultHeaders(headers: Record<string, string>): void;
90
+ declare class SnapStore<T extends object, K extends string = string> {
91
+ private _store;
92
+ private _operations;
93
+ private _generations;
94
+ protected readonly state: StateAccessor<T>;
95
+ protected readonly api: ApiAccessor<K>;
96
+ constructor(initialState: T, options?: StoreOptions);
97
+ /** Subscribe to all state changes. Returns an unsubscribe function. */
98
+ subscribe(callback: Listener): Unsubscribe;
99
+ /** Subscribe to changes at a specific dot-separated path. */
100
+ subscribe(path: string, callback: Listener): Unsubscribe;
101
+ /** Return a snapshot of the current state. Compatible with React's `useSyncExternalStore`. */
102
+ getSnapshot: () => T;
103
+ /** Get the async status of an operation by key. Returns `idle` if never started. */
104
+ getStatus(key: K): OperationState;
105
+ /** Tear down subscriptions and cleanup. */
106
+ destroy(): void;
107
+ }
108
+
109
+ export { type AsyncStatus, type AsyncStatusValue, type ComputedRef, type HttpClient, type Listener, SnapStore, type StoreOptions, type Subscribable, type Unsubscribe, asyncStatus, setDefaultHeaders, setHttpClient };