@volynets/reflex 0.1.1 → 0.1.2
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/README.md +16 -15
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/unstable/index.cjs +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/unstable/index.js +1 -1
- package/dist/globals.d.ts +333 -32
- package/dist/unstable/index.d.ts +195 -0
- package/package.json +10 -1
- package/src/api/derived.ts +90 -0
- package/src/api/effect.ts +120 -0
- package/src/api/event.ts +257 -0
- package/src/api/index.ts +4 -0
- package/src/api/signal.ts +68 -0
- package/src/globals.d.ts +169 -0
- package/src/index.ts +6 -0
- package/src/infra/event.ts +182 -0
- package/src/infra/factory.ts +46 -0
- package/src/infra/index.ts +2 -0
- package/src/infra/runtime.ts +189 -0
- package/src/policy/SCHEDULER_SEMANTICS.md +389 -0
- package/src/policy/event_dispatcher.ts +39 -0
- package/src/policy/index.ts +1 -0
- package/src/policy/scheduler/index.ts +6 -0
- package/src/policy/scheduler/scheduler.constants.ts +17 -0
- package/src/policy/scheduler/scheduler.core.ts +165 -0
- package/src/policy/scheduler/scheduler.infra.ts +37 -0
- package/src/policy/scheduler/scheduler.queue.ts +74 -0
- package/src/policy/scheduler/scheduler.types.ts +54 -0
- package/src/policy/scheduler/variants/index.ts +3 -0
- package/src/policy/scheduler/variants/scheduler.eager.ts +46 -0
- package/src/policy/scheduler/variants/scheduler.flush.ts +35 -0
- package/src/policy/scheduler/variants/scheduler.sab.ts +37 -0
- package/src/unstable/index.ts +4 -0
- package/src/unstable/resource.ts +505 -0
package/dist/globals.d.ts
CHANGED
|
@@ -171,7 +171,84 @@ declare global {
|
|
|
171
171
|
const __DEV__: boolean;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Creates a lazy derived accessor.
|
|
176
|
+
*
|
|
177
|
+
* `computed` runs `fn` only when the returned accessor is read. During that
|
|
178
|
+
* evaluation it tracks the reactive values that `fn` touches, caches the
|
|
179
|
+
* result, and reuses the cached value for subsequent clean reads.
|
|
180
|
+
*
|
|
181
|
+
* @typeParam T - Derived value type.
|
|
182
|
+
*
|
|
183
|
+
* @param fn - Pure synchronous computation that derives a value from reactive
|
|
184
|
+
* reads.
|
|
185
|
+
*
|
|
186
|
+
* @returns Tracked accessor that returns the latest derived value.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```ts
|
|
190
|
+
* createRuntime();
|
|
191
|
+
*
|
|
192
|
+
* const [count, setCount] = signal(1);
|
|
193
|
+
* const doubled = computed(() => count() * 2);
|
|
194
|
+
*
|
|
195
|
+
* console.log(doubled()); // 2
|
|
196
|
+
*
|
|
197
|
+
* setCount(2);
|
|
198
|
+
*
|
|
199
|
+
* console.log(doubled()); // 4
|
|
200
|
+
* ```
|
|
201
|
+
*
|
|
202
|
+
* @remarks
|
|
203
|
+
* - `fn` does not run until the first read.
|
|
204
|
+
* - Dependencies are tracked dynamically on each execution, so branch changes
|
|
205
|
+
* automatically update the dependency set.
|
|
206
|
+
* - Dirty computeds recompute on demand when read again.
|
|
207
|
+
* - Reading a computed does not require `rt.flush()`; `flush()` is only for
|
|
208
|
+
* scheduled effects.
|
|
209
|
+
* - Keep `fn` pure and synchronous.
|
|
210
|
+
*
|
|
211
|
+
* @see memo
|
|
212
|
+
* @see effect
|
|
213
|
+
*/
|
|
174
214
|
declare function computed<T>(fn: () => T): Accessor<T>;
|
|
215
|
+
/**
|
|
216
|
+
* Creates a computed accessor and warms it eagerly once.
|
|
217
|
+
*
|
|
218
|
+
* `memo` has the same dependency tracking and caching semantics as
|
|
219
|
+
* `computed()`, but it performs one eager read immediately after creation.
|
|
220
|
+
* This is useful when you want the initial value materialized up front while
|
|
221
|
+
* still interacting with a normal accessor afterward.
|
|
222
|
+
*
|
|
223
|
+
* @typeParam T - Derived value type.
|
|
224
|
+
*
|
|
225
|
+
* @param fn - Pure synchronous computation that derives a value from reactive
|
|
226
|
+
* reads.
|
|
227
|
+
*
|
|
228
|
+
* @returns Tracked accessor that returns the latest memoized value.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```ts
|
|
232
|
+
* createRuntime();
|
|
233
|
+
*
|
|
234
|
+
* const [price, setPrice] = signal(100);
|
|
235
|
+
* const total = memo(() => price() * 1.2);
|
|
236
|
+
*
|
|
237
|
+
* console.log(total()); // 120
|
|
238
|
+
*
|
|
239
|
+
* setPrice(200);
|
|
240
|
+
*
|
|
241
|
+
* console.log(total()); // 240
|
|
242
|
+
* ```
|
|
243
|
+
*
|
|
244
|
+
* @remarks
|
|
245
|
+
* - `memo(fn)` is equivalent to `computed(fn)` plus one immediate warm-up read.
|
|
246
|
+
* - After the warm-up, clean reads reuse the cached value exactly like
|
|
247
|
+
* `computed()`.
|
|
248
|
+
* - Later invalidations still follow normal computed semantics.
|
|
249
|
+
*
|
|
250
|
+
* @see computed
|
|
251
|
+
*/
|
|
175
252
|
declare function memo<T>(fn: () => T): Accessor<T>;
|
|
176
253
|
|
|
177
254
|
/**
|
|
@@ -207,14 +284,14 @@ interface Reactivable {
|
|
|
207
284
|
type ComputeFn<T> = (() => T) | null;
|
|
208
285
|
declare class ReactiveNode<T = unknown> implements Reactivable {
|
|
209
286
|
state: number;
|
|
210
|
-
compute: ComputeFn<T>;
|
|
211
287
|
firstOut: ReactiveEdge | null;
|
|
212
288
|
firstIn: ReactiveEdge | null;
|
|
213
289
|
lastOut: ReactiveEdge | null;
|
|
214
290
|
lastIn: ReactiveEdge | null;
|
|
215
291
|
depsTail: ReactiveEdge | null;
|
|
292
|
+
compute: ComputeFn<T>;
|
|
216
293
|
payload: T;
|
|
217
|
-
constructor(payload: T
|
|
294
|
+
constructor(payload: T, compute: ComputeFn<T>, state: number);
|
|
218
295
|
}
|
|
219
296
|
|
|
220
297
|
/**
|
|
@@ -223,13 +300,14 @@ declare class ReactiveNode<T = unknown> implements Reactivable {
|
|
|
223
300
|
* incoming list, so pointer rewrites must keep both views in sync.
|
|
224
301
|
*/
|
|
225
302
|
declare class ReactiveEdge {
|
|
226
|
-
|
|
227
|
-
to: ReactiveNode;
|
|
303
|
+
version: number;
|
|
228
304
|
prevOut: ReactiveEdge | null;
|
|
229
305
|
nextOut: ReactiveEdge | null;
|
|
306
|
+
from: ReactiveNode;
|
|
307
|
+
to: ReactiveNode;
|
|
230
308
|
prevIn: ReactiveEdge | null;
|
|
231
309
|
nextIn: ReactiveEdge | null;
|
|
232
|
-
constructor(
|
|
310
|
+
constructor(version: number, prevOut: ReactiveEdge | null, nextOut: ReactiveEdge | null, from: ReactiveNode, to: ReactiveNode, prevIn: ReactiveEdge | null, nextIn: ReactiveEdge | null);
|
|
233
311
|
}
|
|
234
312
|
|
|
235
313
|
interface EngineHooks {
|
|
@@ -237,63 +315,241 @@ interface EngineHooks {
|
|
|
237
315
|
onReactiveSettled?(): void;
|
|
238
316
|
}
|
|
239
317
|
type CleanupRegistrar = (cleanup: () => void) => void;
|
|
318
|
+
type TrackReadFallback = (source: ReactiveNode, consumer: ReactiveNode, prev: ReactiveEdge | null, nextExpected: ReactiveEdge | null, version: number) => ReactiveEdge;
|
|
319
|
+
interface ExecutionContextOptions {
|
|
320
|
+
trackReadFallback?: TrackReadFallback;
|
|
321
|
+
}
|
|
240
322
|
type OnEffectInvalidatedHook = EngineHooks["onEffectInvalidated"];
|
|
241
323
|
type OnReactiveSettledHook = EngineHooks["onReactiveSettled"];
|
|
242
|
-
/**
|
|
243
|
-
* ExecutionContext управляет состоянием вычисления и уведомлениями host'у.
|
|
244
|
-
*
|
|
245
|
-
* Ключевые принципы:
|
|
246
|
-
* - Контекст НЕ глобальный - это объект, передаваемый по параметрам
|
|
247
|
-
* - Host полностью контролирует scheduling эффектов
|
|
248
|
-
* - Контекст только отслеживает текущее состояние вычисления
|
|
249
|
-
*
|
|
250
|
-
* Поля:
|
|
251
|
-
* - activeComputed: текущий узел в процессе вычисления (для trackRead)
|
|
252
|
-
* - propagationDepth: глубина каскада инвалидаций
|
|
253
|
-
* - cleanupRegistrar: функция для регистрации cleanup в эффектах
|
|
254
|
-
*/
|
|
255
324
|
declare class ExecutionContext {
|
|
256
325
|
activeComputed: ReactiveNode | null;
|
|
326
|
+
trackingVersion: number;
|
|
257
327
|
propagationDepth: number;
|
|
258
328
|
cleanupRegistrar: CleanupRegistrar | null;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
329
|
+
trackReadFallback: TrackReadFallback;
|
|
330
|
+
onEffectInvalidated: OnEffectInvalidatedHook;
|
|
331
|
+
onReactiveSettled: OnReactiveSettledHook;
|
|
332
|
+
runtimeOnEffectInvalidated: OnEffectInvalidatedHook;
|
|
333
|
+
runtimeOnReactiveSettled: OnReactiveSettledHook;
|
|
334
|
+
effectInvalidatedDispatch: OnEffectInvalidatedHook;
|
|
335
|
+
settledDispatch: OnReactiveSettledHook;
|
|
336
|
+
constructor(hooks?: EngineHooks, options?: ExecutionContextOptions);
|
|
264
337
|
dispatchWatcherEvent(node: ReactiveNode): void;
|
|
265
338
|
maybeNotifySettled(): void;
|
|
266
339
|
enterPropagation(): void;
|
|
267
340
|
leavePropagation(): void;
|
|
268
341
|
resetState(): void;
|
|
342
|
+
setOptions(options?: ExecutionContextOptions): void;
|
|
269
343
|
setHooks(hooks?: EngineHooks): void;
|
|
344
|
+
setRuntimeHooks(onEffectInvalidated?: OnEffectInvalidatedHook, onReactiveSettled?: OnReactiveSettledHook): void;
|
|
270
345
|
registerWatcherCleanup(cleanup: () => void): void;
|
|
271
346
|
withCleanupRegistrar<T>(registrar: CleanupRegistrar | null, fn: () => T): T;
|
|
272
|
-
private
|
|
273
|
-
private setOnReactiveSettledHook;
|
|
274
|
-
private updateHookMask;
|
|
347
|
+
private refreshDispatchers;
|
|
275
348
|
}
|
|
276
349
|
|
|
277
350
|
|
|
351
|
+
/**
|
|
352
|
+
* Callback used to register cleanup produced by nested helpers with an
|
|
353
|
+
* enclosing effect scope.
|
|
354
|
+
*/
|
|
355
|
+
type EffectCleanupRegistrar = (cleanup: Destructor) => void;
|
|
356
|
+
/**
|
|
357
|
+
* Runs `fn` with a temporary cleanup registrar installed on the active runtime
|
|
358
|
+
* context.
|
|
359
|
+
*
|
|
360
|
+
* Helpers that allocate resources during `fn` can forward their teardown to
|
|
361
|
+
* `registrar`, allowing the surrounding effect or integration to dispose them
|
|
362
|
+
* automatically.
|
|
363
|
+
*
|
|
364
|
+
* @typeParam T - Return type of `fn`.
|
|
365
|
+
*
|
|
366
|
+
* @param registrar - Cleanup registrar to expose during `fn`, or `null` to run
|
|
367
|
+
* without one.
|
|
368
|
+
* @param fn - Callback executed with the temporary registrar installed.
|
|
369
|
+
*
|
|
370
|
+
* @returns The value returned by `fn`.
|
|
371
|
+
*
|
|
372
|
+
* @remarks
|
|
373
|
+
* - The registrar is scoped to the duration of `fn`.
|
|
374
|
+
* - This is a low-level integration helper. Most application code should use
|
|
375
|
+
* `effect()` directly.
|
|
376
|
+
*/
|
|
377
|
+
declare function withEffectCleanupRegistrar<T>(registrar: EffectCleanupRegistrar | null, fn: () => T): T;
|
|
378
|
+
/**
|
|
379
|
+
* Creates a reactive effect.
|
|
380
|
+
*
|
|
381
|
+
* `effect` runs `fn` immediately, tracks any reactive values read during that
|
|
382
|
+
* run, and schedules re-execution when those dependencies change.
|
|
383
|
+
*
|
|
384
|
+
* @param fn - Effect body. It may return a cleanup function that runs before
|
|
385
|
+
* the next execution and when the effect is disposed.
|
|
386
|
+
*
|
|
387
|
+
* @returns Destructor that disposes the effect and runs the latest cleanup, if
|
|
388
|
+
* present.
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* ```ts
|
|
392
|
+
* const rt = createRuntime();
|
|
393
|
+
* const [count, setCount] = signal(0);
|
|
394
|
+
*
|
|
395
|
+
* const stop = effect(() => {
|
|
396
|
+
* console.log(count());
|
|
397
|
+
* });
|
|
398
|
+
*
|
|
399
|
+
* setCount(1);
|
|
400
|
+
* rt.flush();
|
|
401
|
+
*
|
|
402
|
+
* stop();
|
|
403
|
+
* ```
|
|
404
|
+
*
|
|
405
|
+
* @remarks
|
|
406
|
+
* - The first run happens synchronously during `effect()` creation.
|
|
407
|
+
* - With the default runtime strategy, later re-runs are queued until
|
|
408
|
+
* `rt.flush()`.
|
|
409
|
+
* - With `createRuntime({ effectStrategy: "sab" })`, invalidations stay lazy
|
|
410
|
+
* during propagation but auto-deliver after the outermost `rt.batch()`.
|
|
411
|
+
* - With `createRuntime({ effectStrategy: "eager" })`, invalidations flush
|
|
412
|
+
* automatically.
|
|
413
|
+
* - Reads performed inside cleanup do not become dependencies of the next run.
|
|
414
|
+
* - Disposing the returned scope prevents future re-runs.
|
|
415
|
+
*
|
|
416
|
+
* @see createRuntime
|
|
417
|
+
* @see computed
|
|
418
|
+
* @see memo
|
|
419
|
+
*/
|
|
278
420
|
declare function effect(fn: EffectFn): Destructor;
|
|
279
421
|
|
|
280
|
-
type EffectStrategy = "flush" | "eager";
|
|
422
|
+
type EffectStrategy = "flush" | "eager" | "sab";
|
|
281
423
|
|
|
282
424
|
interface RuntimeOptions {
|
|
425
|
+
/**
|
|
426
|
+
* Optional low-level runtime hooks forwarded to the execution context.
|
|
427
|
+
*
|
|
428
|
+
* These hooks are composed with Reflex's scheduler integration rather than
|
|
429
|
+
* replacing it.
|
|
430
|
+
*/
|
|
283
431
|
hooks?: EngineHooks;
|
|
432
|
+
/**
|
|
433
|
+
* Controls when invalidated effects are executed.
|
|
434
|
+
*
|
|
435
|
+
* - `"flush"` queues reruns until `rt.flush()` is called.
|
|
436
|
+
* - `"sab"` keeps lazy enqueue semantics but stabilizes effects after the
|
|
437
|
+
* outermost `rt.batch()` exits.
|
|
438
|
+
* - `"eager"` flushes reruns automatically.
|
|
439
|
+
*
|
|
440
|
+
* @default "flush"
|
|
441
|
+
*/
|
|
284
442
|
effectStrategy?: EffectStrategy;
|
|
285
443
|
}
|
|
444
|
+
/**
|
|
445
|
+
* Push-based event stream that allows observers to subscribe to future values.
|
|
446
|
+
*
|
|
447
|
+
* `Event` is the read-only view of an event source. It does not expose
|
|
448
|
+
* mutation, only observation.
|
|
449
|
+
*
|
|
450
|
+
* @typeParam T - Event payload type.
|
|
451
|
+
*/
|
|
286
452
|
interface Event<T> {
|
|
453
|
+
/**
|
|
454
|
+
* Registers a callback for future event deliveries.
|
|
455
|
+
*
|
|
456
|
+
* @param fn - Callback invoked for each emitted value.
|
|
457
|
+
*
|
|
458
|
+
* @returns Destructor that unsubscribes `fn`.
|
|
459
|
+
*/
|
|
287
460
|
subscribe(fn: (value: T) => void): Destructor;
|
|
288
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Mutable event source created by `Runtime.event()`.
|
|
464
|
+
*
|
|
465
|
+
* @typeParam T - Event payload type.
|
|
466
|
+
*/
|
|
289
467
|
interface EventSource<T> extends Event<T> {
|
|
468
|
+
/**
|
|
469
|
+
* Emits a value to current subscribers using the runtime dispatcher.
|
|
470
|
+
*
|
|
471
|
+
* Nested emits are queued after the current delivery completes, preserving
|
|
472
|
+
* FIFO ordering across sources created by the same runtime.
|
|
473
|
+
*
|
|
474
|
+
* @param value - Event payload to deliver.
|
|
475
|
+
*/
|
|
290
476
|
emit(value: T): void;
|
|
291
477
|
}
|
|
478
|
+
/**
|
|
479
|
+
* Connected Reflex runtime returned by `createRuntime()`.
|
|
480
|
+
*
|
|
481
|
+
* The runtime owns the event dispatcher, effect scheduler, and execution
|
|
482
|
+
* context used by the top-level Reflex primitives.
|
|
483
|
+
*/
|
|
292
484
|
interface Runtime {
|
|
485
|
+
batch<T>(fn: () => T): T;
|
|
486
|
+
/**
|
|
487
|
+
* Creates a new mutable event source associated with this runtime.
|
|
488
|
+
*
|
|
489
|
+
* @typeParam T - Event payload type.
|
|
490
|
+
*
|
|
491
|
+
* @returns Event source with `emit(value)` and `subscribe(fn)`.
|
|
492
|
+
*/
|
|
293
493
|
event<T>(): EventSource<T>;
|
|
494
|
+
/**
|
|
495
|
+
* Flushes queued effect re-runs immediately.
|
|
496
|
+
*
|
|
497
|
+
* In the default `"flush"` strategy, call this after writes when you want
|
|
498
|
+
* scheduled effects to observe the latest stable snapshot. In `"sab"` and
|
|
499
|
+
* `"eager"` it remains available as an explicit synchronization escape hatch.
|
|
500
|
+
*/
|
|
294
501
|
flush(): void;
|
|
502
|
+
/**
|
|
503
|
+
* Underlying execution context used by this runtime.
|
|
504
|
+
*
|
|
505
|
+
* Most application code does not need this. It exists for low-level
|
|
506
|
+
* integrations, tests, and diagnostics.
|
|
507
|
+
*/
|
|
295
508
|
readonly ctx: ExecutionContext;
|
|
296
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* Creates and installs the active Reflex runtime.
|
|
512
|
+
*
|
|
513
|
+
* `createRuntime` wires together an execution context, effect scheduler, and
|
|
514
|
+
* event dispatcher, then makes that context the default runtime used by the
|
|
515
|
+
* top-level Reflex primitives exported from this package.
|
|
516
|
+
*
|
|
517
|
+
* @param options - Optional runtime configuration:
|
|
518
|
+
* - `effectStrategy` controls whether invalidated effects flush on
|
|
519
|
+
* `rt.flush()`, stabilize after the outermost batch, or run automatically.
|
|
520
|
+
* - `hooks` installs low-level runtime hooks that are composed with Reflex's
|
|
521
|
+
* scheduler integration.
|
|
522
|
+
*
|
|
523
|
+
* @returns Connected runtime with event creation, flushing, and context
|
|
524
|
+
* access.
|
|
525
|
+
*
|
|
526
|
+
* @example
|
|
527
|
+
* ```ts
|
|
528
|
+
* const rt = createRuntime();
|
|
529
|
+
* const ticks = rt.event<number>();
|
|
530
|
+
* const [count, setCount] = signal(0);
|
|
531
|
+
*
|
|
532
|
+
* ticks.subscribe((value) => {
|
|
533
|
+
* setCount((current) => current + value);
|
|
534
|
+
* });
|
|
535
|
+
*
|
|
536
|
+
* effect(() => {
|
|
537
|
+
* console.log(count());
|
|
538
|
+
* });
|
|
539
|
+
*
|
|
540
|
+
* ticks.emit(1);
|
|
541
|
+
* rt.flush();
|
|
542
|
+
* ```
|
|
543
|
+
*
|
|
544
|
+
* @remarks
|
|
545
|
+
* - Call this once during app startup or per test case to establish the active
|
|
546
|
+
* runtime.
|
|
547
|
+
* - Creating a new runtime replaces the previously active default context.
|
|
548
|
+
* - `rt.flush()` is primarily for scheduled effects; signals and computed
|
|
549
|
+
* reads stay current without it.
|
|
550
|
+
* - Event sources created by `rt.event()` share one dispatcher and preserve
|
|
551
|
+
* FIFO delivery order.
|
|
552
|
+
*/
|
|
297
553
|
declare function createRuntime(options?: RuntimeOptions): Runtime;
|
|
298
554
|
|
|
299
555
|
type EventValue<E extends Event<unknown>> = E extends Event<infer T> ? T : never;
|
|
@@ -413,9 +669,54 @@ declare function scan<T, A>(source: Event<T>, seed: A, reducer: (acc: A, value:
|
|
|
413
669
|
*/
|
|
414
670
|
declare function hold<T>(source: Event<T>, initial: T): [read: Accessor<T>, dispose: Destructor];
|
|
415
671
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
672
|
+
/**
|
|
673
|
+
* Creates writable reactive state.
|
|
674
|
+
*
|
|
675
|
+
* `signal` returns a tuple containing a tracked read accessor and a setter.
|
|
676
|
+
* Reading the accessor inside `computed()`, `memo()`, or `effect()` registers
|
|
677
|
+
* a dependency. Writing through the setter updates the stored value
|
|
678
|
+
* synchronously and invalidates downstream reactive consumers only when the
|
|
679
|
+
* value actually changes.
|
|
680
|
+
*
|
|
681
|
+
* @typeParam T - Signal value type.
|
|
682
|
+
*
|
|
683
|
+
* @param initialValue - Initial signal value returned until a later write
|
|
684
|
+
* replaces it.
|
|
685
|
+
* @param options - Optional development diagnostics. `options.name` is used
|
|
686
|
+
* only in development builds when formatting setter error messages.
|
|
687
|
+
*
|
|
688
|
+
* @returns A readonly tuple:
|
|
689
|
+
* - `value` - tracked accessor that returns the current signal value.
|
|
690
|
+
* - `setValue` - setter that accepts either a direct value or an updater
|
|
691
|
+
* function receiving the previous value. The setter returns the committed
|
|
692
|
+
* next value.
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* ```ts
|
|
696
|
+
* createRuntime();
|
|
697
|
+
*
|
|
698
|
+
* const [count, setCount] = signal(0);
|
|
699
|
+
*
|
|
700
|
+
* console.log(count()); // 0
|
|
701
|
+
*
|
|
702
|
+
* setCount(1);
|
|
703
|
+
* setCount((prev) => prev + 1);
|
|
704
|
+
*
|
|
705
|
+
* console.log(count()); // 2
|
|
706
|
+
* ```
|
|
707
|
+
*
|
|
708
|
+
* @remarks
|
|
709
|
+
* - Reads are synchronous and always return the latest committed value.
|
|
710
|
+
* - Same-value writes do not invalidate downstream computed values or effects.
|
|
711
|
+
* - Calling `setValue()` with no argument is only valid when `T` includes
|
|
712
|
+
* `undefined`.
|
|
713
|
+
* - In typical app code, call `createRuntime()` during setup before building
|
|
714
|
+
* the rest of the reactive graph.
|
|
715
|
+
*
|
|
716
|
+
* @see computed
|
|
717
|
+
* @see memo
|
|
718
|
+
* @see effect
|
|
719
|
+
*/
|
|
720
|
+
declare function signal<T>(initialValue: T): readonly [Accessor<T>, Setter<T>];
|
|
420
721
|
|
|
421
|
-
export { computed, createRuntime, effect, filter, hold, map, memo, merge, scan, signal, subscribeOnce };
|
|
722
|
+
export { computed, createRuntime, effect, filter, hold, map, memo, merge, scan, signal, subscribeOnce, withEffectCleanupRegistrar };
|
package/dist/unstable/index.d.ts
CHANGED
|
@@ -1,31 +1,226 @@
|
|
|
1
1
|
/// <reference path="../globals.d.ts" />
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Current lifecycle state of a resource request.
|
|
5
|
+
*
|
|
6
|
+
* - `"idle"` - no active request is in flight.
|
|
7
|
+
* - `"pending"` - the current request is still running.
|
|
8
|
+
* - `"resolved"` - the latest current request completed successfully.
|
|
9
|
+
* - `"rejected"` - the latest current request failed.
|
|
10
|
+
*/
|
|
3
11
|
type ResourceStatus = "idle" | "pending" | "resolved" | "rejected";
|
|
12
|
+
/**
|
|
13
|
+
* Guard object bound to a single resource request token.
|
|
14
|
+
*
|
|
15
|
+
* Loaders receive a guard so they can tell whether their work is still current
|
|
16
|
+
* before committing side effects or returning follow-up work.
|
|
17
|
+
*/
|
|
4
18
|
interface ResourceGuard {
|
|
19
|
+
/**
|
|
20
|
+
* Monotonic token identifying the request that produced this guard.
|
|
21
|
+
*/
|
|
5
22
|
readonly token: number;
|
|
23
|
+
/**
|
|
24
|
+
* Returns `true` while this request is still current and the resource has not
|
|
25
|
+
* been disposed.
|
|
26
|
+
*/
|
|
6
27
|
alive(): boolean;
|
|
7
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Mutable handle for manually settling a resource request.
|
|
31
|
+
*
|
|
32
|
+
* Manual resources expose this handle from `start()`. It extends
|
|
33
|
+
* `ResourceGuard` with methods that attempt to resolve or reject the current
|
|
34
|
+
* request.
|
|
35
|
+
*
|
|
36
|
+
* @typeParam T - Resolved value type.
|
|
37
|
+
* @typeParam E - Rejection type.
|
|
38
|
+
*/
|
|
8
39
|
interface ResourceHandle<T, E = unknown> extends ResourceGuard {
|
|
40
|
+
/**
|
|
41
|
+
* Resolves the request with `value`.
|
|
42
|
+
*
|
|
43
|
+
* @param value - Value to commit as the latest successful result.
|
|
44
|
+
*
|
|
45
|
+
* @returns `true` if the value was accepted, or `false` if this handle is
|
|
46
|
+
* stale or the resource has already been disposed.
|
|
47
|
+
*/
|
|
9
48
|
resolve(value: T): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Rejects the request with `error`.
|
|
51
|
+
*
|
|
52
|
+
* @param error - Error to commit as the latest failure.
|
|
53
|
+
*
|
|
54
|
+
* @returns `true` if the error was accepted, or `false` if this handle is
|
|
55
|
+
* stale or the resource has already been disposed.
|
|
56
|
+
*/
|
|
10
57
|
reject(error: E): boolean;
|
|
11
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Reactive view of a resource request lifecycle.
|
|
61
|
+
*
|
|
62
|
+
* A resource exposes tracked accessors for its status, latest value, latest
|
|
63
|
+
* error, and current request token, along with imperative controls to reset or
|
|
64
|
+
* dispose the resource.
|
|
65
|
+
*
|
|
66
|
+
* @typeParam T - Resolved value type.
|
|
67
|
+
* @typeParam E - Rejection type.
|
|
68
|
+
*/
|
|
12
69
|
interface Resource<T, E = unknown> {
|
|
70
|
+
/**
|
|
71
|
+
* Tracked accessor that returns the current request lifecycle state.
|
|
72
|
+
*/
|
|
13
73
|
readonly status: Accessor<ResourceStatus>;
|
|
74
|
+
/**
|
|
75
|
+
* Tracked accessor that returns the latest successfully resolved value, if
|
|
76
|
+
* one exists.
|
|
77
|
+
*
|
|
78
|
+
* The last resolved value is retained while a newer request is pending or
|
|
79
|
+
* rejected.
|
|
80
|
+
*/
|
|
14
81
|
readonly value: Accessor<T | undefined>;
|
|
82
|
+
/**
|
|
83
|
+
* Tracked accessor that returns the latest rejected error, if one exists.
|
|
84
|
+
*/
|
|
15
85
|
readonly error: Accessor<E | undefined>;
|
|
86
|
+
/**
|
|
87
|
+
* Tracked accessor that returns the current monotonic request token.
|
|
88
|
+
*
|
|
89
|
+
* The token increments whenever a new request starts, the resource is
|
|
90
|
+
* cleared, or the resource is disposed.
|
|
91
|
+
*/
|
|
16
92
|
readonly token: Accessor<number>;
|
|
93
|
+
/**
|
|
94
|
+
* Resets the resource to the `"idle"` state and invalidates any in-flight
|
|
95
|
+
* request.
|
|
96
|
+
*/
|
|
17
97
|
clear(): void;
|
|
98
|
+
/**
|
|
99
|
+
* Disposes the resource permanently and invalidates any in-flight request.
|
|
100
|
+
*
|
|
101
|
+
* After disposal, the resource stops reacting to future source changes or
|
|
102
|
+
* refetch requests.
|
|
103
|
+
*/
|
|
18
104
|
dispose(): void;
|
|
19
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Imperative resource whose requests are started and settled manually.
|
|
108
|
+
*
|
|
109
|
+
* @typeParam T - Resolved value type.
|
|
110
|
+
* @typeParam E - Rejection type.
|
|
111
|
+
*/
|
|
20
112
|
interface ManualResource<T, E = unknown> extends Resource<T, E> {
|
|
113
|
+
/**
|
|
114
|
+
* Starts a new request and returns a handle that can resolve or reject it.
|
|
115
|
+
*
|
|
116
|
+
* Starting a new request invalidates all older handles.
|
|
117
|
+
*/
|
|
21
118
|
start(): ResourceHandle<T, E>;
|
|
22
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Auto-loading resource backed by a loader function.
|
|
122
|
+
*
|
|
123
|
+
* @typeParam T - Resolved value type.
|
|
124
|
+
* @typeParam E - Rejection type.
|
|
125
|
+
*/
|
|
23
126
|
interface AsyncResource<T, E = unknown> extends Resource<T, E> {
|
|
127
|
+
/**
|
|
128
|
+
* Requests another load.
|
|
129
|
+
*
|
|
130
|
+
* In the default runtime strategy, the reload starts when the surrounding
|
|
131
|
+
* runtime flushes scheduled effects.
|
|
132
|
+
*/
|
|
24
133
|
refetch(): void;
|
|
25
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Loader used by `resource(load)`.
|
|
137
|
+
*
|
|
138
|
+
* It receives a guard for the current request and may return either a value or
|
|
139
|
+
* a promise-like value.
|
|
140
|
+
*
|
|
141
|
+
* @typeParam T - Resolved value type.
|
|
142
|
+
*/
|
|
26
143
|
type ResourceJob<T> = (guard: ResourceGuard) => T | PromiseLike<T>;
|
|
144
|
+
/**
|
|
145
|
+
* Loader used by `resource(source, load)`.
|
|
146
|
+
*
|
|
147
|
+
* It receives the latest source value plus a guard for the current request, and
|
|
148
|
+
* may return either a value or a promise-like value.
|
|
149
|
+
*
|
|
150
|
+
* @typeParam S - Source value type.
|
|
151
|
+
* @typeParam T - Resolved value type.
|
|
152
|
+
*/
|
|
27
153
|
type ResourceLoader<S, T> = (source: S, guard: ResourceGuard) => T | PromiseLike<T>;
|
|
154
|
+
/**
|
|
155
|
+
* Returns `true` when the resource's current request is pending.
|
|
156
|
+
*
|
|
157
|
+
* @param resource - Resource to inspect.
|
|
158
|
+
*
|
|
159
|
+
* @returns Whether `resource.status()` currently equals `"pending"`.
|
|
160
|
+
*/
|
|
28
161
|
declare function isPending(resource: Resource<unknown, unknown>): boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Creates an unstable resource for manual or loader-driven async state.
|
|
164
|
+
*
|
|
165
|
+
* `resource` models request lifecycles with tracked accessors for `status`,
|
|
166
|
+
* `value`, `error`, and `token`. It can be used in three modes:
|
|
167
|
+
*
|
|
168
|
+
* - `resource<T, E>()` creates a manual resource. Call `start()` to begin a
|
|
169
|
+
* request, then settle that request through the returned handle.
|
|
170
|
+
* - `resource(load)` creates an auto-loading resource that starts immediately
|
|
171
|
+
* and can be reloaded with `refetch()`.
|
|
172
|
+
* - `resource(source, load)` creates a source-driven resource that reloads
|
|
173
|
+
* whenever `source()` changes.
|
|
174
|
+
*
|
|
175
|
+
* @typeParam S - Source value type for the source-driven overload.
|
|
176
|
+
* @typeParam T - Resolved value type.
|
|
177
|
+
* @typeParam E - Rejection type tracked by `error()`.
|
|
178
|
+
*
|
|
179
|
+
* @param sourceOrLoad - Either the reactive source accessor to watch or the
|
|
180
|
+
* no-source loader function, depending on the overload.
|
|
181
|
+
* @param maybeLoad - Loader used with the source-driven overload.
|
|
182
|
+
*
|
|
183
|
+
* @returns Either a `ManualResource` or an `AsyncResource`, depending on the
|
|
184
|
+
* selected overload.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```ts
|
|
188
|
+
* import { createRuntime, signal } from "@volynets/reflex";
|
|
189
|
+
* import { resource } from "@volynets/reflex/unstable";
|
|
190
|
+
*
|
|
191
|
+
* const rt = createRuntime();
|
|
192
|
+
* const [userId, setUserId] = signal(1);
|
|
193
|
+
*
|
|
194
|
+
* const user = resource(() => userId(), async (id) => {
|
|
195
|
+
* await Promise.resolve();
|
|
196
|
+
* return { id, name: `user-${id}` };
|
|
197
|
+
* });
|
|
198
|
+
*
|
|
199
|
+
* console.log(user.status()); // "pending"
|
|
200
|
+
*
|
|
201
|
+
* setUserId(2);
|
|
202
|
+
* rt.flush();
|
|
203
|
+
* ```
|
|
204
|
+
*
|
|
205
|
+
* @remarks
|
|
206
|
+
* - This API is exported from `@volynets/reflex/unstable` and may change
|
|
207
|
+
* between releases.
|
|
208
|
+
* - Each new request increments `token()`. Older handles and stale async
|
|
209
|
+
* resolutions are ignored automatically.
|
|
210
|
+
* - `value()` retains the last resolved value while a newer request is pending
|
|
211
|
+
* or rejected.
|
|
212
|
+
* - `clear()` resets the resource to `"idle"` and invalidates the current
|
|
213
|
+
* request.
|
|
214
|
+
* - `dispose()` invalidates the current request and permanently stops future
|
|
215
|
+
* updates.
|
|
216
|
+
* - `refetch()` and source changes schedule a new load through the runtime.
|
|
217
|
+
* With the default effect strategy, call `rt.flush()` to start it.
|
|
218
|
+
* - If a source accessor or loader throws synchronously, the current request
|
|
219
|
+
* is rejected with that error.
|
|
220
|
+
*
|
|
221
|
+
* @see isPending
|
|
222
|
+
* @see createRuntime
|
|
223
|
+
*/
|
|
29
224
|
declare function resource<T, E = unknown>(): ManualResource<T, E>;
|
|
30
225
|
declare function resource<T, E = unknown>(load: ResourceJob<T>): AsyncResource<T, E>;
|
|
31
226
|
declare function resource<S, T, E = unknown>(source: Accessor<S>, load: ResourceLoader<S, T>): AsyncResource<T, E>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volynets/reflex",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Public Reflex facade with a connected runtime",
|
|
6
6
|
"license": "MIT",
|
|
@@ -10,17 +10,26 @@
|
|
|
10
10
|
"sideEffects": false,
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
+
"source": {
|
|
14
|
+
"types": "./src/index.ts",
|
|
15
|
+
"default": "./src/index.ts"
|
|
16
|
+
},
|
|
13
17
|
"types": "./dist/globals.d.ts",
|
|
14
18
|
"import": "./dist/esm/index.js",
|
|
15
19
|
"require": "./dist/cjs/index.cjs"
|
|
16
20
|
},
|
|
17
21
|
"./unstable": {
|
|
22
|
+
"source": {
|
|
23
|
+
"types": "./src/unstable/index.ts",
|
|
24
|
+
"default": "./src/unstable/index.ts"
|
|
25
|
+
},
|
|
18
26
|
"types": "./dist/unstable/index.d.ts",
|
|
19
27
|
"import": "./dist/esm/unstable/index.js",
|
|
20
28
|
"require": "./dist/cjs/unstable/index.cjs"
|
|
21
29
|
}
|
|
22
30
|
},
|
|
23
31
|
"files": [
|
|
32
|
+
"src",
|
|
24
33
|
"dist/cjs",
|
|
25
34
|
"dist/esm",
|
|
26
35
|
"dist/globals.d.ts",
|