@sylphx/lens-signals 1.0.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,327 @@
1
+ /** Subscription callback */
2
+ type Subscriber<T> = (value: T) => void;
3
+ /** Unsubscribe function */
4
+ type Unsubscribe = () => void;
5
+ /** Read-only signal interface */
6
+ interface Signal<T> {
7
+ /** Current value (read-only) */
8
+ readonly value: T;
9
+ /** Subscribe to value changes */
10
+ subscribe(fn: Subscriber<T>): Unsubscribe;
11
+ /** Get value without tracking */
12
+ peek(): T;
13
+ }
14
+ /** Writable signal interface */
15
+ interface WritableSignal<T> extends Signal<T> {
16
+ /** Current value (read-write) */
17
+ value: T;
18
+ }
19
+ /**
20
+ * Create a writable signal
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const count = signal(0);
25
+ * console.log(count.value); // 0
26
+ *
27
+ * count.value = 1;
28
+ * console.log(count.value); // 1
29
+ * ```
30
+ */
31
+ declare function signal<T>(initial: T): WritableSignal<T>;
32
+ /**
33
+ * Create a computed signal
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const count = signal(0);
38
+ * const doubled = computed(() => count.value * 2);
39
+ *
40
+ * console.log(doubled.value); // 0
41
+ *
42
+ * count.value = 5;
43
+ * console.log(doubled.value); // 10
44
+ * ```
45
+ */
46
+ declare function computed<T>(compute: () => T): Signal<T>;
47
+ /**
48
+ * Run a function whenever dependencies change
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const count = signal(0);
53
+ * const dispose = effect(() => {
54
+ * console.log('Count:', count.value);
55
+ * });
56
+ *
57
+ * count.value = 1; // Logs: "Count: 1"
58
+ * dispose(); // Stop watching
59
+ * ```
60
+ */
61
+ declare function effect(fn: () => void | (() => void)): Unsubscribe;
62
+ /**
63
+ * Batch multiple signal updates
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * const a = signal(1);
68
+ * const b = signal(2);
69
+ *
70
+ * batch(() => {
71
+ * a.value = 10;
72
+ * b.value = 20;
73
+ * });
74
+ * // Subscribers notified only once
75
+ * ```
76
+ */
77
+ declare function batch<T>(fn: () => T): T;
78
+ /**
79
+ * Check if a value is a signal
80
+ */
81
+ declare function isSignal(value: unknown): value is Signal<unknown>;
82
+ /**
83
+ * Convert a signal to a promise that resolves when the signal changes
84
+ */
85
+ declare function toPromise<T>(sig: Signal<T>): Promise<T>;
86
+ /**
87
+ * Create a signal that derives from multiple signals
88
+ */
89
+ declare function derive<
90
+ T,
91
+ U
92
+ >(signals: Signal<T>[], fn: (values: T[]) => U): Signal<U>;
93
+ import { EntityKey, Pipeline, Update } from "@sylphx/lens-core";
94
+ import { PipelineResult } from "@sylphx/reify";
95
+ /** Entity state with metadata */
96
+ interface EntityState<T = unknown> {
97
+ /** The entity data */
98
+ data: T | null;
99
+ /** Loading state */
100
+ loading: boolean;
101
+ /** Error state */
102
+ error: Error | null;
103
+ /** Whether data is stale */
104
+ stale: boolean;
105
+ /** Subscription reference count */
106
+ refCount: number;
107
+ /** Cache timestamp */
108
+ cachedAt?: number | undefined;
109
+ /** Cache tags for invalidation */
110
+ tags?: string[] | undefined;
111
+ }
112
+ /** Optimistic update entry */
113
+ interface OptimisticEntry {
114
+ id: string;
115
+ entityName: string;
116
+ entityId: string;
117
+ type: "create" | "update" | "delete";
118
+ originalData: unknown;
119
+ optimisticData: unknown;
120
+ timestamp: number;
121
+ }
122
+ /** Multi-entity optimistic transaction */
123
+ interface OptimisticTransaction {
124
+ id: string;
125
+ /** Pipeline results from Reify execution */
126
+ results: PipelineResult;
127
+ /** Original data for each entity (for rollback) */
128
+ originalData: Map<string, unknown>;
129
+ timestamp: number;
130
+ }
131
+ /** Store configuration */
132
+ interface StoreConfig {
133
+ /** Enable optimistic updates (default: true) */
134
+ optimistic?: boolean;
135
+ /** Cache TTL in milliseconds (default: 5 minutes) */
136
+ cacheTTL?: number;
137
+ /** Maximum cache size (default: 1000) */
138
+ maxCacheSize?: number;
139
+ /** Cascade invalidation rules */
140
+ cascadeRules?: CascadeRule[];
141
+ }
142
+ /** Cascade invalidation rule */
143
+ interface CascadeRule {
144
+ /** Source entity type that triggers invalidation */
145
+ source: string;
146
+ /** Operation types that trigger cascade (default: all) */
147
+ operations?: ("create" | "update" | "delete")[];
148
+ /** Target entities to invalidate */
149
+ targets: string[];
150
+ }
151
+ /** Invalidation options */
152
+ interface InvalidationOptions {
153
+ /** Invalidate by tag */
154
+ tags?: string[];
155
+ /** Invalidate by entity type pattern (glob-like) */
156
+ pattern?: string;
157
+ /** Cascade to related entities */
158
+ cascade?: boolean;
159
+ }
160
+ /**
161
+ * Reactive store for managing entity state
162
+ */
163
+ declare class ReactiveStore {
164
+ /** Entity signals by key */
165
+ private entities;
166
+ /** List signals by query key */
167
+ private lists;
168
+ /** Optimistic updates pending confirmation */
169
+ private optimisticUpdates;
170
+ /** Multi-entity optimistic transactions */
171
+ private optimisticTransactions;
172
+ /** Configuration */
173
+ private config;
174
+ /** Tag to entity keys mapping */
175
+ private tagIndex;
176
+ constructor(config?: StoreConfig);
177
+ /**
178
+ * Get or create entity signal
179
+ */
180
+ getEntity<T>(entityName: string, entityId: string): Signal<EntityState<T>>;
181
+ /**
182
+ * Set entity data
183
+ */
184
+ setEntity<T>(entityName: string, entityId: string, data: T, tags?: string[]): void;
185
+ /**
186
+ * Update entity with server update
187
+ */
188
+ applyServerUpdate(entityName: string, entityId: string, update: Update): void;
189
+ /**
190
+ * Set entity error state
191
+ */
192
+ setEntityError(entityName: string, entityId: string, error: Error): void;
193
+ /**
194
+ * Set entity loading state
195
+ */
196
+ setEntityLoading(entityName: string, entityId: string, loading: boolean): void;
197
+ /**
198
+ * Remove entity from cache
199
+ */
200
+ removeEntity(entityName: string, entityId: string): void;
201
+ /**
202
+ * Check if entity exists in cache
203
+ */
204
+ hasEntity(entityName: string, entityId: string): boolean;
205
+ /**
206
+ * Get or create list signal
207
+ */
208
+ getList<T>(queryKey: string): Signal<EntityState<T[]>>;
209
+ /**
210
+ * Set list data
211
+ */
212
+ setList<T>(queryKey: string, data: T[]): void;
213
+ /**
214
+ * Apply optimistic update
215
+ */
216
+ applyOptimistic<T extends {
217
+ id: string;
218
+ }>(entityName: string, type: "create" | "update" | "delete", data: Partial<T> & {
219
+ id: string;
220
+ }): string;
221
+ /**
222
+ * Confirm optimistic update (server confirmed)
223
+ */
224
+ confirmOptimistic(optimisticId: string, serverData?: unknown): void;
225
+ /**
226
+ * Rollback optimistic update (server rejected)
227
+ */
228
+ rollbackOptimistic(optimisticId: string): void;
229
+ /**
230
+ * Get pending optimistic updates
231
+ */
232
+ getPendingOptimistic(): OptimisticEntry[];
233
+ /**
234
+ * Apply optimistic update from Reify Pipeline
235
+ * Returns transaction ID for confirmation/rollback
236
+ *
237
+ * Uses Reify's execute() with a cache adapter that wraps ReactiveStore.
238
+ */
239
+ applyPipelineOptimistic<TInput extends Record<string, unknown>>(pipeline: Pipeline, input: TInput): Promise<string>;
240
+ /**
241
+ * Confirm pipeline optimistic transaction
242
+ * Updates entities with server data (replaces temp IDs with real IDs)
243
+ */
244
+ confirmPipelineOptimistic(txId: string, serverResults?: Array<{
245
+ entity: string;
246
+ tempId: string;
247
+ data: unknown;
248
+ }>): void;
249
+ /**
250
+ * Rollback pipeline optimistic transaction
251
+ * Restores all entities to their original state
252
+ */
253
+ rollbackPipelineOptimistic(txId: string): void;
254
+ /**
255
+ * Get pending multi-entity transactions
256
+ */
257
+ getPendingTransactions(): OptimisticTransaction[];
258
+ /**
259
+ * Invalidate entity and mark as stale
260
+ */
261
+ invalidate(entityName: string, entityId: string, options?: InvalidationOptions): void;
262
+ /**
263
+ * Invalidate all entities of a type
264
+ */
265
+ invalidateEntity(entityName: string, options?: InvalidationOptions): void;
266
+ /**
267
+ * Invalidate by tags
268
+ */
269
+ invalidateByTags(tags: string[]): number;
270
+ /**
271
+ * Invalidate by pattern (glob-like: User:*, *:123)
272
+ */
273
+ invalidateByPattern(pattern: string): number;
274
+ /**
275
+ * Tag an entity for group invalidation
276
+ */
277
+ tagEntity(entityName: string, entityId: string, tags: string[]): void;
278
+ /**
279
+ * Check if entity data is stale (past TTL)
280
+ */
281
+ isStale(entityName: string, entityId: string): boolean;
282
+ /**
283
+ * Get data with stale-while-revalidate pattern
284
+ * Returns stale data immediately and triggers revalidation callback
285
+ */
286
+ getStaleWhileRevalidate<T>(entityName: string, entityId: string, revalidate: () => Promise<T>): {
287
+ data: T | null;
288
+ isStale: boolean;
289
+ revalidating: Promise<T> | null;
290
+ };
291
+ private markStale;
292
+ private cascadeInvalidate;
293
+ private patternToRegex;
294
+ /**
295
+ * Increment reference count for entity
296
+ */
297
+ retain(entityName: string, entityId: string): void;
298
+ /**
299
+ * Decrement reference count for entity
300
+ */
301
+ release(entityName: string, entityId: string): void;
302
+ /**
303
+ * Clear all stale entities
304
+ */
305
+ gc(): number;
306
+ /**
307
+ * Clear entire cache
308
+ */
309
+ clear(): void;
310
+ /**
311
+ * Create cache key (delegates to @sylphx/lens-core)
312
+ */
313
+ private makeKey;
314
+ /**
315
+ * Get cache statistics
316
+ */
317
+ getStats(): {
318
+ entities: number;
319
+ lists: number;
320
+ pendingOptimistic: number;
321
+ };
322
+ }
323
+ /**
324
+ * Create a new reactive store
325
+ */
326
+ declare function createStore(config?: StoreConfig): ReactiveStore;
327
+ export { toPromise, signal, isSignal, effect, derive, createStore, computed, batch, WritableSignal, Unsubscribe, Subscriber, StoreConfig, Signal, ReactiveStore, OptimisticTransaction, OptimisticEntry, InvalidationOptions, EntityState, EntityKey, CascadeRule };