@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.
- package/dist/index.d.ts +327 -0
- package/dist/index.js +528 -0
- package/package.json +44 -0
- package/src/index.ts +58 -0
- package/src/reactive-store.ts +805 -0
- package/src/signal.ts +159 -0
- package/src/store-types.ts +78 -0
package/dist/index.d.ts
ADDED
|
@@ -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 };
|