@czap/quantizer 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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -0
  3. package/dist/animated-quantizer.d.ts +108 -0
  4. package/dist/animated-quantizer.d.ts.map +1 -0
  5. package/dist/animated-quantizer.js +196 -0
  6. package/dist/animated-quantizer.js.map +1 -0
  7. package/dist/evaluate.d.ts +79 -0
  8. package/dist/evaluate.d.ts.map +1 -0
  9. package/dist/evaluate.js +128 -0
  10. package/dist/evaluate.js.map +1 -0
  11. package/dist/index.d.ts +18 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +16 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/memo-cache.d.ts +35 -0
  16. package/dist/memo-cache.d.ts.map +1 -0
  17. package/dist/memo-cache.js +39 -0
  18. package/dist/memo-cache.js.map +1 -0
  19. package/dist/quantizer.d.ts +223 -0
  20. package/dist/quantizer.d.ts.map +1 -0
  21. package/dist/quantizer.js +260 -0
  22. package/dist/quantizer.js.map +1 -0
  23. package/dist/schemas.d.ts +44 -0
  24. package/dist/schemas.d.ts.map +1 -0
  25. package/dist/schemas.js +46 -0
  26. package/dist/schemas.js.map +1 -0
  27. package/dist/testing.d.ts +15 -0
  28. package/dist/testing.d.ts.map +1 -0
  29. package/dist/testing.js +15 -0
  30. package/dist/testing.js.map +1 -0
  31. package/dist/transition.d.ts +67 -0
  32. package/dist/transition.d.ts.map +1 -0
  33. package/dist/transition.js +49 -0
  34. package/dist/transition.js.map +1 -0
  35. package/package.json +58 -0
  36. package/src/animated-quantizer.ts +272 -0
  37. package/src/evaluate.ts +160 -0
  38. package/src/index.ts +26 -0
  39. package/src/memo-cache.ts +58 -0
  40. package/src/quantizer.ts +503 -0
  41. package/src/schemas.ts +50 -0
  42. package/src/testing.ts +15 -0
  43. package/src/transition.ts +97 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Eassa Ayoub <eassa@heyoub.dev>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @czap/quantizer
