@ngstato/core 0.2.0 → 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/dist/index.d.ts CHANGED
@@ -44,7 +44,17 @@ declare class StatoHttpError extends Error {
44
44
  constructor(status: number, body: string);
45
45
  }
46
46
 
47
- declare function createStore<S extends object>(config: S & StatoStoreConfig<S>): any;
47
+ declare function createStore<S extends object>(config: S & StatoStoreConfig<S>, __internal?: {
48
+ skipInit?: boolean;
49
+ }): any;
50
+ type OnEvent = {
51
+ name: string;
52
+ args: unknown[];
53
+ status: 'success' | 'error';
54
+ duration: number;
55
+ error?: Error;
56
+ };
57
+ declare function on<S extends object>(sourceAction: Function | Function[], handler: (store: S, event: OnEvent) => void | Promise<void>): () => void;
48
58
 
49
59
  interface RequestOptions {
50
60
  params?: Record<string, string | number | boolean>;
@@ -106,6 +116,149 @@ declare function fromStream<S, T>(setupFn: (state: S) => StatoObservable<T>, upd
106
116
 
107
117
  declare function optimistic<S, A extends unknown[]>(immediate: (state: S, ...args: A) => void, confirm: (state: S, ...args: A) => Promise<void>): (state: S, ...args: A) => Promise<void>;
108
118
 
119
+ declare function exclusive<S, A extends unknown[]>(fn: (state: S, ...args: A) => Promise<void>): (state: S, ...args: A) => Promise<void>;
120
+
121
+ declare function queued<S, A extends unknown[]>(fn: (state: S, ...args: A) => Promise<void>): (state: S, ...args: A) => Promise<void>;
122
+
123
+ type Comparator<T> = (prev: T, next: T) => boolean;
124
+ declare function distinctUntilChanged<S, A extends unknown[], K>(fn: (state: S, ...args: A) => void | Promise<void>, keySelector: (...args: A) => K, comparator?: Comparator<K>): (state: S, ...args: A) => Promise<void>;
125
+
126
+ type TaskContext$1 = {
127
+ signal: AbortSignal;
128
+ };
129
+ type Task$1<T> = (ctx: TaskContext$1) => Promise<T> | T;
130
+ type ForkJoinOptions = {
131
+ signal?: AbortSignal;
132
+ };
133
+ declare function forkJoin<T extends Record<string, Task$1<any>>>(tasks: T, options?: ForkJoinOptions): Promise<{
134
+ [K in keyof T]: Awaited<ReturnType<T[K]>>;
135
+ }>;
136
+
137
+ type TaskContext = {
138
+ signal: AbortSignal;
139
+ };
140
+ type Task<T> = (ctx: TaskContext) => Promise<T> | T;
141
+ type RaceOptions = {
142
+ signal?: AbortSignal;
143
+ };
144
+ declare function race<T>(tasks: Array<Task<T>>, options?: RaceOptions): Promise<T>;
145
+
146
+ type DepFn<S, T> = (state: S) => T;
147
+ declare function combineLatest<S>(): <T extends unknown[]>(...deps: { [K in keyof T]: DepFn<S, T[K]>; }) => (state: S) => T;
148
+
149
+ declare function combineLatestStream<T extends unknown[]>(...sources: {
150
+ [K in keyof T]: StatoObservable<T[K]>;
151
+ }): StatoObservable<T>;
152
+
153
+ type EntityId = string | number;
154
+ interface EntityState<T> {
155
+ ids: EntityId[];
156
+ entities: Record<string, T>;
157
+ }
158
+ type Update<T> = {
159
+ id: EntityId;
160
+ changes: Partial<T>;
161
+ };
162
+ type SelectId<T> = (entity: T) => EntityId;
163
+ interface EntityAdapterOptions<T> {
164
+ selectId?: SelectId<T>;
165
+ sortComparer?: (a: T, b: T) => number;
166
+ }
167
+ declare function createEntityAdapter<T extends Record<string, any>>(options?: EntityAdapterOptions<T>): {
168
+ selectId: SelectId<T>;
169
+ sortComparer: ((a: T, b: T) => number) | undefined;
170
+ getInitialState: <E extends object = {}>(extra?: E) => EntityState<T> & E;
171
+ addOne: (entity: T, state: EntityState<T>) => void;
172
+ addMany: (entities: T[], state: EntityState<T>) => void;
173
+ setAll: (entities: T[], state: EntityState<T>) => void;
174
+ upsertOne: (entity: T, state: EntityState<T>) => void;
175
+ upsertMany: (entities: T[], state: EntityState<T>) => void;
176
+ updateOne: (update: Update<T>, state: EntityState<T>) => void;
177
+ removeOne: (id: EntityId, state: EntityState<T>) => void;
178
+ removeMany: (ids: EntityId[], state: EntityState<T>) => void;
179
+ removeAll: (state: EntityState<T>) => void;
180
+ getSelectors: <S = EntityState<T>>(selectState?: (state: S) => EntityState<T>) => {
181
+ selectIds: (state: S | EntityState<T>) => EntityId[];
182
+ selectEntities: (state: S | EntityState<T>) => Record<string, T>;
183
+ selectAll: (state: S | EntityState<T>) => T[];
184
+ selectTotal: (state: S | EntityState<T>) => number;
185
+ selectById: (state: S | EntityState<T>, id: EntityId) => T;
186
+ };
187
+ };
188
+
189
+ type EntityAdapter<T> = {
190
+ getInitialState: <E extends object = {}>(extra?: E) => EntityState<T> & E;
191
+ addOne: (entity: T, state: EntityState<T>) => void;
192
+ addMany: (entities: T[], state: EntityState<T>) => void;
193
+ setAll: (entities: T[], state: EntityState<T>) => void;
194
+ upsertOne: (entity: T, state: EntityState<T>) => void;
195
+ upsertMany: (entities: T[], state: EntityState<T>) => void;
196
+ updateOne: (update: Update<T>, state: EntityState<T>) => void;
197
+ removeOne: (id: EntityId, state: EntityState<T>) => void;
198
+ removeMany: (ids: EntityId[], state: EntityState<T>) => void;
199
+ removeAll: (state: EntityState<T>) => void;
200
+ getSelectors: <S = EntityState<T>>(selectState?: (state: S) => EntityState<T>) => {
201
+ selectIds: (state: S | EntityState<T>) => EntityId[];
202
+ selectEntities: (state: S | EntityState<T>) => Record<string, T>;
203
+ selectAll: (state: S | EntityState<T>) => T[];
204
+ selectTotal: (state: S | EntityState<T>) => number;
205
+ selectById: (state: S | EntityState<T>, id: EntityId) => T | undefined;
206
+ };
207
+ };
208
+ type WithEntitiesSelectorsNames = Partial<{
209
+ ids: string;
210
+ entities: string;
211
+ all: string;
212
+ total: string;
213
+ byId: string;
214
+ }>;
215
+ type WithEntitiesActionsNames = Partial<{
216
+ addOne: string;
217
+ addMany: string;
218
+ setAll: string;
219
+ upsertOne: string;
220
+ upsertMany: string;
221
+ updateOne: string;
222
+ removeOne: string;
223
+ removeMany: string;
224
+ removeAll: string;
225
+ }>;
226
+ type WithEntitiesOptions<T> = {
227
+ key: string;
228
+ adapter: EntityAdapter<T>;
229
+ initial?: T[];
230
+ selectors?: WithEntitiesSelectorsNames;
231
+ actions?: WithEntitiesActionsNames;
232
+ };
233
+ declare function withEntities<S extends object, T>(config: S & StatoStoreConfig<S>, options: WithEntitiesOptions<T>): S & StatoStoreConfig<S>;
234
+
235
+ type MaybeObservable<T> = StatoObservable<T> | Promise<T> | T;
236
+ type StreamOperator<I, O> = (source: StatoObservable<I>) => StatoObservable<O>;
237
+ declare function pipeStream<T>(source: StatoObservable<T>): StatoObservable<T>;
238
+ declare function pipeStream<T, A>(source: StatoObservable<T>, op1: StreamOperator<T, A>): StatoObservable<A>;
239
+ declare function pipeStream<T, A, B>(source: StatoObservable<T>, op1: StreamOperator<T, A>, op2: StreamOperator<A, B>): StatoObservable<B>;
240
+ declare function pipeStream<T, A, B, C>(source: StatoObservable<T>, op1: StreamOperator<T, A>, op2: StreamOperator<A, B>, op3: StreamOperator<B, C>): StatoObservable<C>;
241
+ declare function mapStream<I, O>(mapFn: (value: I) => O): StreamOperator<I, O>;
242
+ declare function filterStream<T>(predicate: (value: T) => boolean): StreamOperator<T, T>;
243
+ type Mapper<I, O> = (value: I, ctx: {
244
+ signal: AbortSignal;
245
+ }) => MaybeObservable<O>;
246
+ declare function switchMapStream<I, O>(mapper: Mapper<I, O>): StreamOperator<I, O>;
247
+ declare function concatMapStream<I, O>(mapper: Mapper<I, O>): StreamOperator<I, O>;
248
+ declare function exhaustMapStream<I, O>(mapper: Mapper<I, O>): StreamOperator<I, O>;
249
+ declare function mergeMapStream<I, O>(mapper: Mapper<I, O>, options?: {
250
+ concurrency?: number;
251
+ }): StreamOperator<I, O>;
252
+ declare function distinctUntilChangedStream<T, K = T>(keySelector?: (value: T) => K, comparator?: (prev: K, next: K) => boolean): StreamOperator<T, T>;
253
+ declare function debounceStream<T>(ms: number): StreamOperator<T, T>;
254
+ declare function throttleStream<T>(ms: number): StreamOperator<T, T>;
255
+ declare function catchErrorStream<T>(handler: (error: unknown) => MaybeObservable<T>): StreamOperator<T, T>;
256
+ declare function retryStream<T>(options?: {
257
+ attempts?: number;
258
+ delay?: number;
259
+ backoff?: 'fixed' | 'exponential';
260
+ }): StreamOperator<T, T>;
261
+
109
262
  interface PersistStorage {
110
263
  getItem(key: string): string | null;
111
264
  setItem(key: string, value: string): void;
@@ -121,21 +274,103 @@ interface PersistOptions<S extends object> {
121
274
  }
122
275
  declare function withPersist<S extends object>(config: S & StatoStoreConfig<S>, options: PersistOptions<StateSlice<S>>): S & StatoStoreConfig<S>;
123
276
 
277
+ interface FeatureConfig {
278
+ state?: Record<string, unknown>;
279
+ actions?: Record<string, Function>;
280
+ computed?: Record<string, Function>;
281
+ selectors?: Record<string, Function>;
282
+ effects?: EffectEntry<any>[];
283
+ hooks?: Partial<StatoHooks<any>>;
284
+ }
285
+ type MergedFeature = {
286
+ actions?: Record<string, Function>;
287
+ computed?: Record<string, Function>;
288
+ selectors?: Record<string, Function>;
289
+ effects?: EffectEntry<any>[];
290
+ hooks?: Partial<StatoHooks<any>>;
291
+ [key: string]: unknown;
292
+ };
293
+ declare function mergeFeatures(...features: FeatureConfig[]): MergedFeature;
294
+
295
+ /**
296
+ * withProps() — Attach external properties to a store config.
297
+ *
298
+ * Properties are accessible on the store instance but NOT part of the state.
299
+ * Use this to expose injected services on the store object.
300
+ *
301
+ * @example
302
+ * ```ts
303
+ * // Pattern 1: Expose services on the store
304
+ * export const UsersStore = StatoStore(() => {
305
+ * const api = inject(ApiService)
306
+ * const notifier = inject(NotificationService)
307
+ *
308
+ * const store = createStore({
309
+ * users: [] as User[],
310
+ * loading: false,
311
+ *
312
+ * actions: {
313
+ * async loadUsers(state) {
314
+ * state.loading = true
315
+ * state.users = await api.getUsers() // closure over injected service
316
+ * state.loading = false
317
+ * },
318
+ *
319
+ * async deleteUser(state, id: string) {
320
+ * await api.deleteUser(id)
321
+ * state.users = state.users.filter(u => u.id !== id)
322
+ * notifier.success('User deleted')
323
+ * }
324
+ * }
325
+ * })
326
+ *
327
+ * // Attach props to the store — accessible but not in state
328
+ * return withProps(store, { api, notifier })
329
+ * })
330
+ *
331
+ * // In a component:
332
+ * store = injectStore(UsersStore)
333
+ * store.users() // Signal<User[]>
334
+ * store.loadUsers() // action
335
+ * store.api // ApiService — read-only, not in state
336
+ * ```
337
+ *
338
+ * @example
339
+ * ```ts
340
+ * // Pattern 2: Configuration props
341
+ * return withProps(store, {
342
+ * storeName: 'Users',
343
+ * version: '1.0.0',
344
+ * config: { pageSize: 20, cacheTTL: 60_000 }
345
+ * })
346
+ * ```
347
+ */
348
+ declare function withProps<S extends object, P extends Record<string, unknown>>(store: S, props: P): S & Readonly<P>;
349
+
124
350
  interface ActionLog {
125
351
  id: number;
126
352
  name: string;
353
+ storeName: string;
127
354
  args: unknown[];
128
355
  duration: number;
129
356
  status: 'success' | 'error';
130
357
  error?: string;
131
- prevState: unknown;
132
- nextState: unknown;
358
+ prevState: Record<string, unknown>;
359
+ nextState: Record<string, unknown>;
133
360
  at: string;
134
361
  }
135
362
  interface DevToolsState {
136
363
  logs: ActionLog[];
137
364
  isOpen: boolean;
138
365
  maxLogs: number;
366
+ activeLogId: number | null;
367
+ isTimeTraveling: boolean;
368
+ }
369
+ interface DevToolsSnapshot {
370
+ version: number;
371
+ timestamp: string;
372
+ stores: Record<string, unknown>;
373
+ logs: ActionLog[];
139
374
  }
140
375
  interface DevToolsInstance {
141
376
  state: DevToolsState;
@@ -145,9 +380,21 @@ interface DevToolsInstance {
145
380
  close: () => void;
146
381
  toggle: () => void;
147
382
  subscribe: (cb: (state: DevToolsState) => void) => () => void;
383
+ travelTo: (logId: number) => void;
384
+ undo: () => void;
385
+ redo: () => void;
386
+ resume: () => void;
387
+ replay: (logId: number) => void;
388
+ exportSnapshot: () => DevToolsSnapshot;
389
+ importSnapshot: (snapshot: DevToolsSnapshot) => void;
390
+ registerStore: (name: string, publicStore: any, internalStore: any) => void;
391
+ getStoreRegistry: () => Map<string, {
392
+ store: any;
393
+ internalStore: any;
394
+ }>;
148
395
  }
149
396
  declare function createDevTools(maxLogs?: number): DevToolsInstance;
150
397
  declare const devTools: DevToolsInstance;
151
398
  declare function connectDevTools(store: any, storeName: string): void;
152
399
 
153
- export { type ActionLog, type DevToolsInstance, type DevToolsState, type EffectDepsFn, type EffectEntry, type EffectRunner, type PersistOptions, type PersistStorage, type RequestOptions, type StatoConfig, type StatoHooks, StatoHttp, StatoHttpError, type StatoStoreConfig, type StatoStoreInstance, abortable, configureHttp, connectDevTools, createDevTools, createHttp, createStore, debounced, devTools, fromStream, http, optimistic, retryable, throttled, withPersist };
400
+ export { type ActionLog, type DevToolsInstance, type DevToolsSnapshot, type DevToolsState, type EffectDepsFn, type EffectEntry, type EffectRunner, type EntityAdapterOptions, type EntityId, type EntityState, type FeatureConfig, type MergedFeature, type OnEvent, type PersistOptions, type PersistStorage, type RequestOptions, type StatoConfig, type StatoHooks, StatoHttp, StatoHttpError, type StatoStoreConfig, type StatoStoreInstance, type Update, type WithEntitiesOptions, abortable, catchErrorStream, combineLatest, combineLatestStream, concatMapStream, configureHttp, connectDevTools, createDevTools, createEntityAdapter, createHttp, createStore, debounceStream, debounced, devTools, distinctUntilChanged, distinctUntilChangedStream, exclusive, exhaustMapStream, filterStream, forkJoin, fromStream, http, mapStream, mergeFeatures, mergeMapStream, on, optimistic, pipeStream, queued, race, retryStream, retryable, switchMapStream, throttleStream, throttled, withEntities, withPersist, withProps };