@czap/core 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.
- package/LICENSE +21 -0
- package/README.md +19 -0
- package/dist/addressed-digest.d.ts +15 -0
- package/dist/addressed-digest.d.ts.map +1 -0
- package/dist/addressed-digest.js +35 -0
- package/dist/addressed-digest.js.map +1 -0
- package/dist/animation.d.ts +46 -0
- package/dist/animation.d.ts.map +1 -0
- package/dist/animation.js +70 -0
- package/dist/animation.js.map +1 -0
- package/dist/assembly.d.ts +25 -0
- package/dist/assembly.d.ts.map +1 -0
- package/dist/assembly.js +58 -0
- package/dist/assembly.js.map +1 -0
- package/dist/av-bridge.d.ts +74 -0
- package/dist/av-bridge.d.ts.map +1 -0
- package/dist/av-bridge.js +107 -0
- package/dist/av-bridge.js.map +1 -0
- package/dist/av-renderer.d.ts +56 -0
- package/dist/av-renderer.d.ts.map +1 -0
- package/dist/av-renderer.js +65 -0
- package/dist/av-renderer.js.map +1 -0
- package/dist/blend.d.ts +61 -0
- package/dist/blend.d.ts.map +1 -0
- package/dist/blend.js +100 -0
- package/dist/blend.js.map +1 -0
- package/dist/boundary.d.ts +154 -0
- package/dist/boundary.d.ts.map +1 -0
- package/dist/boundary.js +269 -0
- package/dist/boundary.js.map +1 -0
- package/dist/brands.d.ts +63 -0
- package/dist/brands.d.ts.map +1 -0
- package/dist/brands.js +31 -0
- package/dist/brands.js.map +1 -0
- package/dist/caps.d.ts +49 -0
- package/dist/caps.d.ts.map +1 -0
- package/dist/caps.js +73 -0
- package/dist/caps.js.map +1 -0
- package/dist/capsule.d.ts +77 -0
- package/dist/capsule.d.ts.map +1 -0
- package/dist/capsule.js +18 -0
- package/dist/capsule.js.map +1 -0
- package/dist/capsules/boundary-evaluate.d.ts +28 -0
- package/dist/capsules/boundary-evaluate.d.ts.map +1 -0
- package/dist/capsules/boundary-evaluate.js +117 -0
- package/dist/capsules/boundary-evaluate.js.map +1 -0
- package/dist/capsules/canonical-cbor.d.ts +13 -0
- package/dist/capsules/canonical-cbor.d.ts.map +1 -0
- package/dist/capsules/canonical-cbor.js +60 -0
- package/dist/capsules/canonical-cbor.js.map +1 -0
- package/dist/capsules/token-buffer.d.ts +24 -0
- package/dist/capsules/token-buffer.d.ts.map +1 -0
- package/dist/capsules/token-buffer.js +53 -0
- package/dist/capsules/token-buffer.js.map +1 -0
- package/dist/capture.d.ts +40 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +10 -0
- package/dist/capture.js.map +1 -0
- package/dist/cbor.d.ts +33 -0
- package/dist/cbor.d.ts.map +1 -0
- package/dist/cbor.js +179 -0
- package/dist/cbor.js.map +1 -0
- package/dist/cell.d.ts +53 -0
- package/dist/cell.d.ts.map +1 -0
- package/dist/cell.js +83 -0
- package/dist/cell.js.map +1 -0
- package/dist/codec.d.ts +30 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +25 -0
- package/dist/codec.js.map +1 -0
- package/dist/component.d.ts +52 -0
- package/dist/component.d.ts.map +1 -0
- package/dist/component.js +44 -0
- package/dist/component.js.map +1 -0
- package/dist/composable.d.ts +76 -0
- package/dist/composable.d.ts.map +1 -0
- package/dist/composable.js +221 -0
- package/dist/composable.js.map +1 -0
- package/dist/compositor-pool.d.ts +74 -0
- package/dist/compositor-pool.d.ts.map +1 -0
- package/dist/compositor-pool.js +119 -0
- package/dist/compositor-pool.js.map +1 -0
- package/dist/compositor.d.ts +90 -0
- package/dist/compositor.d.ts.map +1 -0
- package/dist/compositor.js +278 -0
- package/dist/compositor.js.map +1 -0
- package/dist/config.d.ts +72 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +97 -0
- package/dist/config.js.map +1 -0
- package/dist/dag.d.ts +251 -0
- package/dist/dag.d.ts.map +1 -0
- package/dist/dag.js +450 -0
- package/dist/dag.js.map +1 -0
- package/dist/defaults.d.ts +45 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +45 -0
- package/dist/defaults.js.map +1 -0
- package/dist/derived.d.ts +34 -0
- package/dist/derived.d.ts.map +1 -0
- package/dist/derived.js +101 -0
- package/dist/derived.js.map +1 -0
- package/dist/diagnostics.d.ts +77 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +122 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/dirty.d.ts +55 -0
- package/dist/dirty.d.ts.map +1 -0
- package/dist/dirty.js +80 -0
- package/dist/dirty.js.map +1 -0
- package/dist/easing.d.ts +55 -0
- package/dist/easing.d.ts.map +1 -0
- package/dist/easing.js +291 -0
- package/dist/easing.js.map +1 -0
- package/dist/ecs.d.ts +105 -0
- package/dist/ecs.d.ts.map +1 -0
- package/dist/ecs.js +245 -0
- package/dist/ecs.js.map +1 -0
- package/dist/fnv.d.ts +14 -0
- package/dist/fnv.d.ts.map +1 -0
- package/dist/fnv.js +28 -0
- package/dist/fnv.js.map +1 -0
- package/dist/frame-budget.d.ts +73 -0
- package/dist/frame-budget.d.ts.map +1 -0
- package/dist/frame-budget.js +114 -0
- package/dist/frame-budget.js.map +1 -0
- package/dist/gen-frame.d.ts +102 -0
- package/dist/gen-frame.d.ts.map +1 -0
- package/dist/gen-frame.js +121 -0
- package/dist/gen-frame.js.map +1 -0
- package/dist/harness/arbitrary-from-schema.d.ts +28 -0
- package/dist/harness/arbitrary-from-schema.d.ts.map +1 -0
- package/dist/harness/arbitrary-from-schema.js +262 -0
- package/dist/harness/arbitrary-from-schema.js.map +1 -0
- package/dist/harness/cached-projection.d.ts +19 -0
- package/dist/harness/cached-projection.d.ts.map +1 -0
- package/dist/harness/cached-projection.js +39 -0
- package/dist/harness/cached-projection.js.map +1 -0
- package/dist/harness/index.d.ts +16 -0
- package/dist/harness/index.d.ts.map +1 -0
- package/dist/harness/index.js +15 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/policy-gate.d.ts +18 -0
- package/dist/harness/policy-gate.d.ts.map +1 -0
- package/dist/harness/policy-gate.js +46 -0
- package/dist/harness/policy-gate.js.map +1 -0
- package/dist/harness/pure-transform.d.ts +42 -0
- package/dist/harness/pure-transform.d.ts.map +1 -0
- package/dist/harness/pure-transform.js +76 -0
- package/dist/harness/pure-transform.js.map +1 -0
- package/dist/harness/receipted-mutation.d.ts +23 -0
- package/dist/harness/receipted-mutation.d.ts.map +1 -0
- package/dist/harness/receipted-mutation.js +52 -0
- package/dist/harness/receipted-mutation.js.map +1 -0
- package/dist/harness/scene-composition.d.ts +19 -0
- package/dist/harness/scene-composition.d.ts.map +1 -0
- package/dist/harness/scene-composition.js +47 -0
- package/dist/harness/scene-composition.js.map +1 -0
- package/dist/harness/site-adapter.d.ts +18 -0
- package/dist/harness/site-adapter.d.ts.map +1 -0
- package/dist/harness/site-adapter.js +38 -0
- package/dist/harness/site-adapter.js.map +1 -0
- package/dist/harness/state-machine.d.ts +19 -0
- package/dist/harness/state-machine.d.ts.map +1 -0
- package/dist/harness/state-machine.js +44 -0
- package/dist/harness/state-machine.js.map +1 -0
- package/dist/hlc.d.ts +99 -0
- package/dist/hlc.d.ts.map +1 -0
- package/dist/hlc.js +219 -0
- package/dist/hlc.js.map +1 -0
- package/dist/index.d.ts +104 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +137 -0
- package/dist/index.js.map +1 -0
- package/dist/interpolate.d.ts +14 -0
- package/dist/interpolate.d.ts.map +1 -0
- package/dist/interpolate.js +31 -0
- package/dist/interpolate.js.map +1 -0
- package/dist/live-cell.d.ts +46 -0
- package/dist/live-cell.d.ts.map +1 -0
- package/dist/live-cell.js +154 -0
- package/dist/live-cell.js.map +1 -0
- package/dist/op.d.ts +58 -0
- package/dist/op.d.ts.map +1 -0
- package/dist/op.js +171 -0
- package/dist/op.js.map +1 -0
- package/dist/plan.d.ts +195 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +211 -0
- package/dist/plan.js.map +1 -0
- package/dist/protocol.d.ts +33 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +10 -0
- package/dist/protocol.js.map +1 -0
- package/dist/quantizer-types.d.ts +28 -0
- package/dist/quantizer-types.d.ts.map +1 -0
- package/dist/quantizer-types.js +9 -0
- package/dist/quantizer-types.js.map +1 -0
- package/dist/receipt.d.ts +294 -0
- package/dist/receipt.d.ts.map +1 -0
- package/dist/receipt.js +352 -0
- package/dist/receipt.js.map +1 -0
- package/dist/runtime-coordinator.d.ts +75 -0
- package/dist/runtime-coordinator.d.ts.map +1 -0
- package/dist/runtime-coordinator.js +149 -0
- package/dist/runtime-coordinator.js.map +1 -0
- package/dist/scheduler.d.ts +58 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +109 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/ship-capsule.d.ts +54 -0
- package/dist/ship-capsule.d.ts.map +1 -0
- package/dist/ship-capsule.js +142 -0
- package/dist/ship-capsule.js.map +1 -0
- package/dist/ship-manifest.d.ts +45 -0
- package/dist/ship-manifest.d.ts.map +1 -0
- package/dist/ship-manifest.js +175 -0
- package/dist/ship-manifest.js.map +1 -0
- package/dist/signal.d.ts +149 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/signal.js +277 -0
- package/dist/signal.js.map +1 -0
- package/dist/speculative.d.ts +67 -0
- package/dist/speculative.d.ts.map +1 -0
- package/dist/speculative.js +139 -0
- package/dist/speculative.js.map +1 -0
- package/dist/store.d.ts +39 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +42 -0
- package/dist/store.js.map +1 -0
- package/dist/style.d.ts +119 -0
- package/dist/style.d.ts.map +1 -0
- package/dist/style.js +168 -0
- package/dist/style.js.map +1 -0
- package/dist/testing.d.ts +14 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +14 -0
- package/dist/testing.js.map +1 -0
- package/dist/theme.d.ts +78 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +109 -0
- package/dist/theme.js.map +1 -0
- package/dist/timeline.d.ts +45 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +101 -0
- package/dist/timeline.js.map +1 -0
- package/dist/token-buffer.d.ts +43 -0
- package/dist/token-buffer.d.ts.map +1 -0
- package/dist/token-buffer.js +112 -0
- package/dist/token-buffer.js.map +1 -0
- package/dist/token.d.ts +107 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +143 -0
- package/dist/token.js.map +1 -0
- package/dist/tuple.d.ts +16 -0
- package/dist/tuple.d.ts.map +1 -0
- package/dist/tuple.js +16 -0
- package/dist/tuple.js.map +1 -0
- package/dist/type-utils.d.ts +41 -0
- package/dist/type-utils.d.ts.map +1 -0
- package/dist/type-utils.js +10 -0
- package/dist/type-utils.js.map +1 -0
- package/dist/typed-ref.d.ts +50 -0
- package/dist/typed-ref.d.ts.map +1 -0
- package/dist/typed-ref.js +59 -0
- package/dist/typed-ref.js.map +1 -0
- package/dist/ui-quality.d.ts +50 -0
- package/dist/ui-quality.d.ts.map +1 -0
- package/dist/ui-quality.js +64 -0
- package/dist/ui-quality.js.map +1 -0
- package/dist/validation-error.d.ts +25 -0
- package/dist/validation-error.d.ts.map +1 -0
- package/dist/validation-error.js +32 -0
- package/dist/validation-error.js.map +1 -0
- package/dist/vector-clock.d.ts +46 -0
- package/dist/vector-clock.d.ts.map +1 -0
- package/dist/vector-clock.js +91 -0
- package/dist/vector-clock.js.map +1 -0
- package/dist/video.d.ts +62 -0
- package/dist/video.d.ts.map +1 -0
- package/dist/video.js +59 -0
- package/dist/video.js.map +1 -0
- package/dist/wasm-dispatch.d.ts +52 -0
- package/dist/wasm-dispatch.d.ts.map +1 -0
- package/dist/wasm-dispatch.js +204 -0
- package/dist/wasm-dispatch.js.map +1 -0
- package/dist/wasm-fallback.d.ts +19 -0
- package/dist/wasm-fallback.d.ts.map +1 -0
- package/dist/wasm-fallback.js +93 -0
- package/dist/wasm-fallback.js.map +1 -0
- package/dist/wire.d.ts +49 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +201 -0
- package/dist/wire.js.map +1 -0
- package/dist/zap.d.ts +42 -0
- package/dist/zap.d.ts.map +1 -0
- package/dist/zap.js +172 -0
- package/dist/zap.js.map +1 -0
- package/package.json +71 -0
- package/src/addressed-digest.ts +48 -0
- package/src/animation.ts +103 -0
- package/src/assembly.ts +76 -0
- package/src/av-bridge.ts +161 -0
- package/src/av-renderer.ts +118 -0
- package/src/blend.ts +135 -0
- package/src/boundary.ts +363 -0
- package/src/brands.ts +86 -0
- package/src/caps.ts +100 -0
- package/src/capsule.ts +95 -0
- package/src/capsules/boundary-evaluate.ts +128 -0
- package/src/capsules/canonical-cbor.ts +60 -0
- package/src/capsules/token-buffer.ts +57 -0
- package/src/capture.ts +48 -0
- package/src/cbor.ts +199 -0
- package/src/cell.ts +130 -0
- package/src/codec.ts +39 -0
- package/src/component.ts +102 -0
- package/src/composable.ts +328 -0
- package/src/compositor-pool.ts +162 -0
- package/src/compositor.ts +387 -0
- package/src/config.ts +157 -0
- package/src/dag.ts +527 -0
- package/src/defaults.ts +60 -0
- package/src/derived.ts +164 -0
- package/src/diagnostics.ts +186 -0
- package/src/dirty.ts +101 -0
- package/src/easing.ts +334 -0
- package/src/ecs.ts +382 -0
- package/src/fnv.ts +31 -0
- package/src/frame-budget.ts +149 -0
- package/src/gen-frame.ts +229 -0
- package/src/harness/arbitrary-from-schema.ts +270 -0
- package/src/harness/cached-projection.ts +46 -0
- package/src/harness/index.ts +16 -0
- package/src/harness/policy-gate.ts +51 -0
- package/src/harness/pure-transform.ts +121 -0
- package/src/harness/receipted-mutation.ts +59 -0
- package/src/harness/scene-composition.ts +54 -0
- package/src/harness/site-adapter.ts +43 -0
- package/src/harness/state-machine.ts +49 -0
- package/src/hlc.ts +238 -0
- package/src/index.ts +274 -0
- package/src/interpolate.ts +37 -0
- package/src/live-cell.ts +199 -0
- package/src/op.ts +233 -0
- package/src/plan.ts +317 -0
- package/src/protocol.ts +49 -0
- package/src/quantizer-types.ts +29 -0
- package/src/receipt.ts +444 -0
- package/src/runtime-coordinator.ts +230 -0
- package/src/scheduler.ts +161 -0
- package/src/ship-capsule.ts +191 -0
- package/src/signal.ts +345 -0
- package/src/speculative.ts +186 -0
- package/src/store.ts +77 -0
- package/src/style.ts +249 -0
- package/src/testing.ts +14 -0
- package/src/theme.ts +153 -0
- package/src/timeline.ts +146 -0
- package/src/token-buffer.ts +151 -0
- package/src/token.ts +197 -0
- package/src/tuple.ts +19 -0
- package/src/type-utils.ts +48 -0
- package/src/typed-ref.ts +79 -0
- package/src/ui-quality.ts +105 -0
- package/src/validation-error.ts +34 -0
- package/src/vector-clock.ts +111 -0
- package/src/video.ts +106 -0
- package/src/wasm-dispatch.ts +300 -0
- package/src/wasm-fallback.ts +102 -0
- package/src/wire.ts +274 -0
- package/src/zap.ts +241 -0
package/dist/signal.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signal -- live data feeds from the browser environment.
|
|
3
|
+
*
|
|
4
|
+
* (viewport, scroll, pointer, time, media queries, custom).
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import type { Stream, Scope } from 'effect';
|
|
9
|
+
import { Effect } from 'effect';
|
|
10
|
+
import type { AVBridge } from './av-bridge.js';
|
|
11
|
+
/** Tag of a {@link SignalSource} — the family of live data feed a signal binds to. */
|
|
12
|
+
export type SignalSourceType = 'viewport' | 'time' | 'pointer' | 'scroll' | 'media' | 'custom' | 'audio';
|
|
13
|
+
/**
|
|
14
|
+
* Configuration describing what a {@link Signal} reads from: viewport axis,
|
|
15
|
+
* time mode, pointer axis, scroll axis, media query, custom push source,
|
|
16
|
+
* or audio sample/normalized mode.
|
|
17
|
+
*/
|
|
18
|
+
export type SignalSource = {
|
|
19
|
+
readonly type: 'viewport';
|
|
20
|
+
readonly axis: 'width' | 'height';
|
|
21
|
+
} | {
|
|
22
|
+
readonly type: 'time';
|
|
23
|
+
readonly mode: 'elapsed' | 'absolute' | 'scheduled';
|
|
24
|
+
} | {
|
|
25
|
+
readonly type: 'pointer';
|
|
26
|
+
readonly axis: 'x' | 'y' | 'pressure';
|
|
27
|
+
} | {
|
|
28
|
+
readonly type: 'scroll';
|
|
29
|
+
readonly axis: 'x' | 'y' | 'progress';
|
|
30
|
+
} | {
|
|
31
|
+
readonly type: 'media';
|
|
32
|
+
readonly query: string;
|
|
33
|
+
} | {
|
|
34
|
+
readonly type: 'custom';
|
|
35
|
+
readonly id: string;
|
|
36
|
+
} | {
|
|
37
|
+
readonly type: 'audio';
|
|
38
|
+
readonly mode: 'sample' | 'normalized';
|
|
39
|
+
};
|
|
40
|
+
interface SignalShape<T> {
|
|
41
|
+
readonly source: SignalSource;
|
|
42
|
+
readonly current: Effect.Effect<T>;
|
|
43
|
+
readonly changes: Stream.Stream<T>;
|
|
44
|
+
}
|
|
45
|
+
interface ControllableSignalShape<T> extends SignalShape<T> {
|
|
46
|
+
seek(to: T): Effect.Effect<void>;
|
|
47
|
+
pause(): Effect.Effect<void>;
|
|
48
|
+
resume(): Effect.Effect<void>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create a reactive signal from a browser environment source.
|
|
52
|
+
*
|
|
53
|
+
* Returns a scoped Effect that sets up event listeners (resize, scroll,
|
|
54
|
+
* pointermove, etc.) and cleans them up when the scope closes. The signal
|
|
55
|
+
* exposes `.current` (latest value) and `.changes` (stream of updates).
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* import { Effect, Scope } from 'effect';
|
|
60
|
+
* import { Signal } from '@czap/core';
|
|
61
|
+
*
|
|
62
|
+
* const program = Effect.scoped(Effect.gen(function* () {
|
|
63
|
+
* const sig = yield* Signal.make({ type: 'viewport', axis: 'width' });
|
|
64
|
+
* const width = yield* sig.current;
|
|
65
|
+
* // width === current window.innerWidth
|
|
66
|
+
* }));
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
declare function _make(source: SignalSource): Effect.Effect<SignalShape<number>, never, Scope.Scope>;
|
|
70
|
+
/**
|
|
71
|
+
* Create a controllable time signal for video rendering / scrubbing.
|
|
72
|
+
*
|
|
73
|
+
* External code drives the signal value via seek(); no automatic ticking.
|
|
74
|
+
* Supports pause/resume to temporarily ignore seek updates.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* import { Effect } from 'effect';
|
|
79
|
+
* import { Signal } from '@czap/core';
|
|
80
|
+
*
|
|
81
|
+
* const program = Effect.scoped(Effect.gen(function* () {
|
|
82
|
+
* const ctrl = yield* Signal.controllable();
|
|
83
|
+
* yield* ctrl.seek(1500);
|
|
84
|
+
* const t = yield* ctrl.current;
|
|
85
|
+
* // t === 1500
|
|
86
|
+
* yield* ctrl.pause();
|
|
87
|
+
* yield* ctrl.seek(2000); // ignored while paused
|
|
88
|
+
* }));
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare function _controllable(): Effect.Effect<ControllableSignalShape<number>, never, Scope.Scope>;
|
|
92
|
+
interface AudioSignalShape extends SignalShape<number> {
|
|
93
|
+
poll(): Effect.Effect<number>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Create an audio signal backed by an AVBridge.
|
|
97
|
+
*
|
|
98
|
+
* In 'sample' mode, returns the raw sample index. In 'normalized' mode,
|
|
99
|
+
* returns a 0..1 progress value based on totalDurationSec. Call `.poll()`
|
|
100
|
+
* to read the latest sample from the bridge and update the signal.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* import { Effect } from 'effect';
|
|
105
|
+
* import { Signal } from '@czap/core';
|
|
106
|
+
*
|
|
107
|
+
* const program = Effect.scoped(Effect.gen(function* () {
|
|
108
|
+
* const audioSig = yield* Signal.audio(bridge, 'normalized', 120);
|
|
109
|
+
* const progress = yield* audioSig.poll();
|
|
110
|
+
* // progress is a number between 0 and 1
|
|
111
|
+
* }));
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
declare function _audio(bridge: AVBridge.Shape, mode?: 'sample' | 'normalized', totalDurationSec?: number): Effect.Effect<AudioSignalShape, never, Scope.Scope>;
|
|
115
|
+
/**
|
|
116
|
+
* Signal namespace -- live data feeds from the browser environment.
|
|
117
|
+
*
|
|
118
|
+
* Create reactive signals from viewport, scroll, pointer, time, media query,
|
|
119
|
+
* audio, or custom sources. Each signal provides `.current` and `.changes`
|
|
120
|
+
* backed by Effect's SubscriptionRef. Scoped for automatic listener cleanup.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* import { Effect } from 'effect';
|
|
125
|
+
* import { Signal } from '@czap/core';
|
|
126
|
+
*
|
|
127
|
+
* const program = Effect.scoped(Effect.gen(function* () {
|
|
128
|
+
* const viewport = yield* Signal.make({ type: 'viewport', axis: 'width' });
|
|
129
|
+
* const width = yield* viewport.current;
|
|
130
|
+
* const ctrl = yield* Signal.controllable();
|
|
131
|
+
* yield* ctrl.seek(500);
|
|
132
|
+
* }));
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
export declare const Signal: {
|
|
136
|
+
make: typeof _make;
|
|
137
|
+
controllable: typeof _controllable;
|
|
138
|
+
audio: typeof _audio;
|
|
139
|
+
};
|
|
140
|
+
export declare namespace Signal {
|
|
141
|
+
/** Structural shape of a passive {@link Signal}: `source` + `current` + `changes`. */
|
|
142
|
+
type Shape<T> = SignalShape<T>;
|
|
143
|
+
/** Structural shape of a seekable, pausable signal — e.g. driven by Remotion or a scrub UI. */
|
|
144
|
+
type Controllable<T> = ControllableSignalShape<T>;
|
|
145
|
+
/** Structural shape of an audio-sourced signal backed by an {@link AVBridge}. */
|
|
146
|
+
type Audio = AudioSignalShape;
|
|
147
|
+
}
|
|
148
|
+
export {};
|
|
149
|
+
//# sourceMappingURL=signal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../src/signal.ts"],"names":[],"mappings":"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"}
|
package/dist/signal.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signal -- live data feeds from the browser environment.
|
|
3
|
+
*
|
|
4
|
+
* (viewport, scroll, pointer, time, media queries, custom).
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { Effect, SubscriptionRef, Ref } from 'effect';
|
|
9
|
+
function initialValueForSource(source) {
|
|
10
|
+
switch (source.type) {
|
|
11
|
+
case 'viewport':
|
|
12
|
+
return typeof globalThis.window !== 'undefined'
|
|
13
|
+
? source.axis === 'width'
|
|
14
|
+
? window.innerWidth
|
|
15
|
+
: window.innerHeight
|
|
16
|
+
: 0;
|
|
17
|
+
case 'scroll':
|
|
18
|
+
if (typeof globalThis.window === 'undefined')
|
|
19
|
+
return 0;
|
|
20
|
+
if (source.axis === 'x')
|
|
21
|
+
return window.scrollX;
|
|
22
|
+
if (source.axis === 'y')
|
|
23
|
+
return window.scrollY;
|
|
24
|
+
{
|
|
25
|
+
const max = document.documentElement.scrollHeight - window.innerHeight;
|
|
26
|
+
return max > 0 ? window.scrollY / max : 0;
|
|
27
|
+
}
|
|
28
|
+
case 'pointer':
|
|
29
|
+
return 0;
|
|
30
|
+
case 'time':
|
|
31
|
+
return source.mode === 'absolute' ? Date.now() : 0;
|
|
32
|
+
case 'media':
|
|
33
|
+
return typeof globalThis.window !== 'undefined' && window.matchMedia(source.query).matches ? 1 : 0;
|
|
34
|
+
case 'custom':
|
|
35
|
+
return 0;
|
|
36
|
+
case 'audio':
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a reactive signal from a browser environment source.
|
|
42
|
+
*
|
|
43
|
+
* Returns a scoped Effect that sets up event listeners (resize, scroll,
|
|
44
|
+
* pointermove, etc.) and cleans them up when the scope closes. The signal
|
|
45
|
+
* exposes `.current` (latest value) and `.changes` (stream of updates).
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { Effect, Scope } from 'effect';
|
|
50
|
+
* import { Signal } from '@czap/core';
|
|
51
|
+
*
|
|
52
|
+
* const program = Effect.scoped(Effect.gen(function* () {
|
|
53
|
+
* const sig = yield* Signal.make({ type: 'viewport', axis: 'width' });
|
|
54
|
+
* const width = yield* sig.current;
|
|
55
|
+
* // width === current window.innerWidth
|
|
56
|
+
* }));
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
function _make(source) {
|
|
60
|
+
return Effect.gen(function* () {
|
|
61
|
+
const initial = initialValueForSource(source);
|
|
62
|
+
const ref = yield* SubscriptionRef.make(initial);
|
|
63
|
+
const setupListener = Effect.gen(function* () {
|
|
64
|
+
switch (source.type) {
|
|
65
|
+
case 'viewport': {
|
|
66
|
+
if (typeof globalThis.window === 'undefined')
|
|
67
|
+
return;
|
|
68
|
+
const handler = () => {
|
|
69
|
+
const val = source.axis === 'width' ? window.innerWidth : window.innerHeight;
|
|
70
|
+
Effect.runSync(SubscriptionRef.set(ref, val));
|
|
71
|
+
};
|
|
72
|
+
yield* Effect.acquireRelease(Effect.sync(() => {
|
|
73
|
+
window.addEventListener('resize', handler);
|
|
74
|
+
}), () => Effect.sync(() => {
|
|
75
|
+
window.removeEventListener('resize', handler);
|
|
76
|
+
}));
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case 'scroll': {
|
|
80
|
+
if (typeof globalThis.window === 'undefined')
|
|
81
|
+
return;
|
|
82
|
+
const handler = () => {
|
|
83
|
+
let val;
|
|
84
|
+
if (source.axis === 'x')
|
|
85
|
+
val = window.scrollX;
|
|
86
|
+
else if (source.axis === 'y')
|
|
87
|
+
val = window.scrollY;
|
|
88
|
+
else {
|
|
89
|
+
const max = document.documentElement.scrollHeight - window.innerHeight;
|
|
90
|
+
val = max > 0 ? window.scrollY / max : 0;
|
|
91
|
+
}
|
|
92
|
+
Effect.runSync(SubscriptionRef.set(ref, val));
|
|
93
|
+
};
|
|
94
|
+
yield* Effect.acquireRelease(Effect.sync(() => {
|
|
95
|
+
window.addEventListener('scroll', handler, { passive: true });
|
|
96
|
+
}), () => Effect.sync(() => {
|
|
97
|
+
window.removeEventListener('scroll', handler);
|
|
98
|
+
}));
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case 'pointer': {
|
|
102
|
+
if (typeof globalThis.window === 'undefined')
|
|
103
|
+
return;
|
|
104
|
+
const handler = (e) => {
|
|
105
|
+
const val = source.axis === 'x' ? e.clientX : source.axis === 'y' ? e.clientY : e.pressure;
|
|
106
|
+
Effect.runSync(SubscriptionRef.set(ref, val));
|
|
107
|
+
};
|
|
108
|
+
yield* Effect.acquireRelease(Effect.sync(() => {
|
|
109
|
+
window.addEventListener('pointermove', handler);
|
|
110
|
+
}), () => Effect.sync(() => {
|
|
111
|
+
window.removeEventListener('pointermove', handler);
|
|
112
|
+
}));
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case 'time': {
|
|
116
|
+
if (source.mode === 'elapsed') {
|
|
117
|
+
if (typeof requestAnimationFrame === 'undefined')
|
|
118
|
+
return;
|
|
119
|
+
const start = Date.now();
|
|
120
|
+
const id = { current: 0 };
|
|
121
|
+
const tick = () => {
|
|
122
|
+
Effect.runSync(SubscriptionRef.set(ref, Date.now() - start));
|
|
123
|
+
id.current = requestAnimationFrame(tick);
|
|
124
|
+
};
|
|
125
|
+
id.current = requestAnimationFrame(tick);
|
|
126
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => cancelAnimationFrame(id.current)));
|
|
127
|
+
}
|
|
128
|
+
else if (source.mode === 'absolute') {
|
|
129
|
+
const id = setInterval(() => {
|
|
130
|
+
Effect.runSync(SubscriptionRef.set(ref, Date.now()));
|
|
131
|
+
}, 1000);
|
|
132
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => clearInterval(id)));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Scheduled mode: no automatic ticking.
|
|
136
|
+
// External code drives this signal via SubscriptionRef.set(ref, value).
|
|
137
|
+
// The ref is already created -- caller controls it via ControllableSignal.
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
case 'media': {
|
|
142
|
+
if (typeof globalThis.window === 'undefined')
|
|
143
|
+
return;
|
|
144
|
+
const mql = window.matchMedia(source.query);
|
|
145
|
+
const handler = (e) => {
|
|
146
|
+
Effect.runSync(SubscriptionRef.set(ref, e.matches ? 1 : 0));
|
|
147
|
+
};
|
|
148
|
+
yield* Effect.acquireRelease(Effect.sync(() => {
|
|
149
|
+
mql.addEventListener('change', handler);
|
|
150
|
+
}), () => Effect.sync(() => {
|
|
151
|
+
mql.removeEventListener('change', handler);
|
|
152
|
+
}));
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case 'custom':
|
|
156
|
+
// Custom signals are driven externally via Signal.custom() push API.
|
|
157
|
+
// No browser listener needed — the caller pushes values directly.
|
|
158
|
+
break;
|
|
159
|
+
case 'audio':
|
|
160
|
+
// Audio signals are driven externally via Signal.audio() / AVBridge.
|
|
161
|
+
// No browser listener needed — audio analysis pushes values on its own cadence.
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
yield* Effect.forkScoped(setupListener);
|
|
166
|
+
return {
|
|
167
|
+
source,
|
|
168
|
+
current: SubscriptionRef.get(ref),
|
|
169
|
+
changes: SubscriptionRef.changes(ref),
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Create a controllable time signal for video rendering / scrubbing.
|
|
175
|
+
*
|
|
176
|
+
* External code drives the signal value via seek(); no automatic ticking.
|
|
177
|
+
* Supports pause/resume to temporarily ignore seek updates.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* import { Effect } from 'effect';
|
|
182
|
+
* import { Signal } from '@czap/core';
|
|
183
|
+
*
|
|
184
|
+
* const program = Effect.scoped(Effect.gen(function* () {
|
|
185
|
+
* const ctrl = yield* Signal.controllable();
|
|
186
|
+
* yield* ctrl.seek(1500);
|
|
187
|
+
* const t = yield* ctrl.current;
|
|
188
|
+
* // t === 1500
|
|
189
|
+
* yield* ctrl.pause();
|
|
190
|
+
* yield* ctrl.seek(2000); // ignored while paused
|
|
191
|
+
* }));
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
function _controllable() {
|
|
195
|
+
return Effect.gen(function* () {
|
|
196
|
+
const ref = yield* SubscriptionRef.make(0);
|
|
197
|
+
const pausedRef = yield* Ref.make(false);
|
|
198
|
+
return {
|
|
199
|
+
source: { type: 'time', mode: 'scheduled' },
|
|
200
|
+
current: SubscriptionRef.get(ref),
|
|
201
|
+
changes: SubscriptionRef.changes(ref),
|
|
202
|
+
seek: (to) => Effect.gen(function* () {
|
|
203
|
+
const paused = yield* Ref.get(pausedRef);
|
|
204
|
+
if (!paused) {
|
|
205
|
+
yield* SubscriptionRef.set(ref, to);
|
|
206
|
+
}
|
|
207
|
+
}),
|
|
208
|
+
pause: () => Ref.set(pausedRef, true),
|
|
209
|
+
resume: () => Ref.set(pausedRef, false),
|
|
210
|
+
};
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Create an audio signal backed by an AVBridge.
|
|
215
|
+
*
|
|
216
|
+
* In 'sample' mode, returns the raw sample index. In 'normalized' mode,
|
|
217
|
+
* returns a 0..1 progress value based on totalDurationSec. Call `.poll()`
|
|
218
|
+
* to read the latest sample from the bridge and update the signal.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```ts
|
|
222
|
+
* import { Effect } from 'effect';
|
|
223
|
+
* import { Signal } from '@czap/core';
|
|
224
|
+
*
|
|
225
|
+
* const program = Effect.scoped(Effect.gen(function* () {
|
|
226
|
+
* const audioSig = yield* Signal.audio(bridge, 'normalized', 120);
|
|
227
|
+
* const progress = yield* audioSig.poll();
|
|
228
|
+
* // progress is a number between 0 and 1
|
|
229
|
+
* }));
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
function _audio(bridge, mode = 'sample', totalDurationSec) {
|
|
233
|
+
return Effect.gen(function* () {
|
|
234
|
+
const ref = yield* SubscriptionRef.make(0);
|
|
235
|
+
const poll = () => Effect.gen(function* () {
|
|
236
|
+
const sample = bridge.getCurrentSample();
|
|
237
|
+
let value;
|
|
238
|
+
if (mode === 'normalized' && totalDurationSec !== undefined && totalDurationSec > 0) {
|
|
239
|
+
const totalSamples = totalDurationSec * bridge.sampleRate;
|
|
240
|
+
value = Math.min(sample / totalSamples, 1);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
value = sample;
|
|
244
|
+
}
|
|
245
|
+
yield* SubscriptionRef.set(ref, value);
|
|
246
|
+
return value;
|
|
247
|
+
});
|
|
248
|
+
return {
|
|
249
|
+
source: { type: 'audio', mode },
|
|
250
|
+
current: SubscriptionRef.get(ref),
|
|
251
|
+
changes: SubscriptionRef.changes(ref),
|
|
252
|
+
poll: () => poll(),
|
|
253
|
+
};
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Signal namespace -- live data feeds from the browser environment.
|
|
258
|
+
*
|
|
259
|
+
* Create reactive signals from viewport, scroll, pointer, time, media query,
|
|
260
|
+
* audio, or custom sources. Each signal provides `.current` and `.changes`
|
|
261
|
+
* backed by Effect's SubscriptionRef. Scoped for automatic listener cleanup.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```ts
|
|
265
|
+
* import { Effect } from 'effect';
|
|
266
|
+
* import { Signal } from '@czap/core';
|
|
267
|
+
*
|
|
268
|
+
* const program = Effect.scoped(Effect.gen(function* () {
|
|
269
|
+
* const viewport = yield* Signal.make({ type: 'viewport', axis: 'width' });
|
|
270
|
+
* const width = yield* viewport.current;
|
|
271
|
+
* const ctrl = yield* Signal.controllable();
|
|
272
|
+
* yield* ctrl.seek(500);
|
|
273
|
+
* }));
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
export const Signal = { make: _make, controllable: _controllable, audio: _audio };
|
|
277
|
+
//# sourceMappingURL=signal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal.js","sourceRoot":"","sources":["../src/signal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpeculativeEvaluator -- threshold proximity prefetching.
|
|
3
|
+
*
|
|
4
|
+
* When a boundary signal is near a threshold, pre-compute the next state.
|
|
5
|
+
* Uses hysteresis dead zone as the prefetch window and linear extrapolation
|
|
6
|
+
* from velocity (last 3-4 signal values) to predict crossings.
|
|
7
|
+
*
|
|
8
|
+
* Wrong prediction cost: ~80ns to recompute (negligible).
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { Boundary } from './boundary.js';
|
|
13
|
+
import type { StateUnion } from './type-utils.js';
|
|
14
|
+
interface SpeculativeResult<B extends Boundary.Shape> {
|
|
15
|
+
readonly current: StateUnion<B>;
|
|
16
|
+
readonly prefetched?: StateUnion<B>;
|
|
17
|
+
readonly confidence: number;
|
|
18
|
+
}
|
|
19
|
+
interface SpeculativeEvaluatorShape<B extends Boundary.Shape> {
|
|
20
|
+
evaluate(value: number, velocity?: number): SpeculativeResult<B>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates a speculative evaluator for a boundary that prefetches the next state
|
|
24
|
+
* when the signal value is near a threshold and moving toward it.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const boundary = Boundary.make({
|
|
29
|
+
* thresholds: [768, 1024],
|
|
30
|
+
* states: ['mobile', 'tablet', 'desktop'] as const,
|
|
31
|
+
* hysteresis: 20,
|
|
32
|
+
* });
|
|
33
|
+
* const spec = SpeculativeEvaluator.make(boundary);
|
|
34
|
+
* const result = spec.evaluate(760, 2.0); // approaching 768 threshold
|
|
35
|
+
* result.current; // 'mobile'
|
|
36
|
+
* result.prefetched; // 'tablet' (pre-computed)
|
|
37
|
+
* result.confidence; // 0.0-1.0 likelihood of crossing
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare function _make<B extends Boundary.Shape>(boundary: B): SpeculativeEvaluatorShape<B>;
|
|
41
|
+
/**
|
|
42
|
+
* SpeculativeEvaluator -- threshold proximity prefetching for boundaries.
|
|
43
|
+
* Pre-computes the next discrete state when a signal is near a threshold,
|
|
44
|
+
* using velocity estimation and hysteresis-based prefetch windows.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const boundary = Boundary.make({
|
|
49
|
+
* thresholds: [600],
|
|
50
|
+
* states: ['small', 'large'] as const,
|
|
51
|
+
* });
|
|
52
|
+
* const spec = SpeculativeEvaluator.make(boundary);
|
|
53
|
+
* const { current, prefetched, confidence } = spec.evaluate(595, 1.5);
|
|
54
|
+
* // current='small', prefetched='large', confidence ~0.85
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare const SpeculativeEvaluator: {
|
|
58
|
+
make: typeof _make;
|
|
59
|
+
};
|
|
60
|
+
export declare namespace SpeculativeEvaluator {
|
|
61
|
+
/** Structural shape of an evaluator bound to a specific {@link Boundary}. */
|
|
62
|
+
type Shape<B extends Boundary.Shape> = SpeculativeEvaluatorShape<B>;
|
|
63
|
+
/** Prediction result from `evaluate()` — current state, optional prefetched next state, and confidence. */
|
|
64
|
+
type Result<B extends Boundary.Shape> = SpeculativeResult<B>;
|
|
65
|
+
}
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=speculative.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speculative.d.ts","sourceRoot":"","sources":["../src/speculative.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAIlD,UAAU,iBAAiB,CAAC,CAAC,SAAS,QAAQ,CAAC,KAAK;IAClD,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACpC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,UAAU,yBAAyB,CAAC,CAAC,SAAS,QAAQ,CAAC,KAAK;IAC1D,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;CAClE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,iBAAS,KAAK,CAAC,CAAC,SAAS,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,GAAG,yBAAyB,CAAC,CAAC,CAAC,CAmHlF;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB;;CAAkB,CAAC;AAEpD,MAAM,CAAC,OAAO,WAAW,oBAAoB,CAAC;IAC5C,6EAA6E;IAC7E,KAAY,KAAK,CAAC,CAAC,SAAS,QAAQ,CAAC,KAAK,IAAI,yBAAyB,CAAC,CAAC,CAAC,CAAC;IAC3E,2GAA2G;IAC3G,KAAY,MAAM,CAAC,CAAC,SAAS,QAAQ,CAAC,KAAK,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC;CACrE"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpeculativeEvaluator -- threshold proximity prefetching.
|
|
3
|
+
*
|
|
4
|
+
* When a boundary signal is near a threshold, pre-compute the next state.
|
|
5
|
+
* Uses hysteresis dead zone as the prefetch window and linear extrapolation
|
|
6
|
+
* from velocity (last 3-4 signal values) to predict crossings.
|
|
7
|
+
*
|
|
8
|
+
* Wrong prediction cost: ~80ns to recompute (negligible).
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { Boundary } from './boundary.js';
|
|
13
|
+
/**
|
|
14
|
+
* Creates a speculative evaluator for a boundary that prefetches the next state
|
|
15
|
+
* when the signal value is near a threshold and moving toward it.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const boundary = Boundary.make({
|
|
20
|
+
* thresholds: [768, 1024],
|
|
21
|
+
* states: ['mobile', 'tablet', 'desktop'] as const,
|
|
22
|
+
* hysteresis: 20,
|
|
23
|
+
* });
|
|
24
|
+
* const spec = SpeculativeEvaluator.make(boundary);
|
|
25
|
+
* const result = spec.evaluate(760, 2.0); // approaching 768 threshold
|
|
26
|
+
* result.current; // 'mobile'
|
|
27
|
+
* result.prefetched; // 'tablet' (pre-computed)
|
|
28
|
+
* result.confidence; // 0.0-1.0 likelihood of crossing
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
function _make(boundary) {
|
|
32
|
+
const thresholds = boundary.thresholds;
|
|
33
|
+
const hysteresis = boundary.hysteresis ?? 0;
|
|
34
|
+
const prefetchWindow = Math.max(hysteresis, 1); // Use hysteresis as window, min 1
|
|
35
|
+
// Compute epsilon from boundary scale rather than hardcoded constant
|
|
36
|
+
const minGap = thresholds.length >= 2
|
|
37
|
+
? Math.min(...Array.from({ length: thresholds.length - 1 }, (_, i) => thresholds[i + 1] - thresholds[i]))
|
|
38
|
+
: 1;
|
|
39
|
+
const epsilon = Math.min(minGap * 0.001, hysteresis > 0 ? hysteresis * 0.01 : 0.001);
|
|
40
|
+
// Velocity estimation ring buffer (last 2 values)
|
|
41
|
+
const history = [];
|
|
42
|
+
// 2-sample velocity estimation buffer — gives instant responsiveness
|
|
43
|
+
const HISTORY_SIZE = 2;
|
|
44
|
+
// Boundary.make guarantees states is non-empty (readonly [string, ...string[]]).
|
|
45
|
+
let previousState = boundary.states[0];
|
|
46
|
+
// Simple finite difference (not least-squares) — 2-sample gives instant responsiveness
|
|
47
|
+
// for UI prefetch.
|
|
48
|
+
function estimateVelocity(currentValue, explicitVelocity) {
|
|
49
|
+
if (explicitVelocity !== undefined)
|
|
50
|
+
return explicitVelocity;
|
|
51
|
+
if (history.length < 2)
|
|
52
|
+
return 0;
|
|
53
|
+
// Linear regression over recent samples
|
|
54
|
+
const last = history[history.length - 1];
|
|
55
|
+
const prev = history[history.length - 2];
|
|
56
|
+
const dt = last.time - prev.time;
|
|
57
|
+
if (dt <= 0)
|
|
58
|
+
return 0;
|
|
59
|
+
return (last.value - prev.value) / dt;
|
|
60
|
+
}
|
|
61
|
+
function findNearestThreshold(value) {
|
|
62
|
+
let nearest = null;
|
|
63
|
+
for (const t of thresholds) {
|
|
64
|
+
const dist = Math.abs(value - t);
|
|
65
|
+
if (nearest === null || dist < nearest.distance) {
|
|
66
|
+
nearest = {
|
|
67
|
+
threshold: t,
|
|
68
|
+
distance: dist,
|
|
69
|
+
direction: value < t ? 'up' : 'down',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return nearest;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
evaluate(value, velocity) {
|
|
77
|
+
const now = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
78
|
+
history.push({ value, time: now });
|
|
79
|
+
if (history.length > HISTORY_SIZE)
|
|
80
|
+
history.shift();
|
|
81
|
+
// Evaluate current state
|
|
82
|
+
const current = boundary.hysteresis
|
|
83
|
+
? Boundary.evaluateWithHysteresis(boundary, value, previousState)
|
|
84
|
+
: Boundary.evaluate(boundary, value);
|
|
85
|
+
previousState = current;
|
|
86
|
+
// Find nearest threshold
|
|
87
|
+
const nearest = findNearestThreshold(value);
|
|
88
|
+
if (!nearest) {
|
|
89
|
+
return { current, confidence: 0 };
|
|
90
|
+
}
|
|
91
|
+
const vel = estimateVelocity(value, velocity);
|
|
92
|
+
// Check if moving toward the threshold
|
|
93
|
+
const movingToward = (nearest.direction === 'up' && vel > 0) || (nearest.direction === 'down' && vel < 0);
|
|
94
|
+
if (!movingToward || nearest.distance > prefetchWindow) {
|
|
95
|
+
return { current, confidence: 0 };
|
|
96
|
+
}
|
|
97
|
+
// Compute confidence: closer to threshold + faster velocity = higher confidence
|
|
98
|
+
const distanceFactor = 1 - nearest.distance / prefetchWindow;
|
|
99
|
+
// Distance weighted 70%, velocity 30% — distance is more reliable for prefetch confidence
|
|
100
|
+
const velocityFactor = Math.min(Math.abs(vel) * 10, 1); // Normalize velocity contribution
|
|
101
|
+
const confidence = distanceFactor * 0.7 + velocityFactor * 0.3;
|
|
102
|
+
// Below 30% confidence, skip prefetch — not worth the speculative cost
|
|
103
|
+
if (confidence < 0.3) {
|
|
104
|
+
return { current, confidence };
|
|
105
|
+
}
|
|
106
|
+
// Pre-compute the predicted next state (jump past hysteresis zone if present)
|
|
107
|
+
const hysteresisJump = boundary.hysteresis ?? 0;
|
|
108
|
+
const predictedValue = nearest.direction === 'up'
|
|
109
|
+
? nearest.threshold + hysteresisJump + epsilon
|
|
110
|
+
: nearest.threshold - hysteresisJump - epsilon;
|
|
111
|
+
const prefetched = boundary.hysteresis
|
|
112
|
+
? Boundary.evaluateWithHysteresis(boundary, predictedValue, current)
|
|
113
|
+
: Boundary.evaluate(boundary, predictedValue);
|
|
114
|
+
// Only return prefetch if it's actually different
|
|
115
|
+
if (prefetched === current) {
|
|
116
|
+
return { current, confidence: 0 };
|
|
117
|
+
}
|
|
118
|
+
return { current, prefetched, confidence };
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* SpeculativeEvaluator -- threshold proximity prefetching for boundaries.
|
|
124
|
+
* Pre-computes the next discrete state when a signal is near a threshold,
|
|
125
|
+
* using velocity estimation and hysteresis-based prefetch windows.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* const boundary = Boundary.make({
|
|
130
|
+
* thresholds: [600],
|
|
131
|
+
* states: ['small', 'large'] as const,
|
|
132
|
+
* });
|
|
133
|
+
* const spec = SpeculativeEvaluator.make(boundary);
|
|
134
|
+
* const { current, prefetched, confidence } = spec.evaluate(595, 1.5);
|
|
135
|
+
* // current='small', prefetched='large', confidence ~0.85
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export const SpeculativeEvaluator = { make: _make };
|
|
139
|
+
//# sourceMappingURL=speculative.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speculative.js","sourceRoot":"","sources":["../src/speculative.ts"],"names":[],"mappings":"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"}
|