2
+
3
+ Q.from() builder, boundary evaluation, animated transitions, MotionTier gating.
4
+
5
+ ## Docs
6
+
7
+ - [Naming & vocabulary](../../docs/GLOSSARY.md) — LiteShip, CZAP, `@czap/*`
8
+
9
+ - [API reference](https://github.com/heyoub/LiteShip/tree/main/docs/api/quantizer/) — generated from source TSDoc
10
+ - [Architecture index](https://github.com/heyoub/LiteShip/blob/main/docs/ARCHITECTURE.md)
11
+ - [ADRs](https://github.com/heyoub/LiteShip/tree/main/docs/adr/)
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pnpm add @czap/quantizer
17
+ ```
18
+
19
+ ## Part of [LiteShip](https://github.com/heyoub/LiteShip#readme)
@@ -0,0 +1,108 @@
1
+ /**
2
+ * AnimatedQuantizer -- wraps a Quantizer with Transitions.
3
+ * On boundary crossing, interpolates between old and new output values
4
+ * over the configured transition duration/easing.
5
+ */
6
+ import type { Scope } from 'effect';
7
+ import { Effect, Stream } from 'effect';
8
+ import type { Boundary, StateUnion, Quantizer } from '@czap/core';
9
+ import type { Transition, TransitionMap } from './transition.js';
10
+ /**
11
+ * Quantizer augmented with transition-aware output interpolation.
12
+ *
13
+ * The `interpolated` stream emits a frame on each animation tick containing
14
+ * the target state, normalized progress (0-1), and the current lerped
15
+ * output record. Non-numeric values snap at the 50% mark.
16
+ */
17
+ export interface AnimatedQuantizerShape<B extends Boundary.Shape> extends Quantizer<B> {
18
+ /** Resolver that maps `from -> to` crossings to {@link TransitionConfig}. */
19
+ readonly transition: Transition<B>;
20
+ /** Stream of interpolated animation frames during crossings. */
21
+ readonly interpolated: Stream.Stream<{
22
+ /** Target state of the in-flight transition. */
23
+ readonly state: StateUnion<B>;
24
+ /** Progress in `[0, 1]`, where `1` means the animation has landed. */
25
+ readonly progress: number;
26
+ /** Interpolated output record for the current frame. */
27
+ readonly outputs: Record<string, number | string>;
28
+ }>;
29
+ }
30
+ /**
31
+ * Create an animated quantizer that interpolates outputs during transitions.
32
+ *
33
+ * Wraps an existing {@link Quantizer} and applies easing/duration-based
34
+ * interpolation between old and new output values when a boundary crossing
35
+ * occurs. Produces an `interpolated` stream of frames with progress and
36
+ * lerped numeric outputs at ~60fps.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { Boundary } from '@czap/core';
41
+ * import { Q, AnimatedQuantizer } from '@czap/quantizer';
42
+ * import { Effect, Stream } from 'effect';
43
+ *
44
+ * const boundary = Boundary.make({
45
+ * input: 'scroll', states: ['top', 'bottom'] as const,
46
+ * thresholds: [0, 500],
47
+ * });
48
+ * const config = Q.from(boundary).outputs({
49
+ * css: { top: { opacity: '1' }, bottom: { opacity: '0.5' } },
50
+ * });
51
+ * const program = Effect.scoped(Effect.gen(function* () {
52
+ * const live = yield* config.create();
53
+ * const animated = yield* AnimatedQuantizer.make(
54
+ * live,
55
+ * { '*->*': { duration: 300 } },
56
+ * { top: { opacity: 1 }, bottom: { opacity: 0.5 } },
57
+ * );
58
+ * live.evaluate(600); // triggers interpolation
59
+ * return animated;
60
+ * }));
61
+ * ```
62
+ *
63
+ * @param quantizer - The base quantizer to wrap
64
+ * @param transitions - Map of state transition configs keyed by `from->to` pattern
65
+ * @param outputs - Per-state numeric output maps for interpolation
66
+ * @returns An Effect yielding an {@link AnimatedQuantizerShape} (scoped)
67
+ */
68
+ declare function makeAnimatedQuantizer<B extends Boundary.Shape>(quantizer: Quantizer<B>, transitions: TransitionMap<StateUnion<B> & string>, outputs?: Record<string, Record<string, number | string>>): Effect.Effect<AnimatedQuantizerShape<B>, never, Scope.Scope>;
69
+ /**
70
+ * Animated quantizer namespace.
71
+ *
72
+ * Wraps a base quantizer with transition-aware interpolation. When a boundary
73
+ * crossing occurs, numeric output values are lerped over a configurable
74
+ * duration and easing curve. Non-numeric values snap at the 50% mark.
75
+ * The `interpolated` stream emits frames containing progress (0-1) and
76
+ * the current interpolated output record.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * import { Boundary } from '@czap/core';
81
+ * import { Q, AnimatedQuantizer } from '@czap/quantizer';
82
+ * import { Effect } from 'effect';
83
+ *
84
+ * const boundary = Boundary.make({
85
+ * input: 'scroll', states: ['top', 'bottom'] as const,
86
+ * thresholds: [0, 500],
87
+ * });
88
+ * const config = Q.from(boundary).outputs({});
89
+ * const program = Effect.scoped(Effect.gen(function* () {
90
+ * const live = yield* config.create();
91
+ * const animated = yield* AnimatedQuantizer.make(
92
+ * live,
93
+ * { '*->*': { duration: 200 } },
94
+ * );
95
+ * return animated.transition; // TransitionResolver
96
+ * }));
97
+ * ```
98
+ */
99
+ export declare const AnimatedQuantizer: {
100
+ /** Wrap a quantizer with transition-aware output interpolation. */
101
+ readonly make: typeof makeAnimatedQuantizer;
102
+ };
103
+ export declare namespace AnimatedQuantizer {
104
+ /** Shape of an animated quantizer parameterized by boundary `B`. */
105
+ type Shape<B extends Boundary.Shape> = AnimatedQuantizerShape<B>;
106
+ }
107
+ export {};
108
+ //# sourceMappingURL=animated-quantizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animated-quantizer.d.ts","sourceRoot":"","sources":["../src/animated-quantizer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAgD,MAAM,QAAQ,CAAC;AACtF,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAoB,SAAS,EAAU,MAAM,YAAY,CAAC;AAC5F,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAOjE;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,QAAQ,CAAC,KAAK,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IACpF,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACnC,gEAAgE;IAChE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC;QACnC,gDAAgD;QAChD,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;QAC9B,sEAAsE;QACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,wDAAwD;QACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;KACnD,CAAC,CAAC;CACJ;AA6CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,iBAAS,qBAAqB,CAAC,CAAC,SAAS,QAAQ,CAAC,KAAK,EACrD,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EACvB,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,EAClD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,GACxD,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAyG9D;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,iBAAiB;IAC5B,mEAAmE;;CAE3D,CAAC;AAEX,MAAM,CAAC,OAAO,WAAW,iBAAiB,CAAC;IACzC,oEAAoE;IACpE,KAAY,KAAK,CAAC,CAAC,SAAS,QAAQ,CAAC,KAAK,IAAI,sBAAsB,CAAC,CAAC,CAAC,CAAC;CACzE"}
@@ -0,0 +1,196 @@
1
+ /**
2
+ * AnimatedQuantizer -- wraps a Quantizer with Transitions.
3
+ * On boundary crossing, interpolates between old and new output values
4
+ * over the configured transition duration/easing.
5
+ */
6
+ import { Effect, Stream, SubscriptionRef, Queue, Fiber, Ref, Duration } from 'effect';
7
+ import { Transition as TransitionFactory } from './transition.js';
8
+ // ---------------------------------------------------------------------------
9
+ // Linear easing fallback
10
+ // ---------------------------------------------------------------------------
11
+ const linearEasing = (t) => t;
12
+ // ---------------------------------------------------------------------------
13
+ // Interpolate numeric values between two output records
14
+ // ---------------------------------------------------------------------------
15
+ function lerpOutputs(from, to, t) {
16
+ const result = {};
17
+ const allKeys = new Set([...Object.keys(from), ...Object.keys(to)]);
18
+ for (const key of allKeys) {
19
+ const a = from[key];
20
+ const b = to[key];
21
+ if (typeof a === 'number' && typeof b === 'number') {
22
+ result[key] = a + (b - a) * t;
23
+ }
24
+ else {
25
+ // Non-numeric values snap to target at progress >= 0.5
26
+ result[key] = (t < 0.5 ? (a ?? b) : (b ?? a));
27
+ }
28
+ }
29
+ return result;
30
+ }
31
+ function nowMs() {
32
+ // performance.now() is standard in browsers and Node ≥ 16.
33
+ // Optional chaining guards against stripped worker/SSR environments.
34
+ if (typeof globalThis.performance?.now === 'function') {
35
+ return globalThis.performance.now();
36
+ }
37
+ return Date.now();
38
+ }
39
+ // ---------------------------------------------------------------------------
40
+ // Factory (internal impl)
41
+ // ---------------------------------------------------------------------------
42
+ /**
43
+ * Create an animated quantizer that interpolates outputs during transitions.
44
+ *
45
+ * Wraps an existing {@link Quantizer} and applies easing/duration-based
46
+ * interpolation between old and new output values when a boundary crossing
47
+ * occurs. Produces an `interpolated` stream of frames with progress and
48
+ * lerped numeric outputs at ~60fps.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { Boundary } from '@czap/core';
53
+ * import { Q, AnimatedQuantizer } from '@czap/quantizer';
54
+ * import { Effect, Stream } from 'effect';
55
+ *
56
+ * const boundary = Boundary.make({
57
+ * input: 'scroll', states: ['top', 'bottom'] as const,
58
+ * thresholds: [0, 500],
59
+ * });
60
+ * const config = Q.from(boundary).outputs({
61
+ * css: { top: { opacity: '1' }, bottom: { opacity: '0.5' } },
62
+ * });
63
+ * const program = Effect.scoped(Effect.gen(function* () {
64
+ * const live = yield* config.create();
65
+ * const animated = yield* AnimatedQuantizer.make(
66
+ * live,
67
+ * { '*->*': { duration: 300 } },
68
+ * { top: { opacity: 1 }, bottom: { opacity: 0.5 } },
69
+ * );
70
+ * live.evaluate(600); // triggers interpolation
71
+ * return animated;
72
+ * }));
73
+ * ```
74
+ *
75
+ * @param quantizer - The base quantizer to wrap
76
+ * @param transitions - Map of state transition configs keyed by `from->to` pattern
77
+ * @param outputs - Per-state numeric output maps for interpolation
78
+ * @returns An Effect yielding an {@link AnimatedQuantizerShape} (scoped)
79
+ */
80
+ function makeAnimatedQuantizer(quantizer, transitions, outputs) {
81
+ return Effect.gen(function* () {
82
+ const boundary = quantizer.boundary;
83
+ const transitionResolver = TransitionFactory.for(quantizer, transitions);
84
+ const initialState = yield* quantizer.state;
85
+ const stateRef = yield* SubscriptionRef.make(initialState);
86
+ const currentOutputsRef = yield* Ref.make(outputs?.[initialState] ?? {});
87
+ const currentFiberRef = yield* Ref.make(null);
88
+ const interpolatedStream = Stream.callback((queue) => Effect.gen(function* () {
89
+ yield* Effect.addFinalizer(() => Effect.gen(function* () {
90
+ const currentFiber = yield* Ref.get(currentFiberRef);
91
+ if (currentFiber !== null) {
92
+ yield* Fiber.interrupt(currentFiber);
93
+ }
94
+ }));
95
+ yield* Stream.runForEach(quantizer.changes, (crossing) => Effect.gen(function* () {
96
+ const existingFiber = yield* Ref.get(currentFiberRef);
97
+ if (existingFiber !== null) {
98
+ yield* Fiber.interrupt(existingFiber);
99
+ }
100
+ // crossing.from/to are StateName<StateUnion<B> & string>, which is a branded
101
+ // subtype of StateUnion<B>; assignable directly without a cast.
102
+ const { from, to } = crossing;
103
+ const config = transitionResolver.getTransition(from, to);
104
+ const duration = config.duration;
105
+ const easing = config.easing ?? linearEasing;
106
+ const delay = config.delay ?? 0;
107
+ const fromOutputs = { ...(yield* Ref.get(currentOutputsRef)) };
108
+ const toOutputs = outputs?.[crossing.to] ?? {};
109
+ const animationLoop = Effect.gen(function* () {
110
+ if (delay > 0) {
111
+ yield* Effect.sleep(Duration.millis(delay));
112
+ }
113
+ if (duration <= 0) {
114
+ Queue.offerUnsafe(queue, { state: to, progress: 1, outputs: toOutputs });
115
+ yield* Ref.set(currentOutputsRef, toOutputs);
116
+ yield* SubscriptionRef.set(stateRef, to);
117
+ return;
118
+ }
119
+ // Time-sliced animation loop (~60fps via 16ms sleep)
120
+ const startTime = nowMs();
121
+ let progress = 0;
122
+ while (progress < 1) {
123
+ const elapsed = nowMs() - startTime;
124
+ progress = Math.min(elapsed / duration, 1);
125
+ const eased = easing(progress);
126
+ const interpolated = lerpOutputs(fromOutputs, toOutputs, eased);
127
+ yield* Ref.set(currentOutputsRef, interpolated);
128
+ Queue.offerUnsafe(queue, { state: to, progress, outputs: interpolated });
129
+ if (progress < 1) {
130
+ yield* Effect.sleep(Duration.millis(16));
131
+ }
132
+ }
133
+ yield* Ref.set(currentOutputsRef, toOutputs);
134
+ yield* SubscriptionRef.set(stateRef, to);
135
+ });
136
+ const fiber = yield* Effect.forkChild(animationLoop);
137
+ yield* Ref.set(currentFiberRef, fiber);
138
+ }));
139
+ const finalFiber = yield* Ref.get(currentFiberRef);
140
+ const fibers = [finalFiber].filter((fiber) => fiber !== null);
141
+ yield* Effect.forEach(fibers, Fiber.join, { discard: true });
142
+ yield* Ref.set(currentFiberRef, null);
143
+ }));
144
+ const animatedQuantizer = {
145
+ _tag: 'Quantizer',
146
+ boundary,
147
+ transition: transitionResolver,
148
+ state: SubscriptionRef.get(stateRef),
149
+ stateSync: quantizer.stateSync ? () => quantizer.stateSync() : undefined,
150
+ changes: quantizer.changes,
151
+ evaluate(value) {
152
+ return quantizer.evaluate(value);
153
+ },
154
+ interpolated: interpolatedStream,
155
+ };
156
+ return animatedQuantizer;
157
+ });
158
+ }
159
+ // ---------------------------------------------------------------------------
160
+ // AnimatedQuantizer module object
161
+ // ---------------------------------------------------------------------------
162
+ /**
163
+ * Animated quantizer namespace.
164
+ *
165
+ * Wraps a base quantizer with transition-aware interpolation. When a boundary
166
+ * crossing occurs, numeric output values are lerped over a configurable
167
+ * duration and easing curve. Non-numeric values snap at the 50% mark.
168
+ * The `interpolated` stream emits frames containing progress (0-1) and
169
+ * the current interpolated output record.
170
+ *
171
+ * @example
172
+ * ```ts
173
+ * import { Boundary } from '@czap/core';
174
+ * import { Q, AnimatedQuantizer } from '@czap/quantizer';
175
+ * import { Effect } from 'effect';
176
+ *
177
+ * const boundary = Boundary.make({
178
+ * input: 'scroll', states: ['top', 'bottom'] as const,
179
+ * thresholds: [0, 500],
180
+ * });
181
+ * const config = Q.from(boundary).outputs({});
182
+ * const program = Effect.scoped(Effect.gen(function* () {
183
+ * const live = yield* config.create();
184
+ * const animated = yield* AnimatedQuantizer.make(
185
+ * live,
186
+ * { '*->*': { duration: 200 } },
187
+ * );
188
+ * return animated.transition; // TransitionResolver
189
+ * }));
190
+ * ```
191
+ */
192
+ export const AnimatedQuantizer = {
193
+ /** Wrap a quantizer with transition-aware output interpolation. */
194
+ make: makeAnimatedQuantizer,
195
+ };
196
+ //# sourceMappingURL=animated-quantizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animated-quantizer.js","sourceRoot":"","sources":["../src/animated-quantizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Binary search evaluation of a value against boundary thresholds.
3
+ * Supports hysteresis to prevent state jitter at threshold edges.
4
+ */
5
+ import type { Boundary, StateUnion } from '@czap/core';
6
+ /**
7
+ * Result of quantizing a single numeric value against a boundary.
8
+ *
9
+ * `crossed` is true only when `previousState` was supplied and differs
10
+ * from the resolved state; it is the signal consumers use to emit
11
+ * transition events and route side effects.
12
+ */
13
+ export interface EvaluateResult<S extends string = string> {
14
+ /** The resolved state literal. */
15
+ readonly state: S;
16
+ /** Index of `state` within the boundary's states tuple. */
17
+ readonly index: number;
18
+ /** The input value that was evaluated. */
19
+ readonly value: number;
20
+ /** Whether evaluation produced a change from `previousState`. */
21
+ readonly crossed: boolean;
22
+ }
23
+ /**
24
+ * Find which state a value maps to via binary search over sorted thresholds.
25
+ * With hysteresis: if previousState is provided and the value is within the
26
+ * hysteresis dead zone of a threshold, transition is suppressed.
27
+ *
28
+ * BoundaryDef contract: `thresholds[i]` = lower bound of `states[i]`.
29
+ * Binary search finds the largest index `i` where `thresholds[i] <= value`.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * import { Boundary } from '@czap/core';
34
+ * import { evaluate } from '@czap/quantizer';
35
+ *
36
+ * const boundary = Boundary.make({
37
+ * input: 'width', states: ['sm', 'md', 'lg'] as const,
38
+ * thresholds: [0, 640, 1024], hysteresis: 20,
39
+ * });
40
+ * const result = evaluate(boundary, 800);
41
+ * // result => { state: 'md', index: 1, value: 800, crossed: false }
42
+ *
43
+ * const cross = evaluate(boundary, 1100, 'md');
44
+ * // cross => { state: 'lg', index: 2, value: 1100, crossed: true }
45
+ * ```
46
+ *
47
+ * @param boundary - The boundary definition with states and thresholds
48
+ * @param value - The numeric value to evaluate
49
+ * @param previousState - Optional previous state for hysteresis and crossing detection
50
+ * @returns An {@link EvaluateResult} with the resolved state, index, and crossing flag
51
+ */
52
+ export declare function evaluate<B extends Boundary.Shape>(boundary: B, value: number, previousState?: StateUnion<B>): EvaluateResult<StateUnion<B> & string>;
53
+ /**
54
+ * Boundary evaluation namespace.
55
+ *
56
+ * Provides `evaluate()` for mapping a numeric value to a discrete state
57
+ * via binary search over boundary thresholds with optional hysteresis
58
+ * to prevent jitter at threshold edges.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * import { Boundary } from '@czap/core';
63
+ * import { Evaluate } from '@czap/quantizer';
64
+ *
65
+ * const boundary = Boundary.make({
66
+ * input: 'width', states: ['sm', 'lg'] as const,
67
+ * thresholds: [0, 768], hysteresis: 10,
68
+ * });
69
+ * const r1 = Evaluate.evaluate(boundary, 500);
70
+ * // r1.state => 'sm', r1.crossed => false
71
+ *
72
+ * const r2 = Evaluate.evaluate(boundary, 900, 'sm');
73
+ * // r2.state => 'lg', r2.crossed => true
74
+ * ```
75
+ */
76
+ export declare const Evaluate: {
77
+ readonly evaluate: typeof evaluate;
78
+ };
79
+ //# sourceMappingURL=evaluate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACvD,kCAAkC;IAClC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,CAAC,KAAK,EAC/C,QAAQ,EAAE,CAAC,EACX,KAAK,EAAE,MAAM,EACb,aAAa,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GAC5B,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CA4ExC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,QAAQ;;CAAwB,CAAC"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Binary search evaluation of a value against boundary thresholds.
3
+ * Supports hysteresis to prevent state jitter at threshold edges.
4
+ */
5
+ /**
6
+ * Find which state a value maps to via binary search over sorted thresholds.
7
+ * With hysteresis: if previousState is provided and the value is within the
8
+ * hysteresis dead zone of a threshold, transition is suppressed.
9
+ *
10
+ * BoundaryDef contract: `thresholds[i]` = lower bound of `states[i]`.
11
+ * Binary search finds the largest index `i` where `thresholds[i] <= value`.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import { Boundary } from '@czap/core';
16
+ * import { evaluate } from '@czap/quantizer';
17
+ *
18
+ * const boundary = Boundary.make({
19
+ * input: 'width', states: ['sm', 'md', 'lg'] as const,
20
+ * thresholds: [0, 640, 1024], hysteresis: 20,
21
+ * });
22
+ * const result = evaluate(boundary, 800);
23
+ * // result => { state: 'md', index: 1, value: 800, crossed: false }
24
+ *
25
+ * const cross = evaluate(boundary, 1100, 'md');
26
+ * // cross => { state: 'lg', index: 2, value: 1100, crossed: true }
27
+ * ```
28
+ *
29
+ * @param boundary - The boundary definition with states and thresholds
30
+ * @param value - The numeric value to evaluate
31
+ * @param previousState - Optional previous state for hysteresis and crossing detection
32
+ * @returns An {@link EvaluateResult} with the resolved state, index, and crossing flag
33
+ */
34
+ export function evaluate(boundary, value, previousState) {
35
+ const { thresholds, states, hysteresis } = boundary;
36
+ // Boundary.make guarantees states is non-empty; index access below yields StateUnion<B>.
37
+ // `& string` is structurally satisfied because every state literal is a string.
38
+ const stateAt = (index) => states[index];
39
+ if (thresholds.length === 0) {
40
+ return {
41
+ state: stateAt(0),
42
+ index: 0,
43
+ value,
44
+ crossed: false,
45
+ };
46
+ }
47
+ // Binary search: find largest index i where thresholds[i] <= value
48
+ // This gives the state whose lower bound is satisfied.
49
+ let lo = 0;
50
+ let hi = thresholds.length - 1;
51
+ let rawIndex = 0; // default: first state (value below all thresholds)
52
+ while (lo <= hi) {
53
+ const mid = (lo + hi) >>> 1;
54
+ if (thresholds[mid] <= value) {
55
+ rawIndex = mid;
56
+ lo = mid + 1;
57
+ }
58
+ else {
59
+ hi = mid - 1;
60
+ }
61
+ }
62
+ const state = stateAt(rawIndex);
63
+ // Without hysteresis or no previous state, return raw result
64
+ if (!hysteresis || hysteresis <= 0 || previousState === undefined) {
65
+ const crossed = previousState !== undefined && previousState !== state;
66
+ return { state, index: rawIndex, value, crossed };
67
+ }
68
+ // Find previous state index
69
+ const prevIndex = states.indexOf(previousState);
70
+ if (prevIndex === -1) {
71
+ return { state, index: rawIndex, value, crossed: true };
72
+ }
73
+ // No crossing needed
74
+ if (rawIndex === prevIndex) {
75
+ return { state, index: rawIndex, value, crossed: false };
76
+ }
77
+ // Half-width hysteresis: dead zone of h/2 each side of threshold
78
+ const half = hysteresis / 2;
79
+ // Check ALL intermediate thresholds for dead zone suppression
80
+ if (rawIndex > prevIndex) {
81
+ // Crossing upward -- check thresholds from prevIndex+1 to rawIndex
82
+ for (let i = prevIndex + 1; i <= rawIndex; i++) {
83
+ const threshold = thresholds[i];
84
+ if (threshold !== undefined && value < threshold + half) {
85
+ // In dead zone -- settle at state just below this threshold
86
+ const settleIndex = i - 1;
87
+ return { state: stateAt(settleIndex), index: settleIndex, value, crossed: settleIndex !== prevIndex };
88
+ }
89
+ }
90
+ }
91
+ else {
92
+ // Crossing downward -- check thresholds from prevIndex down to rawIndex+1
93
+ for (let i = prevIndex; i > rawIndex; i--) {
94
+ const threshold = thresholds[i];
95
+ if (threshold !== undefined && value > threshold - half) {
96
+ // In dead zone -- settle at this state
97
+ return { state: stateAt(i), index: i, value, crossed: i !== prevIndex };
98
+ }
99
+ }
100
+ }
101
+ // Cleared all dead zones -- full transition
102
+ return { state, index: rawIndex, value, crossed: true };
103
+ }
104
+ /**
105
+ * Boundary evaluation namespace.
106
+ *
107
+ * Provides `evaluate()` for mapping a numeric value to a discrete state
108
+ * via binary search over boundary thresholds with optional hysteresis
109
+ * to prevent jitter at threshold edges.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * import { Boundary } from '@czap/core';
114
+ * import { Evaluate } from '@czap/quantizer';
115
+ *
116
+ * const boundary = Boundary.make({
117
+ * input: 'width', states: ['sm', 'lg'] as const,
118
+ * thresholds: [0, 768], hysteresis: 10,
119
+ * });
120
+ * const r1 = Evaluate.evaluate(boundary, 500);
121
+ * // r1.state => 'sm', r1.crossed => false
122
+ *
123
+ * const r2 = Evaluate.evaluate(boundary, 900, 'sm');
124
+ * // r2.state => 'lg', r2.crossed => true
125
+ * ```
126
+ */
127
+ export const Evaluate = { evaluate };
128
+ //# sourceMappingURL=evaluate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `@czap/quantizer` — **LiteShip** quantizer: **rigged** boundary evaluation,
3
+ * live state, animated transitions between bearings, and motion-tier gating on
4
+ * the working line.
5
+ *
6
+ * @module
7
+ */
8
+ export { evaluate, Evaluate } from './evaluate.js';
9
+ export type { EvaluateResult } from './evaluate.js';
10
+ export { Q } from './quantizer.js';
11
+ export type { OutputTarget, QuantizerOutputs, QuantizerConfig, LiveQuantizer, QuantizerBuilder } from './quantizer.js';
12
+ export { Transition } from './transition.js';
13
+ export type { TransitionConfig, TransitionMap, Transition as TransitionType } from './transition.js';
14
+ export { AnimatedQuantizer } from './animated-quantizer.js';
15
+ export type { AnimatedQuantizerShape } from './animated-quantizer.js';
16
+ export { TransitionConfigSchema, TransitionMapSchema, OutputTargetSchema, QuantizerOutputsSchema } from './schemas.js';
17
+ export type { MotionTier, SpringConfig, QuantizerFromOptions } from './quantizer.js';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EAAE,CAAC,EAAE,MAAM,gBAAgB,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAErG,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,YAAY,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEtE,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEvH,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `@czap/quantizer` — **LiteShip** quantizer: **rigged** boundary evaluation,
3
+ * live state, animated transitions between bearings, and motion-tier gating on
4
+ * the working line.
5
+ *
6
+ * @module
7
+ */
8
+ export { evaluate, Evaluate } from './evaluate.js';
9
+ export { Q } from './quantizer.js';
10
+ export { Transition } from './transition.js';
11
+ export { AnimatedQuantizer } from './animated-quantizer.js';
12
+ export { TransitionConfigSchema, TransitionMapSchema, OutputTargetSchema, QuantizerOutputsSchema } from './schemas.js';
13
+ // `MemoCache` and `TIER_TARGETS` ship via `@czap/quantizer/testing` —
14
+ // implementation primitives that power the public `Q.from()` builder
15
+ // internally but are not consumer-facing API.
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGnD,OAAO,EAAE,CAAC,EAAE,MAAM,gBAAgB,CAAC;AAGnC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAGvH,sEAAsE;AACtE,qEAAqE;AACrE,8CAA8C"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * MemoCache -- content-address memoization layer.
3
+ *
4
+ * Boundaries and quantizer configs are already content-addressed via FNV-1a.
5
+ * This cache ensures identical configs never recompute. Content-addressed keys
6
+ * mean the cache is auto-invalidating: change definition → hash changes → miss.
7
+ *
8
+ * @module
9
+ */
10
+ import type { ContentAddress } from '@czap/core';
11
+ interface MemoCacheShape<V> {
12
+ get(key: ContentAddress): V | undefined;
13
+ set(key: ContentAddress, value: V): void;
14
+ has(key: ContentAddress): boolean;
15
+ readonly size: number;
16
+ }
17
+ declare function _make<V>(): MemoCacheShape<V>;
18
+ /**
19
+ * Content-address memoization cache.
20
+ *
21
+ * Keys are {@link ContentAddress} values, so the cache is auto-invalidating:
22
+ * any change to an upstream definition produces a new hash and a guaranteed
23
+ * miss. Backed by an unbounded {@link Map}; callers are responsible for
24
+ * lifetime and eviction if needed.
25
+ */
26
+ export declare const MemoCache: {
27
+ /** Construct a fresh cache with value type `V`. */
28
+ make: typeof _make;
29
+ };
30
+ export declare namespace MemoCache {
31
+ /** Structural shape of a {@link MemoCache} with value type `V`. */
32
+ type Shape<V> = MemoCacheShape<V>;
33
+ }
34
+ export {};
35
+ //# sourceMappingURL=memo-cache.d.ts.map